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 3aeaf67619f19ba9b79c790a47f3b2965a05860a..1c9d97b8e6cbde28d26da64761fbfc20f52aa6b4 100644 |
--- a/runtime/observatory/lib/src/elements/script_inset.dart |
+++ b/runtime/observatory/lib/src/elements/script_inset.dart |
@@ -43,6 +43,16 @@ void addInfoBox(Element content, Function infoBoxGenerator) { |
content.style.cursor = 'pointer'; |
} |
+ |
+void addLink(Element content, String target) { |
+ // Ick, destructive but still compatible with also adding an info box. |
+ var a = new AnchorElement(href: target); |
+ a.text = content.text; |
+ content.text = ''; |
+ content.append(a); |
+} |
+ |
+ |
abstract class Annotation implements Comparable<Annotation> { |
int line; |
int columnStart; |
@@ -101,47 +111,29 @@ class CurrentExecutionAnnotation extends Annotation { |
class LibraryAnnotation extends Annotation { |
Library target; |
- LibraryAnnotation(this.target); |
+ String url; |
+ LibraryAnnotation(this.target, this.url); |
void applyStyleTo(element) { |
if (element == null) { |
return; // TODO(rmacnak): Handling overlapping annotations. |
} |
- element.style.fontWeight = "bold"; |
element.title = "library ${target.uri}"; |
- |
- addInfoBox(element, () { |
- var details = table(); |
- var r = row(); |
- r.append(cell("Library")); |
- r.append(cell(serviceRef(target))); |
- details.append(r); |
- |
- return details; |
- }); |
+ addLink(element, url); |
} |
} |
class PartAnnotation extends Annotation { |
Script part; |
- PartAnnotation(this.part); |
+ String url; |
+ PartAnnotation(this.part, this.url); |
void applyStyleTo(element) { |
if (element == null) { |
return; // TODO(rmacnak): Handling overlapping annotations. |
} |
- element.style.fontWeight = "bold"; |
element.title = "script ${part.uri}"; |
- |
- addInfoBox(element, () { |
- var details = table(); |
- var r = row(); |
- r.append(cell("Script")); |
- r.append(cell(serviceRef(part))); |
- details.append(r); |
- |
- return details; |
- }); |
+ addLink(element, url); |
} |
} |
@@ -207,7 +199,8 @@ class CallSiteAnnotation extends Annotation { |
} |
abstract class DeclarationAnnotation extends Annotation { |
- DeclarationAnnotation(decl) { |
+ String url; |
+ DeclarationAnnotation(decl, this.url) { |
assert(decl.loaded); |
SourceLocation location = decl.location; |
if (location == null) { |
@@ -244,89 +237,58 @@ abstract class DeclarationAnnotation extends Annotation { |
class ClassDeclarationAnnotation extends DeclarationAnnotation { |
Class klass; |
- ClassDeclarationAnnotation(Class cls) : klass = cls, super(cls); |
+ ClassDeclarationAnnotation(Class cls, String url) |
+ : klass = cls, |
+ super(cls, url); |
void applyStyleTo(element) { |
if (element == null) { |
return; // TODO(rmacnak): Handling overlapping annotations. |
} |
- element.style.fontWeight = "bold"; |
element.title = "class ${klass.name}"; |
- |
- addInfoBox(element, () { |
- var details = table(); |
- var r = row(); |
- r.append(cell("Class")); |
- r.append(cell(serviceRef(klass))); |
- details.append(r); |
- |
- return details; |
- }); |
+ addLink(element, url); |
} |
} |
class FieldDeclarationAnnotation extends DeclarationAnnotation { |
Field field; |
- FieldDeclarationAnnotation(Field fld) : field = fld, super(fld); |
+ FieldDeclarationAnnotation(Field fld, String url) |
+ : field = fld, |
+ super(fld, url); |
void applyStyleTo(element) { |
if (element == null) { |
return; // TODO(rmacnak): Handling overlapping annotations. |
} |
- element.style.fontWeight = "bold"; |
- element.title = "field ${field.name}"; |
- |
- addInfoBox(element, () { |
- var details = table(); |
- var r = row(); |
- r.append(cell("Field")); |
- r.append(cell(serviceRef(field))); |
- details.append(r); |
- |
- if (field.isStatic) { |
- if (field.loaded) { |
- r = row(); |
- r.append(cell("Value")); |
- r.append(cell(serviceRef(field.staticValue))); |
- details.append(r); |
- } |
- } else { |
- r = row(); |
- r.append(cell("Nullable")); |
- r.append(cell(field.guardNullable ? "null observed" |
- : "null not observed")); |
- details.append(r); |
- |
- r = row(); |
- r.append(cell("Types")); |
- if (field.guardClass == "dynamic") { |
- r.append(cell("various")); |
- } else if (field.guardClass == "unknown") { |
- r.append(cell("none")); |
- } else { |
- r.append(cell(serviceRef(field.guardClass))); |
- } |
- details.append(r); |
- } |
- |
- return details; |
- }); |
+ var tooltip = "field ${field.name}"; |
+ element.title = tooltip; |
+ addLink(element, url); |
} |
} |
class FunctionDeclarationAnnotation extends DeclarationAnnotation { |
ServiceFunction function; |
- FunctionDeclarationAnnotation(ServiceFunction func) |
- : function = func, super(func); |
+ FunctionDeclarationAnnotation(ServiceFunction func, String url) |
+ : function = func, |
+ super(func, url); |
void applyStyleTo(element) { |
if (element == null) { |
return; // TODO(rmacnak): Handling overlapping annotations. |
} |
- element.style.fontWeight = "bold"; |
- element.title = "method ${function.name}"; |
+ var tooltip = "method ${function.name}"; |
+ if (function.isOptimizable == false) { |
+ tooltip += "\nUnoptimizable!"; |
+ } |
+ if (function.isInlinable == false) { |
+ tooltip += "\nNot inlinable!"; |
+ } |
+ if (function.deoptimizations > 0) { |
+ tooltip += "\nDeoptimized ${function.deoptimizations} times!"; |
+ } |
+ element.title = tooltip; |
if (function.isOptimizable == false || |
function.isInlinable == false || |
@@ -334,29 +296,7 @@ class FunctionDeclarationAnnotation extends DeclarationAnnotation { |
element.style.backgroundColor = "red"; |
} |
- 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; |
- }); |
+ addLink(element, url); |
} |
} |
@@ -385,6 +325,8 @@ class ScriptInsetElement extends ObservatoryElement { |
StreamSubscription scriptChangeSubscription; |
+ bool hasLoadedLibraryDeclarations = false; |
+ |
String makeLineId(int line) { |
return 'line-$line'; |
} |
@@ -502,14 +444,20 @@ class ScriptInsetElement extends ObservatoryElement { |
addCurrentExecutionAnnotation(); |
if (!inDebuggerContext && script.library != null) { |
- loadDeclarationsOfLibrary(script.library); |
- addLibraryAnnotations(); |
- addDependencyAnnotations(); |
- addPartAnnotations(); |
- addClassAnnotations(); |
- addFieldAnnotations(); |
- addFunctionAnnotations(); |
- addCallSiteAnnotations(); |
+ if (hasLoadedLibraryDeclarations) { |
+ addLibraryAnnotations(); |
+ addDependencyAnnotations(); |
+ addPartAnnotations(); |
+ addClassAnnotations(); |
+ addFieldAnnotations(); |
+ addFunctionAnnotations(); |
+ addCallSiteAnnotations(); |
+ } else { |
+ loadDeclarationsOfLibrary(script.library).then((_) { |
+ hasLoadedLibraryDeclarations = true; |
+ update(); |
+ }); |
+ } |
} |
addLocalVariableAnnotations(); |
@@ -527,34 +475,47 @@ class ScriptInsetElement extends ObservatoryElement { |
} |
} |
- void loadDeclarationsOfLibrary(Library lib) { |
- lib.load().then((lib) { |
+ Future loadDeclarationsOfLibrary(Library lib) { |
+ return lib.load().then((lib) { |
+ var loads = []; |
for (var func in lib.functions) { |
- func.load(); |
+ loads.add(func.load()); |
} |
for (var field in lib.variables) { |
- field.load(); |
+ loads.add(field.load()); |
} |
for (var cls in lib.classes) { |
- cls.load().then((cls) { |
- for (var func in cls.functions) { |
- func.load(); |
- } |
- for (var field in cls.fields) { |
- field.load(); |
- } |
- }); |
+ loads.add(loadDeclarationsOfClass(cls)); |
} |
+ return Future.wait(loads); |
}); |
} |
+ Future loadDeclarationsOfClass(Class cls) { |
+ return cls.load().then((cls) { |
+ var loads = []; |
+ for (var func in cls.functions) { |
+ loads.add(func.load()); |
+ } |
+ for (var field in cls.fields) { |
+ loads.add(field.load()); |
+ } |
+ return Future.wait(loads); |
+ }); |
+ } |
+ |
+ String inspectLink(ServiceObject ref) { |
+ return gotoLink('/inspect', ref); |
+ } |
+ |
void addLibraryAnnotations() { |
for (ScriptLine line in script.lines) { |
// TODO(rmacnak): Use a real scanner. |
var pattern = new RegExp("library ${script.library.name}"); |
var match = pattern.firstMatch(line.text); |
if (match != null) { |
- var anno = new LibraryAnnotation(script.library); |
+ var anno = new LibraryAnnotation(script.library, |
+ inspectLink(script.library)); |
anno.line = line.line; |
anno.columnStart = match.start + 8; |
anno.columnStop = match.end; |
@@ -564,7 +525,8 @@ class ScriptInsetElement extends ObservatoryElement { |
pattern = new RegExp("part of ${script.library.name}"); |
match = pattern.firstMatch(line.text); |
if (match != null) { |
- var anno = new LibraryAnnotation(script.library); |
+ var anno = new LibraryAnnotation(script.library, |
+ inspectLink(script.library)); |
anno.line = line.line; |
anno.columnStart = match.start + 8; |
anno.columnStop = match.end; |
@@ -598,7 +560,7 @@ class ScriptInsetElement extends ObservatoryElement { |
if (match != null) { |
Library target = resolveDependency(match[1]); |
if (target != null) { |
- var anno = new LibraryAnnotation(target); |
+ var anno = new LibraryAnnotation(target, inspectLink(target)); |
anno.line = line.line; |
anno.columnStart = match.start + 8; |
anno.columnStop = match.end - 1; |
@@ -637,7 +599,7 @@ class ScriptInsetElement extends ObservatoryElement { |
if (match != null) { |
Script part = resolvePart(match[1]); |
if (part != null) { |
- var anno = new PartAnnotation(part); |
+ var anno = new PartAnnotation(part, inspectLink(part)); |
anno.line = line.line; |
anno.columnStart = match.start + 6; |
anno.columnStop = match.end - 1; |
@@ -651,7 +613,8 @@ class ScriptInsetElement extends ObservatoryElement { |
void addClassAnnotations() { |
for (var cls in script.library.classes) { |
if ((cls.location != null) && (cls.location.script == script)) { |
- annotations.add(new ClassDeclarationAnnotation(cls)); |
+ var a = new ClassDeclarationAnnotation(cls, inspectLink(cls)); |
+ annotations.add(a); |
} |
} |
} |
@@ -659,13 +622,15 @@ class ScriptInsetElement extends ObservatoryElement { |
void addFieldAnnotations() { |
for (var field in script.library.variables) { |
if ((field.location != null) && (field.location.script == script)) { |
- annotations.add(new FieldDeclarationAnnotation(field)); |
+ var a = new FieldDeclarationAnnotation(field, inspectLink(field)); |
+ annotations.add(a); |
} |
} |
for (var cls in script.library.classes) { |
for (var field in cls.fields) { |
if ((field.location != null) && (field.location.script == script)) { |
- annotations.add(new FieldDeclarationAnnotation(field)); |
+ var a = new FieldDeclarationAnnotation(field, inspectLink(field)); |
+ annotations.add(a); |
} |
} |
} |
@@ -679,7 +644,8 @@ class ScriptInsetElement extends ObservatoryElement { |
(func.kind != FunctionKind.kImplicitSetterFunction)) { |
// We annotate a field declaration with the field instead of the |
// implicit getter or setter. |
- annotations.add(new FunctionDeclarationAnnotation(func)); |
+ var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); |
+ annotations.add(a); |
} |
} |
for (var cls in script.library.classes) { |
@@ -690,7 +656,8 @@ class ScriptInsetElement extends ObservatoryElement { |
(func.kind != FunctionKind.kImplicitSetterFunction)) { |
// We annotate a field declaration with the field instead of the |
// implicit getter or setter. |
- annotations.add(new FunctionDeclarationAnnotation(func)); |
+ var a = new FunctionDeclarationAnnotation(func, inspectLink(func)); |
+ annotations.add(a); |
} |
} |
} |