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

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

Issue 1617083002: Base JavaScript code position computation on JavaScript tracer. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Updated cf. comments. Created 4 years, 11 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/js_parser_test.dart ('k') | tests/compiler/dart2js/sourcemaps/js_tracer.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
new file mode 100644
index 0000000000000000000000000000000000000000..fc9eb6197cf3f0dc9f9ec6d388536cdec4bd765d
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/diff_view.dart
@@ -0,0 +1,871 @@
+// Copyright (c) 2015, 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:io';
+import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/diagnostics/invariant.dart';
+import 'package:compiler/src/io/position_information.dart';
+import 'package:compiler/src/js/js.dart' as js;
+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;';
+
+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];
+ int argGroup = 0;
+ for (String arg in args) {
+ if (arg == '--') {
+ currentOptions = [];
+ options.add(currentOptions);
+ argGroup++;
+ } else if (arg.startsWith('-o')) {
+ out = arg.substring('-o'.length);
+ } else if (arg.startsWith('--out=')) {
+ out = arg.substring('--out='.length);
+ } else if (arg.startsWith('-')) {
+ currentOptions.add(arg);
+ } else {
+ filename = arg;
+ }
+ }
+ List<String> commonArguments = options[0];
+ List<String> options1;
+ List<String> options2;
+ if (options.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) {
+ // Use alternative options for the second output column.
+ options1 = commonArguments;
+ options2 = options[1]..addAll(commonArguments);
+ } else {
+ // Use specific options for both output columns.
+ options1 = options[1]..addAll(commonArguments);
+ options2 = options[2]..addAll(commonArguments);
+ }
+
+ print('Compiling ${options1.join(' ')} $filename');
+ CodeLinesResult result1 = await computeCodeLines(options1, filename);
+ print('Compiling ${options2.join(' ')} $filename');
+ CodeLinesResult result2 = await computeCodeLines(options2, filename);
+
+ StringBuffer sb = new StringBuffer();
+ sb.write('''
+<html>
+<head>
+<title>Diff for $filename</title>
+<style>
+.lineNumber {
+ font-size: smaller;
+ color: #888;
+}
+.header {
+ position: fixed;
+ width: 50%;
+ background-color: #400000;
+ color: #FFFFFF;
+ height: 20px;
+ top: 0px;
+ z-index: 1000;
+}
+.cell {
+ max-width:500px;
+ overflow-x:auto;
+ vertical-align:top;
+}
+.corresponding1 {
+ background-color: #FFFFE0;
+}
+.corresponding2 {
+ background-color: #EFEFD0;
+}
+.identical1 {
+ background-color: #E0F0E0;
+}
+.identical2 {
+ background-color: #C0E0C0;
+}
+</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;top:22px;width:100%;height:18px;">
+ <span class="identical1">&nbsp;&nbsp;&nbsp;</span>
+ <span class="identical2">&nbsp;&nbsp;&nbsp;</span>
+ identical blocks
+ <span class="corresponding1">&nbsp;&nbsp;&nbsp;</span>
+ <span class="corresponding2">&nbsp;&nbsp;&nbsp;</span>
+ corresponding blocks
+ <span style="$WITH_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ offset with source information
+ <span style="$WITHOUT_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ offset without source information
+ <span style="$ADDITIONAL_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
+ offset with unneeded source information
+</div>
+<table style="position:absolute;top:40px;width:100%;"><tr>
+''');
+
+ void addCell(String content) {
+ sb.write('''
+<td class="cell"><pre>
+''');
+ sb.write(content);
+ sb.write('''
+</pre></td>
+''');
+ }
+
+ 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++]));
+ }
+ }
+
+ /// 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));
+ }
+ 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);
+ } else {
+ withMatching(() {
+ addBothLines(entities.map((e) => e.interval).toList());
+ });
+ }
+ },
+ 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]);
+ }
+
+
+ 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());
+
+ sb.write('''
+</tr></table>
+</body>
+</html>
+''');
+
+ new File(out).writeAsStringSync(sb.toString());
+ 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;
+
+ 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));
+ }
+}
+
+// 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;
+
+ List<OutputEntity> get children;
+
+ Interval getChildInterval(Interval childIndex) {
+ return new Interval(
+ children[childIndex.from].interval.from,
+ children[childIndex.to - 1].interval.to);
+
+ }
+
+ OutputEntity getChild(int index) {
+ return children[index];
+ }
+}
+
+/// 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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));
+ }
+ }
+ if (current != null) {
+ current.to = end;
+ }
+ return libraryBlocks;
+ }
+
+ 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);
+ } else {
+ Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
+ if (matchValue != null) {
+ next = new BasicEntity(matchValue.group(1), index);
+ }
+ }
+ }
+ 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;
+ }
+
+ for (BasicEntity entity in children) {
+ entity.preprocess(lines);
+ }
+ }
+}
+
+/// 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);
+ }
+ }
+ }
+ 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);
+ }
+}
+
+class CodeLineHtmlPart implements HtmlPart {
+ final CodeLine line;
+
+ CodeLineHtmlPart(this.line);
+
+ @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);
+ }
+ }
+ 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) 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);
+ 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.codeOffset;
+ } 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));
+ }
+ }
+ }
+ 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);
+ }
+}
« no previous file with comments | « tests/compiler/dart2js/js_parser_test.dart ('k') | tests/compiler/dart2js/sourcemaps/js_tracer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698