Chromium Code Reviews| Index: runtime/observatory/lib/src/elements/script_inset.dart |
| diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart |
| index 1f57b2abeb9a4eb4bba5658204d982d620e992f9..335cf4a3c171f8dbc07742fdc0b0d826bffe0268 100644 |
| --- a/runtime/observatory/lib/src/elements/script_inset.dart |
| +++ b/runtime/observatory/lib/src/elements/script_inset.dart |
| @@ -13,53 +13,59 @@ import 'package:polymer/polymer.dart'; |
| const nbsp = "\u00A0"; |
| -void addInfoBox(content, infoBox) { |
| - infoBox.style.position = 'absolute'; |
| - infoBox.style.padding = '1em'; |
| - infoBox.style.border = 'solid black 2px'; |
| - infoBox.style.zIndex = '10'; |
| - infoBox.style.backgroundColor = 'white'; |
| - infoBox.style.cursor = 'auto'; |
| - infoBox.style.display = 'none'; // Initially hidden. |
| - |
| +void addInfoBox(Element content, Function infoBoxGenerator) { |
| + var infoBox; |
| var show = false; |
| + var originalBackground = content.style.backgroundColor; |
| content.onClick.listen((event) { |
| show = !show; |
| + if (infoBox == null) { |
|
Cutch
2015/04/16 14:55:27
add comment that you are lazily generating infobox
|
| + infoBox = infoBoxGenerator(); |
| + infoBox.style.position = 'absolute'; |
| + infoBox.style.padding = '1em'; |
| + infoBox.style.border = 'solid black 2px'; |
| + infoBox.style.zIndex = '10'; |
| + infoBox.style.backgroundColor = 'white'; |
| + infoBox.style.cursor = 'auto'; |
| + content.append(infoBox); |
| + } |
| + |
| infoBox.style.display = show ? 'block' : 'none'; |
| - content.style.backgroundColor = show ? 'white' : ''; |
| + content.style.backgroundColor = show ? 'white' : originalBackground; |
| }); |
| // Causes infoBox to be positioned relative to the bottom-left of content. |
| content.style.display = 'inline-block'; |
| content.style.cursor = 'pointer'; |
| - content.append(infoBox); |
| } |
| -abstract class Annotation { |
| +abstract class Annotation implements Comparable<Annotation>{ |
|
Cutch
2015/04/16 14:55:27
whitespace between > and {
rmacnak
2015/04/16 18:43:08
Done.
|
| int line; |
| int columnStart; |
| int columnStop; |
| void applyStyleTo(element); |
| -} |
| -class CurrentExecutionAnnotation extends Annotation { |
| - void applyStyleTo(element) { |
| - if (element == null) { |
| - return; // TODO(rmacnak): Handling overlapping annotations. |
| + int compareTo(Annotation other) { |
| + if (line == other.line) { |
| + return columnStart.compareTo(other.columnStart); |
| } |
| - element.classes.add("currentCol"); |
| - element.title = "Current execution"; |
| + return line.compareTo(other.line); |
| } |
| -} |
| -class CallSiteAnnotation extends Annotation { |
| - CallSite callSite; |
| + Element table() { |
| + var e = new DivElement(); |
| + e.style.display = "table"; |
| + e.style.color = "#333"; |
| + e.style.font = "400 14px 'Montserrat', sans-serif"; |
| + return e; |
| + } |
| Element row([content]) { |
| var e = new DivElement(); |
| e.style.display = "table-row"; |
| if (content is String) e.text = content; |
| + if (content is Element) e.children.add(content); |
| return e; |
| } |
| @@ -77,32 +83,87 @@ class CallSiteAnnotation extends Annotation { |
| e.ref = object; |
| return e; |
| } |
| +} |
| - Element entriesTable() { |
| - var e = new DivElement(); |
| - e.style.display = "table"; |
| - e.style.color = "#333"; |
| - e.style.font = "400 14px 'Montserrat', sans-serif"; |
| +class CurrentExecutionAnnotation extends Annotation { |
| + void applyStyleTo(element) { |
| + if (element == null) { |
| + return; // TODO(rmacnak): Handling overlapping annotations. |
| + } |
| + element.classes.add("currentCol"); |
| + element.title = "Current execution"; |
| + } |
| +} |
| - if (callSite.entries.isEmpty) { |
| - e.append(row('Did not execute')); |
| - } else { |
| - var r = row(); |
| - r.append(cell("Container")); |
| - r.append(cell("Count")); |
| - r.append(cell("Target")); |
| - e.append(r); |
| +class CallSiteAnnotation extends Annotation { |
| + CallSite callSite; |
| + |
| + CallSiteAnnotation(this.callSite) { |
| + line = callSite.line; |
| + columnStart = callSite.column - 1; // Call site is 1-origin. |
| + var tokenLength = callSite.name.length; // Approximate. |
| + if (callSite.name.startsWith("get:") || |
| + callSite.name.startsWith("set:")) tokenLength -= 4; |
| + columnStop = columnStart + tokenLength; |
| + } |
| + |
| + void applyStyleTo(element) { |
| + if (element == null) { |
| + return; // TODO(rmacnak): Handling overlapping annotations. |
| + } |
| + element.style.fontWeight = "bold"; |
| + element.title = "Call site: ${callSite.name}"; |
| - for (var entry in callSite.entries) { |
| + addInfoBox(element, () { |
| + var details = table(); |
| + if (callSite.entries.isEmpty) { |
| + details.append(row('Did not execute')); |
| + } else { |
| var r = row(); |
| - r.append(cell(serviceRef(entry.receiverContainer))); |
| - r.append(cell(entry.count.toString())); |
| - r.append(cell(serviceRef(entry.target))); |
| - e.append(r); |
| + r.append(cell("Container")); |
| + r.append(cell("Count")); |
| + r.append(cell("Target")); |
| + details.append(r); |
| + |
| + for (var entry in callSite.entries) { |
| + var r = row(); |
| + r.append(cell(serviceRef(entry.receiverContainer))); |
| + r.append(cell(entry.count.toString())); |
| + r.append(cell(serviceRef(entry.target))); |
| + details.append(r); |
| + } |
| } |
| - } |
| + return details; |
| + }); |
| + } |
| +} |
| - return e; |
| + |
| +class FunctionDeclarationAnnotation extends Annotation { |
| + ServiceFunction function; |
| + |
| + FunctionDeclarationAnnotation(this.function) { |
| + assert(function.loaded); |
| + var script = function.script; |
| + line = script.tokenToLine(function.tokenPos); |
| + columnStart = script.tokenToCol(function.tokenPos); |
| + if (line == null || columnStart == null) { |
|
Cutch
2015/04/16 14:55:27
if ((line == null) || (columnStart == null)) {
(h
rmacnak
2015/04/16 18:43:08
Done.
|
| + line = 0; |
| + columnStart = 0; |
| + columnStop = 0; |
| + } else { |
| + columnStart--; // 1-origin -> 0-origin. |
| + |
| + // The method's token position is at the beginning of the method |
| + // declaration, which may be a return type annotation, metadata, static |
| + // modifier, etc. Try to scan forward to position this annotation on the |
| + // function's name instead. |
| + var lineSource = script.getLine(line).text; |
| + var betterStart = lineSource.indexOf(function.name, columnStart); |
| + if (betterStart != -1) columnStart = betterStart; |
|
Cutch
2015/04/16 14:55:27
if (...) {\n...\n}\n
rmacnak
2015/04/16 18:43:08
Done.
|
| + |
| + columnStop = columnStart + function.name.length; |
| + } |
| } |
| void applyStyleTo(element) { |
| @@ -110,9 +171,37 @@ class CallSiteAnnotation extends Annotation { |
| return; // TODO(rmacnak): Handling overlapping annotations. |
| } |
| element.style.fontWeight = "bold"; |
| - element.title = "Call site: ${callSite.name}"; |
| + element.title = "Function declaration: ${function.name}"; |
| + |
| + if (function.isOptimizable == false || |
| + function.isInlinable == false || |
| + function.deoptimizations > 0) { |
| + element.style.backgroundColor = "red"; |
| + } |
| - addInfoBox(element, entriesTable()); |
| + addInfoBox(element, () { |
| + var details = table(); |
| + var r = row(); |
| + r.append(cell("Function")); |
| + r.append(cell(serviceRef(function))); |
| + details.append(r); |
| + |
| + r = row(); |
| + r.append(cell("Usage Count")); |
| + r.append(cell("${function.usageCounter}")); |
| + details.append(r); |
| + |
| + if (function.isOptimizable == false) { |
| + details.append(row(cell("Unoptimizable!"))); |
| + } |
| + if (function.isInlinable == false) { |
| + details.append(row(cell("Not inlinable!"))); |
| + } |
| + if (function.deoptimizations > 0) { |
| + details.append(row("Deoptimized ${function.deoptimizations} times!")); |
| + } |
| + return details; |
| + }); |
| } |
| } |
| @@ -226,6 +315,17 @@ class ScriptInsetElement extends ObservatoryElement { |
| container.children.add(table); |
| } |
| + void loadFunctionsOf(Library lib) { |
| + lib.load().then((lib) { |
| + for (var func in lib.functions) func.load(); |
|
Cutch
2015/04/16 14:55:27
braces and new lines
rmacnak
2015/04/16 18:43:07
Done.
|
| + for (var cls in lib.classes) { |
| + cls.load().then((cls) { |
| + for (var func in cls.functions) func.load(); |
| + }); |
| + } |
| + }); |
| + } |
| + |
| void computeAnnotations() { |
| startLine = (startPos != null |
| ? script.tokenToLine(startPos) |
| @@ -249,22 +349,26 @@ class ScriptInsetElement extends ObservatoryElement { |
| annotations.add(a); |
| } |
| + loadFunctionsOf(script.library); |
| + |
| + for (var func in script.library.functions) { |
| + if (func.script == script) { |
| + annotations.add(new FunctionDeclarationAnnotation(func)); |
| + } |
| + } |
| + for (var cls in script.library.classes) { |
| + for (var func in cls.functions) { |
| + if (func.script == script) { |
| + annotations.add(new FunctionDeclarationAnnotation(func)); |
| + } |
| + } |
| + } |
| + |
| for (var callSite in script.callSites) { |
| - var a = new CallSiteAnnotation(); |
| - a.line = callSite.line; |
| - a.columnStart = callSite.column - 1; // Call site is 1-origin. |
| - var tokenLength = callSite.name.length; // Approximate. |
| - a.columnStop = a.columnStart + tokenLength; |
| - a.callSite = callSite; |
| - annotations.add(a); |
| + annotations.add(new CallSiteAnnotation(callSite)); |
| } |
| - annotations.sort((a, b) { |
| - if (a.line == b.line) { |
| - return a.columnStart.compareTo(b.columnStart); |
| - } |
| - return a.line.compareTo(b.line); |
| - }); |
| + annotations.sort(); |
| } |
| Element linesTable() { |
| @@ -322,8 +426,59 @@ class ScriptInsetElement extends ObservatoryElement { |
| } |
| Element lineBreakpointElement(ScriptLine line) { |
| - BreakpointToggleElement e = new Element.tag("breakpoint-toggle"); |
| - e.line = line; |
| + var e = new DivElement(); |
| + var busy = false; |
| + if (line == null || !line.possibleBpt) { |
| + e.classes.add("emptyBreakpoint"); |
| + e.text = nbsp; |
| + return e; |
| + } |
| + e.text = 'B'; |
| + update() { |
| + if (busy) { |
| + e.classes.clear(); |
| + e.classes.add("busyBreakpoint"); |
| + } else { |
| + if (line.breakpoints != null) { |
| + if (line.breakpointResolved) { |
| + e.classes.clear(); |
| + e.classes.add("resolvedBreakpoint"); |
| + } else { |
| + e.classes.clear(); |
| + e.classes.add("unresolvedBreakpoint"); |
| + } |
| + } else { |
| + e.classes.clear(); |
| + e.classes.add("possibleBreakpoint"); |
| + } |
| + } |
| + } |
| + line.changes.listen((_) => update()); |
| + e.onClick.listen((event) { |
| + if (busy) { |
| + return; |
| + } |
| + busy = true; |
| + if (line.breakpoints == null) { |
| + // No breakpoint. Add it. |
| + line.script.isolate.addBreakpoint(line.script, line.line).then((_) { |
| + busy = false; |
| + update(); |
| + }); |
| + } else { |
| + // Existing breakpoint. Remove it. |
| + List pending = []; |
| + for (var bpt in line.breakpoints) { |
| + pending.add(line.script.isolate.removeBreakpoint(bpt)); |
| + } |
| + Future.wait(pending).then((_) { |
| + busy = false; |
| + update(); |
| + }); |
| + } |
| + update(); |
| + }); |
| + update(); |
| return e; |
| } |
| @@ -384,33 +539,3 @@ class ScriptInsetElement extends ObservatoryElement { |
| ScriptInsetElement.created() : super.created(); |
| } |
| - |
| -@CustomTag('breakpoint-toggle') |
| -class BreakpointToggleElement extends ObservatoryElement { |
| - @published ScriptLine line; |
| - @observable bool busy = false; |
| - |
| - void toggleBreakpoint(var a, var b, var c) { |
| - if (busy) { |
| - return; |
| - } |
| - busy = true; |
| - if (line.breakpoints == null) { |
| - // No breakpoint. Add it. |
| - line.script.isolate.addBreakpoint(line.script, line.line).then((_) { |
| - busy = false; |
| - }); |
| - } else { |
| - // Existing breakpoint. Remove it. |
| - List pending = []; |
| - for (var bpt in line.breakpoints) { |
| - pending.add(line.script.isolate.removeBreakpoint(bpt)); |
| - } |
| - Future.wait(pending).then((_) { |
| - busy = false; |
| - }); |
| - } |
| - } |
| - |
| - BreakpointToggleElement.created() : super.created(); |
| -} |