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

Unified Diff: tests/compiler/dart2js/sourcemaps/diff_view.dart

Issue 1752393002: Add source mappings and inlined Dart code to diff_view. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Updated cf. comments. Created 4 years, 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tests/compiler/dart2js/sourcemaps/diff.dart ('k') | tests/compiler/dart2js/sourcemaps/html_parts.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tests/compiler/dart2js/sourcemaps/diff_view.dart
diff --git a/tests/compiler/dart2js/sourcemaps/diff_view.dart b/tests/compiler/dart2js/sourcemaps/diff_view.dart
index c58a21edaddaa94eb75f99cb5963b52b54bce5c8..918cc44f1763b5514ba0f900b67f8f6375b0ac42 100644
--- a/tests/compiler/dart2js/sourcemaps/diff_view.dart
+++ b/tests/compiler/dart2js/sourcemaps/diff_view.dart
@@ -8,6 +8,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
+import 'package:compiler/src/common.dart';
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/diagnostics/invariant.dart';
import 'package:compiler/src/elements/elements.dart';
@@ -25,11 +26,6 @@ import 'sourcemap_helper.dart';
import 'sourcemap_html_helper.dart';
import 'trace_graph.dart';
-const String WITH_SOURCE_INFO_STYLE = 'border: solid 1px #FF8080;';
-const String WITHOUT_SOURCE_INFO_STYLE = 'background-color: #8080FF;';
-const String ADDITIONAL_SOURCE_INFO_STYLE = 'border: solid 1px #80FF80;';
-const String UNUSED_SOURCE_INFO_STYLE = 'border: solid 1px #8080FF;';
-
main(List<String> args) async {
DEBUG_MODE = true;
String out = 'out.js.diff_view.html';
@@ -39,14 +35,14 @@ main(List<String> args) async {
Map<int, String> loadFrom = {};
Map<int, String> saveTo = {};
int argGroup = 0;
- bool addAnnotations = true;
+ bool showAnnotations = true;
for (String arg in args) {
if (arg == '--') {
currentOptions = [];
optionSegments.add(currentOptions);
argGroup++;
} else if (arg == '-h') {
- addAnnotations = false;
+ showAnnotations = false;
print('Hiding annotations');
} else if (arg == '-l') {
loadFrom[argGroup] = 'out.js.diff$argGroup.json';
@@ -92,7 +88,7 @@ main(List<String> args) async {
} else {
print('Compiling ${options[i].join(' ')} $filename');
CodeLinesResult result = await computeCodeLines(
- options[i], filename, addAnnotations: addAnnotations);
+ options[i], filename);
OutputStructure structure = OutputStructure.parse(result.codeLines);
computeEntityCodeSources(result, structure);
output = new AnnotatedOutput(
@@ -112,7 +108,9 @@ main(List<String> args) async {
sourceFileManager);
outputDiffView(
- out, outputs, blocks, addAnnotations: addAnnotations);
+ out, outputs, blocks,
+ showMarkers: showAnnotations,
+ showSourceMapped: showAnnotations);
}
/// Attaches [CodeSource]s to the entities in [structure] using the
@@ -127,6 +125,44 @@ void computeEntityCodeSources(
});
}
+class CodeLineAnnotationJsonStrategy implements JsonStrategy {
+ const CodeLineAnnotationJsonStrategy();
+
+ Map encodeAnnotation(Annotation annotation) {
+ CodeLineAnnotation data = annotation.data;
+ return {
+ 'id': annotation.id,
+ 'codeOffset': annotation.codeOffset,
+ 'title': annotation.title,
+ 'data': data.toJson(this),
+ };
+ }
+
+ Annotation decodeAnnotation(Map json) {
+ return new Annotation(
+ json['id'],
+ json['codeOffset'],
+ json['title'],
+ data: CodeLineAnnotation.fromJson(json['data'], this));
+ }
+
+ @override
+ decodeLineAnnotation(json) {
+ if (json != null) {
+ return CodeSource.fromJson(json);
+ }
+ return null;
+ }
+
+ @override
+ encodeLineAnnotation(CodeSource lineAnnotation) {
+ if (lineAnnotation != null) {
+ return lineAnnotation.toJson();
+ }
+ return null;
+ }
+}
+
/// The structured output of a compilation.
class AnnotatedOutput {
final String filename;
@@ -142,7 +178,7 @@ class AnnotatedOutput {
return {
'filename': filename,
'options': options,
- 'structure': structure.toJson(),
+ 'structure': structure.toJson(const CodeLineAnnotationJsonStrategy()),
'coverage': coverage,
};
}
@@ -150,7 +186,8 @@ class AnnotatedOutput {
static AnnotatedOutput fromJson(Map json) {
String filename = json['filename'];
List<String> options = json['options'];
- OutputStructure structure = OutputStructure.fromJson(json['structure']);
+ OutputStructure structure = OutputStructure.fromJson(
+ json['structure'], const CodeLineAnnotationJsonStrategy());
String coverage = json['coverage'];
return new AnnotatedOutput(filename, options, structure, coverage);
}
@@ -175,7 +212,8 @@ void outputDiffView(
String out,
List<AnnotatedOutput> outputs,
List<DiffBlock> blocks,
- {bool addAnnotations: true}) {
+ {bool showMarkers: true,
+ bool showSourceMapped: true}) {
assert(outputs[0].filename == outputs[1].filename);
bool usePre = true;
@@ -185,16 +223,16 @@ void outputDiffView(
<head>
<title>Diff for ${outputs[0].filename}</title>
<style>
-.lineNumber {
+.${ClassNames.lineNumber} {
font-size: smaller;
color: #888;
}
-.comment {
+.${ClassNames.comment} {
font-size: smaller;
color: #888;
font-family: initial;
}
-.header {
+.${ClassNames.header} {
position: fixed;
width: 100%;
background-color: #FFFFFF;
@@ -203,31 +241,39 @@ void outputDiffView(
height: 42px;
z-index: 1000;
}
-.header-table {
+.${ClassNames.headerTable} {
width: 100%;
background-color: #400000;
color: #FFFFFF;
border-spacing: 0px;
}
-.header-column {
- width: 34%;
+.${ClassNames.headerColumn} {
}
-.legend {
+.${ClassNames.legend} {
padding: 2px;
}
-.table {
+.${ClassNames.buttons} {
+ position: fixed;
+ right: 0px;
+ top: 0px;
+ width: 220px;
+ background-color: #FFFFFF;
+ border: 1px solid #C0C0C0;
+ z-index: 2000;
+}
+.${ClassNames.table} {
position: absolute;
left: 0px;
top: 42px;
width: 100%;
border-spacing: 0px;
}
-.cell {
- max-width: 500px;
+.${ClassNames.cell},
+.${ClassNames.innerCell},
+.${ClassNames.originalDart},
+.${ClassNames.inlinedDart} {
overflow-y: hidden;
vertical-align: top;
- border-top: 1px solid #F0F0F0;
- border-left: 1px solid #F0F0F0;
''');
if (usePre) {
sb.write('''
@@ -245,89 +291,191 @@ void outputDiffView(
font-family: monospace;
padding: 0px;
}
-.corresponding1 {
+.${ClassNames.cell} {
+ border-top: 1px solid #F0F0F0;
+ border-left: 1px solid #C0C0C0;
+}
+.${ClassNames.innerCell} {
+ /*border-top: 1px solid #F8F8F8;*/
+ width: 50%;
+ max-width: 250px;
+}
+.${ClassNames.corresponding(false)} {
background-color: #FFFFE0;
}
-.corresponding2 {
+.${ClassNames.corresponding(true)} {
background-color: #EFEFD0;
}
-.identical1 {
+.${ClassNames.identical(false)} {
background-color: #E0F0E0;
}
-.identical2 {
+.${ClassNames.identical(true)} {
background-color: #C0E0C0;
}
-.line {
+.${ClassNames.line} {
padding-left: 7em;
text-indent: -7em;
margin: 0px;
}
-.column0 {
+.${ClassNames.column(column_js0)} {
+ max-width: 500px;
+ width: 500px;
+}
+.${ClassNames.column(column_js1)} {
+ max-width: 500px;
+ width: 500px;
+}
+.${ClassNames.column(column_dart)} {
+ max-width: 300px;
+ width: 300px;
+}
+.${ClassNames.colored(0)} {
+ color: #FF0000;
+}
+.${ClassNames.colored(1)} {
+ color: #C0C000;
+}
+.${ClassNames.colored(2)} {
+ color: #008000;
+}
+.${ClassNames.colored(3)} {
+ color: #00C0C0;
+}
+.${ClassNames.withSourceInfo} {
+ border: solid 1px #FF8080;
+}
+.${ClassNames.withoutSourceInfo} {
+ background-color: #8080FF;
}
-.column1 {
+.${ClassNames.additionalSourceInfo} {
+ border: solid 1px #80FF80;
}
-.column2 {
+.${ClassNames.unusedSourceInfo} {
+ border: solid 1px #8080FF;
+}
+.${ClassNames.originalDart} {
+}
+.${ClassNames.inlinedDart} {
+}
+''');
+ for (int i = 0; i < HUE_COUNT; i++) {
+ sb.write('''
+.${ClassNames.sourceMappingIndex(i)} {
+ background-color: ${toColorCss(i)};
+}
+''');
+ }
+ sb.write('''
+.${ClassNames.sourceMapped} {
+ ${showSourceMapped ? '' : 'display: none;'}
+}
+.${ClassNames.sourceMapping} {
+ ${showSourceMapped ? '' : 'border: 0px;'}
+ ${showSourceMapped ? '' : 'background-color: transparent;'}
+}
+.${ClassNames.markers} {
+ ${showMarkers ? '' : 'display: none;'}
+}
+.${ClassNames.marker} {
+ ${showMarkers ? '' : 'border: 0px;'}
+ ${showMarkers ? '' : 'background-color: transparent;'}
}
</style>
+<script>
+function isChecked(name) {
+ var box = document.getElementById('box-' + name);
+ return box.checked;
+}
+function toggleDisplay(name) {
+ var checked = isChecked(name);
+ var styleSheet = document.styleSheets[0];
+ for (var index = 0; index < styleSheet.cssRules.length; index++) {
+ var cssRule = styleSheet.cssRules[index];
+ if (cssRule.selectorText == '.' + name) {
+ if (checked) {
+ cssRule.style.removeProperty('display');
+ } else {
+ cssRule.style.display = 'none';
+ }
+ }
+ }
+ return checked;
+}
+function toggle${ClassNames.sourceMapped}() {
+ var checked = toggleDisplay('${ClassNames.sourceMapped}');
+ toggleAnnotations(checked, '${ClassNames.sourceMapping}');
+}
+function toggle${ClassNames.markers}() {
+ var checked = toggleDisplay('${ClassNames.markers}');
+ toggleAnnotations(checked, '${ClassNames.marker}');
+}
+function toggleAnnotations(show, name) {
+ var styleSheet = document.styleSheets[0];
+ for (var index = 0; index < styleSheet.cssRules.length; index++) {
+ var cssRule = styleSheet.cssRules[index];
+ if (cssRule.selectorText == '.' + name) {
+ if (show) {
+ cssRule.style.removeProperty('border');
+ cssRule.style.removeProperty('background-color');
+ } else {
+ cssRule.style.border = '0px';
+ cssRule.style.backgroundColor = 'transparent';
+ }
+ }
+ }
+}
+</script>
</head>
<body>''');
sb.write('''
-<div class="header">
-<table class="header-table"><tr>
-<td class="header-column">[${outputs[0].options.join(',')}]</td>
-<td class="header-column">[${outputs[1].options.join(',')}]</td>
-<td class="header-column">Dart code</td>
-</tr></table>
-<div class="legend">
- <span class="identical1">&nbsp;&nbsp;&nbsp;</span>
- <span class="identical2">&nbsp;&nbsp;&nbsp;</span>
+<div class="${ClassNames.header}">
+<div class="${ClassNames.legend}">
+ <span class="${ClassNames.identical(false)}">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.identical(true)}">&nbsp;&nbsp;&nbsp;</span>
identical blocks
- <span class="corresponding1">&nbsp;&nbsp;&nbsp;</span>
- <span class="corresponding2">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.corresponding(false)}">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.corresponding(true)}">&nbsp;&nbsp;&nbsp;</span>
corresponding blocks
''');
- if (addAnnotations) {
- sb.write('''
- <span style="$WITH_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ sb.write('''
+ <span class="${ClassNames.markers}">
+ <span class="${ClassNames.withSourceInfo}">&nbsp;&nbsp;&nbsp;</span>
<span title="'offset with source information' means that source information
is available for an offset which is expected to have a source location
attached. This offset has source information as intended.">
offset with source information</span>
- <span style="$WITHOUT_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.withoutSourceInfo}">&nbsp;&nbsp;&nbsp;</span>
<span title="'offset without source information' means that _no_ source
information is available for an offset which was expected to have a source
location attached. Source information must be found for this offset.">
offset without source information</span>
- <span style="$ADDITIONAL_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.additionalSourceInfo}">&nbsp;&nbsp;&nbsp;</span>
<span title="'offset with unneeded source information' means that a source
location was attached to an offset which was _not_ expected to have a source
location attached. The source location should be removed from this offset.">
offset with unneeded source information</span>
- <span style="$UNUSED_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ <span class="${ClassNames.unusedSourceInfo}">&nbsp;&nbsp;&nbsp;</span>
<span title="'offset with unused source information' means that source
information is available for an offset which is _not_ expected to have a source
location attached. This source information _could_ be used by a parent AST node
offset that is an 'offset without source information'.">
offset with unused source information</span>
+ </span>
+ <span class="${ClassNames.sourceMapped}">
''');
+ for (int i = 0; i < HUE_COUNT; i++) {
+ sb.write('''
+<span class="${ClassNames.sourceMappingIndex(i)}">&nbsp;&nbsp;</span>''');
}
-
sb.write('''
-</div></div>
-<table class="table">
+ <span title="JavaScript offsets and their corresponding Dart Code offset
+as mapped through source-maps.">
+ mapped source locations</span>
+ </span>
''');
- void addCell(String content) {
- sb.write('''
-<td class="cell"><pre>
-''');
- sb.write(content);
- sb.write('''
-</pre></td>
-''');
- }
/// Marker to alternate output colors.
bool alternating = false;
@@ -338,39 +486,73 @@ offset that is an 'offset without source information'.">
if (outputs[i].codeLines.isNotEmpty) {
lineNoWidth = '${outputs[i].codeLines.last.lineNo + 1}'.length;
}
- printContexts.add(new HtmlPrintContext(lineNoWidth: lineNoWidth));
+ printContexts.add(new HtmlPrintContext(
+ lineNoWidth: lineNoWidth,
+ getAnnotationData: getAnnotationData,
+ getLineData: getLineData));
+ }
+
+ Set<DiffColumn> allColumns = new Set<DiffColumn>();
+ for (DiffBlock block in blocks) {
+ allColumns.addAll(block.columns);
+ }
+
+ List<DiffColumn> columns = [column_js0, column_js1, column_dart]
+ .where((c) => allColumns.contains(c)).toList();
+
+ sb.write('''
+</div>
+<table class="${ClassNames.headerTable}"><tr>''');
+ for (DiffColumn column in columns) {
+ sb.write('''
+<td class="${ClassNames.headerColumn} ${ClassNames.column(column)}">''');
+ if (column.type == 'js') {
+ sb.write('''[${outputs[column.index].options.join(',')}]''');
+ } else {
+ sb.write('''Dart code''');
+ }
+ sb.write('''</td>''');
}
+ sb.write('''
+</tr></table>
+</div>
+<table class="${ClassNames.table}">
+''');
+
for (DiffBlock block in blocks) {
String className;
switch (block.kind) {
case DiffKind.UNMATCHED:
- className = 'cell';
+ className = '${ClassNames.cell}';
break;
case DiffKind.MATCHING:
- className = 'cell corresponding${alternating ? '1' : '2'}';
+ className =
+ '${ClassNames.cell} ${ClassNames.corresponding(alternating)}';
alternating = !alternating;
break;
case DiffKind.IDENTICAL:
- className = 'cell identical${alternating ? '1' : '2'}';
+ className =
+ '${ClassNames.cell} ${ClassNames.identical(alternating)}';
alternating = !alternating;
break;
}
sb.write('<tr>');
- for (int index = 0; index < 3; index++) {
- sb.write('''<td class="$className column$index">''');
- List<HtmlPart> lines = block.getColumn(index);
- if (lines.isNotEmpty) {
- for (HtmlPart line in lines) {
- sb.write('<p class="line">');
- if (index < printContexts.length) {
- line.printHtmlOn(sb, printContexts[index]);
- } else {
- line.printHtmlOn(sb, new HtmlPrintContext());
- }
- sb.write('</p>');
- }
+ for (DiffColumn column in columns) {
+ sb.write('''<td class="$className ${ClassNames.column(column)}">''');
+ HtmlPrintContext context = new HtmlPrintContext(
+ lineNoWidth: 4,
+ includeAnnotation: (Annotation annotation) {
+ CodeLineAnnotation data = annotation.data;
+ return data.annotationType == AnnotationType.WITH_SOURCE_INFO ||
+ data.annotationType == AnnotationType.ADDITIONAL_SOURCE_INFO;
+ },
+ getAnnotationData: getAnnotationData,
+ getLineData: getLineData);
+ if (column.type == 'js') {
+ context = printContexts[column.index];
}
+ block.printHtmlOn(column, sb, context);
sb.write('''</td>''');
}
sb.write('</tr>');
@@ -378,11 +560,51 @@ offset that is an 'offset without source information'.">
sb.write('''</tr><tr>''');
- addCell(outputs[0].coverage);
- addCell(outputs[1].coverage);
+ for (DiffColumn column in columns) {
+ sb.write('''
+<td class="${ClassNames.cell} ${ClassNames.column(column)}"><pre>''');
+ if (column.type == 'js') {
+ sb.write(outputs[column.index].coverage);
+ }
+ sb.write('''</td>''');
+ }
sb.write('''
</table>
+<div class="${ClassNames.buttons}">
+<input type="checkbox" id="box-${ClassNames.column(column_js0)}"
+ onclick="javascript:toggleDisplay('${ClassNames.column(column_js0)}')"
+ checked>
+Left JavaScript code<br/>
+
+<input type="checkbox" id="box-${ClassNames.column(column_js1)}"
+ onclick="javascript:toggleDisplay('${ClassNames.column(column_js1)}')"
+ checked>
+Right JavaScript code<br/>
+
+<input type="checkbox" id="box-${ClassNames.column(column_dart)}"
+ onclick="javascript:toggleDisplay('${ClassNames.column(column_dart)}')"
+ checked>
+<span title="Show column with Dart code corresponding to the block.">
+Dart code</span><br/>
+
+<input type="checkbox" id="box-${ClassNames.inlinedDart}"
+ onclick="javascript:toggleDisplay('${ClassNames.inlinedDart}')" checked>
+<span title="Show Dart code inlined into the block.">
+Inlined Dart code</span><br/>
+
+<input type="checkbox" id="box-${ClassNames.markers}"
+ onclick="javascript:toggle${ClassNames.markers}()"
+ ${showMarkers ? 'checked' : ''}>
+<span title="Show markers for JavaScript offsets with source information.">
+Source information markers</span><br/>
+
+<input type="checkbox" id="box-${ClassNames.sourceMapped}"
+ onclick="javascript:toggle${ClassNames.sourceMapped}()"
+ ${showSourceMapped ? 'checked' : ''}>
+<span title="Show line-per-line mappings of JavaScript to Dart code.">
+Source mapped Dart code</span><br/>
+</div>
</body>
</html>
''');
@@ -396,95 +618,259 @@ class CodeLinesResult {
final Coverage coverage;
final Map<int, Element> elementMap;
final SourceFileManager sourceFileManager;
+ final CodeSources codeSources;
+
+ CodeLinesResult(
+ this.codeLines,
+ this.coverage,
+ this.elementMap,
+ this.sourceFileManager,
+ this.codeSources);
+}
- CodeLinesResult(this.codeLines, this.coverage,
- this.elementMap, this.sourceFileManager);
+class CodeSources {
+ Map<Element, CodeSource> codeSourceMap = <Element, CodeSource>{};
+ Map<Uri, Map<Interval, CodeSource>> uriCodeSourceMap =
+ <Uri, Map<Interval, CodeSource>>{};
+
+ CodeSources(
+ SourceMapProcessor processor,
+ SourceMaps sourceMaps) {
+
+ CodeSource computeCodeSource(Element element) {
+ return codeSourceMap.putIfAbsent(element, () {
+ CodeSource codeSource = codeSourceFromElement(element);
+ if (codeSource.begin != null) {
+ Interval interval = new Interval(codeSource.begin, codeSource.end);
+ Map<Interval, CodeSource> intervals =
+ uriCodeSourceMap[codeSource.uri];
+ if (intervals == null) {
+ intervals = <Interval, CodeSource>{};
+ uriCodeSourceMap[codeSource.uri] = intervals;
+ } else {
+ for (Interval existingInterval in intervals.keys.toList()) {
+ if (existingInterval.contains(interval.from)) {
+ CodeSource existingCodeSource = intervals[existingInterval];
+ intervals.remove(existingInterval);
+ if (existingInterval.from < interval.from) {
+ Interval preInterval = new Interval(
+ existingInterval.from, interval.from);
+ intervals[preInterval] = existingCodeSource;
+ }
+ if (interval.to < existingInterval.to) {
+ Interval postInterval = new Interval(
+ interval.to, existingInterval.to);
+ intervals[postInterval] = existingCodeSource;
+ }
+ }
+ }
+ }
+ intervals[interval] = codeSource;
+ }
+ if (element is ClassElement) {
+ element.forEachLocalMember((Element member) {
+ codeSource.members.add(computeCodeSource(member));
+ });
+ element.implementation.forEachLocalMember((Element member) {
+ codeSource.members.add(computeCodeSource(member));
+ });
+ } else if (element is MemberElement) {
+ element.nestedClosures.forEach((Element closure) {
+ codeSource.members.add(computeCodeSource(closure));
+ });
+ }
+ return codeSource;
+ });
+ }
+
+ for (LibraryElement library in
+ sourceMaps.compiler.libraryLoader.libraries) {
+ library.forEachLocalMember(computeCodeSource);
+ library.implementation.forEachLocalMember(computeCodeSource);
+ }
+
+ uriCodeSourceMap.forEach((Uri uri, Map<Interval, CodeSource> intervals) {
+ List<Interval> sortedKeys = intervals.keys.toList()..sort(
+ (i1, i2) => i1.from.compareTo(i2.from));
+ Map<Interval, CodeSource> sortedintervals = <Interval, CodeSource>{};
+ sortedKeys.forEach((Interval interval) {
+ sortedintervals[interval] = intervals[interval];
+ });
+ uriCodeSourceMap[uri] = sortedintervals;
+ });
+ }
+
+ CodeSource sourceLocationToCodeSource(SourceLocation sourceLocation) {
+ Map<Interval, CodeSource> intervals =
+ uriCodeSourceMap[sourceLocation.sourceUri];
+ if (intervals == null) {
+ print('No code source for $sourceLocation(${sourceLocation.offset})');
+ print(' -- no intervals for ${sourceLocation.sourceUri}');
+ return null;
+ }
+ for (Interval interval in intervals.keys) {
+ if (interval.contains(sourceLocation.offset)) {
+ return intervals[interval];
+ }
+ }
+ print('No code source for $sourceLocation(${sourceLocation.offset})');
+ intervals.forEach((k, v) => print(' $k: ${v.name}'));
+ return null;
+ }
}
/// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
Future<CodeLinesResult> computeCodeLines(
List<String> options,
- String filename,
- {bool addAnnotations: true}) async {
+ String filename) async {
SourceMapProcessor processor = new SourceMapProcessor(filename);
SourceMaps sourceMaps =
await processor.process(options, perElement: true, forMain: true);
- const int WITH_SOURCE_INFO = 0;
- const int WITHOUT_SOURCE_INFO = 1;
- const int ADDITIONAL_SOURCE_INFO = 2;
- const int UNUSED_SOURCE_INFO = 3;
+ CodeSources codeSources = new CodeSources(processor, sourceMaps);
SourceMapInfo info = sourceMaps.mainSourceMapInfo;
+ int nextAnnotationId = 0;
List<CodeLine> codeLines;
Coverage coverage = new Coverage();
- List<Annotation> annotations = <Annotation>[];
-
- void addAnnotation(int id, int offset, String title) {
- annotations.add(new Annotation(id, offset, title));
+ Map<int, List<CodeLineAnnotation>> codeLineAnnotationMap =
+ <int, List<CodeLineAnnotation>>{};
+
+ /// Create a [CodeLineAnnotation] for [codeOffset].
+ void addCodeLineAnnotation(
+ {AnnotationType annotationType,
+ int codeOffset,
+ List<SourceLocation> locations: const <SourceLocation>[],
+ String stepInfo}) {
+ if (annotationType == AnnotationType.WITHOUT_SOURCE_INFO ||
+ annotationType == AnnotationType.UNUSED_SOURCE_INFO) {
+ locations = [];
+ }
+ List<CodeLocation> codeLocations = locations
+ .map((l) => new CodeLocation(l.sourceUri, l.sourceName, l.offset))
+ .toList();
+ List<CodeSource> codeSourceList = locations
+ .map(codeSources.sourceLocationToCodeSource)
+ .where((c) => c != null)
+ .toList();
+ CodeLineAnnotation data = new CodeLineAnnotation(
+ annotationId: nextAnnotationId++,
+ annotationType: annotationType,
+ codeLocations: codeLocations,
+ codeSources: codeSourceList,
+ stepInfo: stepInfo);
+ codeLineAnnotationMap.putIfAbsent(
+ codeOffset, () => <CodeLineAnnotation>[]).add(data);
}
String code = info.code;
TraceGraph graph = createTraceGraph(info, coverage);
- if (addAnnotations) {
- Set<js.Node> mappedNodes = new Set<js.Node>();
- void addSourceLocations(
- int kind, int offset, List<SourceLocation> locations, String prefix) {
+ Set<js.Node> mappedNodes = new Set<js.Node>();
+
+ /// Add an annotation for [codeOffset] pointing to [locations].
+ void addSourceLocations(
+ {AnnotationType annotationType,
+ int codeOffset,
+ List<SourceLocation> locations,
+ String stepInfo}) {
+ locations = locations.where((l) => l != null).toList();
+ addCodeLineAnnotation(
+ annotationType: annotationType,
+ codeOffset: codeOffset,
+ stepInfo: stepInfo,
+ locations: locations);
+ }
- addAnnotation(kind, offset,
- '${prefix}${locations
- .where((l) => l != null)
- .map((l) => l.shortText)
- .join('\n')}');
+ /// Add annotations for all mappings created for [node].
+ bool addSourceLocationsForNode(
+ {AnnotationType annotationType,
+ js.Node node,
+ String stepInfo}) {
+ Map<int, List<SourceLocation>> locations = info.nodeMap[node];
+ if (locations == null || locations.isEmpty) {
+ return false;
}
+ locations.forEach((int offset, List<SourceLocation> locations) {
+ addSourceLocations(
+ annotationType: annotationType,
+ codeOffset: offset,
+ locations: locations,
+ stepInfo: stepInfo);
+ });
+ mappedNodes.add(node);
+ return true;
+ }
- bool addSourceLocationsForNode(int kind, js.Node node, String prefix) {
- Map<int, List<SourceLocation>> locations = info.nodeMap[node];
- if (locations == null || locations.isEmpty) {
- return false;
+ // Add annotations based on trace steps.
+ for (TraceStep step in graph.steps) {
+ String stepInfo = '${step.id}:${step.kind}:${step.offset}';
+ bool added = addSourceLocationsForNode(
+ annotationType: AnnotationType.WITH_SOURCE_INFO,
+ node: step.node,
+ stepInfo: stepInfo);
+ if (!added) {
+ int offset;
+ if (options.contains(USE_NEW_SOURCE_INFO)) {
+ offset = step.offset.subexpressionOffset;
+ } else {
+ offset = info.jsCodePositions[step.node].startPosition;
+ }
+ if (offset != null) {
+ addCodeLineAnnotation(
+ annotationType: AnnotationType.WITHOUT_SOURCE_INFO,
+ codeOffset: offset,
+ stepInfo: stepInfo);
}
- locations.forEach(
- (int offset, List<SourceLocation> locations) {
- addSourceLocations(kind, offset, locations,
- '${prefix}\n${truncate(nodeToString(node), 80)}\n');
- });
- mappedNodes.add(node);
- return true;
}
+ }
+ // Add additional annotations for mappings created for particular nodes.
+ for (js.Node node in info.nodeMap.nodes) {
+ if (!mappedNodes.contains(node)) {
+ addSourceLocationsForNode(
+ annotationType: AnnotationType.ADDITIONAL_SOURCE_INFO,
+ node: node);
+ }
+ }
- for (TraceStep step in graph.steps) {
- String title = '${step.id}:${step.kind}:${step.offset}';
- if (!addSourceLocationsForNode(WITH_SOURCE_INFO, step.node, title)) {
- int offset;
- if (options.contains(USE_NEW_SOURCE_INFO)) {
- offset = step.offset.subexpressionOffset;
- } else {
- offset = info.jsCodePositions[step.node].startPosition;
- }
- if (offset != null) {
- addAnnotation(WITHOUT_SOURCE_INFO, offset, title);
- }
- }
+ // Add annotations for unused source information associated with nodes.
+ SourceLocationCollector collector = new SourceLocationCollector();
+ info.node.accept(collector);
+ collector.sourceLocations.forEach(
+ (js.Node node, List<SourceLocation> locations) {
+ if (!mappedNodes.contains(node)) {
+ int offset = info.jsCodePositions[node].startPosition;
+ addSourceLocations(
+ annotationType: AnnotationType.UNUSED_SOURCE_INFO,
+ codeOffset: offset,
+ locations: locations);
}
- for (js.Node node in info.nodeMap.nodes) {
- if (!mappedNodes.contains(node)) {
- addSourceLocationsForNode(ADDITIONAL_SOURCE_INFO, node, '');
+ });
+
+ // Assign consecutive ids to source mappings.
+ int nextSourceMappedLocationIndex = 0;
+ List<Annotation> annotations = <Annotation>[];
+ for (int codeOffset in codeLineAnnotationMap.keys.toList()..sort()) {
+ bool hasSourceMappedLocation = false;
+ for (CodeLineAnnotation data in codeLineAnnotationMap[codeOffset]) {
+ if (data.annotationType.isSourceMapped) {
+ data.sourceMappingIndex = nextSourceMappedLocationIndex;
+ hasSourceMappedLocation = true;
}
+ annotations.add(new Annotation(
+ data.annotationType.index,
+ codeOffset,
+ 'id=${data.annotationId}',
+ data: data));
+ }
+ if (hasSourceMappedLocation) {
+ nextSourceMappedLocationIndex++;
}
- SourceLocationCollector collector = new SourceLocationCollector();
- info.node.accept(collector);
- collector.sourceLocations.forEach(
- (js.Node node, List<SourceLocation> locations) {
- if (!mappedNodes.contains(node)) {
- int offset = info.jsCodePositions[node].startPosition;
- addSourceLocations(UNUSED_SOURCE_INFO, offset, locations, '');
- }
- });
}
+ // Associate JavaScript offsets with [Element]s.
StringSourceFile sourceFile = new StringSourceFile.fromName(filename, code);
Map<int, Element> elementMap = <int, Element>{};
sourceMaps.elementSourceMapInfos.forEach(
@@ -493,33 +879,11 @@ Future<CodeLinesResult> computeCodeLines(
elementMap[sourceFile.getLine(position.startPosition)] = element;
});
- codeLines = convertAnnotatedCodeToCodeLines(
- code,
- annotations,
- colorScheme: new CustomColorScheme(
- single: (int id) {
- if (id == WITH_SOURCE_INFO) {
- return WITH_SOURCE_INFO_STYLE;
- } else if (id == ADDITIONAL_SOURCE_INFO) {
- return ADDITIONAL_SOURCE_INFO_STYLE;
- } else if (id == UNUSED_SOURCE_INFO) {
- return UNUSED_SOURCE_INFO_STYLE;
- }
- return WITHOUT_SOURCE_INFO_STYLE;
- },
- multi: (List ids) {
- if (ids.contains(WITH_SOURCE_INFO)) {
- return WITH_SOURCE_INFO_STYLE;
- } else if (ids.contains(ADDITIONAL_SOURCE_INFO)) {
- return ADDITIONAL_SOURCE_INFO_STYLE;
- } else if (ids.contains(UNUSED_SOURCE_INFO)) {
- return UNUSED_SOURCE_INFO_STYLE;
- }
- return WITHOUT_SOURCE_INFO_STYLE;
- }
- ));
- return new CodeLinesResult(codeLines, coverage, elementMap,
- sourceMaps.sourceFileManager);
+ codeLines = convertAnnotatedCodeToCodeLines(code, annotations);
+ return new CodeLinesResult(
+ codeLines, coverage, elementMap,
+ sourceMaps.sourceFileManager,
+ codeSources);
}
/// Visitor that computes a map from [js.Node]s to all attached source
@@ -565,4 +929,70 @@ CodeSource codeSourceFromElement(Element element) {
}
}
return new CodeSource(kind, uri, name, begin, end);
-}
+}
+
+/// Create [LineData] that colors line numbers according to the [CodeSource]s
+/// origin if available.
+LineData getLineData(CodeSource lineAnnotation) {
+ if (lineAnnotation != null) {
+ return new LineData(
+ lineClass: ClassNames.line,
+ lineNumberClass:
+ '${ClassNames.lineNumber} '
+ '${ClassNames.colored(lineAnnotation.hashCode % 4)}');
+ }
+ return new LineData(
+ lineClass: ClassNames.line,
+ lineNumberClass: ClassNames.lineNumber);
+}
+
+AnnotationData getAnnotationData(Iterable<Annotation> annotations,
+ {bool forSpan}) {
+ for (Annotation annotation in annotations) {
+ CodeLineAnnotation data = annotation.data;
+ if (data.annotationType.isSourceMapped) {
+ if (forSpan) {
+ int index = data.sourceMappingIndex;
+ return new AnnotationData(
+ tag: 'span',
+ properties: {
+ 'class':
+ '${ClassNames.sourceMapping} '
+ '${ClassNames.sourceMappingIndex(index % HUE_COUNT)}',
+ 'title': 'index=$index',
+ });
+ } else {
+ return new AnnotationData(
+ tag: 'span',
+ properties: {
+ 'title': annotation.title,
+ 'class': '${ClassNames.marker} '
+ '${data.annotationType.className}'});
+ }
+ }
+ }
+ if (forSpan) return null;
+ for (Annotation annotation in annotations) {
+ CodeLineAnnotation data = annotation.data;
+ if (data.annotationType == AnnotationType.UNUSED_SOURCE_INFO) {
+ return new AnnotationData(
+ tag: 'span',
+ properties: {
+ 'title': annotation.title,
+ 'class': '${ClassNames.marker} '
+ '${data.annotationType.className}'});
+ }
+ }
+ for (Annotation annotation in annotations) {
+ CodeLineAnnotation data = annotation.data;
+ if (data.annotationType == AnnotationType.WITHOUT_SOURCE_INFO) {
+ return new AnnotationData(
+ tag: 'span',
+ properties: {
+ 'title': annotation.title,
+ 'class': '${ClassNames.marker} '
+ '${data.annotationType.className}'});
+ }
+ }
+ return null;
+}
« no previous file with comments | « tests/compiler/dart2js/sourcemaps/diff.dart ('k') | tests/compiler/dart2js/sourcemaps/html_parts.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698