| 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 if (_inProgressReloadBpts == null) { |
| 941 _inProgressReloadBpts = |
| 942 get('debug/breakpoints').then((newBpts) { |
| 943 _updateBreakpoints(newBpts); |
| 944 }).whenComplete(() { |
| 945 _inProgressReloadBpts = null; |
| 946 }); |
| 947 } |
| 948 return _inProgressReloadBpts; |
| 949 } |
| 950 |
| 951 Future<ServiceObject> setBreakpoint(Script script, int line) { |
| 952 return get(script.id + "/setBreakpoint?line=${line}").then((result) { |
| 953 if (result is DartError) { |
| 954 // Unable to set a breakpoint at desired line. |
| 955 script.lines[line - 1].possibleBpt = false; |
| 956 } |
| 957 return reloadBreakpoints(); |
| 958 }); |
| 959 } |
| 960 |
| 961 Future clearBreakpoint(ServiceMap bpt) { |
| 962 return get('${bpt.id}/clear').then((result) { |
| 963 if (result is DartError) { |
| 964 // TODO(turnidge): Handle this more gracefully. |
| 965 Logger.root.severe(result.message); |
| 966 } |
| 967 if (pauseEvent != null && |
| 968 pauseEvent.breakpoint != null && |
| 969 (pauseEvent.breakpoint['id'] == bpt['id'])) { |
| 970 return isolate.reload(); |
| 971 } else { |
| 972 return reloadBreakpoints(); |
| 973 } |
| 974 }); |
| 975 } |
| 976 |
| 977 Future pause() { |
| 978 return get("debug/pause").then((result) { |
| 979 if (result is DartError) { |
| 980 // TODO(turnidge): Handle this more gracefully. |
| 981 Logger.root.severe(result.message); |
| 982 } |
| 983 return isolate.reload(); |
| 984 }); |
| 985 } |
| 986 |
| 987 Future resume() { |
| 988 return get("debug/resume").then((result) { |
| 989 if (result is DartError) { |
| 990 // TODO(turnidge): Handle this more gracefully. |
| 991 Logger.root.severe(result.message); |
| 992 } |
| 993 return isolate.reload(); |
| 994 }); |
| 995 } |
| 996 |
| 997 Future stepInto() { |
| 998 print('isolate.stepInto'); |
| 999 return get("debug/resume?step=into").then((result) { |
| 1000 if (result is DartError) { |
| 1001 // TODO(turnidge): Handle this more gracefully. |
| 1002 Logger.root.severe(result.message); |
| 1003 } |
| 1004 return isolate.reload(); |
| 1005 }); |
| 1006 } |
| 1007 |
| 1008 Future stepOver() { |
| 1009 return get("debug/resume?step=over").then((result) { |
| 1010 if (result is DartError) { |
| 1011 // TODO(turnidge): Handle this more gracefully. |
| 1012 Logger.root.severe(result.message); |
| 1013 } |
| 1014 return isolate.reload(); |
| 1015 }); |
| 1016 } |
| 1017 |
| 1018 Future stepOut() { |
| 1019 return get("debug/resume?step=out").then((result) { |
| 1020 if (result is DartError) { |
| 1021 // TODO(turnidge): Handle this more gracefully. |
| 1022 Logger.root.severe(result.message); |
| 1023 } |
| 1024 return isolate.reload(); |
| 1025 }); |
| 1026 } |
| 888 } | 1027 } |
| 889 | 1028 |
| 890 /// A [ServiceObject] which implements [ObservableMap]. | 1029 /// A [ServiceObject] which implements [ObservableMap]. |
| 891 class ServiceMap extends ServiceObject implements ObservableMap { | 1030 class ServiceMap extends ServiceObject implements ObservableMap { |
| 892 final ObservableMap _map = new ObservableMap(); | 1031 final ObservableMap _map = new ObservableMap(); |
| 893 static String objectIdRingPrefix = 'objects/'; | 1032 static String objectIdRingPrefix = 'objects/'; |
| 894 | 1033 |
| 895 bool get canCache { | 1034 bool get canCache { |
| 896 return (_serviceType == 'Class' || | 1035 return (_serviceType == 'Class' || |
| 897 _serviceType == 'Function' || | 1036 _serviceType == 'Function' || |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1211 } | 1350 } |
| 1212 children.add(cls); | 1351 children.add(cls); |
| 1213 } | 1352 } |
| 1214 | 1353 |
| 1215 Future<ServiceObject> get(String command) { | 1354 Future<ServiceObject> get(String command) { |
| 1216 return isolate.get(id + "/$command"); | 1355 return isolate.get(id + "/$command"); |
| 1217 } | 1356 } |
| 1218 } | 1357 } |
| 1219 | 1358 |
| 1220 class ScriptLine extends Observable { | 1359 class ScriptLine extends Observable { |
| 1360 final Script script; |
| 1221 final int line; | 1361 final int line; |
| 1222 final String text; | 1362 final String text; |
| 1223 @observable int hits; | 1363 @observable int hits; |
| 1224 ScriptLine(this.line, this.text); | 1364 @observable ServiceMap bpt; |
| 1365 @observable bool possibleBpt = true; |
| 1366 |
| 1367 static bool _isTrivialToken(String token) { |
| 1368 if (token == 'else') { |
| 1369 return true; |
| 1370 } |
| 1371 for (var c in token.split('')) { |
| 1372 switch (c) { |
| 1373 case '{': |
| 1374 case '}': |
| 1375 case '(': |
| 1376 case ')': |
| 1377 case ';': |
| 1378 break; |
| 1379 default: |
| 1380 return false; |
| 1381 } |
| 1382 } |
| 1383 return true; |
| 1384 } |
| 1385 |
| 1386 static bool _isTrivialLine(String text) { |
| 1387 var wsTokens = text.split(new RegExp(r"(\s)+")); |
| 1388 for (var wsToken in wsTokens) { |
| 1389 var tokens = wsToken.split(new RegExp(r"(\b)")); |
| 1390 for (var token in tokens) { |
| 1391 if (!_isTrivialToken(token)) { |
| 1392 return false; |
| 1393 } |
| 1394 } |
| 1395 } |
| 1396 return true; |
| 1397 } |
| 1398 |
| 1399 ScriptLine(this.script, this.line, this.text) { |
| 1400 possibleBpt = !_isTrivialLine(text); |
| 1401 |
| 1402 // TODO(turnidge): This is not so efficient. Consider improving. |
| 1403 for (var bpt in this.script.isolate.breakpoints['breakpoints']) { |
| 1404 var bptScript = bpt['location']['script']; |
| 1405 var bptTokenPos = bpt['location']['tokenPos']; |
| 1406 if (bptScript == this.script && |
| 1407 bptScript.tokenToLine(bptTokenPos) == line) { |
| 1408 this.bpt = bpt; |
| 1409 } |
| 1410 } |
| 1411 } |
| 1225 } | 1412 } |
| 1226 | 1413 |
| 1227 class Script extends ServiceObject with Coverage { | 1414 class Script extends ServiceObject with Coverage { |
| 1228 final lines = new ObservableList<ScriptLine>(); | 1415 final lines = new ObservableList<ScriptLine>(); |
| 1229 final _hits = new Map<int, int>(); | 1416 final _hits = new Map<int, int>(); |
| 1230 @observable String kind; | 1417 @observable String kind; |
| 1231 @observable int firstTokenPos; | 1418 @observable int firstTokenPos; |
| 1232 @observable int lastTokenPos; | 1419 @observable int lastTokenPos; |
| 1233 bool get canCache => true; | 1420 bool get canCache => true; |
| 1234 bool get immutable => true; | 1421 bool get immutable => true; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1262 } | 1449 } |
| 1263 | 1450 |
| 1264 void _parseTokenPosTable(List<List<int>> table) { | 1451 void _parseTokenPosTable(List<List<int>> table) { |
| 1265 if (table == null) { | 1452 if (table == null) { |
| 1266 return; | 1453 return; |
| 1267 } | 1454 } |
| 1268 _tokenToLine.clear(); | 1455 _tokenToLine.clear(); |
| 1269 _tokenToCol.clear(); | 1456 _tokenToCol.clear(); |
| 1270 firstTokenPos = null; | 1457 firstTokenPos = null; |
| 1271 lastTokenPos = null; | 1458 lastTokenPos = null; |
| 1459 var lineSet = new Set(); |
| 1460 |
| 1272 for (var line in table) { | 1461 for (var line in table) { |
| 1273 // Each entry begins with a line number... | 1462 // Each entry begins with a line number... |
| 1274 var lineNumber = line[0]; | 1463 var lineNumber = line[0]; |
| 1464 lineSet.add(lineNumber); |
| 1275 for (var pos = 1; pos < line.length; pos += 2) { | 1465 for (var pos = 1; pos < line.length; pos += 2) { |
| 1276 // ...and is followed by (token offset, col number) pairs. | 1466 // ...and is followed by (token offset, col number) pairs. |
| 1277 var tokenOffset = line[pos]; | 1467 var tokenOffset = line[pos]; |
| 1278 var colNumber = line[pos+1]; | 1468 var colNumber = line[pos+1]; |
| 1279 if (firstTokenPos == null) { | 1469 if (firstTokenPos == null) { |
| 1280 // Mark first token position. | 1470 // Mark first token position. |
| 1281 firstTokenPos = tokenOffset; | 1471 firstTokenPos = tokenOffset; |
| 1282 lastTokenPos = tokenOffset; | 1472 lastTokenPos = tokenOffset; |
| 1283 } else { | 1473 } else { |
| 1284 // Keep track of max and min token positions. | 1474 // Keep track of max and min token positions. |
| 1285 firstTokenPos = (firstTokenPos <= tokenOffset) ? | 1475 firstTokenPos = (firstTokenPos <= tokenOffset) ? |
| 1286 firstTokenPos : tokenOffset; | 1476 firstTokenPos : tokenOffset; |
| 1287 lastTokenPos = (lastTokenPos >= tokenOffset) ? | 1477 lastTokenPos = (lastTokenPos >= tokenOffset) ? |
| 1288 lastTokenPos : tokenOffset; | 1478 lastTokenPos : tokenOffset; |
| 1289 } | 1479 } |
| 1290 _tokenToLine[tokenOffset] = lineNumber; | 1480 _tokenToLine[tokenOffset] = lineNumber; |
| 1291 _tokenToCol[tokenOffset] = colNumber; | 1481 _tokenToCol[tokenOffset] = colNumber; |
| 1292 } | 1482 } |
| 1293 } | 1483 } |
| 1484 |
| 1485 for (var line in lines) { |
| 1486 // Remove possible breakpoints on lines with no tokens. |
| 1487 if (!lineSet.contains(line.line)) { |
| 1488 line.possibleBpt = false; |
| 1489 } |
| 1490 } |
| 1294 } | 1491 } |
| 1295 | 1492 |
| 1296 void _processHits(List scriptHits) { | 1493 void _processHits(List scriptHits) { |
| 1297 // Update hits table. | 1494 // Update hits table. |
| 1298 for (var i = 0; i < scriptHits.length; i += 2) { | 1495 for (var i = 0; i < scriptHits.length; i += 2) { |
| 1299 var line = scriptHits[i]; | 1496 var line = scriptHits[i]; |
| 1300 var hit = scriptHits[i + 1]; // hit status. | 1497 var hit = scriptHits[i + 1]; // hit status. |
| 1301 assert(line >= 1); // Lines start at 1. | 1498 assert(line >= 1); // Lines start at 1. |
| 1302 var oldHits = _hits[line]; | 1499 var oldHits = _hits[line]; |
| 1303 if (oldHits != null) { | 1500 if (oldHits != null) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1316 } | 1513 } |
| 1317 var sourceLines = source.split('\n'); | 1514 var sourceLines = source.split('\n'); |
| 1318 if (sourceLines.length == 0) { | 1515 if (sourceLines.length == 0) { |
| 1319 return; | 1516 return; |
| 1320 } | 1517 } |
| 1321 // We have the source to the script. This is now loaded. | 1518 // We have the source to the script. This is now loaded. |
| 1322 _loaded = true; | 1519 _loaded = true; |
| 1323 lines.clear(); | 1520 lines.clear(); |
| 1324 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); | 1521 Logger.root.info('Adding ${sourceLines.length} source lines for ${_url}'); |
| 1325 for (var i = 0; i < sourceLines.length; i++) { | 1522 for (var i = 0; i < sourceLines.length; i++) { |
| 1326 lines.add(new ScriptLine(i + 1, sourceLines[i])); | 1523 lines.add(new ScriptLine(this, i + 1, sourceLines[i])); |
| 1327 } | 1524 } |
| 1328 _applyHitsToLines(); | 1525 _applyHitsToLines(); |
| 1329 } | 1526 } |
| 1330 | 1527 |
| 1331 void _applyHitsToLines() { | 1528 void _applyHitsToLines() { |
| 1332 if (lines.length == 0) { | |
| 1333 return; | |
| 1334 } | |
| 1335 for (var line in lines) { | 1529 for (var line in lines) { |
| 1336 var hits = _hits[line.line]; | 1530 var hits = _hits[line.line]; |
| 1337 line.hits = hits; | 1531 line.hits = hits; |
| 1338 } | 1532 } |
| 1339 } | 1533 } |
| 1340 } | 1534 } |
| 1341 | 1535 |
| 1342 class CodeTick { | 1536 class CodeTick { |
| 1343 final int address; | 1537 final int address; |
| 1344 final int exclusiveTicks; | 1538 final int exclusiveTicks; |
| (...skipping 571 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1916 var v = list[i]; | 2110 var v = list[i]; |
| 1917 if ((v is ObservableMap) && _isServiceMap(v)) { | 2111 if ((v is ObservableMap) && _isServiceMap(v)) { |
| 1918 list[i] = owner.getFromMap(v); | 2112 list[i] = owner.getFromMap(v); |
| 1919 } else if (v is ObservableList) { | 2113 } else if (v is ObservableList) { |
| 1920 _upgradeObservableList(v, owner); | 2114 _upgradeObservableList(v, owner); |
| 1921 } else if (v is ObservableMap) { | 2115 } else if (v is ObservableMap) { |
| 1922 _upgradeObservableMap(v, owner); | 2116 _upgradeObservableMap(v, owner); |
| 1923 } | 2117 } |
| 1924 } | 2118 } |
| 1925 } | 2119 } |
| OLD | NEW |