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

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

Issue 1678043003: Add Dart code to diff_view (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Update status. Created 4 years, 10 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 b689360c5271bba122aab5981182e099295287e4..c58a21edaddaa94eb75f99cb5963b52b54bce5c8 100644
--- a/tests/compiler/dart2js/sourcemaps/diff_view.dart
+++ b/tests/compiler/dart2js/sourcemaps/diff_view.dart
@@ -1,40 +1,61 @@
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library sourcemap.diff_view;
import 'dart:async';
+import 'dart:convert';
import 'dart:io';
+
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/diagnostics/invariant.dart';
+import 'package:compiler/src/elements/elements.dart';
import 'package:compiler/src/io/position_information.dart';
+import 'package:compiler/src/io/source_information.dart';
+import 'package:compiler/src/io/source_file.dart';
import 'package:compiler/src/js/js.dart' as js;
+import 'package:compiler/src/js/js_debug.dart';
+
+import 'diff.dart';
+import 'html_parts.dart';
+import 'js_tracer.dart';
+import 'output_structure.dart';
import 'sourcemap_helper.dart';
import 'sourcemap_html_helper.dart';
import 'trace_graph.dart';
-import 'js_tracer.dart';
-const String WITH_SOURCE_INFO_STYLE = 'background-color:#FF8080;';
-const String WITHOUT_SOURCE_INFO_STYLE = 'border: solid 1px #FF8080;';
-const String ADDITIONAL_SOURCE_INFO_STYLE = 'border: solid 1px #8080FF;';
+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';
String filename;
List<String> currentOptions = [];
- List<List<String>> options = [currentOptions];
+ List<List<String>> optionSegments = [currentOptions];
+ Map<int, String> loadFrom = {};
+ Map<int, String> saveTo = {};
int argGroup = 0;
bool addAnnotations = true;
for (String arg in args) {
if (arg == '--') {
currentOptions = [];
- options.add(currentOptions);
+ optionSegments.add(currentOptions);
argGroup++;
} else if (arg == '-h') {
addAnnotations = false;
print('Hiding annotations');
+ } else if (arg == '-l') {
+ loadFrom[argGroup] = 'out.js.diff$argGroup.json';
+ } else if (arg.startsWith('--load=')) {
+ loadFrom[argGroup] = arg.substring('--load='.length);
+ } else if (arg == '-s') {
+ saveTo[argGroup] = 'out.js.diff$argGroup.json';
+ } else if (arg.startsWith('--save=')) {
+ saveTo[argGroup] = arg.substring('--save='.length);
} else if (arg.startsWith('-o')) {
out = arg.substring('-o'.length);
} else if (arg.startsWith('--out=')) {
@@ -45,54 +66,184 @@ main(List<String> args) async {
filename = arg;
}
}
- List<String> commonArguments = options[0];
- List<String> options1;
- List<String> options2;
- if (options.length == 1) {
+ List<String> commonArguments = optionSegments[0];
+ List<List<String>> options = <List<String>>[];
+ if (optionSegments.length == 1) {
// Use default options; comparing SSA and CPS output using the new
// source information strategy.
- options1 = [USE_NEW_SOURCE_INFO]..addAll(commonArguments);
- options2 = [USE_NEW_SOURCE_INFO, Flags.useCpsIr]..addAll(commonArguments);
- } else if (options.length == 2) {
+ options.add([USE_NEW_SOURCE_INFO]..addAll(commonArguments));
+ options.add([USE_NEW_SOURCE_INFO, Flags.useCpsIr]..addAll(commonArguments));
+ } else if (optionSegments.length == 2) {
// Use alternative options for the second output column.
- options1 = commonArguments;
- options2 = options[1]..addAll(commonArguments);
+ options.add(commonArguments);
+ options.add(optionSegments[1]..addAll(commonArguments));
} else {
// Use specific options for both output columns.
- options1 = options[1]..addAll(commonArguments);
- options2 = options[2]..addAll(commonArguments);
+ options.add(optionSegments[1]..addAll(commonArguments));
+ options.add(optionSegments[2]..addAll(commonArguments));
+ }
+
+ SourceFileManager sourceFileManager = new IOSourceFileManager(Uri.base);
+ List<AnnotatedOutput> outputs = <AnnotatedOutput>[];
+ for (int i = 0; i < 2; i++) {
+ AnnotatedOutput output;
+ if (loadFrom.containsKey(i)) {
+ output = AnnotatedOutput.loadOutput(loadFrom[i]);
+ } else {
+ print('Compiling ${options[i].join(' ')} $filename');
+ CodeLinesResult result = await computeCodeLines(
+ options[i], filename, addAnnotations: addAnnotations);
+ OutputStructure structure = OutputStructure.parse(result.codeLines);
+ computeEntityCodeSources(result, structure);
+ output = new AnnotatedOutput(
+ filename,
+ options[i],
+ structure,
+ result.coverage.getCoverageReport());
+ }
+ if (saveTo.containsKey(i)) {
+ AnnotatedOutput.saveOutput(output, saveTo[i]);
+ }
+ outputs.add(output);
+ }
+
+ List<DiffBlock> blocks = createDiffBlocks(
+ outputs.map((o) => o.structure).toList(),
+ sourceFileManager);
+
+ outputDiffView(
+ out, outputs, blocks, addAnnotations: addAnnotations);
+}
+
+/// Attaches [CodeSource]s to the entities in [structure] using the
+/// element-to-offset in [result].
+void computeEntityCodeSources(
+ CodeLinesResult result, OutputStructure structure) {
+ result.elementMap.forEach((int line, Element element) {
+ OutputEntity entity = structure.getEntityForLine(line);
+ if (entity != null) {
+ entity.codeSource = codeSourceFromElement(element);
+ }
+ });
+}
+
+/// The structured output of a compilation.
+class AnnotatedOutput {
+ final String filename;
+ final List<String> options;
+ final OutputStructure structure;
+ final String coverage;
+
+ AnnotatedOutput(this.filename, this.options, this.structure, this.coverage);
+
+ List<CodeLine> get codeLines => structure.lines;
+
+ Map toJson() {
+ return {
+ 'filename': filename,
+ 'options': options,
+ 'structure': structure.toJson(),
+ 'coverage': coverage,
+ };
+ }
+
+ static AnnotatedOutput fromJson(Map json) {
+ String filename = json['filename'];
+ List<String> options = json['options'];
+ OutputStructure structure = OutputStructure.fromJson(json['structure']);
+ String coverage = json['coverage'];
+ return new AnnotatedOutput(filename, options, structure, coverage);
+ }
+
+ static AnnotatedOutput loadOutput(filename) {
+ AnnotatedOutput output = AnnotatedOutput.fromJson(
+ JSON.decode(new File(filename).readAsStringSync()));
+ print('Output loaded from $filename');
+ return output;
}
- print('Compiling ${options1.join(' ')} $filename');
- CodeLinesResult result1 = await computeCodeLines(
- options1, filename, addAnnotations: addAnnotations);
- print('Compiling ${options2.join(' ')} $filename');
- CodeLinesResult result2 = await computeCodeLines(
- options2, filename, addAnnotations: addAnnotations);
+ static void saveOutput(AnnotatedOutput output, String filename) {
+ if (filename != null) {
+ new File(filename).writeAsStringSync(
+ const JsonEncoder.withIndent(' ').convert(output.toJson()));
+ print('Output saved in $filename');
+ }
+ }
+}
+
+void outputDiffView(
+ String out,
+ List<AnnotatedOutput> outputs,
+ List<DiffBlock> blocks,
+ {bool addAnnotations: true}) {
+ assert(outputs[0].filename == outputs[1].filename);
+ bool usePre = true;
StringBuffer sb = new StringBuffer();
sb.write('''
<html>
<head>
-<title>Diff for $filename</title>
+<title>Diff for ${outputs[0].filename}</title>
<style>
.lineNumber {
font-size: smaller;
color: #888;
}
+.comment {
+ font-size: smaller;
+ color: #888;
+ font-family: initial;
+}
.header {
position: fixed;
- width: 50%;
- background-color: #400000;
- color: #FFFFFF;
- height: 20px;
+ width: 100%;
+ background-color: #FFFFFF;
+ left: 0px;
top: 0px;
+ height: 42px;
z-index: 1000;
}
+.header-table {
+ width: 100%;
+ background-color: #400000;
+ color: #FFFFFF;
+ border-spacing: 0px;
+}
+.header-column {
+ width: 34%;
+}
+.legend {
+ padding: 2px;
+}
+.table {
+ position: absolute;
+ left: 0px;
+ top: 42px;
+ width: 100%;
+ border-spacing: 0px;
+}
.cell {
- max-width:500px;
- overflow-x:auto;
- vertical-align:top;
+ max-width: 500px;
+ overflow-y: hidden;
+ vertical-align: top;
+ border-top: 1px solid #F0F0F0;
+ border-left: 1px solid #F0F0F0;
+''');
+ if (usePre) {
+ sb.write('''
+ overflow-x: hidden;
+ white-space: pre-wrap;
+''');
+ } else {
+ sb.write('''
+ overflow-x: hidden;
+ padding-left: 100px;
+ text-indent: -100px;
+''');
+ }
+ sb.write('''
+ font-family: monospace;
+ padding: 0px;
}
.corresponding1 {
background-color: #FFFFE0;
@@ -106,14 +257,29 @@ main(List<String> args) async {
.identical2 {
background-color: #C0E0C0;
}
+.line {
+ padding-left: 7em;
+ text-indent: -7em;
+ margin: 0px;
+}
+.column0 {
+}
+.column1 {
+}
+.column2 {
+}
</style>
</head>
<body>''');
sb.write('''
-<div class="header" style="left: 0px;">[${options1.join(',')}]</div>
-<div class="header" style="right: 0px;">[${options2.join(',')}]</div>
-<div style="position:absolute;left:0px;top:22px;width:100%;height:18px;">
+<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>
identical blocks
@@ -121,19 +287,36 @@ main(List<String> args) async {
<span class="corresponding2">&nbsp;&nbsp;&nbsp;</span>
corresponding blocks
''');
+
if (addAnnotations) {
sb.write('''
<span style="$WITH_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
- offset with source information
+ <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>
- offset without source information
+ <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>
- offset with unneeded source information
+ <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 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>
''');
}
+
sb.write('''
-</div>
-<table style="position:absolute;left:0px;top:40px;width:100%;"><tr>
+</div></div>
+<table class="table">
''');
void addCell(String content) {
@@ -146,225 +329,60 @@ main(List<String> args) async {
''');
}
- List<OutputStructure> structures = [
- OutputStructure.parse(result1.codeLines),
- OutputStructure.parse(result2.codeLines)];
- List<List<CodeLine>> inputLines = [result1.codeLines, result2.codeLines];
- List<List<HtmlPart>> outputLines = [<HtmlPart>[], <HtmlPart>[]];
-
/// Marker to alternate output colors.
bool alternating = false;
- /// Enable 'corresponding' background colors for [f].
- void withMatching(f()) {
- HtmlPart start = new ConstHtmlPart(
- '<div class="corresponding${alternating ? '1' : '2'}">');
- HtmlPart end = new ConstHtmlPart('</div>');
- alternating = !alternating;
- outputLines[0].add(start);
- outputLines[1].add(start);
- f();
- outputLines[0].add(end);
- outputLines[1].add(end);
- }
-
- /// Enable 'identical' background colors for [f].
- void withIdentical(f()) {
- HtmlPart start = new ConstHtmlPart(
- '<div class="identical${alternating ? '1' : '2'}">');
- HtmlPart end = new ConstHtmlPart('</div>');
- alternating = !alternating;
- outputLines[0].add(start);
- outputLines[1].add(start);
- f();
- outputLines[0].add(end);
- outputLines[1].add(end);
- }
-
- /// Output code lines in [range] from input number [index], padding the other
- /// column with empty lines.
- void handleSkew(int index, Interval range) {
- int from = range.from;
- while (from < range.to) {
- outputLines[1 - index].add(const ConstHtmlPart('\n'));
- outputLines[index].add(
- new CodeLineHtmlPart(inputLines[index][from++]));
+ List<HtmlPrintContext> printContexts = <HtmlPrintContext>[];
+ for (int i = 0; i < 2; i++) {
+ int lineNoWidth;
+ if (outputs[i].codeLines.isNotEmpty) {
+ lineNoWidth = '${outputs[i].codeLines.last.lineNo + 1}'.length;
}
+ printContexts.add(new HtmlPrintContext(lineNoWidth: lineNoWidth));
}
- /// Output code lines of the [indices] from the corresponding inputs.
- void addBoth(List<int> indices) {
- outputLines[0].add(new CodeLineHtmlPart(inputLines[0][indices[0]]));
- outputLines[1].add(new CodeLineHtmlPart(inputLines[1][indices[1]]));
- }
-
- /// Output code lines of the [ranges] from the corresponding inputs.
- void addBothLines(List<Interval> ranges) {
- Interval range1 = ranges[0];
- Interval range2 = ranges[1];
- int offset = 0;
- while (range1.from + offset < range1.to &&
- range2.from + offset < range2.to) {
- addBoth([range1.from + offset, range2.from + offset]);
- offset++;
- }
- if (range1.from + offset < range1.to) {
- handleSkew(0, new Interval(range1.from + offset, range1.to));
+ for (DiffBlock block in blocks) {
+ String className;
+ switch (block.kind) {
+ case DiffKind.UNMATCHED:
+ className = 'cell';
+ break;
+ case DiffKind.MATCHING:
+ className = 'cell corresponding${alternating ? '1' : '2'}';
+ alternating = !alternating;
+ break;
+ case DiffKind.IDENTICAL:
+ className = 'cell identical${alternating ? '1' : '2'}';
+ alternating = !alternating;
+ break;
}
- if (range2.from + offset < range2.to) {
- handleSkew(1, new Interval(range2.from + offset, range2.to));
- }
- }
-
- /// Merge the code lines in [range1] and [range2] of the corresponding input.
- void addRaw(Interval range1, Interval range2) {
- match(a, b) => a.code == b.code;
-
- List<Interval> currentMatchedIntervals;
-
- void flushMatching() {
- if (currentMatchedIntervals != null) {
- withIdentical(() {
- addBothLines(currentMatchedIntervals);
- });
- }
- currentMatchedIntervals = null;
- }
-
- align(
- inputLines[0],
- inputLines[1],
- range1: range1,
- range2: range2,
- match: match,
- handleSkew: (int listIndex, Interval range) {
- flushMatching();
- handleSkew(listIndex, range);
- },
- handleMatched: (List<int> indices) {
- if (currentMatchedIntervals == null) {
- currentMatchedIntervals = [
- new Interval(indices[0], indices[0] + 1),
- new Interval(indices[1], indices[1] + 1)];
- } else {
- currentMatchedIntervals[0] =
- new Interval(currentMatchedIntervals[0].from, indices[0] + 1);
- currentMatchedIntervals[1] =
- new Interval(currentMatchedIntervals[1].from, indices[1] + 1);
- }
- },
- handleUnmatched: (List<int> indices) {
- flushMatching();
- addBoth(indices);
- });
-
- flushMatching();
- }
-
- /// Output the lines of the library blocks in [childRange] in
- /// `structures[index]`, padding the other column with empty lines.
- void addBlock(int index, Interval childRange) {
- handleSkew(index, structures[index].getChildInterval(childRange));
- }
-
- /// Output the members of the [classes] aligned.
- void addMatchingClasses(List<LibraryClass> classes) {
- withMatching(() {
- addBothLines(classes.map((c) => c.header).toList());
- });
- align(classes[0].children, classes[1].children,
- match: (a, b) => a.name == b.name,
- handleSkew: (int listIndex, Interval childRange) {
- handleSkew(listIndex,
- classes[listIndex].getChildInterval(childRange));
- },
- handleMatched: (List<int> indices) {
- List<Interval> intervals = [
- classes[0].getChild(indices[0]).interval,
- classes[1].getChild(indices[1]).interval];
- withMatching(() {
- addBothLines(intervals);
- });
- },
- handleUnmatched: (List<int> indices) {
- List<Interval> intervals = [
- classes[0].getChild(indices[0]).interval,
- classes[1].getChild(indices[1]).interval];
- addBothLines(intervals);
- });
- withMatching(() {
- addBothLines(classes.map((c) => c.footer).toList());
- });
- }
-
- /// Output the library blocks in [indices] from the corresponding
- /// [OutputStructure]s, aligning their content.
- void addMatchingBlocks(List<int> indices) {
- List<LibraryBlock> blocks = [
- structures[0].getChild(indices[0]),
- structures[1].getChild(indices[1])];
-
- withMatching(() {
- addBothLines(blocks.map((b) => b.header).toList());
- });
- align(blocks[0].children, blocks[1].children,
- match: (a, b) => a.name == b.name,
- handleSkew: (int listIndex, Interval childRange) {
- handleSkew(listIndex, blocks[listIndex].getChildInterval(childRange));
- },
- handleMatched: (List<int> indices) {
- List<BasicEntity> entities = [
- blocks[0].getChild(indices[0]),
- blocks[1].getChild(indices[1])];
- if (entities.every((e) => e is LibraryClass)) {
- addMatchingClasses(entities);
+ 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 {
- withMatching(() {
- addBothLines(entities.map((e) => e.interval).toList());
- });
+ line.printHtmlOn(sb, new HtmlPrintContext());
}
- },
- handleUnmatched: (List<int> indices) {
- List<Interval> intervals = [
- blocks[0].getChild(indices[0]).interval,
- blocks[1].getChild(indices[1]).interval];
- addBothLines(intervals);
- });
- withMatching(() {
- addBothLines(blocks.map((b) => b.footer).toList());
- });
- }
-
- /// Output the lines of the blocks in [indices] from the corresponding
- /// [OutputStructure]s.
- void addUnmatchedBlocks(List<int> indices) {
- List<LibraryBlock> blocks = [
- structures[0].getChild(indices[0]),
- structures[1].getChild(indices[1])];
- addBothLines([blocks[0].interval, blocks[1].interval]);
+ sb.write('</p>');
+ }
+ }
+ sb.write('''</td>''');
+ }
+ sb.write('</tr>');
}
-
- addRaw(structures[0].header, structures[1].header);
-
- align(structures[0].children,
- structures[1].children,
- match: (a, b) => a.name == b.name,
- handleSkew: addBlock,
- handleMatched: addMatchingBlocks,
- handleUnmatched: addUnmatchedBlocks);
-
- addRaw(structures[0].footer, structures[1].footer);
-
- addCell(htmlPartsToString(outputLines[0], inputLines[0]));
- addCell(htmlPartsToString(outputLines[1], inputLines[1]));
-
sb.write('''</tr><tr>''');
- addCell(result1.coverage.getCoverageReport());
- addCell(result2.coverage.getCoverageReport());
+
+ addCell(outputs[0].coverage);
+ addCell(outputs[1].coverage);
sb.write('''
-</tr></table>
+</table>
</body>
</html>
''');
@@ -373,515 +391,178 @@ main(List<String> args) async {
print('Diff generated in $out');
}
-/// Align the content of [list1] and [list2].
-///
-/// If provided, [range1] and [range2] aligned the subranges of [list1] and
-/// [list2], otherwise the whole lists are aligned.
-///
-/// If provided, [match] determines the equality between members of [list1] and
-/// [list2], otherwise `==` is used.
-///
-/// [handleSkew] is called when a subrange of one list is not found in the
-/// other.
-///
-/// [handleMatched] is called when two indices match up.
-///
-/// [handleUnmatched] is called when two indices don't match up (none are found
-/// in the other list).
-void align(List list1,
- List list2,
- {Interval range1,
- Interval range2,
- bool match(a, b),
- void handleSkew(int listIndex, Interval range),
- void handleMatched(List<int> indices),
- void handleUnmatched(List<int> indices)}) {
- if (match == null) {
- match = (a, b) => a == b;
- }
-
- if (range1 == null) {
- range1 = new Interval(0, list1.length);
- }
- if (range2 == null) {
- range2 = new Interval(0, list2.length);
- }
-
- Interval findInOther(
- List thisLines, Interval thisRange,
- List otherLines, Interval otherRange) {
- for (int index = otherRange.from; index < otherRange.to; index++) {
- if (match(thisLines[thisRange.from], otherLines[index])) {
- int offset = 1;
- while (thisRange.from + offset < thisRange.to &&
- otherRange.from + offset < otherRange.to &&
- match(thisLines[thisRange.from + offset],
- otherLines[otherRange.from + offset])) {
- offset++;
- }
- return new Interval(index, index + offset);
- }
- }
- return null;
- }
-
- int start1 = range1.from;
- int end1 = range1.to;
- int start2 = range2.from;
- int end2 = range2.to;
-
- const int ALIGN1 = -1;
- const int UNMATCHED = 0;
- const int ALIGN2 = 1;
+class CodeLinesResult {
+ final List<CodeLine> codeLines;
+ final Coverage coverage;
+ final Map<int, Element> elementMap;
+ final SourceFileManager sourceFileManager;
- while (start1 < end1 && start2 < end2) {
- if (match(list1[start1], list2[start2])) {
- handleMatched([start1++, start2++]);
- } else {
- Interval subrange1 = new Interval(start1, end1);
- Interval subrange2 = new Interval(start2, end2);
- Interval element2inList1 =
- findInOther(list1, subrange1, list2, subrange2);
- Interval element1inList2 =
- findInOther(list2, subrange2, list1, subrange1);
- int choice = 0;
- if (element2inList1 != null) {
- if (element1inList2 != null) {
- if (element1inList2.length > 1 && element2inList1.length > 1) {
- choice =
- element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
- } else if (element2inList1.length > 1) {
- choice = ALIGN2;
- } else if (element1inList2.length > 1) {
- choice = ALIGN1;
- } else {
- choice =
- element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
- }
- } else {
- choice = ALIGN2;
- }
- } else if (element1inList2 != null) {
- choice = ALIGN1;
- }
- switch (choice) {
- case ALIGN1:
- handleSkew(0, new Interval(start1, element1inList2.from));
- start1 = element1inList2.from;
- break;
- case ALIGN2:
- handleSkew(1, new Interval(start2, element2inList1.from));
- start2 = element2inList1.from;
- break;
- case UNMATCHED:
- handleUnmatched([start1++, start2++]);
- break;
- }
- }
- }
- if (start1 < end1) {
- handleSkew(0, new Interval(start1, end1));
- }
- if (start2 < end2) {
- handleSkew(1, new Interval(start2, end2));
- }
+ CodeLinesResult(this.codeLines, this.coverage,
+ this.elementMap, this.sourceFileManager);
}
-// Constants used to identify the subsection of the JavaScript output. These
-// are specifically for the unminified full_emitter output.
-const String HEAD = ' var dart = [';
-const String TAIL = ' }], ';
-const String END = ' setupProgram(dart';
-
-final RegExp TOP_LEVEL_VALUE = new RegExp(r'^ (".+?"):');
-final RegExp TOP_LEVEL_FUNCTION =
- new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
-final RegExp TOP_LEVEL_CLASS = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
-
-final RegExp MEMBER_VALUE = new RegExp(r'^ (".+?"):');
-final RegExp MEMBER_FUNCTION =
- new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
-final RegExp MEMBER_OBJECT = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
-
-/// Subrange of the JavaScript output.
-abstract class OutputEntity {
- Interval get interval;
- Interval get header;
- Interval get footer;
+/// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
+Future<CodeLinesResult> computeCodeLines(
+ List<String> options,
+ String filename,
+ {bool addAnnotations: true}) async {
+ SourceMapProcessor processor = new SourceMapProcessor(filename);
+ SourceMaps sourceMaps =
+ await processor.process(options, perElement: true, forMain: true);
- List<OutputEntity> get children;
+ const int WITH_SOURCE_INFO = 0;
+ const int WITHOUT_SOURCE_INFO = 1;
+ const int ADDITIONAL_SOURCE_INFO = 2;
+ const int UNUSED_SOURCE_INFO = 3;
- Interval getChildInterval(Interval childIndex) {
- return new Interval(
- children[childIndex.from].interval.from,
- children[childIndex.to - 1].interval.to);
+ SourceMapInfo info = sourceMaps.mainSourceMapInfo;
- }
+ List<CodeLine> codeLines;
+ Coverage coverage = new Coverage();
+ List<Annotation> annotations = <Annotation>[];
- OutputEntity getChild(int index) {
- return children[index];
+ void addAnnotation(int id, int offset, String title) {
+ annotations.add(new Annotation(id, offset, title));
}
-}
-
-/// The whole JavaScript output.
-class OutputStructure extends OutputEntity {
- final List<CodeLine> lines;
- final int headerEnd;
- final int footerStart;
- final List<LibraryBlock> children;
-
- OutputStructure(
- this.lines,
- this.headerEnd,
- this.footerStart,
- this.children);
- Interval get interval => new Interval(0, lines.length);
-
- Interval get header => new Interval(0, headerEnd);
-
- Interval get footer => new Interval(footerStart, lines.length);
-
- /// Compute the structure of the JavaScript [lines].
- static OutputStructure parse(List<CodeLine> lines) {
-
- int findHeaderStart(List<CodeLine> lines) {
- int index = 0;
- for (CodeLine line in lines) {
- if (line.code.startsWith(HEAD)) {
- return index;
- }
- index++;
- }
- return lines.length;
- }
+ String code = info.code;
+ TraceGraph graph = createTraceGraph(info, coverage);
+ if (addAnnotations) {
+ Set<js.Node> mappedNodes = new Set<js.Node>();
- int findHeaderEnd(int start, List<CodeLine> lines) {
- int index = start;
- for (CodeLine line in lines.skip(start)) {
- if (line.code.startsWith(END)) {
- return index;
- }
- index++;
- }
- return lines.length;
- }
+ void addSourceLocations(
+ int kind, int offset, List<SourceLocation> locations, String prefix) {
- String readHeader(CodeLine line) {
- String code = line.code;
- String ssaLineHeader;
- if (code.startsWith(HEAD)) {
- return code.substring(HEAD.length);
- } else if (code.startsWith(TAIL)) {
- return code.substring(TAIL.length);
- }
- return null;
+ addAnnotation(kind, offset,
+ '${prefix}${locations
+ .where((l) => l != null)
+ .map((l) => l.shortText)
+ .join('\n')}');
}
- List<LibraryBlock> computeHeaderMap(
- List<CodeLine> lines, int start, int end) {
- List<LibraryBlock> libraryBlocks = <LibraryBlock>[];
- LibraryBlock current;
- for (int index = start; index < end; index++) {
- String header = readHeader(lines[index]);
- if (header != null) {
- if (current != null) {
- current.to = index;
- }
- libraryBlocks.add(current = new LibraryBlock(header, index));
- }
+ bool addSourceLocationsForNode(int kind, js.Node node, String prefix) {
+ Map<int, List<SourceLocation>> locations = info.nodeMap[node];
+ if (locations == null || locations.isEmpty) {
+ return false;
}
- if (current != null) {
- current.to = end;
- }
- return libraryBlocks;
+ locations.forEach(
+ (int offset, List<SourceLocation> locations) {
+ addSourceLocations(kind, offset, locations,
+ '${prefix}\n${truncate(nodeToString(node), 80)}\n');
+ });
+ mappedNodes.add(node);
+ return true;
}
- int headerEnd = findHeaderStart(lines);
- int footerStart = findHeaderEnd(headerEnd, lines);
- List<LibraryBlock> libraryBlocks =
- computeHeaderMap(lines, headerEnd, footerStart);
- for (LibraryBlock block in libraryBlocks) {
- block.preprocess(lines);
- }
- return new OutputStructure(
- lines, headerEnd, footerStart, libraryBlocks);
- }
-}
-
-abstract class AbstractEntity extends OutputEntity {
- final String name;
- final int from;
- int to;
-
- AbstractEntity(this.name, this.from);
-
- Interval get interval => new Interval(from, to);
-}
-
-/// A block defining the content of a Dart library.
-class LibraryBlock extends AbstractEntity {
- List<BasicEntity> children = <BasicEntity>[];
- int get headerEnd => from + 2;
- int get footerStart => to - 1;
-
- LibraryBlock(String name, int from) : super(name, from);
-
- Interval get header => new Interval(from, headerEnd);
-
- Interval get footer => new Interval(footerStart, to);
-
- void preprocess(List<CodeLine> lines) {
- int index = headerEnd;
- BasicEntity current;
- while (index < footerStart) {
- String line = lines[index].code;
- BasicEntity next;
- Match matchFunction = TOP_LEVEL_FUNCTION.firstMatch(line);
- if (matchFunction != null) {
- next = new BasicEntity(matchFunction.group(1), index);
- } else {
- Match matchClass = TOP_LEVEL_CLASS.firstMatch(line);
- if (matchClass != null) {
- next = new LibraryClass(matchClass.group(1), index);
+ 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 {
- Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
- if (matchValue != null) {
- next = new BasicEntity(matchValue.group(1), index);
- }
+ offset = info.jsCodePositions[step.node].startPosition;
}
- }
- if (next != null) {
- if (current != null) {
- current.to = index;
+ if (offset != null) {
+ addAnnotation(WITHOUT_SOURCE_INFO, offset, title);
}
- children.add(current = next);
- } else if (index == headerEnd) {
- throw 'Failed to match first library block line:\n$line';
}
-
- index++;
- }
- if (current != null) {
- current.to = footerStart;
}
-
- for (BasicEntity entity in children) {
- entity.preprocess(lines);
+ for (js.Node node in info.nodeMap.nodes) {
+ if (!mappedNodes.contains(node)) {
+ addSourceLocationsForNode(ADDITIONAL_SOURCE_INFO, node, '');
+ }
}
+ 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, '');
+ }
+ });
}
-}
-
-/// A simple member of a library or class.
-class BasicEntity extends AbstractEntity {
- BasicEntity(String name, int from) : super(name, from);
-
- Interval get header => new Interval(from, to);
-
- Interval get footer => new Interval(to, to);
- List<OutputEntity> get children => const <OutputEntity>[];
-
- void preprocess(List<CodeLine> lines) {}
-}
-
-/// A block defining a Dart class.
-class LibraryClass extends BasicEntity {
- List<BasicEntity> children = <BasicEntity>[];
- int get headerEnd => from + 1;
- int get footerStart => to - 1;
-
- LibraryClass(String name, int from) : super(name, from);
-
- Interval get header => new Interval(from, headerEnd);
-
- Interval get footer => new Interval(footerStart, to);
-
- void preprocess(List<CodeLine> lines) {
- int index = headerEnd;
- BasicEntity current;
- while (index < footerStart) {
- String line = lines[index].code;
- BasicEntity next;
- Match matchFunction = MEMBER_FUNCTION.firstMatch(line);
- if (matchFunction != null) {
- next = new BasicEntity(matchFunction.group(1), index);
- } else {
- Match matchClass = MEMBER_OBJECT.firstMatch(line);
- if (matchClass != null) {
- next = new BasicEntity(matchClass.group(1), index);
- } else {
- Match matchValue = MEMBER_VALUE.firstMatch(line);
- if (matchValue != null) {
- next = new BasicEntity(matchValue.group(1), index);
+ StringSourceFile sourceFile = new StringSourceFile.fromName(filename, code);
+ Map<int, Element> elementMap = <int, Element>{};
+ sourceMaps.elementSourceMapInfos.forEach(
+ (Element element, SourceMapInfo info) {
+ CodePosition position = info.jsCodePositions[info.node];
+ 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;
}
- }
- if (next != null) {
- if (current != null) {
- current.to = index;
- }
- children.add(current = next);
- } else if (index == headerEnd) {
- throw 'Failed to match first library block line:\n$line';
- }
-
- index++;
- }
- if (current != null) {
- current.to = footerStart;
- }
- }
-}
-
-class Interval {
- final int from;
- final int to;
-
- const Interval(this.from, this.to);
-
- int get length => to - from;
-}
-
-class HtmlPart {
- void printHtmlOn(StringBuffer buffer) {}
-}
-
-class ConstHtmlPart implements HtmlPart {
- final String html;
-
- const ConstHtmlPart(this.html);
-
- @override
- void printHtmlOn(StringBuffer buffer) {
- buffer.write(html);
- }
+ ));
+ return new CodeLinesResult(codeLines, coverage, elementMap,
+ sourceMaps.sourceFileManager);
}
-class CodeLineHtmlPart implements HtmlPart {
- final CodeLine line;
-
- CodeLineHtmlPart(this.line);
+/// Visitor that computes a map from [js.Node]s to all attached source
+/// locations.
+class SourceLocationCollector extends js.BaseVisitor {
+ Map<js.Node, List<SourceLocation>> sourceLocations =
+ <js.Node, List<SourceLocation>>{};
@override
- void printHtmlOn(StringBuffer buffer, [int lineNoWidth]) {
- line.printHtmlOn(buffer, lineNoWidth);
- }
-}
-
-/// Convert [parts] to an HTML string while checking invariants for [lines].
-String htmlPartsToString(List<HtmlPart> parts, List<CodeLine> lines) {
- int lineNoWidth;
- if (lines.isNotEmpty) {
- lineNoWidth = '${lines.last.lineNo + 1}'.length;
- }
- StringBuffer buffer = new StringBuffer();
- int expectedLineNo = 0;
- for (HtmlPart part in parts) {
- if (part is CodeLineHtmlPart) {
- if (part.line.lineNo != expectedLineNo) {
- print('Expected line no $expectedLineNo, found ${part.line.lineNo}');
- if (part.line.lineNo < expectedLineNo) {
- print('Duplicate lines:');
- int index = part.line.lineNo;
- while (index <= expectedLineNo) {
- print(lines[index++].code);
- }
- } else {
- print('Missing lines:');
- int index = expectedLineNo;
- while (index <= part.line.lineNo) {
- print(lines[index++].code);
- }
- }
- expectedLineNo = part.line.lineNo;
- }
- expectedLineNo++;
- part.printHtmlOn(buffer, lineNoWidth);
- } else {
- part.printHtmlOn(buffer);
+ visitNode(js.Node node) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ if (sourceInformation != null) {
+ sourceLocations[node] = sourceInformation.sourceLocations;
}
+ node.visitChildren(this);
}
- return buffer.toString();
}
-class CodeLinesResult {
- final List<CodeLine> codeLines;
- final Coverage coverage;
-
- CodeLinesResult(this.codeLines, this.coverage);
-}
-
-/// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
-Future<CodeLinesResult> computeCodeLines(
- List<String> options,
- String filename,
- {bool addAnnotations: true}) async {
- SourceMapProcessor processor = new SourceMapProcessor(filename);
- List<SourceMapInfo> sourceMapInfoList =
- await processor.process(options, perElement: false);
-
- const int WITH_SOURCE_INFO = 0;
- const int WITHOUT_SOURCE_INFO = 1;
- const int ADDITIONAL_SOURCE_INFO = 2;
-
- for (SourceMapInfo info in sourceMapInfoList) {
- if (info.element != null) continue;
-
- List<CodeLine> codeLines;
- Coverage coverage = new Coverage();
- List<Annotation> annotations = <Annotation>[];
-
- String code = info.code;
- TraceGraph graph = createTraceGraph(info, coverage);
- if (addAnnotations) {
- Set<js.Node> mappedNodes = new Set<js.Node>();
- for (TraceStep step in graph.steps) {
- int offset;
- if (options.contains(USE_NEW_SOURCE_INFO)) {
- offset = step.offset.subexpressionOffset;
- } else {
- offset = info.jsCodePositions[step.node].startPosition;
- }
- if (offset != null) {
- int id = step.sourceLocation != null
- ? WITH_SOURCE_INFO : WITHOUT_SOURCE_INFO;
- annotations.add(
- new Annotation(id, offset, null));
- }
- }
- if (!options.contains(USE_NEW_SOURCE_INFO)) {
- for (js.Node node in info.nodeMap.nodes) {
- if (!mappedNodes.contains(node)) {
- int offset = info.jsCodePositions[node].startPosition;
- annotations.add(
- new Annotation(ADDITIONAL_SOURCE_INFO, offset, null));
- }
- }
- }
+/// Compute a [CodeSource] for source span of [element].
+CodeSource codeSourceFromElement(Element element) {
+ CodeKind kind;
+ Uri uri;
+ String name;
+ int begin;
+ int end;
+ if (element.isLibrary) {
+ LibraryElement library = element;
+ kind = CodeKind.LIBRARY;
+ name = library.libraryOrScriptName;
+ uri = library.entryCompilationUnit.script.resourceUri;
+ } else if (element.isClass) {
+ kind = CodeKind.CLASS;
+ name = element.name;
+ uri = element.compilationUnit.script.resourceUri;
+ } else {
+ AstElement astElement = element.implementation;
+ kind = CodeKind.MEMBER;
+ uri = astElement.compilationUnit.script.resourceUri;
+ name = computeElementNameForSourceMaps(astElement);
+ if (astElement.hasNode) {
+ begin = astElement.node.getBeginToken().charOffset;
+ end = astElement.node.getEndToken().charEnd;
}
- 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;
- }
- 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;
- }
- return WITHOUT_SOURCE_INFO_STYLE;
- }
- ));
- return new CodeLinesResult(codeLines, coverage);
}
-}
+ return new CodeSource(kind, uri, name, begin, end);
+}
« 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