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 |