Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library script_inset_element; | 5 library script_inset_element; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'dart:math'; | |
| 9 import 'observatory_element.dart'; | 10 import 'observatory_element.dart'; |
| 10 import 'service_ref.dart'; | 11 import 'service_ref.dart'; |
| 11 import 'package:observatory/service.dart'; | 12 import 'package:observatory/service.dart'; |
| 12 import 'package:observatory/utils.dart'; | 13 import 'package:observatory/utils.dart'; |
| 13 import 'package:polymer/polymer.dart'; | 14 import 'package:polymer/polymer.dart'; |
| 14 import 'package:logging/logging.dart'; | 15 import 'package:logging/logging.dart'; |
| 15 | 16 |
| 16 const nbsp = "\u00A0"; | 17 const nbsp = "\u00A0"; |
| 17 | 18 |
| 18 void addInfoBox(Element content, Function infoBoxGenerator) { | 19 void addInfoBox(Element content, Function infoBoxGenerator) { |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 237 details.append(row('Call of "${callSite.name}" did not execute')); | 238 details.append(row('Call of "${callSite.name}" did not execute')); |
| 238 } else { | 239 } else { |
| 239 var r = row(); | 240 var r = row(); |
| 240 r.append(cell("Container")); | 241 r.append(cell("Container")); |
| 241 r.append(cell("Count")); | 242 r.append(cell("Count")); |
| 242 r.append(cell("Target")); | 243 r.append(cell("Target")); |
| 243 details.append(r); | 244 details.append(r); |
| 244 | 245 |
| 245 for (var entry in callSite.entries) { | 246 for (var entry in callSite.entries) { |
| 246 var r = row(); | 247 var r = row(); |
| 247 r.append(cell(serviceRef(entry.receiverContainer))); | 248 r.append(cell(serviceRef(entry.receiver))); |
| 248 r.append(cell(entry.count.toString())); | 249 r.append(cell(entry.count.toString())); |
| 249 r.append(cell(serviceRef(entry.target))); | 250 r.append(cell(serviceRef(entry.target))); |
| 250 details.append(r); | 251 details.append(r); |
| 251 } | 252 } |
| 252 } | 253 } |
| 253 return details; | 254 return details; |
| 254 }); | 255 }); |
| 255 } | 256 } |
| 256 } | 257 } |
| 257 | 258 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 367 @published int endPos; | 368 @published int endPos; |
| 368 | 369 |
| 369 /// Set the height to make the script inset scroll. Otherwise it | 370 /// Set the height to make the script inset scroll. Otherwise it |
| 370 /// will show from startPos to endPos. | 371 /// will show from startPos to endPos. |
| 371 @published String height = null; | 372 @published String height = null; |
| 372 | 373 |
| 373 @published int currentPos; | 374 @published int currentPos; |
| 374 @published bool inDebuggerContext = false; | 375 @published bool inDebuggerContext = false; |
| 375 @published ObservableList variables; | 376 @published ObservableList variables; |
| 376 | 377 |
| 378 @published Element scroller; | |
| 379 RefreshButtonElement _refreshButton; | |
| 380 | |
| 377 int _currentLine; | 381 int _currentLine; |
| 378 int _currentCol; | 382 int _currentCol; |
| 379 int _startLine; | 383 int _startLine; |
| 380 int _endLine; | 384 int _endLine; |
| 381 | 385 |
| 386 Map<int, List<ServiceMap>> _rangeMap = {}; | |
| 387 Set _callSites = new Set<CallSite>(); | |
| 388 | |
| 382 var annotations = []; | 389 var annotations = []; |
| 383 var annotationsCursor; | 390 var annotationsCursor; |
| 384 | 391 |
| 385 StreamSubscription _scriptChangeSubscription; | 392 StreamSubscription _scriptChangeSubscription; |
| 386 Future<StreamSubscription> _debugSubscriptionFuture; | 393 Future<StreamSubscription> _debugSubscriptionFuture; |
| 394 StreamSubscription _scrollSubscription; | |
| 387 | 395 |
| 388 bool hasLoadedLibraryDeclarations = false; | 396 bool hasLoadedLibraryDeclarations = false; |
| 389 | 397 |
| 390 String makeLineId(int line) { | 398 String makeLineId(int line) { |
| 391 return 'line-$line'; | 399 return 'line-$line'; |
| 392 } | 400 } |
| 393 | 401 |
| 394 void _scrollToCurrentPos() { | 402 void _scrollToCurrentPos() { |
| 395 var line = shadowRoot.getElementById(makeLineId(_currentLine)); | 403 var line = shadowRoot.getElementById(makeLineId(_currentLine)); |
| 396 if (line != null) { | 404 if (line != null) { |
| 397 line.scrollIntoView(); | 405 line.scrollIntoView(); |
| 398 } | 406 } |
| 399 } | 407 } |
| 400 | 408 |
| 401 void attached() { | 409 void attached() { |
| 402 super.attached(); | 410 super.attached(); |
| 403 _debugSubscriptionFuture = | 411 _debugSubscriptionFuture = |
| 404 app.vm.listenEventStream(VM.kDebugStream, _onDebugEvent); | 412 app.vm.listenEventStream(VM.kDebugStream, _onDebugEvent); |
| 413 if (scroller != null) { | |
| 414 _scrollSubscription = scroller.onScroll.listen(_onScroll); | |
| 415 } else { | |
| 416 _scrollSubscription = window.onScroll.listen(_onScroll); | |
| 417 } | |
| 405 } | 418 } |
| 406 | 419 |
| 407 void detached() { | 420 void detached() { |
| 408 cancelFutureSubscription(_debugSubscriptionFuture); | 421 cancelFutureSubscription(_debugSubscriptionFuture); |
| 409 _debugSubscriptionFuture = null; | 422 _debugSubscriptionFuture = null; |
| 423 if (_scrollSubscription != null) { | |
| 424 _scrollSubscription.cancel(); | |
| 425 _scrollSubscription = null; | |
| 426 } | |
| 410 if (_scriptChangeSubscription != null) { | 427 if (_scriptChangeSubscription != null) { |
| 411 // Don't leak. If only Dart and Javascript exposed weak references... | 428 // Don't leak. If only Dart and Javascript exposed weak references... |
| 412 _scriptChangeSubscription.cancel(); | 429 _scriptChangeSubscription.cancel(); |
| 413 _scriptChangeSubscription = null; | 430 _scriptChangeSubscription = null; |
| 414 } | 431 } |
| 415 super.detached(); | 432 super.detached(); |
| 416 } | 433 } |
| 417 | 434 |
| 435 void _onScroll(event) { | |
| 436 var currentTop = _refreshButton.style.top; | |
| 437 var newTop = _refreshButtonTop(); | |
| 438 if (currentTop != newTop) { | |
| 439 _refreshButton.style.top = '${newTop}px'; | |
| 440 } | |
| 441 } | |
| 442 | |
| 418 void _onDebugEvent(event) { | 443 void _onDebugEvent(event) { |
| 419 if (script == null) { | 444 if (script == null) { |
| 420 return; | 445 return; |
| 421 } | 446 } |
| 422 switch (event.kind) { | 447 switch (event.kind) { |
| 423 case ServiceEvent.kBreakpointAdded: | 448 case ServiceEvent.kBreakpointAdded: |
| 424 case ServiceEvent.kBreakpointResolved: | 449 case ServiceEvent.kBreakpointResolved: |
| 425 case ServiceEvent.kBreakpointRemoved: | 450 case ServiceEvent.kBreakpointRemoved: |
| 426 var loc = event.breakpoint.location; | 451 var loc = event.breakpoint.location; |
| 427 if (loc.script == script) { | 452 if (loc.script == script) { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 479 Element hitsNotExecuted(Element element) { | 504 Element hitsNotExecuted(Element element) { |
| 480 element.classes.add('hitsNotExecuted'); | 505 element.classes.add('hitsNotExecuted'); |
| 481 element.title = "Line did not execute"; | 506 element.title = "Line did not execute"; |
| 482 return element; | 507 return element; |
| 483 } | 508 } |
| 484 Element hitsExecuted(Element element) { | 509 Element hitsExecuted(Element element) { |
| 485 element.classes.add('hitsExecuted'); | 510 element.classes.add('hitsExecuted'); |
| 486 element.title = "Line did execute"; | 511 element.title = "Line did execute"; |
| 487 return element; | 512 return element; |
| 488 } | 513 } |
| 514 Element hitsCompiled(Element element) { | |
| 515 element.classes.add('hitsCompiled'); | |
| 516 element.title = "Line in compiled function"; | |
| 517 return element; | |
| 518 } | |
| 519 Element hitsNotCompiled(Element element) { | |
| 520 element.classes.add('hitsNotCompiled'); | |
| 521 element.title = "Line in uncompiled function"; | |
| 522 return element; | |
| 523 } | |
| 489 | 524 |
| 490 Element container; | 525 Element container; |
| 491 | 526 |
| 527 Future _refresh() async { | |
| 528 await update(); | |
| 529 } | |
| 530 | |
| 531 // Build _rangeMap and _callSites from a source report. | |
| 532 Future _refreshSourceReport() async { | |
| 533 var sourceReport = await script.isolate.getSourceReport( | |
| 534 [Isolate.kCallSitesReport], script, startPos, endPos); | |
| 535 _rangeMap.clear(); | |
| 536 _callSites.clear(); | |
| 537 for (var range in sourceReport['ranges']) { | |
| 538 int startLine = script.tokenToLine(range['startPos']); | |
| 539 int endLine = script.tokenToLine(range['endPos']); | |
| 540 for (var line = startLine; line <= endLine; line++) { | |
| 541 var rangeList = _rangeMap[line]; | |
| 542 if (rangeList == null) { | |
| 543 _rangeMap[line] = [range]; | |
| 544 } else { | |
| 545 rangeList.add(range); | |
| 546 } | |
| 547 } | |
| 548 var rangeCallSites = range['callSites']; | |
| 549 if (rangeCallSites != null) { | |
| 550 for (var callSiteMap in rangeCallSites) { | |
| 551 _callSites.add(new CallSite.fromMap(callSiteMap, script)); | |
| 552 } | |
| 553 } | |
| 554 } | |
| 555 } | |
| 556 | |
| 492 Task _updateTask; | 557 Task _updateTask; |
| 493 void update() { | 558 Future update() async { |
| 494 assert(_updateTask != null); | 559 assert(_updateTask != null); |
| 495 if (script == null) { | 560 if (script == null) { |
| 496 // We may have previously had a script. | 561 // We may have previously had a script. |
| 497 if (container != null) { | 562 if (container != null) { |
| 498 container.children.clear(); | 563 container.children.clear(); |
| 499 } | 564 } |
| 500 return; | 565 return; |
| 501 } | 566 } |
| 502 if (!script.loaded) { | 567 if (!script.loaded) { |
| 503 script.load().then((_) => update()); | 568 await script.load(); |
| 504 return; | |
| 505 } | 569 } |
| 506 | |
| 507 if (_scriptChangeSubscription == null) { | 570 if (_scriptChangeSubscription == null) { |
| 508 _scriptChangeSubscription = script.changes.listen((_) => update()); | 571 _scriptChangeSubscription = script.changes.listen((_) => update()); |
| 509 } | 572 } |
| 573 await _refreshSourceReport(); | |
| 510 | 574 |
| 511 computeAnnotations(); | 575 computeAnnotations(); |
| 512 | 576 |
| 513 var table = linesTable(); | 577 var table = linesTable(); |
| 514 var firstBuild = false; | 578 var firstBuild = false; |
| 515 if (container == null) { | 579 if (container == null) { |
| 516 // Indirect to avoid deleting the style element. | 580 // Indirect to avoid deleting the style element. |
| 517 container = new DivElement(); | 581 container = new DivElement(); |
| 518 shadowRoot.append(container); | 582 shadowRoot.append(container); |
| 519 firstBuild = true; | 583 firstBuild = true; |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 796 // We annotate a field declaration with the field instead of the | 860 // We annotate a field declaration with the field instead of the |
| 797 // implicit getter or setter. | 861 // implicit getter or setter. |
| 798 var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); | 862 var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); |
| 799 annotations.add(a); | 863 annotations.add(a); |
| 800 } | 864 } |
| 801 } | 865 } |
| 802 } | 866 } |
| 803 } | 867 } |
| 804 | 868 |
| 805 void addCallSiteAnnotations() { | 869 void addCallSiteAnnotations() { |
| 806 for (var callSite in script.callSites) { | 870 for (var callSite in _callSites) { |
| 807 annotations.add(new CallSiteAnnotation(callSite)); | 871 annotations.add(new CallSiteAnnotation(callSite)); |
| 808 } | 872 } |
| 809 } | 873 } |
| 810 | 874 |
| 811 void addLocalVariableAnnotations() { | 875 void addLocalVariableAnnotations() { |
| 812 // We have local variable information. | 876 // We have local variable information. |
| 813 if (variables != null) { | 877 if (variables != null) { |
| 814 // For each variable. | 878 // For each variable. |
| 815 for (var variable in variables) { | 879 for (var variable in variables) { |
| 816 // Find variable usage locations. | 880 // Find variable usage locations. |
| 817 var locations = script.scanForLocalVariableLocations( | 881 var locations = script.scanForLocalVariableLocations( |
| 818 variable['name'], | 882 variable['name'], |
| 819 variable['_tokenPos'], | 883 variable['_tokenPos'], |
| 820 variable['_endTokenPos']); | 884 variable['_endTokenPos']); |
| 821 | 885 |
| 822 // Annotate locations. | 886 // Annotate locations. |
| 823 for (var location in locations) { | 887 for (var location in locations) { |
| 824 annotations.add(new LocalVariableAnnotation(location, | 888 annotations.add(new LocalVariableAnnotation(location, |
| 825 variable['value'])); | 889 variable['value'])); |
| 826 } | 890 } |
| 827 } | 891 } |
| 828 } | 892 } |
| 829 } | 893 } |
| 830 | 894 |
| 895 int _refreshButtonTop() { | |
| 896 if (_refreshButton == null) { | |
| 897 return 5; | |
| 898 } | |
| 899 const padding = 5; | |
| 900 const navbarHeight = 40; | |
|
Cutch
2016/01/14 21:37:40
The navbarHeight constant is hardcoded in two plac
| |
| 901 var rect = getBoundingClientRect(); | |
| 902 var buttonHeight = _refreshButton.clientHeight; | |
| 903 return min(max(0, navbarHeight - rect.top) + padding, | |
| 904 rect.height - (buttonHeight + padding)); | |
| 905 } | |
| 906 | |
| 907 RefreshButtonElement _newRefreshButton() { | |
| 908 var button = new Element.tag('refresh-button'); | |
| 909 button.style.position = 'absolute'; | |
| 910 button.style.display = 'inline-block'; | |
| 911 button.style.top = '${_refreshButtonTop()}px'; | |
| 912 button.style.right = '5px'; | |
| 913 button.callback = _refresh; | |
| 914 return button; | |
| 915 } | |
| 916 | |
| 831 Element linesTable() { | 917 Element linesTable() { |
| 832 var table = new DivElement(); | 918 var table = new DivElement(); |
| 833 table.classes.add("sourceTable"); | 919 table.classes.add("sourceTable"); |
| 834 | 920 |
| 921 _refreshButton = _newRefreshButton(); | |
| 922 table.append(_refreshButton); | |
| 923 | |
| 835 if (_startLine == null || _endLine == null) { | 924 if (_startLine == null || _endLine == null) { |
| 836 return table; | 925 return table; |
| 837 } | 926 } |
| 838 | 927 |
| 839 var endLine = (endPos != null | 928 var endLine = (endPos != null |
| 840 ? script.tokenToLine(endPos) | 929 ? script.tokenToLine(endPos) |
| 841 : script.lines.length + script.lineOffset); | 930 : script.lines.length + script.lineOffset); |
| 842 var lineNumPad = endLine.toString().length; | 931 var lineNumPad = endLine.toString().length; |
| 843 | 932 |
| 844 annotationsCursor = 0; | 933 annotationsCursor = 0; |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 966 update(); | 1055 update(); |
| 967 }); | 1056 }); |
| 968 update(); | 1057 update(); |
| 969 return e; | 1058 return e; |
| 970 } | 1059 } |
| 971 | 1060 |
| 972 Element lineNumberElement(ScriptLine line, int lineNumPad) { | 1061 Element lineNumberElement(ScriptLine line, int lineNumPad) { |
| 973 var lineNumber = line == null ? "..." : line.line; | 1062 var lineNumber = line == null ? "..." : line.line; |
| 974 var e = span("$nbsp${lineNumber.toString().padLeft(lineNumPad,nbsp)}$nbsp"); | 1063 var e = span("$nbsp${lineNumber.toString().padLeft(lineNumPad,nbsp)}$nbsp"); |
| 975 e.classes.add('noCopy'); | 1064 e.classes.add('noCopy'); |
| 976 | |
| 977 if (lineNumber == _currentLine) { | 1065 if (lineNumber == _currentLine) { |
| 978 hitsCurrent(e); | 1066 hitsCurrent(e); |
| 979 } else if ((line == null) || (line.hits == null)) { | 1067 return e; |
| 1068 } | |
| 1069 var ranges = _rangeMap[lineNumber]; | |
| 1070 if ((ranges == null) || ranges.isEmpty) { | |
| 1071 // This line is not code. | |
| 980 hitsUnknown(e); | 1072 hitsUnknown(e); |
| 981 } else if (line.hits == 0) { | 1073 return e; |
| 1074 } | |
| 1075 bool compiled = false; | |
| 1076 bool hasCallInfo = false; | |
| 1077 bool executed = false; | |
| 1078 for (var range in ranges) { | |
| 1079 if (range['compiled']) { | |
| 1080 compiled = true; | |
| 1081 for (var callSite in range['callSites']) { | |
| 1082 var callLine = line.script.tokenToLine(callSite['tokenPos']); | |
| 1083 if (lineNumber == callLine) { | |
| 1084 // The call site is on the current line. | |
| 1085 hasCallInfo = true; | |
| 1086 for (var cacheEntry in callSite['cacheEntries']) { | |
| 1087 if (cacheEntry['count'] > 0) { | |
| 1088 // If any call site on the line has been executed, we | |
| 1089 // mark the line as executed. | |
| 1090 executed = true; | |
| 1091 break; | |
| 1092 } | |
| 1093 } | |
| 1094 } | |
| 1095 } | |
| 1096 } | |
| 1097 } | |
| 1098 if (executed) { | |
| 1099 hitsExecuted(e); | |
| 1100 } else if (hasCallInfo) { | |
| 982 hitsNotExecuted(e); | 1101 hitsNotExecuted(e); |
| 1102 } else if (compiled) { | |
| 1103 hitsCompiled(e); | |
| 983 } else { | 1104 } else { |
| 984 hitsExecuted(e); | 1105 hitsNotCompiled(e); |
| 985 } | 1106 } |
| 986 | |
| 987 return e; | 1107 return e; |
| 988 } | 1108 } |
| 989 | 1109 |
| 990 Element lineSourceElement(ScriptLine line) { | 1110 Element lineSourceElement(ScriptLine line) { |
| 991 var e = new DivElement(); | 1111 var e = new DivElement(); |
| 992 e.classes.add("sourceItem"); | 1112 e.classes.add("sourceItem"); |
| 993 | 1113 |
| 994 if (line != null) { | 1114 if (line != null) { |
| 995 if (line.line == _currentLine) { | 1115 if (line.line == _currentLine) { |
| 996 e.classes.add("currentLine"); | 1116 e.classes.add("currentLine"); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1029 | 1149 |
| 1030 return e; | 1150 return e; |
| 1031 } | 1151 } |
| 1032 | 1152 |
| 1033 ScriptInsetElement.created() | 1153 ScriptInsetElement.created() |
| 1034 : super.created() { | 1154 : super.created() { |
| 1035 _updateTask = new Task(update); | 1155 _updateTask = new Task(update); |
| 1036 } | 1156 } |
| 1037 } | 1157 } |
| 1038 | 1158 |
| 1159 @CustomTag('refresh-button') | |
| 1160 class RefreshButtonElement extends PolymerElement { | |
| 1161 RefreshButtonElement.created() : super.created(); | |
| 1162 | |
| 1163 @published var callback = null; | |
| 1164 bool busy = false; | |
| 1165 | |
| 1166 Future buttonClick(var event, var b, var c) async { | |
| 1167 if (busy) { | |
| 1168 return; | |
| 1169 } | |
| 1170 busy = true; | |
| 1171 if (callback != null) { | |
| 1172 await callback(); | |
| 1173 } | |
| 1174 busy = false; | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 | |
| 1039 @CustomTag('source-inset') | 1179 @CustomTag('source-inset') |
| 1040 class SourceInsetElement extends PolymerElement { | 1180 class SourceInsetElement extends PolymerElement { |
| 1041 SourceInsetElement.created() : super.created(); | 1181 SourceInsetElement.created() : super.created(); |
| 1042 | 1182 |
| 1043 @published SourceLocation location; | 1183 @published SourceLocation location; |
| 1044 @published String height = null; | 1184 @published String height = null; |
| 1045 @published int currentPos; | 1185 @published int currentPos; |
| 1046 @published bool inDebuggerContext = false; | 1186 @published bool inDebuggerContext = false; |
| 1047 @published ObservableList variables; | 1187 @published ObservableList variables; |
| 1188 @published Element scroller; | |
| 1048 } | 1189 } |
| OLD | NEW |