| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of service; | 5 part of service; |
| 6 | 6 |
| 7 /// A [ServiceObject] is an object known to the VM service and is tied | 7 /// A [ServiceObject] is an object known to the VM service and is tied |
| 8 /// to an owning [Isolate]. | 8 /// to an owning [Isolate]. |
| 9 abstract class ServiceObject extends Observable { | 9 abstract class ServiceObject extends Observable { |
| 10 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { | 10 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 map = _parseJSON(eventMessage); | 256 map = _parseJSON(eventMessage); |
| 257 } catch (e, st) { | 257 } catch (e, st) { |
| 258 Logger.root.severe('Ignoring malformed event message: ${eventMessage}'); | 258 Logger.root.severe('Ignoring malformed event message: ${eventMessage}'); |
| 259 return; | 259 return; |
| 260 } | 260 } |
| 261 if (map['type'] != 'ServiceEvent') { | 261 if (map['type'] != 'ServiceEvent') { |
| 262 Logger.root.severe( | 262 Logger.root.severe( |
| 263 "Expected 'ServiceEvent' but found '${map['type']}'"); | 263 "Expected 'ServiceEvent' but found '${map['type']}'"); |
| 264 return; | 264 return; |
| 265 } | 265 } |
| 266 |
| 266 // Extract the owning isolate from the event itself. | 267 // Extract the owning isolate from the event itself. |
| 267 String owningIsolateId = map['isolate']['id']; | 268 String owningIsolateId = map['isolate']['id']; |
| 268 _getIsolate(owningIsolateId).then((owningIsolate) { | 269 _getIsolate(owningIsolateId).then((owningIsolate) { |
| 269 var event = new ServiceObject._fromMap(owningIsolate, map); | 270 var event = new ServiceObject._fromMap(owningIsolate, map); |
| 270 events.add(event); | 271 events.add(event); |
| 271 }); | 272 }); |
| 272 } | 273 } |
| 273 | 274 |
| 274 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); | 275 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); |
| 275 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); | 276 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); |
| (...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 742 void _update(ObservableMap map, bool mapIsRef) { | 743 void _update(ObservableMap map, bool mapIsRef) { |
| 743 mainPort = map['mainPort']; | 744 mainPort = map['mainPort']; |
| 744 name = map['name']; | 745 name = map['name']; |
| 745 vmName = map['name']; | 746 vmName = map['name']; |
| 746 if (mapIsRef) { | 747 if (mapIsRef) { |
| 747 return; | 748 return; |
| 748 } | 749 } |
| 749 _loaded = true; | 750 _loaded = true; |
| 750 loading = false; | 751 loading = false; |
| 751 | 752 |
| 753 reloadBreakpoints(); |
| 754 |
| 752 // Remap DebuggerEvent to ServiceEvent so that the observatory can | 755 // Remap DebuggerEvent to ServiceEvent so that the observatory can |
| 753 // work against 1.5 vms in the short term. | 756 // work against 1.5 vms in the short term. |
| 754 // | 757 // |
| 755 // TODO(turnidge): Remove this when no longer needed. | 758 // TODO(turnidge): Remove this when no longer needed. |
| 756 var pause = map['pauseEvent']; | 759 var pause = map['pauseEvent']; |
| 757 if (pause != null) { | 760 if (pause != null) { |
| 758 if (pause['type'] == 'DebuggerEvent') { | 761 if (pause['type'] == 'DebuggerEvent') { |
| 759 pause['type'] = 'ServiceEvent'; | 762 pause['type'] = 'ServiceEvent'; |
| 760 } | 763 } |
| 761 } | 764 } |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 878 // Number of children. | 881 // Number of children. |
| 879 var children = _trieData[_trieDataCursor++]; | 882 var children = _trieData[_trieDataCursor++]; |
| 880 // Recursively read child nodes. | 883 // Recursively read child nodes. |
| 881 for (var i = 0; i < children; i++) { | 884 for (var i = 0; i < children; i++) { |
| 882 var child = _readTrieNode(codeTable); | 885 var child = _readTrieNode(codeTable); |
| 883 node.children.add(child); | 886 node.children.add(child); |
| 884 node.summedChildCount += child.count; | 887 node.summedChildCount += child.count; |
| 885 } | 888 } |
| 886 return node; | 889 return node; |
| 887 } | 890 } |
| 891 |
| 892 ServiceMap breakpoints; |
| 893 |
| 894 void _removeBreakpoint(ServiceMap bpt) { |
| 895 var script = bpt['location']['script']; |
| 896 var tokenPos = bpt['location']['tokenPos']; |
| 897 assert(tokenPos != null); |
| 898 if (script.loaded) { |
| 899 var line = script.tokenToLine(tokenPos); |
| 900 assert(line != null); |
| 901 assert(script.lines[line - 1].bpt == bpt); |
| 902 script.lines[line - 1].bpt = null; |
| 903 } |
| 904 } |
| 905 |
| 906 void _addBreakpoint(ServiceMap bpt) { |
| 907 var script = bpt['location']['script']; |
| 908 var tokenPos = bpt['location']['tokenPos']; |
| 909 assert(tokenPos != null); |
| 910 if (script.loaded) { |
| 911 var line = script.tokenToLine(tokenPos); |
| 912 assert(line != null); |
| 913 assert(script.lines[line - 1].bpt == null); |
| 914 script.lines[line - 1].bpt = bpt; |
| 915 } else { |
| 916 // Load the script and then plop in the breakpoint. |
| 917 script.load().then((_) { |
| 918 _addBreakpoint(bpt); |
| 919 }); |
| 920 } |
| 921 } |
| 922 |
| 923 void _updateBreakpoints(ServiceMap newBreakpoints) { |
| 924 // Remove all of the old breakpoints from the Script lines. |
| 925 if (breakpoints != null) { |
| 926 for (var bpt in breakpoints['breakpoints']) { |
| 927 _removeBreakpoint(bpt); |
| 928 } |
| 929 } |
| 930 // Add all of the new breakpoints to the Script lines. |
| 931 for (var bpt in newBreakpoints['breakpoints']) { |
| 932 _addBreakpoint(bpt); |
| 933 } |
| 934 breakpoints = newBreakpoints; |
| 935 } |
| 936 |
| 937 Future<ServiceObject> _inProgressReloadBpts; |
| 938 |
| 939 Future reloadBreakpoints() { |
| 940 // TODO(turnidge): Can reusing the Future here ever cause us to |
| 941 // get stale breakpoints? |
| 942 if (_inProgressReloadBpts == null) { |
| 943 _inProgressReloadBpts = |
| 944 get('debug/breakpoints').then((newBpts) { |
| 945 _updateBreakpoints(newBpts); |
| 946 }).whenComplete(() { |
| 947 _inProgressReloadBpts = null; |
| 948 }); |
| 949 } |
| 950 return _inProgressReloadBpts; |
| 951 } |
| 952 |
| 953 Future<ServiceObject> setBreakpoint(Script script, int line) { |
| 954 return get(script.id + "/setBreakpoint?line=${line}").then((result) { |
| 955 if (result is DartError) { |
| 956 // Unable to set a breakpoint at desired line. |
| 957 script.lines[line - 1].possibleBpt = false; |
| 958 } |
| 959 return reloadBreakpoints(); |
| 960 }); |
| 961 } |
| 962 |
| 963 Future clearBreakpoint(ServiceMap bpt) { |
| 964 return get('${bpt.id}/clear').then((result) { |
| 965 if (result is DartError) { |
| 966 // TODO(turnidge): Handle this more gracefully. |
| 967 Logger.root.severe(result.message); |
| 968 } |
| 969 if (pauseEvent != null && |
| 970 pauseEvent.breakpoint != null && |
| 971 (pauseEvent.breakpoint['id'] == bpt['id'])) { |
| 972 return isolate.reload(); |
| 973 } else { |
| 974 return reloadBreakpoints(); |
| 975 } |
| 976 }); |
| 977 } |
| 978 |
| 979 Future pause() { |
| 980 return get("debug/pause").then((result) { |
| 981 if (result is DartError) { |
| 982 // TODO(turnidge): Handle this more gracefully. |
| 983 Logger.root.severe(result.message); |
| 984 } |
| 985 return isolate.reload(); |
| 986 }); |
| 987 } |
| 988 |
| 989 Future resume() { |
| 990 return get("debug/resume").then((result) { |
| 991 if (result is DartError) { |
| 992 // TODO(turnidge): Handle this more gracefully. |
| 993 Logger.root.severe(result.message); |
| 994 } |
| 995 return isolate.reload(); |
| 996 }); |
| 997 } |
| 998 |
| 999 Future stepInto() { |
| 1000 print('isolate.stepInto'); |
| 1001 return get("debug/resume?step=into").then((result) { |
| 1002 if (result is DartError) { |
| 1003 // TODO(turnidge): Handle this more gracefully. |
| 1004 Logger.root.severe(result.message); |
| 1005 } |
| 1006 return isolate.reload(); |
| 1007 }); |
| 1008 } |
| 1009 |
| 1010 Future stepOver() { |
| 1011 return get("debug/resume?step=over").then((result) { |
| 1012 if (result is DartError) { |
| 1013 // TODO(turnidge): Handle this more gracefully. |
| 1014 Logger.root.severe(result.message); |
| 1015 } |
| 1016 return isolate.reload(); |
| 1017 }); |
| 1018 } |
| 1019 |
| 1020 Future stepOut() { |
| 1021 return get("debug/resume?step=out").then((result) { |
| 1022 if (result is DartError) { |
| 1023 // TODO(turnidge): Handle this more gracefully. |
| 1024 Logger.root.severe(result.message); |
| 1025 } |
| 1026 return isolate.reload(); |
| 1027 }); |
| 1028 } |
| 888 } | 1029 } |
| 889 | 1030 |
| 890 /// A [ServiceObject] which implements [ObservableMap]. | 1031 /// A [ServiceObject] which implements [ObservableMap]. |
| 891 class ServiceMap extends ServiceObject implements ObservableMap { | 1032 class ServiceMap extends ServiceObject implements ObservableMap { |
| 892 final ObservableMap _map = new ObservableMap(); | 1033 final ObservableMap _map = new ObservableMap(); |
| 893 static String objectIdRingPrefix = 'objects/'; | 1034 static String objectIdRingPrefix = 'objects/'; |
| 894 | 1035 |
| 895 bool get canCache { | 1036 bool get canCache { |
| 896 return (_serviceType == 'Class' || | 1037 return (_serviceType == 'Class' || |
| 897 _serviceType == 'Function' || | 1038 _serviceType == 'Function' || |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1211 } | 1352 } |
| 1212 children.add(cls); | 1353 children.add(cls); |
| 1213 } | 1354 } |
| 1214 | 1355 |
| 1215 Future<ServiceObject> get(String command) { | 1356 Future<ServiceObject> get(String command) { |
| 1216 return isolate.get(id + "/$command"); | 1357 return isolate.get(id + "/$command"); |
| 1217 } | 1358 } |
| 1218 } | 1359 } |
| 1219 | 1360 |
| 1220 class ScriptLine extends Observable { | 1361 class ScriptLine extends Observable { |
| 1362 final Script script; |
| 1221 final int line; | 1363 final int line; |
| 1222 final String text; | 1364 final String text; |
| 1223 @observable int hits; | 1365 @observable int hits; |
| 1224 ScriptLine(this.line, this.text); | 1366 @observable ServiceMap bpt; |
| 1367 @observable bool possibleBpt = true; |
| 1368 |
| 1369 static bool _isTrivialToken(String token) { |
| 1370 if (token == 'else') { |
| 1371 return true; |
| 1372 } |
| 1373 for (var c in token.split('')) { |
| 1374 switch (c) { |
| 1375 case '{': |
| 1376 case '}': |
| 1377 case '(': |
| 1378 case ')': |
| 1379 case ';': |
| 1380 break; |
| 1381 default: |
| 1382 return false; |
| 1383 } |
| 1384 } |
| 1385 return true; |
| 1386 } |
| 1387 |
| 1388 static bool _isTrivialLine(String text) { |
| 1389 var wsTokens = text.split(new RegExp(r"(\s)+")); |
| 1390 for (var wsToken in wsTokens) { |
| 1391 var tokens = wsToken.split(new RegExp(r"(\b)")); |
| 1392 for (var token in tokens) { |
| 1393 if (!_isTrivialToken(token)) { |
| 1394 return false; |
| 1395 } |
| 1396 } |
| 1397 } |
| 1398 return true; |
| 1399 } |
| 1400 |
| 1401 ScriptLine(this.script, this.line, this.text) { |
| 1402 possibleBpt = !_isTrivialLine(text); |
| 1403 |
| 1404 // TODO(turnidge): This is not so efficient. Consider improving. |
| 1405 for (var bpt in this.script.isolate.breakpoints['breakpoints']) { |
| 1406 var bptScript = bpt['location']['script']; |
| 1407 var bptTokenPos = bpt['location']['tokenPos']; |
| 1408 if (bptScript == this.script && |
| 1409 bptScript.tokenToLine(bptTokenPos) == line) { |
| 1410 this.bpt = bpt; |
| 1411 } |
| 1412 } |
| 1413 } |
| 1225 } | 1414 } |
| 1226 | 1415 |
| 1227 class Script extends ServiceObject with Coverage { | 1416 class Script extends ServiceObject with Coverage { |
| 1228 final lines = new ObservableList<ScriptLine>(); | 1417 final lines = new ObservableList<ScriptLine>(); |
| 1229 final _hits = new Map<int, int>(); | 1418 final _hits = new Map<int, int>(); |
| 1230 @observable String kind; | 1419 @observable String kind; |
| 1231 @observable int firstTokenPos; | 1420 @observable int firstTokenPos; |
| 1232 @observable int lastTokenPos; | 1421 @observable int lastTokenPos; |
| 1233 bool get canCache => true; | 1422 bool get canCache => true; |
| 1234 bool get immutable => true; | 1423 bool get immutable => true; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1262 } | 1451 } |
| 1263 | 1452 |
| 1264 void _parseTokenPosTable(List<List<int>> table) { | 1453 void _parseTokenPosTable(List<List<int>> table) { |
| 1265 if (table == null) { | 1454 if (table == null) { |
| 1266 return; | 1455 return; |
| 1267 } | 1456 } |
| 1268 _tokenToLine.clear(); | 1457 _tokenToLine.clear(); |
| 1269 _tokenToCol.clear(); | 1458 _tokenToCol.clear(); |
| 1270 firstTokenPos = null; | 1459 firstTokenPos = null; |
| 1271 lastTokenPos = null; | 1460 lastTokenPos = null; |
| 1461 var lineSet = new Set(); |
| 1462 |
| 1272 for (var line in table) { | 1463 for (var line in table) { |
| 1273 // Each entry begins with a line number... | 1464 // Each entry begins with a line number... |
| 1274 var lineNumber = line[0]; | 1465 var lineNumber = line[0]; |
| 1466 lineSet.add(lineNumber); |
| 1275 for (var pos = 1; pos < line.length; pos += 2) { | 1467 for (var pos = 1; pos < line.length; pos += 2) { |
| 1276 // ...and is followed by (token offset, col number) pairs. | 1468 // ...and is followed by (token offset, col number) pairs. |
| 1277 var tokenOffset = line[pos]; | 1469 var tokenOffset = line[pos]; |
| 1278 var colNumber = line[pos+1]; | 1470 var colNumber = line[pos+1]; |
| 1279 if (firstTokenPos == null) { | 1471 if (firstTokenPos == null) { |
| 1280 // Mark first token position. | 1472 // Mark first token position. |
| 1281 firstTokenPos = tokenOffset; | 1473 firstTokenPos = tokenOffset; |
| 1282 lastTokenPos = tokenOffset; | 1474 lastTokenPos = tokenOffset; |
| 1283 } else { | 1475 } else { |
| 1284 // Keep track of max and min token positions. | 1476 // Keep track of max and min token positions. |
| 1285 firstTokenPos = (firstTokenPos <= tokenOffset) ? | 1477 firstTokenPos = (firstTokenPos <= tokenOffset) ? |
| 1286 firstTokenPos : tokenOffset; | 1478 firstTokenPos : tokenOffset; |
| 1287 lastTokenPos = (lastTokenPos >= tokenOffset) ? | 1479 lastTokenPos = (lastTokenPos >= tokenOffset) ? |
| 1288 lastTokenPos : tokenOffset; | 1480 lastTokenPos : tokenOffset; |
| 1289 } | 1481 } |
| 1290 _tokenToLine[tokenOffset] = lineNumber; | 1482 _tokenToLine[tokenOffset] = lineNumber; |
| 1291 _tokenToCol[tokenOffset] = colNumber; | 1483 _tokenToCol[tokenOffset] = colNumber; |
| 1292 } | 1484 } |
| 1293 } | 1485 } |
| 1486 |
| 1487 for (var line in lines) { |
| 1488 // Remove possible breakpoints on lines with no tokens. |
| 1489 if (!lineSet.contains(line.line)) { |
| 1490 line.possibleBpt = false; |
| 1491 } |
| 1492 } |
| 1294 } | 1493 } |
| 1295 | 1494 |
| 1296 void _processHits(List scriptHits) { | 1495 void _processHits(List scriptHits) { |
| 1297 // Update hits table. | 1496 // Update hits table. |
| 1298 for (var i = 0; i < scriptHits.length; i += 2) { | 1497 for (var i = 0; i < scriptHits.length; i += 2) { |
| 1299 var line = scriptHits[i]; | 1498 var line = scriptHits[i]; |
| 1300 var hit = scriptHits[i + 1]; // hit status. | 1499 var hit = scriptHits[i + 1]; // hit status. |
| 1301 assert(line >= 1); // Lines start at 1. | 1500 assert(line >= 1); // Lines start at 1. |
| 1302 var oldHits = _hits[line]; | 1501 var oldHits = _hits[line]; |
| 1303 if (oldHits != null) { | 1502 if (oldHits != null) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1316 } | 1515 } |
| 1317 var sourceLines = source.split('\n'); | 1516 var sourceLines = source.split('\n'); |
| 1318 if (sourceLines.length == 0) { | 1517 if (sourceLines.length == 0) { |
| 1319 return; | 1518 return; |
| 1320 } | 1519 } |
| 1321 // We have the source to the script. This is now loaded. | 1520 // We have the source to the script. This is now loaded. |
| 1322 _loaded = true; | 1521 _loaded = true; |
| 1323 lines.clear(); | 1522 lines.clear(); |
| 1324 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); | 1523 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); |
| 1325 for (var i = 0; i < sourceLines.length; i++) { | 1524 for (var i = 0; i < sourceLines.length; i++) { |
| 1326 lines.add(new ScriptLine(i + 1, sourceLines[i])); | 1525 lines.add(new ScriptLine(this, i + 1, sourceLines[i])); |
| 1327 } | 1526 } |
| 1328 _applyHitsToLines(); | 1527 _applyHitsToLines(); |
| 1329 } | 1528 } |
| 1330 | 1529 |
| 1331 void _applyHitsToLines() { | 1530 void _applyHitsToLines() { |
| 1332 if (lines.length == 0) { | |
| 1333 return; | |
| 1334 } | |
| 1335 for (var line in lines) { | 1531 for (var line in lines) { |
| 1336 var hits = _hits[line.line]; | 1532 var hits = _hits[line.line]; |
| 1337 line.hits = hits; | 1533 line.hits = hits; |
| 1338 } | 1534 } |
| 1339 } | 1535 } |
| 1340 } | 1536 } |
| 1341 | 1537 |
| 1342 class CodeTick { | 1538 class CodeTick { |
| 1343 final int address; | 1539 final int address; |
| 1344 final int exclusiveTicks; | 1540 final int exclusiveTicks; |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1916 var v = list[i]; | 2112 var v = list[i]; |
| 1917 if ((v is ObservableMap) && _isServiceMap(v)) { | 2113 if ((v is ObservableMap) && _isServiceMap(v)) { |
| 1918 list[i] = owner.getFromMap(v); | 2114 list[i] = owner.getFromMap(v); |
| 1919 } else if (v is ObservableList) { | 2115 } else if (v is ObservableList) { |
| 1920 _upgradeObservableList(v, owner); | 2116 _upgradeObservableList(v, owner); |
| 1921 } else if (v is ObservableMap) { | 2117 } else if (v is ObservableMap) { |
| 1922 _upgradeObservableMap(v, owner); | 2118 _upgradeObservableMap(v, owner); |
| 1923 } | 2119 } |
| 1924 } | 2120 } |
| 1925 } | 2121 } |
| OLD | NEW |