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

Side by Side 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: 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library sourcemap.diff_view;
Siggi Cherem (dart-lang) 2016/01/21 18:18:34 I only did a cursory review of this file, let me k
6
7 import 'dart:async';
8 import 'dart:io';
9 import 'package:compiler/src/commandline_options.dart';
10 import 'package:compiler/src/diagnostics/invariant.dart';
11 import 'package:compiler/src/io/position_information.dart';
12 import 'package:compiler/src/js/js.dart' as js;
13 import 'sourcemap_helper.dart';
14 import 'sourcemap_html_helper.dart';
15 import 'trace_graph.dart';
16 import 'js_tracer.dart';
17
18 const String WITH_SOURCE_INFO_STYLE = 'background-color:#FF8080;';
19 const String WITHOUT_SOURCE_INFO_STYLE = 'border: solid 1px #FF8080;';
20 const String ADDITIONAL_SOURCE_INFO_STYLE = 'border: solid 1px #8080FF;';
21
22 main(List<String> args) async {
23 DEBUG_MODE = true;
24 String out = 'out.js.diff_view.html';
25 String filename;
26 List<String> currentOptions = [];
27 List<List<String>> options = [currentOptions];
28 int argGroup = 0;
29 for (String arg in args) {
30 if (arg == '--') {
31 currentOptions = [];
32 options.add(currentOptions);
33 argGroup++;
34 } else if (arg.startsWith('-o')) {
35 out = arg.substring('-o'.length);
36 } else if (arg.startsWith('--out=')) {
37 out = arg.substring('--out='.length);
38 } else if (arg.startsWith('-')) {
39 currentOptions.add(arg);
40 } else {
41 filename = arg;
42 }
43 }
44 List<String> commonArguments = options[0];
45 List<String> options1;
46 List<String> options2;
47 if (options.length == 1) {
48 // Use default options; comparing SSA and CPS output using the new
49 // source information strategy.
Siggi Cherem (dart-lang) 2016/01/21 18:18:34 is a goal also to compare the new and old source-i
Johnni Winther 2016/01/22 15:12:38 It is support as well.
50 options1 = [USE_NEW_SOURCE_INFO]..addAll(commonArguments);
51 options2 = [USE_NEW_SOURCE_INFO, Flags.useCpsIr]..addAll(commonArguments);
52 } else if (options.length == 2) {
53 // Use alternative options for the second output column.
54 options1 = commonArguments;
55 options2 = options[1]..addAll(commonArguments);
56 } else {
57 // Use specific options for both output columns.
58 options1 = options[1]..addAll(commonArguments);
59 options2 = options[2]..addAll(commonArguments);
60 }
61
62 print('Compiling ${options1.join(' ')} $filename');
63 CodeLinesResult result1 = await computeCodeLines(options1, filename);
64 print('Compiling ${options2.join(' ')} $filename');
65 CodeLinesResult result2 = await computeCodeLines(options2, filename);
66
67 StringBuffer sb = new StringBuffer();
68 sb.write('''
69 <html>
70 <head>
71 <title>Diff for $filename</title>
72 <style>
73 .lineNumber {
74 font-size: smaller;
75 color: #888;
76 }
77 .header {
78 position: fixed;
79 width: 50%;
80 background-color: #400000;
81 color: #FFFFFF;
82 height: 20px;
83 top: 0px;
84 z-index: 1000;
85 }
86 .cell {
87 max-width:500px;
88 overflow-x:auto;
89 vertical-align:top;
90 }
91 .corresponding1 {
92 background-color: #FFFFE0;
93 }
94 .corresponding2 {
95 background-color: #EFEFD0;
96 }
97 .identical1 {
98 background-color: #E0F0E0;
99 }
100 .identical2 {
101 background-color: #C0E0C0;
102 }
103 </style>
104 </head>
105 <body>''');
106
107 sb.write('''
108 <div class="header" style="left: 0px;">[${options1.join(',')}]</div>
109 <div class="header" style="right: 0px;">[${options2.join(',')}]</div>
110 <div style="position:absolute;top:22px;width:100%;height:18px;">
111 <span class="identical1">&nbsp;&nbsp;&nbsp;</span>
112 <span class="identical2">&nbsp;&nbsp;&nbsp;</span>
113 identical blocks
114 <span class="corresponding1">&nbsp;&nbsp;&nbsp;</span>
115 <span class="corresponding2">&nbsp;&nbsp;&nbsp;</span>
116 corresponding blocks
117 <span style="$WITH_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
118 offset with source information
119 <span style="$WITHOUT_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
120 offset without source information
121 <span style="$ADDITIONAL_SOURCE_INFO_STYLE">&nbsp;&nbsp;&nbsp;</span>
122 offset with unneeded source information
123 </div>
124 <table style="position:absolute;top:40px;width:100%;"><tr>
125 ''');
126
127 void addCell(String content) {
128 sb.write('''
129 <td class="cell"><pre>
130 ''');
131 sb.write(content);
132 sb.write('''
133 </pre></td>
134 ''');
135 }
136
137 List<OutputStructure> structures = [
138 OutputStructure.parse(result1.codeLines),
139 OutputStructure.parse(result2.codeLines)];
140 List<List<CodeLine>> inputLines = [result1.codeLines, result2.codeLines];
141 List<List<HtmlPart>> outputLines = [<HtmlPart>[], <HtmlPart>[]];
142
143 /// Marker to alternate output colors.
144 bool alternating = false;
145
146 /// Enable 'corresponding' background colors for [f].
147 void withMatching(f()) {
148 HtmlPart start = new ConstHtmlPart(
149 '<div class="corresponding${alternating ? '1' : '2'}">');
150 HtmlPart end = new ConstHtmlPart('</div>');
151 alternating = !alternating;
152 outputLines[0].add(start);
153 outputLines[1].add(start);
154 f();
155 outputLines[0].add(end);
156 outputLines[1].add(end);
157 }
158
159 /// Enable 'identical' background colors for [f].
160 void withIdentical(f()) {
161 HtmlPart start = new ConstHtmlPart(
162 '<div class="identical${alternating ? '1' : '2'}">');
163 HtmlPart end = new ConstHtmlPart('</div>');
164 alternating = !alternating;
165 outputLines[0].add(start);
166 outputLines[1].add(start);
167 f();
168 outputLines[0].add(end);
169 outputLines[1].add(end);
170 }
171
172 /// Output code lines in [range] from input number [index], padding the other
173 /// column with empty lines.
174 void handleSkew(int index, Interval range) {
175 int from = range.from;
176 while (from < range.to) {
177 outputLines[1 - index].add(const ConstHtmlPart('\n'));
178 outputLines[index].add(
179 new CodeLineHtmlPart(inputLines[index][from++]));
180 }
181 }
182
183 /// Output code lines of the [indices] from the corresponding inputs.
184 void addBoth(List<int> indices) {
185 outputLines[0].add(new CodeLineHtmlPart(inputLines[0][indices[0]]));
186 outputLines[1].add(new CodeLineHtmlPart(inputLines[1][indices[1]]));
187 }
188
189 /// Output code lines of the [ranges] from the corresponding inputs.
190 void addBothLines(List<Interval> ranges) {
191 Interval range1 = ranges[0];
192 Interval range2 = ranges[1];
193 int offset = 0;
194 while (range1.from + offset < range1.to &&
195 range2.from + offset < range2.to) {
196 addBoth([range1.from + offset, range2.from + offset]);
197 offset++;
198 }
199 if (range1.from + offset < range1.to) {
200 handleSkew(0, new Interval(range1.from + offset, range1.to));
201 }
202 if (range2.from + offset < range2.to) {
203 handleSkew(1, new Interval(range2.from + offset, range2.to));
204 }
205 }
206
207 /// Merge the code lines in [range1] and [range2] of the corresponding input.
208 void addRaw(Interval range1, Interval range2) {
209 match(a, b) => a.code == b.code;
210
211 List<Interval> currentMatchedIntervals;
212
213 void flushMatching() {
214 if (currentMatchedIntervals != null) {
215 withIdentical(() {
216 addBothLines(currentMatchedIntervals);
217 });
218 }
219 currentMatchedIntervals = null;
220 }
221
222 align(
223 inputLines[0],
224 inputLines[1],
225 range1: range1,
226 range2: range2,
227 match: match,
228 handleSkew: (int listIndex, Interval range) {
229 flushMatching();
230 handleSkew(listIndex, range);
231 },
232 handleMatched: (List<int> indices) {
233 if (currentMatchedIntervals == null) {
234 currentMatchedIntervals = [
235 new Interval(indices[0], indices[0] + 1),
236 new Interval(indices[1], indices[1] + 1)];
237 } else {
238 currentMatchedIntervals[0] =
239 new Interval(currentMatchedIntervals[0].from, indices[0] + 1);
240 currentMatchedIntervals[1] =
241 new Interval(currentMatchedIntervals[1].from, indices[1] + 1);
242 }
243 },
244 handleUnmatched: (List<int> indices) {
245 flushMatching();
246 addBoth(indices);
247 });
248
249 flushMatching();
250 }
251
252 /// Output the lines of the library blocks in [childRange] in
253 /// `structures[index]`, padding the other column with empty lines.
254 void addBlock(int index, Interval childRange) {
255 handleSkew(index, structures[index].getChildInterval(childRange));
256 }
257
258 /// Output the members of the [classes] aligned.
259 void addMatchingClasses(List<LibraryClass> classes) {
260 withMatching(() {
261 addBothLines(classes.map((c) => c.header).toList());
262 });
263 align(classes[0].children, classes[1].children,
264 match: (a, b) => a.name == b.name,
265 handleSkew: (int listIndex, Interval childRange) {
266 handleSkew(listIndex,
267 classes[listIndex].getChildInterval(childRange));
268 },
269 handleMatched: (List<int> indices) {
270 List<Interval> intervals = [
271 classes[0].getChild(indices[0]).interval,
272 classes[1].getChild(indices[1]).interval];
273 withMatching(() {
274 addBothLines(intervals);
275 });
276 },
277 handleUnmatched: (List<int> indices) {
278 List<Interval> intervals = [
279 classes[0].getChild(indices[0]).interval,
280 classes[1].getChild(indices[1]).interval];
281 addBothLines(intervals);
282 });
283 withMatching(() {
284 addBothLines(classes.map((c) => c.footer).toList());
285 });
286 }
287
288 /// Output the library blocks in [indices] from the corresponding
289 /// [OutputStructure]s, aligning their content.
290 void addMatchingBlocks(List<int> indices) {
291 List<LibraryBlock> blocks = [
292 structures[0].getChild(indices[0]),
293 structures[1].getChild(indices[1])];
294
295 withMatching(() {
296 addBothLines(blocks.map((b) => b.header).toList());
297 });
298 align(blocks[0].children, blocks[1].children,
299 match: (a, b) => a.name == b.name,
300 handleSkew: (int listIndex, Interval childRange) {
301 handleSkew(listIndex, blocks[listIndex].getChildInterval(childRange));
302 },
303 handleMatched: (List<int> indices) {
304 List<BasicEntity> entities = [
305 blocks[0].getChild(indices[0]),
306 blocks[1].getChild(indices[1])];
307 if (entities.every((e) => e is LibraryClass)) {
308 addMatchingClasses(entities);
309 } else {
310 withMatching(() {
311 addBothLines(entities.map((e) => e.interval).toList());
312 });
313 }
314 },
315 handleUnmatched: (List<int> indices) {
316 List<Interval> intervals = [
317 blocks[0].getChild(indices[0]).interval,
318 blocks[1].getChild(indices[1]).interval];
319 addBothLines(intervals);
320 });
321 withMatching(() {
322 addBothLines(blocks.map((b) => b.footer).toList());
323 });
324 }
325
326 /// Output the lines of the blocks in [indices] from the corresponding
327 /// [OutputStructure]s.
328 void addUnmatchedBlocks(List<int> indices) {
329 List<LibraryBlock> blocks = [
330 structures[0].getChild(indices[0]),
331 structures[1].getChild(indices[1])];
332 addBothLines([blocks[0].interval, blocks[1].interval]);
333 }
334
335
336 addRaw(structures[0].header, structures[1].header);
337
338 align(structures[0].children,
339 structures[1].children,
340 match: (a, b) => a.name == b.name,
341 handleSkew: addBlock,
342 handleMatched: addMatchingBlocks,
343 handleUnmatched: addUnmatchedBlocks);
344
345 addRaw(structures[0].footer, structures[1].footer);
346
347 addCell(htmlPartsToString(outputLines[0], inputLines[0]));
348 addCell(htmlPartsToString(outputLines[1], inputLines[1]));
349
350 sb.write('''</tr><tr>''');
351 addCell(result1.coverage.getCoverageReport());
352 addCell(result2.coverage.getCoverageReport());
353
354 sb.write('''
355 </tr></table>
356 </body>
357 </html>
358 ''');
359
360 new File(out).writeAsStringSync(sb.toString());
361 print('Diff generated in $out');
362 }
363
364 /// Align the content of [list1] and [list2].
365 ///
366 /// If provided, [range1] and [range2] aligned the subranges of [list1] and
367 /// [list2], otherwise the whole lists are aligned.
368 ///
369 /// If provided, [match] determines the equality between members of [list1] and
370 /// [list2], otherwise `==` is used.
371 ///
372 /// [handleSkew] is called when a subrange of one list is not found in the
373 /// other.
374 ///
375 /// [handleMatched] is called when two indices match up.
376 ///
377 /// [handleUnmatched] is called when two indices don't match up (none are found
378 /// in the other list).
379 void align(List list1,
380 List list2,
381 {Interval range1,
382 Interval range2,
383 bool match(a, b),
384 void handleSkew(int listIndex, Interval range),
385 void handleMatched(List<int> indices),
386 void handleUnmatched(List<int> indices)}) {
387 if (match == null) {
388 match = (a, b) => a == b;
389 }
390
391 if (range1 == null) {
392 range1 = new Interval(0, list1.length);
393 }
394 if (range2 == null) {
395 range2 = new Interval(0, list2.length);
396 }
397
398 Interval findInOther(
399 List thisLines, Interval thisRange,
400 List otherLines, Interval otherRange) {
401 for (int index = otherRange.from; index < otherRange.to; index++) {
402 if (match(thisLines[thisRange.from], otherLines[index])) {
403 int offset = 1;
404 while (thisRange.from + offset < thisRange.to &&
405 otherRange.from + offset < otherRange.to &&
406 match(thisLines[thisRange.from + offset],
407 otherLines[otherRange.from + offset])) {
408 offset++;
409 }
410 return new Interval(index, index + offset);
411 }
412 }
413 return null;
414 }
415
416 int start1 = range1.from;
417 int end1 = range1.to;
418 int start2 = range2.from;
419 int end2 = range2.to;
420
421 const int ALIGN1 = -1;
422 const int UNMATCHED = 0;
423 const int ALIGN2 = 1;
424
425 while (start1 < end1 && start2 < end2) {
426 if (match(list1[start1], list2[start2])) {
427 handleMatched([start1++, start2++]);
428 } else {
429 Interval subrange1 = new Interval(start1, end1);
430 Interval subrange2 = new Interval(start2, end2);
431 Interval element2inList1 =
432 findInOther(list1, subrange1, list2, subrange2);
433 Interval element1inList2 =
434 findInOther(list2, subrange2, list1, subrange1);
435 int choice = 0;
436 if (element2inList1 != null) {
437 if (element1inList2 != null) {
438 if (element1inList2.length > 1 && element2inList1.length > 1) {
439 choice =
440 element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
441 } else if (element2inList1.length > 1) {
442 choice = ALIGN2;
443 } else if (element1inList2.length > 1) {
444 choice = ALIGN1;
445 } else {
446 choice =
447 element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
448 }
449 } else {
450 choice = ALIGN2;
451 }
452 } else if (element1inList2 != null) {
453 choice = ALIGN1;
454 }
455 switch (choice) {
456 case ALIGN1:
457 handleSkew(0, new Interval(start1, element1inList2.from));
458 start1 = element1inList2.from;
459 break;
460 case ALIGN2:
461 handleSkew(1, new Interval(start2, element2inList1.from));
462 start2 = element2inList1.from;
463 break;
464 case UNMATCHED:
465 handleUnmatched([start1++, start2++]);
466 break;
467 }
468 }
469 }
470 if (start1 < end1) {
471 handleSkew(0, new Interval(start1, end1));
472 }
473 if (start2 < end2) {
474 handleSkew(1, new Interval(start2, end2));
475 }
476 }
477
478 // Constants used to identify the subsection of the JavaScript output. These
479 // are specifically for the unminified full_emitter output.
480 const String HEAD = ' var dart = [';
481 const String TAIL = ' }], ';
482 const String END = ' setupProgram(dart';
483
484 final RegExp TOP_LEVEL_VALUE = new RegExp(r'^ (".+?"):');
485 final RegExp TOP_LEVEL_FUNCTION =
486 new RegExp(r'^ ([a-zA-Z0-9_$]+): function');
487 final RegExp TOP_LEVEL_CLASS = new RegExp(r'^ ([a-zA-Z0-9_$]+): \{');
488
489 final RegExp MEMBER_VALUE = new RegExp(r'^ (".+?"):');
490 final RegExp MEMBER_FUNCTION = new RegExp(r'^ ([a-zA-Z0-9_$]+): function');
491 final RegExp MEMBER_OBJECT = new RegExp(r'^ ([a-zA-Z0-9_$]+): \{');
492
493 /// Subrange of the JavaScript output.
494 abstract class OutputEntity {
495 Interval get interval;
496 Interval get header;
497 Interval get footer;
498
499 List<OutputEntity> get children;
500
501 Interval getChildInterval(Interval childIndex) {
502 return new Interval(
503 children[childIndex.from].interval.from,
504 children[childIndex.to - 1].interval.to);
505
506 }
507
508 OutputEntity getChild(int index) {
509 return children[index];
510 }
511 }
512
513 /// The whole JavaScript output.
514 class OutputStructure extends OutputEntity {
515 final List<CodeLine> lines;
516 final int headerEnd;
517 final int footerStart;
518 final List<LibraryBlock> children;
519
520 OutputStructure(
521 this.lines,
522 this.headerEnd,
523 this.footerStart,
524 this.children);
525
526 Interval get interval => new Interval(0, lines.length);
527
528 Interval get header => new Interval(0, headerEnd);
529
530 Interval get footer => new Interval(footerStart, lines.length);
531
532 /// Compute the structure of the JavaScript [lines].
533 static OutputStructure parse(List<CodeLine> lines) {
534
535 int findHeaderStart(List<CodeLine> lines) {
536 int index = 0;
537 for (CodeLine line in lines) {
538 if (line.code.startsWith(HEAD)) {
539 return index;
540 }
541 index++;
542 }
543 return lines.length;
544 }
545
546 int findHeaderEnd(int start, List<CodeLine> lines) {
547 int index = start;
548 for (CodeLine line in lines.skip(start)) {
549 if (line.code.startsWith(END)) {
550 return index;
551 }
552 index++;
553 }
554 return lines.length;
555 }
556
557 String readHeader(CodeLine line) {
558 String code = line.code;
559 String ssaLineHeader;
560 if (code.startsWith(HEAD)) {
561 return code.substring(HEAD.length);
562 } else if (code.startsWith(TAIL)) {
563 return code.substring(TAIL.length);
564 }
565 return null;
566 }
567
568 List<LibraryBlock> computeHeaderMap(
569 List<CodeLine> lines, int start, int end) {
570 List<LibraryBlock> libraryBlocks = <LibraryBlock>[];
571 LibraryBlock current;
572 for (int index = start; index < end; index++) {
573 String header = readHeader(lines[index]);
574 if (header != null) {
575 if (current != null) {
576 current.to = index;
577 }
578 libraryBlocks.add(current = new LibraryBlock(header, index));
579 }
580 }
581 if (current != null) {
582 current.to = end;
583 }
584 return libraryBlocks;
585 }
586
587 int headerEnd = findHeaderStart(lines);
588 int footerStart = findHeaderEnd(headerEnd, lines);
589 List<LibraryBlock> libraryBlocks =
590 computeHeaderMap(lines, headerEnd, footerStart);
591 for (LibraryBlock block in libraryBlocks) {
592 block.preprocess(lines);
593 }
594
595 return new OutputStructure(
596 lines, headerEnd, footerStart, libraryBlocks);
597 }
598 }
599
600 abstract class AbstractEntity extends OutputEntity {
601 final String name;
602 final int from;
603 int to;
604
605 AbstractEntity(this.name, this.from);
606
607 Interval get interval => new Interval(from, to);
608 }
609
610 /// A block defining the content of a Dart library.
611 class LibraryBlock extends AbstractEntity {
612 List<BasicEntity> children = <BasicEntity>[];
613 int get headerEnd => from + 2;
614 int get footerStart => to - 1;
615
616 LibraryBlock(String name, int from) : super(name, from);
617
618 Interval get header => new Interval(from, headerEnd);
619
620 Interval get footer => new Interval(footerStart, to);
621
622 void preprocess(List<CodeLine> lines) {
623 int index = headerEnd;
624 BasicEntity current;
625 while (index < footerStart) {
626 String line = lines[index].code;
627 BasicEntity next;
628 Match matchFunction = TOP_LEVEL_FUNCTION.firstMatch(line);
629 if (matchFunction != null) {
630 next = new BasicEntity(matchFunction.group(1), index);
631 } else {
632 Match matchClass = TOP_LEVEL_CLASS.firstMatch(line);
633 if (matchClass != null) {
634 next = new LibraryClass(matchClass.group(1), index);
635 } else {
636 Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
637 if (matchValue != null) {
638 next = new BasicEntity(matchValue.group(1), index);
639 }
640 }
641 }
642 if (next != null) {
643 if (current != null) {
644 current.to = index;
645 }
646 children.add(current = next);
647 } else if (index == headerEnd) {
648 throw 'Failed to match first library block line:\n$line';
649 }
650
651 index++;
652 }
653 if (current != null) {
654 current.to = footerStart;
655 }
656
657 for (BasicEntity entity in children) {
658 entity.preprocess(lines);
659 }
660 }
661 }
662
663 /// A simple member of a library or class.
664 class BasicEntity extends AbstractEntity {
665 BasicEntity(String name, int from) : super(name, from);
666
667 Interval get header => new Interval(from, to);
668
669 Interval get footer => new Interval(to, to);
670
671 List<OutputEntity> get children => const <OutputEntity>[];
672
673 void preprocess(List<CodeLine> lines) {}
674 }
675
676 /// A block defining a Dart class.
677 class LibraryClass extends BasicEntity {
678 List<BasicEntity> children = <BasicEntity>[];
679 int get headerEnd => from + 1;
680 int get footerStart => to - 1;
681
682 LibraryClass(String name, int from) : super(name, from);
683
684 Interval get header => new Interval(from, headerEnd);
685
686 Interval get footer => new Interval(footerStart, to);
687
688 void preprocess(List<CodeLine> lines) {
689 int index = headerEnd;
690 BasicEntity current;
691 while (index < footerStart) {
692 String line = lines[index].code;
693 BasicEntity next;
694 Match matchFunction = MEMBER_FUNCTION.firstMatch(line);
695 if (matchFunction != null) {
696 next = new BasicEntity(matchFunction.group(1), index);
697 } else {
698 Match matchClass = MEMBER_OBJECT.firstMatch(line);
699 if (matchClass != null) {
700 next = new BasicEntity(matchClass.group(1), index);
701 } else {
702 Match matchValue = MEMBER_VALUE.firstMatch(line);
703 if (matchValue != null) {
704 next = new BasicEntity(matchValue.group(1), index);
705 }
706 }
707 }
708 if (next != null) {
709 if (current != null) {
710 current.to = index;
711 }
712 children.add(current = next);
713 } else if (index == headerEnd) {
714 throw 'Failed to match first library block line:\n$line';
715 }
716
717 index++;
718 }
719 if (current != null) {
720 current.to = footerStart;
721 }
722 }
723 }
724
725 class Interval {
726 final int from;
727 final int to;
728
729 const Interval(this.from, this.to);
730
731 int get length => to - from;
732 }
733
734 class HtmlPart {
735 void printHtmlOn(StringBuffer buffer) {}
736 }
737
738 class ConstHtmlPart implements HtmlPart {
739 final String html;
740
741 const ConstHtmlPart(this.html);
742
743 @override
744 void printHtmlOn(StringBuffer buffer) {
745 buffer.write(html);
746 }
747 }
748
749 class CodeLineHtmlPart implements HtmlPart {
750 final CodeLine line;
751
752 CodeLineHtmlPart(this.line);
753
754 @override
755 void printHtmlOn(StringBuffer buffer, [int lineNoWidth]) {
756 line.printHtmlOn(buffer, lineNoWidth);
757 }
758 }
759
760 /// Convert [parts] to an HTML string while checking invariants for [lines].
761 String htmlPartsToString(List<HtmlPart> parts, List<CodeLine> lines) {
762 int lineNoWidth;
763 if (lines.isNotEmpty) {
764 lineNoWidth = '${lines.last.lineNo + 1}'.length;
765 }
766 StringBuffer buffer = new StringBuffer();
767 int expectedLineNo = 0;
768 for (HtmlPart part in parts) {
769 if (part is CodeLineHtmlPart) {
770 if (part.line.lineNo != expectedLineNo) {
771 print('Expected line no $expectedLineNo, found ${part.line.lineNo}');
772 if (part.line.lineNo < expectedLineNo) {
773 print('Duplicate lines:');
774 int index = part.line.lineNo;
775 while (index <= expectedLineNo) {
776 print(lines[index++].code);
777 }
778 } else {
779 print('Missing lines:');
780 int index = expectedLineNo;
781 while (index <= part.line.lineNo) {
782 print(lines[index++].code);
783 }
784 }
785 expectedLineNo = part.line.lineNo;
786 }
787 expectedLineNo++;
788 part.printHtmlOn(buffer, lineNoWidth);
789 } else {
790 part.printHtmlOn(buffer);
791 }
792 }
793 return buffer.toString();
794 }
795
796 class CodeLinesResult {
797 final List<CodeLine> codeLines;
798 final Coverage coverage;
799
800 CodeLinesResult(this.codeLines, this.coverage);
801 }
802
803 /// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
804 Future<CodeLinesResult> computeCodeLines(
805 List<String> options,
806 String filename) async {
807 SourceMapProcessor processor = new SourceMapProcessor(filename);
808 List<SourceMapInfo> sourceMapInfoList =
809 await processor.process(options, perElement: false);
810
811 const int WITH_SOURCE_INFO = 0;
812 const int WITHOUT_SOURCE_INFO = 1;
813 const int ADDITIONAL_SOURCE_INFO = 2;
814
815 for (SourceMapInfo info in sourceMapInfoList) {
816 if (info.element != null) continue;
817
818 List<CodeLine> codeLines;
819 Coverage coverage = new Coverage();
820 List<Annotation> annotations = <Annotation>[];
821 String code = info.code;
822 TraceGraph graph = createTraceGraph(info, coverage);
823 Set<js.Node> mappedNodes = new Set<js.Node>();
824 for (TraceStep step in graph.steps) {
825 int offset;
826 if (options.contains(USE_NEW_SOURCE_INFO)) {
827 offset = step.offset.codeOffset;
828 } else {
829 offset = info.jsCodePositions[step.node].startPosition;
830 }
831 if (offset != null) {
832 int id = step.sourceLocation != null
833 ? WITH_SOURCE_INFO : WITHOUT_SOURCE_INFO;
834 annotations.add(
835 new Annotation(id, offset, null));
836 }
837 }
838 if (!options.contains(USE_NEW_SOURCE_INFO)) {
839 for (js.Node node in info.nodeMap.nodes) {
840 if (!mappedNodes.contains(node)) {
841 int offset = info.jsCodePositions[node].startPosition;
842 annotations.add(
843 new Annotation(ADDITIONAL_SOURCE_INFO, offset, null));
844 }
845 }
846 }
847 codeLines = convertAnnotatedCodeToCodeLines(
848 code,
849 annotations,
850 colorScheme: new CustomColorScheme(
851 single: (int id) {
852 if (id == WITH_SOURCE_INFO) {
853 return WITH_SOURCE_INFO_STYLE;
854 } else if (id == ADDITIONAL_SOURCE_INFO) {
855 return ADDITIONAL_SOURCE_INFO_STYLE;
856 }
857 return WITHOUT_SOURCE_INFO_STYLE;
858 },
859 multi: (List ids) {
860 if (ids.contains(WITH_SOURCE_INFO)) {
861 return WITH_SOURCE_INFO_STYLE;
862 } else if (ids.contains(ADDITIONAL_SOURCE_INFO)) {
863 return ADDITIONAL_SOURCE_INFO_STYLE;
864 }
865 return WITHOUT_SOURCE_INFO_STYLE;
866 }
867 ));
868 return new CodeLinesResult(codeLines, coverage);
869 }
870 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698