Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(732)

Side by Side Diff: runtime/observatory/lib/src/elements/script_inset.dart

Issue 2277543004: Converted Observatory script-inset & source-inset elements (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fixed long line Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 'package:observatory/app.dart';
10 import 'observatory_element.dart';
11 import 'service_ref.dart';
12 import 'package:observatory/models.dart' as M; 10 import 'package:observatory/models.dart' as M;
13 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart' as S;
12 import 'package:observatory/src/elements/helpers/any_ref.dart';
13 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
14 import 'package:observatory/src/elements/helpers/tag.dart';
15 import 'package:observatory/src/elements/helpers/uris.dart';
14 import 'package:observatory/utils.dart'; 16 import 'package:observatory/utils.dart';
15 import 'package:polymer/polymer.dart'; 17
16 import 'package:logging/logging.dart'; 18 class ScriptInsetElement extends HtmlElement implements Renderable {
17 19 static const tag = const Tag<ScriptInsetElement>('script-inset-wrapped');
18 const nbsp = "\u00A0"; 20
19 21 RenderingScheduler _r;
20 void addInfoBox(Element content, Function infoBoxGenerator) { 22
21 var infoBox; 23 Stream<RenderedEvent<ScriptInsetElement>> get onRendered => _r.onRendered;
22 var show = false; 24
23 var originalBackground = content.style.backgroundColor; 25
24 buildInfoBox() { 26 M.IsolateRef _isolate;
25 infoBox = infoBoxGenerator(); 27 M.ScriptRef _script;
26 infoBox.style.position = 'absolute'; 28 M.Script _loadedScript;
27 infoBox.style.padding = '1em'; 29 M.ScriptRepository _scripts;
28 infoBox.style.border = 'solid black 2px'; 30 M.InstanceRepository _instances;
29 infoBox.style.zIndex = '10'; 31 M.EventRepository _events;
30 infoBox.style.backgroundColor = 'white'; 32 StreamSubscription _subscription;
31 infoBox.style.cursor = 'auto'; 33 int _startPos;
32 // Don't inherit pre formating from the script lines. 34 int _endPos;
33 infoBox.style.whiteSpace = 'normal'; 35 int _currentPos;
34 content.append(infoBox); 36 bool _inDebuggerContext;
35 } 37 Iterable _variables;
36 content.onClick.listen((event) { 38
37 show = !show; 39 M.IsolateRef get isolate => _isolate;
38 if (infoBox == null) buildInfoBox(); // Created lazily on the first click. 40 M.ScriptRef get script => _script;
39 infoBox.style.display = show ? 'block' : 'none'; 41
40 content.style.backgroundColor = show ? 'white' : originalBackground; 42 factory ScriptInsetElement(M.IsolateRef isolate, M.ScriptRef script,
41 }); 43 M.ScriptRepository scripts,
42 44 M.InstanceRepository instances,
43 // Causes infoBox to be positioned relative to the bottom-left of content. 45 M.EventRepository events,
44 content.style.display = 'inline-block'; 46 {int startPos, int endPos, int currentPos,
45 content.style.cursor = 'pointer'; 47 bool inDebuggerContext: false,
46 } 48 Iterable variables: const [],
47 49 RenderingQueue queue}) {
48 50 assert(isolate != null);
49 void addLink(Element content, String target) { 51 assert(script != null);
50 // Ick, destructive but still compatible with also adding an info box. 52 assert(scripts != null);
51 var a = new AnchorElement(href: target); 53 assert(instances != null);
52 a.text = content.text; 54 assert(events != null);
53 content.text = ''; 55 assert(inDebuggerContext != null);
54 content.append(a); 56 assert(variables != null);
55 } 57 ScriptInsetElement e = document.createElement(tag.name);
56 58 e._r = new RenderingScheduler(e, queue: queue);
57 59 e._isolate = isolate;
58 abstract class Annotation implements Comparable<Annotation> { 60 e._script = script;
59 int line; 61 e._scripts = scripts;
60 int columnStart; 62 e._instances = instances;
61 int columnStop; 63 e._events = events;
62 int get priority; 64 e._startPos = startPos;
63 65 e._endPos = endPos;
64 void applyStyleTo(element); 66 e._currentPos = currentPos;
65 67 e._inDebuggerContext = inDebuggerContext;
66 int compareTo(Annotation other) { 68 e._variables = new List.unmodifiable(variables);
67 if (line == other.line) {
68 if (columnStart == other.columnStart) {
69 return priority.compareTo(other.priority);
70 }
71 return columnStart.compareTo(other.columnStart);
72 }
73 return line.compareTo(other.line);
74 }
75
76 Element table() {
77 var e = new DivElement();
78 e.style.display = "table";
79 e.style.color = "#333";
80 e.style.font = "400 14px 'Montserrat', sans-serif";
81 return e; 69 return e;
82 } 70 }
83 71
84 Element row([content]) { 72 ScriptInsetElement.created() : super.created();
85 var e = new DivElement(); 73
86 e.style.display = "table-row"; 74 @override
87 if (content is String) e.text = content; 75 void attached() {
88 if (content is Element) e.children.add(content); 76 super.attached();
89 return e; 77 _r.enable();
90 } 78 _subscription = _events.onDebugEvent
91 79 .where((e) => (e is M.BreakpointAddedEvent) ||
92 Element cell(content) { 80 (e is M.BreakpointResolvedEvent) ||
93 var e = new DivElement(); 81 (e is M.BreakpointRemovedEvent))
94 e.style.display = "table-cell"; 82 .map((e) => e.breakpoint)
95 e.style.padding = "3px"; 83 .listen((M.Breakpoint b) {
96 if (content is String) e.text = content; 84 final loc = b.location;
97 if (content is Element) e.children.add(content); 85 int line;
98 return e; 86 if (loc.script.id == script.id) {
99 } 87 if (loc.tokenPos != null) {
100 88 line = _loadedScript.tokenToLine(loc.tokenPos);
101 Element serviceRef(object) { 89 } else {
102 AnyServiceRefElement e = new Element.tag("any-service-ref"); 90 line = loc.line;
103 e.ref = object; 91 }
104 return e; 92 } else {
105 } 93 line = loc.line;
106 } 94 }
107 95 if ((line == null) || ((line >= _startLine) && (line <= _endLine))) {
108 class CurrentExecutionAnnotation extends Annotation { 96 _r.dirty();
109 int priority = 0; // highest priority. 97 }
110 98 });
111 void applyStyleTo(element) { 99 _refresh();
112 if (element == null) { 100 }
113 return; // TODO(rmacnak): Handling overlapping annotations. 101
114 } 102 @override
115 element.classes.add("currentCol"); 103 void detached() {
116 element.title = "Current execution"; 104 super.detached();
117 } 105 children = [];
118 } 106 _r.disable(notify: true);
119 107 _subscription.cancel();
120 class BreakpointAnnotation extends Annotation { 108 }
121 Breakpoint bpt; 109
122 int priority = 1; 110 void render() {
123 111 if (_loadedScript == null) {
124 BreakpointAnnotation(this.bpt) { 112 children = [new SpanElement()..text = 'Loading...'];
125 var script = bpt.location.script;
126 var location = bpt.location;
127 if (location.tokenPos != null) {
128 var pos = location.tokenPos;
129 line = script.tokenToLine(pos);
130 columnStart = script.tokenToCol(pos) - 1; // tokenToCol is 1-origin.
131 } else if (location is UnresolvedSourceLocation) {
132 line = location.line;
133 columnStart = location.column;
134 if (columnStart == null) {
135 columnStart = 0;
136 }
137 }
138 var length = script.guessTokenLength(line, columnStart);
139 if (length == null) {
140 length = 1;
141 }
142 columnStop = columnStart + length;
143 }
144
145 void applyStyleTo(element) {
146 if (element == null) {
147 return; // TODO(rmacnak): Handling overlapping annotations.
148 }
149 var script = bpt.location.script;
150 var pos = bpt.location.tokenPos;
151 int line = script.tokenToLine(pos);
152 int column = script.tokenToCol(pos);
153 if (bpt.resolved) {
154 element.classes.add("resolvedBreakAnnotation");
155 } else { 113 } else {
156 element.classes.add("unresolvedBreakAnnotation"); 114 final table = linesTable();
157 } 115 var firstBuild = false;
158 element.title = "Breakpoint ${bpt.number} at ${line}:${column}"; 116 if (container == null) {
159 } 117 // Indirect to avoid deleting the style element.
160 } 118 container = new DivElement();
161 119
162 class LibraryAnnotation extends Annotation { 120 firstBuild = true;
163 Library target; 121 }
164 String url; 122 children = [container];
165 int priority = 2; 123 container.children.clear();
166 124 container.children.add(table);
167 LibraryAnnotation(this.target, this.url); 125 _makeCssClassUncopyable(table, "noCopy");
168 126 if (firstBuild) {
169 void applyStyleTo(element) { 127 _scrollToCurrentPos();
170 if (element == null) { 128 }
171 return; // TODO(rmacnak): Handling overlapping annotations. 129 }
172 } 130 }
173 element.title = "library ${target.uri}"; 131
174 addLink(element, url); 132 Future _refresh() async {
175 } 133 _loadedScript = await _scripts.get(_isolate, _script.id);
176 } 134 await _refreshSourceReport();
177 135 await _computeAnnotations();
178 class PartAnnotation extends Annotation { 136 _r.dirty();
179 Script part; 137 }
180 String url; 138
181 int priority = 2; 139 ButtonElement _refreshButton;
182 140 ButtonElement _toggleProfileButton;
183 PartAnnotation(this.part, this.url);
184
185 void applyStyleTo(element) {
186 if (element == null) {
187 return; // TODO(rmacnak): Handling overlapping annotations.
188 }
189 element.title = "script ${part.uri}";
190 addLink(element, url);
191 }
192 }
193
194 class LocalVariableAnnotation extends Annotation {
195 final value;
196 int priority = 2;
197
198 LocalVariableAnnotation(LocalVarLocation location, this.value) {
199 line = location.line;
200 columnStart = location.column;
201 columnStop = location.endColumn;
202 }
203
204 void applyStyleTo(element) {
205 if (element == null) {
206 return; // TODO(rmacnak): Handling overlapping annotations.
207 }
208 element.style.fontWeight = "bold";
209 element.title = "${value.shortName}";
210 }
211 }
212
213 class CallSiteAnnotation extends Annotation {
214 CallSite callSite;
215 int priority = 2;
216
217 CallSiteAnnotation(this.callSite) {
218 line = callSite.line;
219 columnStart = callSite.column - 1; // Call site is 1-origin.
220 var tokenLength = callSite.script.guessTokenLength(line, columnStart);
221 if (tokenLength == null) {
222 tokenLength = callSite.name.length; // Approximate.
223 if (callSite.name.startsWith("get:") ||
224 callSite.name.startsWith("set:")) tokenLength -= 4;
225 }
226 columnStop = columnStart + tokenLength;
227 }
228
229 void applyStyleTo(element) {
230 if (element == null) {
231 return; // TODO(rmacnak): Handling overlapping annotations.
232 }
233 element.style.fontWeight = "bold";
234 element.title = "Call site: ${callSite.name}";
235
236 addInfoBox(element, () {
237 var details = table();
238 if (callSite.entries.isEmpty) {
239 details.append(row('Call of "${callSite.name}" did not execute'));
240 } else {
241 var r = row();
242 r.append(cell("Container"));
243 r.append(cell("Count"));
244 r.append(cell("Target"));
245 details.append(r);
246
247 for (var entry in callSite.entries) {
248 var r = row();
249 r.append(cell(serviceRef(entry.receiver)));
250 r.append(cell(entry.count.toString()));
251 r.append(cell(serviceRef(entry.target)));
252 details.append(r);
253 }
254 }
255 return details;
256 });
257 }
258 }
259
260 abstract class DeclarationAnnotation extends Annotation {
261 String url;
262 int priority = 2;
263
264 DeclarationAnnotation(decl, this.url) {
265 assert(decl.loaded);
266 SourceLocation location = decl.location;
267 if (location == null) {
268 line = 0;
269 columnStart = 0;
270 columnStop = 0;
271 return;
272 }
273
274 Script script = location.script;
275 line = script.tokenToLine(location.tokenPos);
276 columnStart = script.tokenToCol(location.tokenPos);
277 if ((line == null) || (columnStart == null)) {
278 line = 0;
279 columnStart = 0;
280 columnStop = 0;
281 } else {
282 columnStart--; // 1-origin -> 0-origin.
283
284 // The method's token position is at the beginning of the method
285 // declaration, which may be a return type annotation, metadata, static
286 // modifier, etc. Try to scan forward to position this annotation on the
287 // function's name instead.
288 var lineSource = script.getLine(line).text;
289 var betterStart = lineSource.indexOf(decl.name, columnStart);
290 if (betterStart != -1) {
291 columnStart = betterStart;
292 }
293 columnStop = columnStart + decl.name.length;
294 }
295 }
296 }
297
298 class ClassDeclarationAnnotation extends DeclarationAnnotation {
299 Class klass;
300
301 ClassDeclarationAnnotation(Class cls, String url)
302 : klass = cls,
303 super(cls, url);
304
305 void applyStyleTo(element) {
306 if (element == null) {
307 return; // TODO(rmacnak): Handling overlapping annotations.
308 }
309 element.title = "class ${klass.name}";
310 addLink(element, url);
311 }
312 }
313
314 class FieldDeclarationAnnotation extends DeclarationAnnotation {
315 Field field;
316
317 FieldDeclarationAnnotation(Field fld, String url)
318 : field = fld,
319 super(fld, url);
320
321 void applyStyleTo(element) {
322 if (element == null) {
323 return; // TODO(rmacnak): Handling overlapping annotations.
324 }
325 var tooltip = "field ${field.name}";
326 element.title = tooltip;
327 addLink(element, url);
328 }
329 }
330
331 class FunctionDeclarationAnnotation extends DeclarationAnnotation {
332 ServiceFunction function;
333
334 FunctionDeclarationAnnotation(ServiceFunction func, String url)
335 : function = func,
336 super(func, url);
337
338 void applyStyleTo(element) {
339 if (element == null) {
340 return; // TODO(rmacnak): Handling overlapping annotations.
341 }
342 var tooltip = "method ${function.name}";
343 if (function.isOptimizable == false) {
344 tooltip += "\nUnoptimizable!";
345 }
346 if (function.isInlinable == false) {
347 tooltip += "\nNot inlinable!";
348 }
349 if (function.deoptimizations > 0) {
350 tooltip += "\nDeoptimized ${function.deoptimizations} times!";
351 }
352 element.title = tooltip;
353
354 if (function.isOptimizable == false ||
355 function.isInlinable == false ||
356 function.deoptimizations >0) {
357 element.style.backgroundColor = "#EEA7A7"; // Low-saturation red.
358 }
359
360 addLink(element, url);
361 }
362 }
363
364 class ScriptLineProfile {
365 ScriptLineProfile(this.line, this.sampleCount);
366
367 static const kHotThreshold = 0.05; // 5%.
368 static const kMediumThreshold = 0.02; // 2%.
369
370 final int line;
371 final int sampleCount;
372
373 int selfTicks = 0;
374 int totalTicks = 0;
375
376 void process(int exclusive, int inclusive) {
377 selfTicks += exclusive;
378 totalTicks += inclusive;
379 }
380
381 String get formattedSelfTicks {
382 return Utils.formatPercent(selfTicks, sampleCount);
383 }
384
385 String get formattedTotalTicks {
386 return Utils.formatPercent(totalTicks, sampleCount);
387 }
388
389 double _percent(bool self) {
390 if (sampleCount == 0) {
391 return 0.0;
392 }
393 if (self) {
394 return selfTicks / sampleCount;
395 } else {
396 return totalTicks / sampleCount;
397 }
398 }
399
400 bool isHot(bool self) => _percent(self) > kHotThreshold;
401 bool isMedium(bool self) => _percent(self) > kMediumThreshold;
402 }
403
404 /// Box with script source code in it.
405 @CustomTag('script-inset')
406 class ScriptInsetElement extends ObservatoryElement {
407 @published Script script;
408 @published int startPos;
409 @published int endPos;
410
411 /// Set the height to make the script inset scroll. Otherwise it
412 /// will show from startPos to endPos.
413 @published String height = null;
414
415 @published int currentPos;
416 @published bool inDebuggerContext = false;
417 @published ObservableList variables;
418
419 @published Element scroller;
420 RefreshButtonElement _refreshButton;
421 ToggleButtonElement _toggleProfileButton;
422 141
423 int _currentLine; 142 int _currentLine;
424 int _currentCol; 143 int _currentCol;
425 int _startLine; 144 int _startLine;
426 int _endLine; 145 int _endLine;
427 146
428 Map<int, List<ServiceMap>> _rangeMap = {}; 147 Map<int, List<S.ServiceMap>> _rangeMap = {};
429 Set _callSites = new Set<CallSite>(); 148 Set _callSites = new Set<S.CallSite>();
430 Set _possibleBreakpointLines = new Set<int>(); 149 Set _possibleBreakpointLines = new Set<int>();
431 Map<int, ScriptLineProfile> _profileMap = {}; 150 Map<int, ScriptLineProfile> _profileMap = {};
432 151
433 var annotations = []; 152 var _annotations = [];
434 var annotationsCursor; 153 var _annotationsCursor;
435 154
436 StreamSubscription _scriptChangeSubscription;
437 Future<StreamSubscription> _debugSubscriptionFuture;
438 StreamSubscription _scrollSubscription;
439
440 bool hasLoadedLibraryDeclarations = false;
441 bool _includeProfile = false; 155 bool _includeProfile = false;
442 156
443 String makeLineId(int line) { 157 String makeLineClass(int line) {
444 return 'line-$line'; 158 return 'script-inset-line-$line';
445 } 159 }
446 160
447 void _scrollToCurrentPos() { 161 void _scrollToCurrentPos() {
448 var line = shadowRoot.getElementById(makeLineId(_currentLine)); 162 var lines = getElementsByClassName(makeLineClass(_currentLine));
449 if (line != null) { 163 if (lines.length > 0) {
450 line.scrollIntoView(); 164 lines[0].scrollIntoView();
451 } 165 }
452 }
453
454 void attached() {
455 super.attached();
456 _debugSubscriptionFuture =
457 app.vm.listenEventStream(VM.kDebugStream, _onDebugEvent);
458 if (scroller != null) {
459 _scrollSubscription = scroller.onScroll.listen(_onScroll);
460 } else {
461 _scrollSubscription = window.onScroll.listen(_onScroll);
462 }
463 }
464
465 void detached() {
466 cancelFutureSubscription(_debugSubscriptionFuture);
467 _debugSubscriptionFuture = null;
468 if (_scrollSubscription != null) {
469 _scrollSubscription.cancel();
470 _scrollSubscription = null;
471 }
472 if (_scriptChangeSubscription != null) {
473 // Don't leak. If only Dart and Javascript exposed weak references...
474 _scriptChangeSubscription.cancel();
475 _scriptChangeSubscription = null;
476 }
477 super.detached();
478 }
479
480 void _onScroll(event) {
481 if (_refreshButton != null) {
482 var newTop = _buttonTop(_refreshButton);
483 if (_refreshButton.style.top != newTop) {
484 _refreshButton.style.top = '${newTop}px';
485 }
486 }
487 if (_toggleProfileButton != null) {
488 var newTop = _buttonTop(_toggleProfileButton);
489 if (_toggleProfileButton.style.top != newTop) {
490 _toggleProfileButton.style.top = '${newTop}px';
491 }
492 }
493 }
494
495 void _onDebugEvent(event) {
496 if (script == null) {
497 return;
498 }
499 switch (event.kind) {
500 case ServiceEvent.kBreakpointAdded:
501 case ServiceEvent.kBreakpointResolved:
502 case ServiceEvent.kBreakpointRemoved:
503 var loc = event.breakpoint.location;
504 if (loc.script == script) {
505 int line;
506 if (loc.tokenPos != null) {
507 line = script.tokenToLine(loc.tokenPos);
508 } else {
509 line = loc.line;
510 }
511 if ((line >= _startLine) && (line <= _endLine)) {
512 _updateTask.queue();
513 }
514 }
515 break;
516 default:
517 // Ignore.
518 break;
519 }
520 }
521
522 void currentPosChanged(oldValue) {
523 _updateTask.queue();
524 _scrollToCurrentPos();
525 }
526
527 void startPosChanged(oldValue) {
528 _updateTask.queue();
529 }
530
531 void endPosChanged(oldValue) {
532 _updateTask.queue();
533 }
534
535 void scriptChanged(oldValue) {
536 _updateTask.queue();
537 }
538
539 void variablesChanged(oldValue) {
540 _updateTask.queue();
541 } 166 }
542 167
543 Element a(String text) => new AnchorElement()..text = text; 168 Element a(String text) => new AnchorElement()..text = text;
544 Element span(String text) => new SpanElement()..text = text; 169 Element span(String text) => new SpanElement()..text = text;
545 170
546 Element hitsCurrent(Element element) { 171 Element hitsCurrent(Element element) {
547 element.classes.add('hitsCurrent'); 172 element.classes.add('hitsCurrent');
548 element.title = ""; 173 element.title = "";
549 return element; 174 return element;
550 } 175 }
(...skipping 18 matching lines...) Expand all
569 return element; 194 return element;
570 } 195 }
571 Element hitsNotCompiled(Element element) { 196 Element hitsNotCompiled(Element element) {
572 element.classes.add('hitsNotCompiled'); 197 element.classes.add('hitsNotCompiled');
573 element.title = "Line in uncompiled function"; 198 element.title = "Line in uncompiled function";
574 return element; 199 return element;
575 } 200 }
576 201
577 Element container; 202 Element container;
578 203
579 Future _refresh() async {
580 await update();
581 }
582
583 // Build _rangeMap and _callSites from a source report. 204 // Build _rangeMap and _callSites from a source report.
584 Future _refreshSourceReport() async { 205 Future _refreshSourceReport() async {
585 var reports = [Isolate.kCallSitesReport, 206 var reports = [S.Isolate.kCallSitesReport,
586 Isolate.kPossibleBreakpointsReport]; 207 S.Isolate.kPossibleBreakpointsReport];
587 if (_includeProfile) { 208 if (_includeProfile) {
588 reports.add(Isolate.kProfileReport); 209 reports.add(S.Isolate.kProfileReport);
589 } 210 }
590 var sourceReport = await script.isolate.getSourceReport( 211 S.Isolate isolate = _isolate as S.Isolate;
212 var sourceReport = await isolate.getSourceReport(
591 reports, 213 reports,
592 script, startPos, endPos); 214 script, _startPos, _endPos);
593 _possibleBreakpointLines = getPossibleBreakpointLines(sourceReport, script); 215 _possibleBreakpointLines = S.getPossibleBreakpointLines(sourceReport,
216 script);
594 _rangeMap.clear(); 217 _rangeMap.clear();
595 _callSites.clear(); 218 _callSites.clear();
596 _profileMap.clear(); 219 _profileMap.clear();
597 for (var range in sourceReport['ranges']) { 220 for (var range in sourceReport['ranges']) {
598 int startLine = script.tokenToLine(range['startPos']); 221 int startLine = _loadedScript.tokenToLine(range['startPos']);
599 int endLine = script.tokenToLine(range['endPos']); 222 int endLine = _loadedScript.tokenToLine(range['endPos']);
600 // TODO(turnidge): Track down the root cause of null startLine/endLine. 223 // TODO(turnidge): Track down the root cause of null startLine/endLine.
601 if ((startLine != null) && (endLine != null)) { 224 if ((startLine != null) && (endLine != null)) {
602 for (var line = startLine; line <= endLine; line++) { 225 for (var line = startLine; line <= endLine; line++) {
603 var rangeList = _rangeMap[line]; 226 var rangeList = _rangeMap[line];
604 if (rangeList == null) { 227 if (rangeList == null) {
605 _rangeMap[line] = [range]; 228 _rangeMap[line] = [range];
606 } else { 229 } else {
607 rangeList.add(range); 230 rangeList.add(range);
608 } 231 }
609 } 232 }
610 } 233 }
611 if (_includeProfile && range['profile'] != null) { 234 if (_includeProfile && range['profile'] != null) {
612 List positions = range['profile']['positions']; 235 List positions = range['profile']['positions'];
613 List exclusiveTicks = range['profile']['exclusiveTicks']; 236 List exclusiveTicks = range['profile']['exclusiveTicks'];
614 List inclusiveTicks = range['profile']['inclusiveTicks']; 237 List inclusiveTicks = range['profile']['inclusiveTicks'];
615 int sampleCount = range['profile']['metadata']['sampleCount']; 238 int sampleCount = range['profile']['metadata']['sampleCount'];
616 assert(positions.length == exclusiveTicks.length); 239 assert(positions.length == exclusiveTicks.length);
617 assert(positions.length == inclusiveTicks.length); 240 assert(positions.length == inclusiveTicks.length);
618 for (int i = 0; i < positions.length; i++) { 241 for (int i = 0; i < positions.length; i++) {
619 if (positions[i] is String) { 242 if (positions[i] is String) {
620 // String positions are classifying token positions. 243 // String positions are classifying token positions.
621 // TODO(johnmccutchan): Add classifier data to UI. 244 // TODO(johnmccutchan): Add classifier data to UI.
622 continue; 245 continue;
623 } 246 }
624 int line = script.tokenToLine(positions[i]); 247 int line = _loadedScript.tokenToLine(positions[i]);
625 ScriptLineProfile lineProfile = _profileMap[line]; 248 ScriptLineProfile lineProfile = _profileMap[line];
626 if (lineProfile == null) { 249 if (lineProfile == null) {
627 lineProfile = new ScriptLineProfile(line, sampleCount); 250 lineProfile = new ScriptLineProfile(line, sampleCount);
628 _profileMap[line] = lineProfile; 251 _profileMap[line] = lineProfile;
629 } 252 }
630 lineProfile.process(exclusiveTicks[i], inclusiveTicks[i]); 253 lineProfile.process(exclusiveTicks[i], inclusiveTicks[i]);
631 } 254 }
632 } 255 }
633 if (range['compiled']) { 256 if (range['compiled']) {
634 var rangeCallSites = range['callSites']; 257 var rangeCallSites = range['callSites'];
635 if (rangeCallSites != null) { 258 if (rangeCallSites != null) {
636 for (var callSiteMap in rangeCallSites) { 259 for (var callSiteMap in rangeCallSites) {
637 _callSites.add(new CallSite.fromMap(callSiteMap, script)); 260 _callSites.add(new S.CallSite.fromMap(callSiteMap, script));
638 } 261 }
639 } 262 }
640 } 263 }
641 } 264 }
642 } 265 }
643 266
644 Task _updateTask; 267 Future _computeAnnotations() async {
645 Future update() async { 268 _startLine = (_startPos != null
646 assert(_updateTask != null); 269 ? _loadedScript.tokenToLine(_startPos)
647 if (script == null) { 270 : 1 + _loadedScript.lineOffset);
648 // We may have previously had a script. 271 _currentLine = (_currentPos != null
649 if (container != null) { 272 ? _loadedScript.tokenToLine(_currentPos)
650 container.children.clear();
651 }
652 return;
653 }
654 if (!script.loaded) {
655 await script.load();
656 }
657 if (_scriptChangeSubscription == null) {
658 _scriptChangeSubscription = script.changes.listen((_) => update());
659 }
660 await _refreshSourceReport();
661
662 computeAnnotations();
663
664 var table = linesTable();
665 var firstBuild = false;
666 if (container == null) {
667 // Indirect to avoid deleting the style element.
668 container = new DivElement();
669 shadowRoot.append(container);
670 firstBuild = true;
671 }
672 container.children.clear();
673 container.children.add(table);
674 makeCssClassUncopyable(table, "noCopy");
675 if (firstBuild) {
676 _scrollToCurrentPos();
677 }
678 }
679
680 void computeAnnotations() {
681 _startLine = (startPos != null
682 ? script.tokenToLine(startPos)
683 : 1 + script.lineOffset);
684 _currentLine = (currentPos != null
685 ? script.tokenToLine(currentPos)
686 : null); 273 : null);
687 _currentCol = (currentPos != null 274 _currentCol = (_currentPos != null
688 ? (script.tokenToCol(currentPos)) 275 ? (_loadedScript.tokenToCol(_currentPos))
689 : null); 276 : null);
690 if (_currentCol != null) { 277 if (_currentCol != null) {
691 _currentCol--; // make this 0-based. 278 _currentCol--; // make this 0-based.
692 } 279 }
693 280
694 _endLine = (endPos != null 281 S.Script script = _loadedScript as S.Script;
695 ? script.tokenToLine(endPos) 282
696 : script.lines.length + script.lineOffset); 283 _endLine = (_endPos != null
284 ? _loadedScript.tokenToLine(_endPos)
285 : script.lines.length + _loadedScript.lineOffset);
697 286
698 if (_startLine == null || _endLine == null) { 287 if (_startLine == null || _endLine == null) {
699 return; 288 return;
700 } 289 }
701 290
702 annotations.clear(); 291 _annotations.clear();
703 292
704 addCurrentExecutionAnnotation(); 293 addCurrentExecutionAnnotation();
705 addBreakpointAnnotations(); 294 addBreakpointAnnotations();
706 295
707 if (!inDebuggerContext && script.library != null) { 296 if (!_inDebuggerContext && script.library != null) {
708 if (hasLoadedLibraryDeclarations) { 297 await loadDeclarationsOfLibrary(script.library);
709 addLibraryAnnotations(); 298 addLibraryAnnotations();
710 addDependencyAnnotations(); 299 addDependencyAnnotations();
711 addPartAnnotations(); 300 addPartAnnotations();
712 addClassAnnotations(); 301 addClassAnnotations();
713 addFieldAnnotations(); 302 addFieldAnnotations();
714 addFunctionAnnotations(); 303 addFunctionAnnotations();
715 addCallSiteAnnotations(); 304 addCallSiteAnnotations();
716 } else {
717 loadDeclarationsOfLibrary(script.library).then((_) {
718 hasLoadedLibraryDeclarations = true;
719 update();
720 });
721 }
722 } 305 }
723 306
724 addLocalVariableAnnotations(); 307 addLocalVariableAnnotations();
725 308
726 annotations.sort(); 309 _annotations.sort();
727 } 310 }
728 311
729 void addCurrentExecutionAnnotation() { 312 void addCurrentExecutionAnnotation() {
730 if (_currentLine != null) { 313 if (_currentLine != null) {
731 var a = new CurrentExecutionAnnotation(); 314 var a = new CurrentExecutionAnnotation(_isolate, _instances, _r.queue);
732 a.line = _currentLine; 315 a.line = _currentLine;
733 a.columnStart = _currentCol; 316 a.columnStart = _currentCol;
317 S.Script script = _loadedScript as S.Script;
734 var length = script.guessTokenLength(_currentLine, _currentCol); 318 var length = script.guessTokenLength(_currentLine, _currentCol);
735 if (length == null) { 319 if (length == null) {
736 length = 1; 320 length = 1;
737 } 321 }
738 a.columnStop = _currentCol + length; 322 a.columnStop = _currentCol + length;
739 annotations.add(a); 323 _annotations.add(a);
740 } 324 }
741 } 325 }
742 326
743 void addBreakpointAnnotations() { 327 void addBreakpointAnnotations() {
328 S.Script script = _loadedScript as S.Script;
744 for (var line = _startLine; line <= _endLine; line++) { 329 for (var line = _startLine; line <= _endLine; line++) {
745 var bpts = script.getLine(line).breakpoints; 330 var bpts = script.getLine(line).breakpoints;
746 if (bpts != null) { 331 if (bpts != null) {
747 for (var bpt in bpts) { 332 for (var bpt in bpts) {
748 if (bpt.location != null) { 333 if (bpt.location != null) {
749 annotations.add(new BreakpointAnnotation(bpt)); 334 _annotations.add(new BreakpointAnnotation(_isolate, _instances,
335 _r.queue, bpt));
750 } 336 }
751 } 337 }
752 } 338 }
753 } 339 }
754 } 340 }
755 341
756 Future loadDeclarationsOfLibrary(Library lib) { 342 Future loadDeclarationsOfLibrary(S.Library lib) {
757 return lib.load().then((lib) { 343 return lib.load().then((lib) {
758 var loads = []; 344 var loads = [];
759 for (var func in lib.functions) { 345 for (var func in lib.functions) {
760 loads.add(func.load()); 346 loads.add(func.load());
761 } 347 }
762 for (var field in lib.variables) { 348 for (var field in lib.variables) {
763 loads.add(field.load()); 349 loads.add(field.load());
764 } 350 }
765 for (var cls in lib.classes) { 351 for (var cls in lib.classes) {
766 loads.add(loadDeclarationsOfClass(cls)); 352 loads.add(loadDeclarationsOfClass(cls));
767 } 353 }
768 return Future.wait(loads); 354 return Future.wait(loads);
769 }); 355 });
770 } 356 }
771 357
772 Future loadDeclarationsOfClass(Class cls) { 358 Future loadDeclarationsOfClass(S.Class cls) {
773 return cls.load().then((cls) { 359 return cls.load().then((cls) {
774 var loads = []; 360 var loads = [];
775 for (var func in cls.functions) { 361 for (var func in cls.functions) {
776 loads.add(func.load()); 362 loads.add(func.load());
777 } 363 }
778 for (var field in cls.fields) { 364 for (var field in cls.fields) {
779 loads.add(field.load()); 365 loads.add(field.load());
780 } 366 }
781 return Future.wait(loads); 367 return Future.wait(loads);
782 }); 368 });
783 } 369 }
784 370
785 String inspectLink(ServiceObject ref) {
786 return gotoLink('/inspect', ref);
787 }
788
789 void addLibraryAnnotations() { 371 void addLibraryAnnotations() {
790 for (ScriptLine line in script.lines) { 372 S.Script script = _loadedScript as S.Script;
373 for (S.ScriptLine line in script.lines) {
791 // TODO(rmacnak): Use a real scanner. 374 // TODO(rmacnak): Use a real scanner.
792 var pattern = new RegExp("library ${script.library.name}"); 375 var pattern = new RegExp("library ${script.library.name}");
793 var match = pattern.firstMatch(line.text); 376 var match = pattern.firstMatch(line.text);
794 if (match != null) { 377 if (match != null) {
795 var anno = new LibraryAnnotation(script.library, 378 var anno = new LibraryAnnotation(_isolate, _instances, _r.queue,
796 inspectLink(script.library)); 379 _loadedScript.library,
380 Uris.inspect(isolate, object: _loadedScript.library));
797 anno.line = line.line; 381 anno.line = line.line;
798 anno.columnStart = match.start + 8; 382 anno.columnStart = match.start + 8;
799 anno.columnStop = match.end; 383 anno.columnStop = match.end;
800 annotations.add(anno); 384 _annotations.add(anno);
801 } 385 }
802 // TODO(rmacnak): Use a real scanner. 386 // TODO(rmacnak): Use a real scanner.
803 pattern = new RegExp("part of ${script.library.name}"); 387 pattern = new RegExp("part of ${script.library.name}");
804 match = pattern.firstMatch(line.text); 388 match = pattern.firstMatch(line.text);
805 if (match != null) { 389 if (match != null) {
806 var anno = new LibraryAnnotation(script.library, 390 var anno = new LibraryAnnotation(_isolate, _instances, _r.queue,
807 inspectLink(script.library)); 391 _loadedScript.library,
392 Uris.inspect(isolate, object: _loadedScript.library));
808 anno.line = line.line; 393 anno.line = line.line;
809 anno.columnStart = match.start + 8; 394 anno.columnStart = match.start + 8;
810 anno.columnStop = match.end; 395 anno.columnStop = match.end;
811 annotations.add(anno); 396 _annotations.add(anno);
812 } 397 }
813 } 398 }
814 } 399 }
815 400
816 Library resolveDependency(String relativeUri) { 401 M.Library resolveDependency(String relativeUri) {
402 S.Script script = _loadedScript as S.Script;
817 // This isn't really correct: we need to ask the embedder to do the 403 // This isn't really correct: we need to ask the embedder to do the
818 // uri canonicalization for us, but Observatory isn't in a position 404 // uri canonicalization for us, but Observatory isn't in a position
819 // to invoke the library tag handler. Handle the most common cases. 405 // to invoke the library tag handler. Handle the most common cases.
820 var targetUri = Uri.parse(script.library.uri).resolve(relativeUri); 406 var targetUri = Uri.parse(_loadedScript.library.uri).resolve(relativeUri);
821 for (Library l in script.isolate.libraries) { 407 for (M.Library l in script.isolate.libraries) {
822 if (targetUri.toString() == l.uri) { 408 if (targetUri.toString() == l.uri) {
823 return l; 409 return l;
824 } 410 }
825 } 411 }
826 if (targetUri.scheme == 'package') { 412 if (targetUri.scheme == 'package') {
827 targetUri = "packages/${targetUri.path}"; 413 targetUri = "packages/${targetUri.path}";
828 for (Library l in script.isolate.libraries) { 414 for (M.Library l in script.isolate.libraries) {
829 if (targetUri.toString() == l.uri) { 415 if (targetUri.toString() == l.uri) {
830 return l; 416 return l;
831 } 417 }
832 } 418 }
833 } 419 }
834 420
835 Logger.root.info("Could not resolve library dependency: $relativeUri"); 421 print("Could not resolve library dependency: $relativeUri");
836 return null; 422 return null;
837 } 423 }
838 424
839 void addDependencyAnnotations() { 425 void addDependencyAnnotations() {
426 S.Script script = _loadedScript as S.Script;
840 // TODO(rmacnak): Use a real scanner. 427 // TODO(rmacnak): Use a real scanner.
841 var patterns = [ 428 var patterns = [
842 new RegExp("import '(.*)'"), 429 new RegExp("import '(.*)'"),
843 new RegExp('import "(.*)"'), 430 new RegExp('import "(.*)"'),
844 new RegExp("export '(.*)'"), 431 new RegExp("export '(.*)'"),
845 new RegExp('export "(.*)"'), 432 new RegExp('export "(.*)"'),
846 ]; 433 ];
847 for (ScriptLine line in script.lines) { 434 for (S.ScriptLine line in script.lines) {
848 for (var pattern in patterns) { 435 for (var pattern in patterns) {
849 var match = pattern.firstMatch(line.text); 436 var match = pattern.firstMatch(line.text);
850 if (match != null) { 437 if (match != null) {
851 Library target = resolveDependency(match[1]); 438 M.Library target = resolveDependency(match[1]);
852 if (target != null) { 439 if (target != null) {
853 var anno = new LibraryAnnotation(target, inspectLink(target)); 440 var anno = new LibraryAnnotation(_isolate, _instances, _r.queue,
441 target, Uris.inspect(isolate, object: target));
854 anno.line = line.line; 442 anno.line = line.line;
855 anno.columnStart = match.start + 8; 443 anno.columnStart = match.start + 8;
856 anno.columnStop = match.end - 1; 444 anno.columnStop = match.end - 1;
857 annotations.add(anno); 445 _annotations.add(anno);
858 } 446 }
859 } 447 }
860 } 448 }
861 } 449 }
862 } 450 }
863 451
864 Script resolvePart(String relativeUri) { 452 M.Script resolvePart(String relativeUri) {
453 S.Script script = _loadedScript as S.Script;
865 var rootUri = Uri.parse(script.library.uri); 454 var rootUri = Uri.parse(script.library.uri);
866 if (rootUri.scheme == 'dart') { 455 if (rootUri.scheme == 'dart') {
867 // The relative paths from dart:* libraries to their parts are not valid. 456 // The relative paths from dart:* libraries to their parts are not valid.
868 rootUri = new Uri.directory(script.library.uri); 457 rootUri = new Uri.directory(script.library.uri);
869 } 458 }
870 var targetUri = rootUri.resolve(relativeUri); 459 var targetUri = rootUri.resolve(relativeUri);
871 for (Script s in script.library.scripts) { 460 for (M.Script s in script.library.scripts) {
872 if (targetUri.toString() == s.uri) { 461 if (targetUri.toString() == s.uri) {
873 return s; 462 return s;
874 } 463 }
875 } 464 }
876 Logger.root.info("Could not resolve part: $relativeUri"); 465 print("Could not resolve part: $relativeUri");
877 return null; 466 return null;
878 } 467 }
879 468
880 void addPartAnnotations() { 469 void addPartAnnotations() {
470 S.Script script = _loadedScript as S.Script;
881 // TODO(rmacnak): Use a real scanner. 471 // TODO(rmacnak): Use a real scanner.
882 var patterns = [ 472 var patterns = [
883 new RegExp("part '(.*)'"), 473 new RegExp("part '(.*)'"),
884 new RegExp('part "(.*)"'), 474 new RegExp('part "(.*)"'),
885 ]; 475 ];
886 for (ScriptLine line in script.lines) { 476 for (S.ScriptLine line in script.lines) {
887 for (var pattern in patterns) { 477 for (var pattern in patterns) {
888 var match = pattern.firstMatch(line.text); 478 var match = pattern.firstMatch(line.text);
889 if (match != null) { 479 if (match != null) {
890 Script part = resolvePart(match[1]); 480 S.Script part = resolvePart(match[1]);
891 if (part != null) { 481 if (part != null) {
892 var anno = new PartAnnotation(part, inspectLink(part)); 482 var anno = new PartAnnotation(_isolate, _instances, _r.queue, part,
483 Uris.inspect(isolate, object: part));
893 anno.line = line.line; 484 anno.line = line.line;
894 anno.columnStart = match.start + 6; 485 anno.columnStart = match.start + 6;
895 anno.columnStop = match.end - 1; 486 anno.columnStop = match.end - 1;
896 annotations.add(anno); 487 _annotations.add(anno);
897 } 488 }
898 } 489 }
899 } 490 }
900 } 491 }
901 } 492 }
902 493
903 void addClassAnnotations() { 494 void addClassAnnotations() {
495 S.Script script = _loadedScript as S.Script;
904 for (var cls in script.library.classes) { 496 for (var cls in script.library.classes) {
905 if ((cls.location != null) && (cls.location.script == script)) { 497 if ((cls.location != null) && (cls.location.script == script)) {
906 var a = new ClassDeclarationAnnotation(cls, inspectLink(cls)); 498 var a = new ClassDeclarationAnnotation(_isolate, _instances, _r.queue,
907 annotations.add(a); 499 cls, Uris.inspect(isolate, object: cls));
500 _annotations.add(a);
908 } 501 }
909 } 502 }
910 } 503 }
911 504
912 void addFieldAnnotations() { 505 void addFieldAnnotations() {
506 S.Script script = _loadedScript as S.Script;
913 for (var field in script.library.variables) { 507 for (var field in script.library.variables) {
914 if ((field.location != null) && (field.location.script == script)) { 508 if ((field.location != null) && (field.location.script == script)) {
915 var a = new FieldDeclarationAnnotation(field, inspectLink(field)); 509 var a = new FieldDeclarationAnnotation(_isolate, _instances, _r.queue,
916 annotations.add(a); 510 field, Uris.inspect(isolate, object: field));
511 _annotations.add(a);
917 } 512 }
918 } 513 }
919 for (var cls in script.library.classes) { 514 for (var cls in script.library.classes) {
920 for (var field in cls.fields) { 515 for (var field in cls.fields) {
921 if ((field.location != null) && (field.location.script == script)) { 516 if ((field.location != null) && (field.location.script == script)) {
922 var a = new FieldDeclarationAnnotation(field, inspectLink(field)); 517 var a = new FieldDeclarationAnnotation(_isolate, _instances, _r.queue,
923 annotations.add(a); 518 field, Uris.inspect(isolate, object: field));
519 _annotations.add(a);
924 } 520 }
925 } 521 }
926 } 522 }
927 } 523 }
928 524
929 void addFunctionAnnotations() { 525 void addFunctionAnnotations() {
526 S.Script script = _loadedScript as S.Script;
930 for (var func in script.library.functions) { 527 for (var func in script.library.functions) {
931 if ((func.location != null) && 528 if ((func.location != null) &&
932 (func.location.script == script) && 529 (func.location.script == script) &&
933 (func.kind != M.FunctionKind.implicitGetter) && 530 (func.kind != M.FunctionKind.implicitGetter) &&
934 (func.kind != M.FunctionKind.implicitSetter)) { 531 (func.kind != M.FunctionKind.implicitSetter)) {
935 // We annotate a field declaration with the field instead of the 532 // We annotate a field declaration with the field instead of the
936 // implicit getter or setter. 533 // implicit getter or setter.
937 var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); 534 var a = new FunctionDeclarationAnnotation(_isolate, _instances,
938 annotations.add(a); 535 _r.queue, func, Uris.inspect(isolate, object: func));
536 _annotations.add(a);
939 } 537 }
940 } 538 }
941 for (var cls in script.library.classes) { 539 for (var cls in script.library.classes) {
540 S.Script script = _loadedScript as S.Script;
942 for (var func in cls.functions) { 541 for (var func in cls.functions) {
943 if ((func.location != null) && 542 if ((func.location != null) &&
944 (func.location.script == script) && 543 (func.location.script == script) &&
945 (func.kind != M.FunctionKind.implicitGetter) && 544 (func.kind != M.FunctionKind.implicitGetter) &&
946 (func.kind != M.FunctionKind.implicitSetter)) { 545 (func.kind != M.FunctionKind.implicitSetter)) {
947 // We annotate a field declaration with the field instead of the 546 // We annotate a field declaration with the field instead of the
948 // implicit getter or setter. 547 // implicit getter or setter.
949 var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); 548 var a = new FunctionDeclarationAnnotation(_isolate, _instances,
950 annotations.add(a); 549 _r.queue, func, Uris.inspect(isolate, object: func));
550 _annotations.add(a);
951 } 551 }
952 } 552 }
953 } 553 }
954 } 554 }
955 555
956 void addCallSiteAnnotations() { 556 void addCallSiteAnnotations() {
957 for (var callSite in _callSites) { 557 for (var callSite in _callSites) {
958 annotations.add(new CallSiteAnnotation(callSite)); 558 _annotations.add(new CallSiteAnnotation(_isolate, _instances, _r.queue,
559 callSite));
959 } 560 }
960 } 561 }
961 562
962 void addLocalVariableAnnotations() { 563 void addLocalVariableAnnotations() {
564 S.Script script = _loadedScript as S.Script;
963 // We have local variable information. 565 // We have local variable information.
964 if (variables != null) { 566 if (_variables != null) {
965 // For each variable. 567 // For each variable.
966 for (var variable in variables) { 568 for (var variable in _variables) {
967 // Find variable usage locations. 569 // Find variable usage locations.
968 var locations = script.scanForLocalVariableLocations( 570 var locations = script.scanForLocalVariableLocations(
969 variable['name'], 571 variable['name'],
970 variable['_tokenPos'], 572 variable['_tokenPos'],
971 variable['_endTokenPos']); 573 variable['_endTokenPos']);
972 574
973 // Annotate locations. 575 // Annotate locations.
974 for (var location in locations) { 576 for (var location in locations) {
975 annotations.add(new LocalVariableAnnotation(location, 577 _annotations.add(new LocalVariableAnnotation(_isolate, _instances,
976 variable['value'])); 578 _r.queue, location,
977 } 579 variable['value']));
978 } 580 }
979 } 581 }
980 } 582 }
981 583 }
982 int _buttonTop(Element element) { 584
983 if (element == null) { 585 ButtonElement _newRefreshButton() {
984 return 5; 586 var button = new ButtonElement();
985 } 587 button.classes = const ['refresh'];
986 const padding = 5; 588 button.onClick.listen((_) async {
987 // TODO (cbernaschina) check if this is needed. 589 button.disabled = true;
988 const navbarHeight = 40; 590 await _refresh();
989 var rect = getBoundingClientRect(); 591 button.disabled = false;
990 var buttonHeight = element.clientHeight; 592 });
991 return min(max(0, navbarHeight - rect.top) + padding,
992 rect.height - (buttonHeight + padding));
993 }
994
995 RefreshButtonElement _newRefreshButton() {
996 var button = new Element.tag('refresh-button');
997 button.style.position = 'absolute';
998 button.style.display = 'inline-block';
999 button.style.top = '${_buttonTop(null)}px';
1000 button.style.right = '5px';
1001 button.callback = _refresh;
1002 button.title = 'Refresh coverage'; 593 button.title = 'Refresh coverage';
594 button.text = '↺';
1003 return button; 595 return button;
1004 } 596 }
1005 597
1006 ToggleButtonElement _newToggleProfileButton() { 598 ButtonElement _newToggleProfileButton() {
1007 ToggleButtonElement button = new Element.tag('toggle-button'); 599 ButtonElement button = new ButtonElement();
1008 button.style.position = 'absolute'; 600 button.classes = _includeProfile ? const ['toggle-profile', 'enabled']
1009 button.style.display = 'inline-block'; 601 : const ['toggle-profile'];
1010 button.style.top = '${_buttonTop(null)}px';
1011 button.style.right = '30px';
1012 button.title = 'Toggle CPU profile information'; 602 button.title = 'Toggle CPU profile information';
1013 final String enabledColor = 'black'; 603 button.onClick.listen((_) async {
1014 final String disabledColor = 'rgba(0, 0, 0 ,.3)'; 604 _includeProfile = !_includeProfile;
1015 button.callback = (enabled) async { 605 button.classes.toggle('enabled');
1016 _includeProfile = enabled; 606 button.disabled = true;
1017 if (button.children.length > 0) { 607 _refresh();
1018 var content = button.children[0]; 608 button.disabled = false;
1019 if (enabled) { 609 });
1020 content.style.color = enabledColor; 610 button.text = '🔥';
1021 } else {
1022 content.style.color = disabledColor;
1023 }
1024 }
1025 await update();
1026 };
1027 button.children.add(new Element.tag('icon-whatshot'));
1028 button.children[0].style.color = disabledColor;
1029 button.enabled = _includeProfile;
1030 return button; 611 return button;
1031 } 612 }
1032 613
1033 Element linesTable() { 614 Element linesTable() {
615 S.Script script = _loadedScript as S.Script;
1034 var table = new DivElement(); 616 var table = new DivElement();
1035 table.classes.add("sourceTable"); 617 table.classes.add("sourceTable");
1036 618
1037 _refreshButton = _newRefreshButton(); 619 _refreshButton = _newRefreshButton();
1038 _toggleProfileButton = _newToggleProfileButton(); 620 _toggleProfileButton = _newToggleProfileButton();
1039 table.append(_refreshButton); 621 table.append(_refreshButton);
1040 table.append(_toggleProfileButton); 622 table.append(_toggleProfileButton);
1041 623
1042 if (_startLine == null || _endLine == null) { 624 if (_startLine == null || _endLine == null) {
1043 return table; 625 return table;
1044 } 626 }
1045 627
1046 var endLine = (endPos != null 628 var endLine = (_endPos != null
1047 ? script.tokenToLine(endPos) 629 ? _loadedScript.tokenToLine(_endPos)
1048 : script.lines.length + script.lineOffset); 630 : script.lines.length + _loadedScript.lineOffset);
1049 var lineNumPad = endLine.toString().length; 631 var lineNumPad = endLine.toString().length;
1050 632
1051 annotationsCursor = 0; 633 _annotationsCursor = 0;
1052 634
1053 int blankLineCount = 0; 635 int blankLineCount = 0;
1054 for (int i = _startLine; i <= _endLine; i++) { 636 for (int i = _startLine; i <= _endLine; i++) {
1055 var line = script.getLine(i); 637 var line = script.getLine(i);
1056 if (line.isBlank) { 638 if (line.isBlank) {
1057 // Try to introduce elipses if there are 4 or more contiguous 639 // Try to introduce elipses if there are 4 or more contiguous
1058 // blank lines. 640 // blank lines.
1059 blankLineCount++; 641 blankLineCount++;
1060 } else { 642 } else {
1061 if (blankLineCount > 0) { 643 if (blankLineCount > 0) {
(...skipping 14 matching lines...) Expand all
1076 } 658 }
1077 table.append(lineElement(line, lineNumPad)); 659 table.append(lineElement(line, lineNumPad));
1078 } 660 }
1079 } 661 }
1080 662
1081 return table; 663 return table;
1082 } 664 }
1083 665
1084 // Assumes annotations are sorted. 666 // Assumes annotations are sorted.
1085 Annotation nextAnnotationOnLine(int line) { 667 Annotation nextAnnotationOnLine(int line) {
1086 if (annotationsCursor >= annotations.length) return null; 668 if (_annotationsCursor >= _annotations.length) return null;
1087 var annotation = annotations[annotationsCursor]; 669 var annotation = _annotations[_annotationsCursor];
1088 670
1089 // Fast-forward past any annotations before the first line that 671 // Fast-forward past any annotations before the first line that
1090 // we are displaying. 672 // we are displaying.
1091 while (annotation.line < line) { 673 while (annotation.line < line) {
1092 annotationsCursor++; 674 _annotationsCursor++;
1093 if (annotationsCursor >= annotations.length) return null; 675 if (_annotationsCursor >= _annotations.length) return null;
1094 annotation = annotations[annotationsCursor]; 676 annotation = _annotations[_annotationsCursor];
1095 } 677 }
1096 678
1097 // Next annotation is for a later line, don't advance past it. 679 // Next annotation is for a later line, don't advance past it.
1098 if (annotation.line != line) return null; 680 if (annotation.line != line) return null;
1099 annotationsCursor++; 681 _annotationsCursor++;
1100 return annotation; 682 return annotation;
1101 } 683 }
1102 684
1103 Element lineElement(ScriptLine line, int lineNumPad) { 685 Element lineElement(S.ScriptLine line, int lineNumPad) {
1104 var e = new DivElement(); 686 var e = new DivElement();
1105 e.classes.add("sourceRow"); 687 e.classes.add("sourceRow");
1106 e.append(lineBreakpointElement(line)); 688 e.append(lineBreakpointElement(line));
1107 e.append(lineNumberElement(line, lineNumPad)); 689 e.append(lineNumberElement(line, lineNumPad));
1108 if (_includeProfile) { 690 if (_includeProfile) {
1109 e.append(lineProfileElement(line, false)); 691 e.append(lineProfileElement(line, false));
1110 e.append(lineProfileElement(line, true)); 692 e.append(lineProfileElement(line, true));
1111 } 693 }
1112 e.append(lineSourceElement(line)); 694 e.append(lineSourceElement(line));
1113 return e; 695 return e;
1114 } 696 }
1115 697
1116 Element lineProfileElement(ScriptLine line, bool self) { 698 Element lineProfileElement(S.ScriptLine line, bool self) {
1117 var e = span(''); 699 var e = span('');
1118 e.classes.add('noCopy'); 700 e.classes.add('noCopy');
1119 if (self) { 701 if (self) {
1120 e.title = 'Self %'; 702 e.title = 'Self %';
1121 } else { 703 } else {
1122 e.title = 'Total %'; 704 e.title = 'Total %';
1123 } 705 }
1124 706
1125 if (line == null) { 707 if (line == null) {
1126 e.classes.add('notSourceProfile'); 708 e.classes.add('notSourceProfile');
(...skipping 25 matching lines...) Expand all
1152 e.classes.add('hotProfile'); 734 e.classes.add('hotProfile');
1153 } else if (lineProfile.isMedium(self)) { 735 } else if (lineProfile.isMedium(self)) {
1154 e.classes.add('mediumProfile'); 736 e.classes.add('mediumProfile');
1155 } else { 737 } else {
1156 e.classes.add('coldProfile'); 738 e.classes.add('coldProfile');
1157 } 739 }
1158 740
1159 return e; 741 return e;
1160 } 742 }
1161 743
1162 Element lineBreakpointElement(ScriptLine line) { 744 Element lineBreakpointElement(S.ScriptLine line) {
1163 var e = new DivElement(); 745 var e = new DivElement();
1164 if (line == null || !_possibleBreakpointLines.contains(line.line)) { 746 if (line == null || !_possibleBreakpointLines.contains(line.line)) {
1165 e.classes.add('noCopy'); 747 e.classes.add('noCopy');
1166 e.classes.add("emptyBreakpoint"); 748 e.classes.add("emptyBreakpoint");
1167 e.text = nbsp; 749 e.text = nbsp;
1168 return e; 750 return e;
1169 } 751 }
1170 752
1171 e.text = 'B'; 753 e.text = 'B';
1172 var busy = false; 754 var busy = false;
(...skipping 23 matching lines...) Expand all
1196 line.changes.listen((_) => update()); 778 line.changes.listen((_) => update());
1197 e.onClick.listen((event) { 779 e.onClick.listen((event) {
1198 if (busy) { 780 if (busy) {
1199 return; 781 return;
1200 } 782 }
1201 busy = true; 783 busy = true;
1202 if (line.breakpoints == null) { 784 if (line.breakpoints == null) {
1203 // No breakpoint. Add it. 785 // No breakpoint. Add it.
1204 line.script.isolate.addBreakpoint(line.script, line.line) 786 line.script.isolate.addBreakpoint(line.script, line.line)
1205 .catchError((e, st) { 787 .catchError((e, st) {
1206 if (e is! ServerRpcException || 788 if (e is! S.ServerRpcException ||
1207 (e as ServerRpcException).code != 789 (e as S.ServerRpcException).code !=
1208 ServerRpcException.kCannotAddBreakpoint) { 790 S.ServerRpcException.kCannotAddBreakpoint) {
1209 app.handleException(e, st); 791 ObservatoryApplication.app.handleException(e, st);
1210 }}) 792 }})
1211 .whenComplete(() { 793 .whenComplete(() {
1212 busy = false; 794 busy = false;
1213 update(); 795 update();
1214 }); 796 });
1215 } else { 797 } else {
1216 // Existing breakpoint. Remove it. 798 // Existing breakpoint. Remove it.
1217 List pending = []; 799 List pending = [];
1218 for (var bpt in line.breakpoints) { 800 for (var bpt in line.breakpoints) {
1219 pending.add(line.script.isolate.removeBreakpoint(bpt)); 801 pending.add(line.script.isolate.removeBreakpoint(bpt));
1220 } 802 }
1221 Future.wait(pending).then((_) { 803 Future.wait(pending).then((_) {
1222 busy = false; 804 busy = false;
1223 update(); 805 update();
1224 }); 806 });
1225 } 807 }
1226 update(); 808 update();
1227 }); 809 });
1228 update(); 810 update();
1229 return e; 811 return e;
1230 } 812 }
1231 813
1232 Element lineNumberElement(ScriptLine line, int lineNumPad) { 814 Element lineNumberElement(S.ScriptLine line, int lineNumPad) {
1233 var lineNumber = line == null ? "..." : line.line; 815 var lineNumber = line == null ? "..." : line.line;
1234 var e = span("$nbsp${lineNumber.toString().padLeft(lineNumPad,nbsp)}$nbsp"); 816 var e = span("$nbsp${lineNumber.toString().padLeft(lineNumPad,nbsp)}$nbsp");
1235 e.classes.add('noCopy'); 817 e.classes.add('noCopy');
1236 if (lineNumber == _currentLine) { 818 if (lineNumber == _currentLine) {
1237 hitsCurrent(e); 819 hitsCurrent(e);
1238 return e; 820 return e;
1239 } 821 }
1240 var ranges = _rangeMap[lineNumber]; 822 var ranges = _rangeMap[lineNumber];
1241 if ((ranges == null) || ranges.isEmpty) { 823 if ((ranges == null) || ranges.isEmpty) {
1242 // This line is not code. 824 // This line is not code.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1274 } else if (hasCallInfo) { 856 } else if (hasCallInfo) {
1275 hitsNotExecuted(e); 857 hitsNotExecuted(e);
1276 } else if (compiled) { 858 } else if (compiled) {
1277 hitsCompiled(e); 859 hitsCompiled(e);
1278 } else { 860 } else {
1279 hitsNotCompiled(e); 861 hitsNotCompiled(e);
1280 } 862 }
1281 return e; 863 return e;
1282 } 864 }
1283 865
1284 Element lineSourceElement(ScriptLine line) { 866 Element lineSourceElement(S.ScriptLine line) {
1285 var e = new DivElement(); 867 var e = new DivElement();
1286 e.classes.add("sourceItem"); 868 e.classes.add("sourceItem");
1287 869
1288 if (line != null) { 870 if (line != null) {
871 e.classes.add(makeLineClass(line.line));
1289 if (line.line == _currentLine) { 872 if (line.line == _currentLine) {
1290 e.classes.add("currentLine"); 873 e.classes.add("currentLine");
1291 } 874 }
1292 875
1293 e.id = makeLineId(line.line);
1294
1295 var position = 0; 876 var position = 0;
1296 consumeUntil(var stop) { 877 consumeUntil(var stop) {
1297 if (stop <= position) { 878 if (stop <= position) {
1298 return null; // Empty gap between annotations/boundries. 879 return null; // Empty gap between annotations/boundries.
1299 } 880 }
1300 if (stop > line.text.length) { 881 if (stop > line.text.length) {
1301 // Approximated token length can run past the end of the line. 882 // Approximated token length can run past the end of the line.
1302 stop = line.text.length; 883 stop = line.text.length;
1303 } 884 }
1304 885
(...skipping 12 matching lines...) Expand all
1317 } 898 }
1318 consumeUntil(line.text.length); 899 consumeUntil(line.text.length);
1319 } 900 }
1320 901
1321 // So blank lines are included when copying script to the clipboard. 902 // So blank lines are included when copying script to the clipboard.
1322 e.append(span('\n')); 903 e.append(span('\n'));
1323 904
1324 return e; 905 return e;
1325 } 906 }
1326 907
1327 ScriptInsetElement.created() 908 /// Exclude nodes from being copied, for example the line numbers and
1328 : super.created() { 909 /// breakpoint toggles in script insets. Must be called after [root]'s
1329 _updateTask = new Task(update); 910 /// children have been added, and only supports one node at a time.
1330 } 911 static void _makeCssClassUncopyable(Element root, String className) {
1331 } 912 var noCopyNodes = root.getElementsByClassName(className);
1332 913 for (var node in noCopyNodes) {
1333 @CustomTag('refresh-button') 914 node.style.setProperty('-moz-user-select', 'none');
1334 class RefreshButtonElement extends PolymerElement { 915 node.style.setProperty('-khtml-user-select', 'none');
1335 RefreshButtonElement.created() : super.created(); 916 node.style.setProperty('-webkit-user-select', 'none');
1336 917 node.style.setProperty('-ms-user-select', 'none');
1337 @published var callback = null; 918 node.style.setProperty('user-select', 'none');
1338 bool busy = false; 919 }
1339 920 root.onCopy.listen((event) {
1340 Future buttonClick(var event, var b, var c) async { 921 // Mark the nodes as hidden before the copy happens, then mark them as
1341 if (busy) { 922 // visible on the next event loop turn.
923 for (var node in noCopyNodes) {
924 node.style.visibility = 'hidden';
925 }
926 Timer.run(() {
927 for (var node in noCopyNodes) {
928 node.style.visibility = 'visible';
929 }
930 });
931 });
932 }
933 }
934
935 const nbsp = "\u00A0";
936
937 void addInfoBox(Element content, Function infoBoxGenerator) {
938 var infoBox;
939 var show = false;
940 var originalBackground = content.style.backgroundColor;
941 buildInfoBox() {
942 infoBox = infoBoxGenerator();
943 infoBox.style.position = 'absolute';
944 infoBox.style.padding = '1em';
945 infoBox.style.border = 'solid black 2px';
946 infoBox.style.zIndex = '10';
947 infoBox.style.backgroundColor = 'white';
948 infoBox.style.cursor = 'auto';
949 // Don't inherit pre formating from the script lines.
950 infoBox.style.whiteSpace = 'normal';
951 content.append(infoBox);
952 }
953 content.onClick.listen((event) {
954 show = !show;
955 if (infoBox == null) buildInfoBox(); // Created lazily on the first click.
956 infoBox.style.display = show ? 'block' : 'none';
957 content.style.backgroundColor = show ? 'white' : originalBackground;
958 });
959
960 // Causes infoBox to be positioned relative to the bottom-left of content.
961 content.style.display = 'inline-block';
962 content.style.cursor = 'pointer';
963 }
964
965
966 void addLink(Element content, String target) {
967 // Ick, destructive but still compatible with also adding an info box.
968 var a = new AnchorElement(href: target);
969 a.text = content.text;
970 content.text = '';
971 content.append(a);
972 }
973
974
975 abstract class Annotation implements Comparable<Annotation> {
976 M.IsolateRef _isolate;
977 M.InstanceRepository _instances;
978 RenderingQueue queue;
979 int line;
980 int columnStart;
981 int columnStop;
982 int get priority;
983
984 Annotation(this._isolate, this._instances, this.queue);
985
986 void applyStyleTo(element);
987
988 int compareTo(Annotation other) {
989 if (line == other.line) {
990 if (columnStart == other.columnStart) {
991 return priority.compareTo(other.priority);
992 }
993 return columnStart.compareTo(other.columnStart);
994 }
995 return line.compareTo(other.line);
996 }
997
998 Element table() {
999 var e = new DivElement();
1000 e.style.display = "table";
1001 e.style.color = "#333";
1002 e.style.font = "400 14px 'Montserrat', sans-serif";
1003 return e;
1004 }
1005
1006 Element row([content]) {
1007 var e = new DivElement();
1008 e.style.display = "table-row";
1009 if (content is String) e.text = content;
1010 if (content is Element) e.children.add(content);
1011 return e;
1012 }
1013
1014 Element cell(content) {
1015 var e = new DivElement();
1016 e.style.display = "table-cell";
1017 e.style.padding = "3px";
1018 if (content is String) e.text = content;
1019 if (content is Element) e.children.add(content);
1020 return e;
1021 }
1022
1023 Element serviceRef(object) {
1024 return anyRef(_isolate, object, _instances, queue: queue);
1025 }
1026 }
1027
1028 class CurrentExecutionAnnotation extends Annotation {
1029 int priority = 0; // highest priority.
1030
1031 CurrentExecutionAnnotation(M.IsolateRef isolate,
1032 M.InstanceRepository instances,
1033 RenderingQueue queue)
1034 : super(isolate, instances, queue);
1035
1036 void applyStyleTo(element) {
1037 if (element == null) {
1038 return; // TODO(rmacnak): Handling overlapping annotations.
1039 }
1040 element.classes.add("currentCol");
1041 element.title = "Current execution";
1042 }
1043 }
1044
1045 class BreakpointAnnotation extends Annotation {
1046 M.Breakpoint bpt;
1047 int priority = 1;
1048
1049 BreakpointAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1050 RenderingQueue queue, this.bpt)
1051 : super(isolate, instances, queue) {
1052 var script = bpt.location.script;
1053 var location = bpt.location;
1054 if (location.tokenPos != null) {
1055 var pos = location.tokenPos;
1056 line = script.tokenToLine(pos);
1057 columnStart = script.tokenToCol(pos) - 1; // tokenToCol is 1-origin.
1058 } else if (location is M.UnresolvedSourceLocation) {
1059 line = location.line;
1060 columnStart = location.column;
1061 if (columnStart == null) {
1062 columnStart = 0;
1063 }
1064 }
1065 var length = script.guessTokenLength(line, columnStart);
1066 if (length == null) {
1067 length = 1;
1068 }
1069 columnStop = columnStart + length;
1070 }
1071
1072 void applyStyleTo(element) {
1073 if (element == null) {
1074 return; // TODO(rmacnak): Handling overlapping annotations.
1075 }
1076 var script = bpt.location.script;
1077 var pos = bpt.location.tokenPos;
1078 int line = script.tokenToLine(pos);
1079 int column = script.tokenToCol(pos);
1080 if (bpt.resolved) {
1081 element.classes.add("resolvedBreakAnnotation");
1082 } else {
1083 element.classes.add("unresolvedBreakAnnotation");
1084 }
1085 element.title = "Breakpoint ${bpt.number} at ${line}:${column}";
1086 }
1087 }
1088
1089 class LibraryAnnotation extends Annotation {
1090 S.Library target;
1091 String url;
1092 int priority = 2;
1093
1094 LibraryAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1095 RenderingQueue queue, this.target, this.url)
1096 : super(isolate, instances, queue);
1097
1098 void applyStyleTo(element) {
1099 if (element == null) {
1100 return; // TODO(rmacnak): Handling overlapping annotations.
1101 }
1102 element.title = "library ${target.uri}";
1103 addLink(element, url);
1104 }
1105 }
1106
1107 class PartAnnotation extends Annotation {
1108 S.Script part;
1109 String url;
1110 int priority = 2;
1111
1112 PartAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1113 RenderingQueue queue, this.part, this.url)
1114 : super(isolate, instances, queue);
1115
1116 void applyStyleTo(element) {
1117 if (element == null) {
1118 return; // TODO(rmacnak): Handling overlapping annotations.
1119 }
1120 element.title = "script ${part.uri}";
1121 addLink(element, url);
1122 }
1123 }
1124
1125 class LocalVariableAnnotation extends Annotation {
1126 final value;
1127 int priority = 2;
1128
1129 LocalVariableAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1130 RenderingQueue queue, S.LocalVarLocation location,
1131 this.value): super(isolate, instances, queue) {
1132 line = location.line;
1133 columnStart = location.column;
1134 columnStop = location.endColumn;
1135 }
1136
1137 void applyStyleTo(element) {
1138 if (element == null) {
1139 return; // TODO(rmacnak): Handling overlapping annotations.
1140 }
1141 element.style.fontWeight = "bold";
1142 element.title = "${value.shortName}";
1143 }
1144 }
1145
1146 class CallSiteAnnotation extends Annotation {
1147 S.CallSite callSite;
1148 int priority = 2;
1149
1150 CallSiteAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1151 RenderingQueue queue, this.callSite)
1152 : super(isolate, instances, queue) {
1153 line = callSite.line;
1154 columnStart = callSite.column - 1; // Call site is 1-origin.
1155 var tokenLength = callSite.script.guessTokenLength(line, columnStart);
1156 if (tokenLength == null) {
1157 tokenLength = callSite.name.length; // Approximate.
1158 if (callSite.name.startsWith("get:") ||
1159 callSite.name.startsWith("set:")) tokenLength -= 4;
1160 }
1161 columnStop = columnStart + tokenLength;
1162 }
1163
1164 void applyStyleTo(element) {
1165 if (element == null) {
1166 return; // TODO(rmacnak): Handling overlapping annotations.
1167 }
1168 element.style.fontWeight = "bold";
1169 element.title = "Call site: ${callSite.name}";
1170
1171 addInfoBox(element, () {
1172 var details = table();
1173 if (callSite.entries.isEmpty) {
1174 details.append(row('Call of "${callSite.name}" did not execute'));
1175 } else {
1176 var r = row();
1177 r.append(cell("Container"));
1178 r.append(cell("Count"));
1179 r.append(cell("Target"));
1180 details.append(r);
1181
1182 for (var entry in callSite.entries) {
1183 var r = row();
1184 r.append(cell(serviceRef(entry.receiver)));
1185 r.append(cell(entry.count.toString()));
1186 r.append(cell(serviceRef(entry.target)));
1187 details.append(r);
1188 }
1189 }
1190 return details;
1191 });
1192 }
1193 }
1194
1195 abstract class DeclarationAnnotation extends Annotation {
1196 String url;
1197 int priority = 2;
1198
1199 DeclarationAnnotation(M.IsolateRef isolate, M.InstanceRepository instances,
1200 RenderingQueue queue, decl, this.url)
1201 : super(isolate, instances, queue) {
1202 assert(decl.loaded);
1203 S.SourceLocation location = decl.location;
1204 if (location == null) {
1205 line = 0;
1206 columnStart = 0;
1207 columnStop = 0;
1342 return; 1208 return;
1343 } 1209 }
1344 busy = true; 1210
1345 if (callback != null) { 1211 S.Script script = location.script;
1346 await callback(); 1212 line = script.tokenToLine(location.tokenPos);
1347 } 1213 columnStart = script.tokenToCol(location.tokenPos);
1348 busy = false; 1214 if ((line == null) || (columnStart == null)) {
1349 } 1215 line = 0;
1350 } 1216 columnStart = 0;
1351 1217 columnStop = 0;
1352 1218 } else {
1353 @CustomTag('toggle-button') 1219 columnStart--; // 1-origin -> 0-origin.
1354 class ToggleButtonElement extends PolymerElement { 1220
1355 ToggleButtonElement.created() : super.created(); 1221 // The method's token position is at the beginning of the method
1356 1222 // declaration, which may be a return type annotation, metadata, static
1357 @published var callback = null; 1223 // modifier, etc. Try to scan forward to position this annotation on the
1358 @observable bool enabled = false; 1224 // function's name instead.
1359 1225 var lineSource = script.getLine(line).text;
1360 Future buttonClick(var event, var b, var c) async { 1226 var betterStart = lineSource.indexOf(decl.name, columnStart);
1361 enabled = !enabled; 1227 if (betterStart != -1) {
1362 if (callback != null) { 1228 columnStart = betterStart;
1363 await callback(enabled); 1229 }
1364 } 1230 columnStop = columnStart + decl.name.length;
1365 } 1231 }
1366 } 1232 }
1367 1233 }
1368 1234
1369 @CustomTag('source-inset') 1235 class ClassDeclarationAnnotation extends DeclarationAnnotation {
1370 class SourceInsetElement extends PolymerElement { 1236 S.Class klass;
1371 SourceInsetElement.created() : super.created(); 1237
1372 1238 ClassDeclarationAnnotation(M.IsolateRef isolate,
1373 @published SourceLocation location; 1239 M.InstanceRepository instances,
1374 @published String height = null; 1240 RenderingQueue queue, S.Class cls, String url)
1375 @published int currentPos; 1241 : klass = cls,
1376 @published bool inDebuggerContext = false; 1242 super(isolate, instances, queue, cls, url);
1377 @published ObservableList variables; 1243
1378 @published Element scroller; 1244 void applyStyleTo(element) {
1379 } 1245 if (element == null) {
1246 return; // TODO(rmacnak): Handling overlapping annotations.
1247 }
1248 element.title = "class ${klass.name}";
1249 addLink(element, url);
1250 }
1251 }
1252
1253 class FieldDeclarationAnnotation extends DeclarationAnnotation {
1254 S.Field field;
1255
1256 FieldDeclarationAnnotation(M.IsolateRef isolate,
1257 M.InstanceRepository instances,
1258 RenderingQueue queue, S.Field fld, String url)
1259 : field = fld,
1260 super(isolate, instances, queue, fld, url);
1261
1262 void applyStyleTo(element) {
1263 if (element == null) {
1264 return; // TODO(rmacnak): Handling overlapping annotations.
1265 }
1266 var tooltip = "field ${field.name}";
1267 element.title = tooltip;
1268 addLink(element, url);
1269 }
1270 }
1271
1272 class FunctionDeclarationAnnotation extends DeclarationAnnotation {
1273 S.ServiceFunction function;
1274
1275 FunctionDeclarationAnnotation(M.IsolateRef isolate,
1276 M.InstanceRepository instances,
1277 RenderingQueue queue, S.ServiceFunction func,
1278 String url)
1279 : function = func,
1280 super(isolate, instances, queue, func, url);
1281
1282 void applyStyleTo(element) {
1283 if (element == null) {
1284 return; // TODO(rmacnak): Handling overlapping annotations.
1285 }
1286 var tooltip = "method ${function.name}";
1287 if (function.isOptimizable == false) {
1288 tooltip += "\nUnoptimizable!";
1289 }
1290 if (function.isInlinable == false) {
1291 tooltip += "\nNot inlinable!";
1292 }
1293 if (function.deoptimizations > 0) {
1294 tooltip += "\nDeoptimized ${function.deoptimizations} times!";
1295 }
1296 element.title = tooltip;
1297
1298 if (function.isOptimizable == false ||
1299 function.isInlinable == false ||
1300 function.deoptimizations >0) {
1301 element.style.backgroundColor = "#EEA7A7"; // Low-saturation red.
1302 }
1303
1304 addLink(element, url);
1305 }
1306 }
1307
1308 class ScriptLineProfile {
1309 ScriptLineProfile(this.line, this.sampleCount);
1310
1311 static const kHotThreshold = 0.05; // 5%.
1312 static const kMediumThreshold = 0.02; // 2%.
1313
1314 final int line;
1315 final int sampleCount;
1316
1317 int selfTicks = 0;
1318 int totalTicks = 0;
1319
1320 void process(int exclusive, int inclusive) {
1321 selfTicks += exclusive;
1322 totalTicks += inclusive;
1323 }
1324
1325 String get formattedSelfTicks {
1326 return Utils.formatPercent(selfTicks, sampleCount);
1327 }
1328
1329 String get formattedTotalTicks {
1330 return Utils.formatPercent(totalTicks, sampleCount);
1331 }
1332
1333 double _percent(bool self) {
1334 if (sampleCount == 0) {
1335 return 0.0;
1336 }
1337 if (self) {
1338 return selfTicks / sampleCount;
1339 } else {
1340 return totalTicks / sampleCount;
1341 }
1342 }
1343
1344 bool isHot(bool self) => _percent(self) > kHotThreshold;
1345 bool isMedium(bool self) => _percent(self) > kMediumThreshold;
1346 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/observatory_element.dart ('k') | runtime/observatory/lib/src/elements/script_inset.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698