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

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: 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 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;
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.
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 =
491 new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
492 final RegExp MEMBER_OBJECT = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
493
494 /// Subrange of the JavaScript output.
495 abstract class OutputEntity {
496 Interval get interval;
497 Interval get header;
498 Interval get footer;
499
500 List<OutputEntity> get children;
501
502 Interval getChildInterval(Interval childIndex) {
503 return new Interval(
504 children[childIndex.from].interval.from,
505 children[childIndex.to - 1].interval.to);
506
507 }
508
509 OutputEntity getChild(int index) {
510 return children[index];
511 }
512 }
513
514 /// The whole JavaScript output.
515 class OutputStructure extends OutputEntity {
516 final List<CodeLine> lines;
517 final int headerEnd;
518 final int footerStart;
519 final List<LibraryBlock> children;
520
521 OutputStructure(
522 this.lines,
523 this.headerEnd,
524 this.footerStart,
525 this.children);
526
527 Interval get interval => new Interval(0, lines.length);
528
529 Interval get header => new Interval(0, headerEnd);
530
531 Interval get footer => new Interval(footerStart, lines.length);
532
533 /// Compute the structure of the JavaScript [lines].
534 static OutputStructure parse(List<CodeLine> lines) {
535
536 int findHeaderStart(List<CodeLine> lines) {
537 int index = 0;
538 for (CodeLine line in lines) {
539 if (line.code.startsWith(HEAD)) {
540 return index;
541 }
542 index++;
543 }
544 return lines.length;
545 }
546
547 int findHeaderEnd(int start, List<CodeLine> lines) {
548 int index = start;
549 for (CodeLine line in lines.skip(start)) {
550 if (line.code.startsWith(END)) {
551 return index;
552 }
553 index++;
554 }
555 return lines.length;
556 }
557
558 String readHeader(CodeLine line) {
559 String code = line.code;
560 String ssaLineHeader;
561 if (code.startsWith(HEAD)) {
562 return code.substring(HEAD.length);
563 } else if (code.startsWith(TAIL)) {
564 return code.substring(TAIL.length);
565 }
566 return null;
567 }
568
569 List<LibraryBlock> computeHeaderMap(
570 List<CodeLine> lines, int start, int end) {
571 List<LibraryBlock> libraryBlocks = <LibraryBlock>[];
572 LibraryBlock current;
573 for (int index = start; index < end; index++) {
574 String header = readHeader(lines[index]);
575 if (header != null) {
576 if (current != null) {
577 current.to = index;
578 }
579 libraryBlocks.add(current = new LibraryBlock(header, index));
580 }
581 }
582 if (current != null) {
583 current.to = end;
584 }
585 return libraryBlocks;
586 }
587
588 int headerEnd = findHeaderStart(lines);
589 int footerStart = findHeaderEnd(headerEnd, lines);
590 List<LibraryBlock> libraryBlocks =
591 computeHeaderMap(lines, headerEnd, footerStart);
592 for (LibraryBlock block in libraryBlocks) {
593 block.preprocess(lines);
594 }
595
596 return new OutputStructure(
597 lines, headerEnd, footerStart, libraryBlocks);
598 }
599 }
600
601 abstract class AbstractEntity extends OutputEntity {
602 final String name;
603 final int from;
604 int to;
605
606 AbstractEntity(this.name, this.from);
607
608 Interval get interval => new Interval(from, to);
609 }
610
611 /// A block defining the content of a Dart library.
612 class LibraryBlock extends AbstractEntity {
613 List<BasicEntity> children = <BasicEntity>[];
614 int get headerEnd => from + 2;
615 int get footerStart => to - 1;
616
617 LibraryBlock(String name, int from) : super(name, from);
618
619 Interval get header => new Interval(from, headerEnd);
620
621 Interval get footer => new Interval(footerStart, to);
622
623 void preprocess(List<CodeLine> lines) {
624 int index = headerEnd;
625 BasicEntity current;
626 while (index < footerStart) {
627 String line = lines[index].code;
628 BasicEntity next;
629 Match matchFunction = TOP_LEVEL_FUNCTION.firstMatch(line);
630 if (matchFunction != null) {
631 next = new BasicEntity(matchFunction.group(1), index);
632 } else {
633 Match matchClass = TOP_LEVEL_CLASS.firstMatch(line);
634 if (matchClass != null) {
635 next = new LibraryClass(matchClass.group(1), index);
636 } else {
637 Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
638 if (matchValue != null) {
639 next = new BasicEntity(matchValue.group(1), index);
640 }
641 }
642 }
643 if (next != null) {
644 if (current != null) {
645 current.to = index;
646 }
647 children.add(current = next);
648 } else if (index == headerEnd) {
649 throw 'Failed to match first library block line:\n$line';
650 }
651
652 index++;
653 }
654 if (current != null) {
655 current.to = footerStart;
656 }
657
658 for (BasicEntity entity in children) {
659 entity.preprocess(lines);
660 }
661 }
662 }
663
664 /// A simple member of a library or class.
665 class BasicEntity extends AbstractEntity {
666 BasicEntity(String name, int from) : super(name, from);
667
668 Interval get header => new Interval(from, to);
669
670 Interval get footer => new Interval(to, to);
671
672 List<OutputEntity> get children => const <OutputEntity>[];
673
674 void preprocess(List<CodeLine> lines) {}
675 }
676
677 /// A block defining a Dart class.
678 class LibraryClass extends BasicEntity {
679 List<BasicEntity> children = <BasicEntity>[];
680 int get headerEnd => from + 1;
681 int get footerStart => to - 1;
682
683 LibraryClass(String name, int from) : super(name, from);
684
685 Interval get header => new Interval(from, headerEnd);
686
687 Interval get footer => new Interval(footerStart, to);
688
689 void preprocess(List<CodeLine> lines) {
690 int index = headerEnd;
691 BasicEntity current;
692 while (index < footerStart) {
693 String line = lines[index].code;
694 BasicEntity next;
695 Match matchFunction = MEMBER_FUNCTION.firstMatch(line);
696 if (matchFunction != null) {
697 next = new BasicEntity(matchFunction.group(1), index);
698 } else {
699 Match matchClass = MEMBER_OBJECT.firstMatch(line);
700 if (matchClass != null) {
701 next = new BasicEntity(matchClass.group(1), index);
702 } else {
703 Match matchValue = MEMBER_VALUE.firstMatch(line);
704 if (matchValue != null) {
705 next = new BasicEntity(matchValue.group(1), index);
706 }
707 }
708 }
709 if (next != null) {
710 if (current != null) {
711 current.to = index;
712 }
713 children.add(current = next);
714 } else if (index == headerEnd) {
715 throw 'Failed to match first library block line:\n$line';
716 }
717
718 index++;
719 }
720 if (current != null) {
721 current.to = footerStart;
722 }
723 }
724 }
725
726 class Interval {
727 final int from;
728 final int to;
729
730 const Interval(this.from, this.to);
731
732 int get length => to - from;
733 }
734
735 class HtmlPart {
736 void printHtmlOn(StringBuffer buffer) {}
737 }
738
739 class ConstHtmlPart implements HtmlPart {
740 final String html;
741
742 const ConstHtmlPart(this.html);
743
744 @override
745 void printHtmlOn(StringBuffer buffer) {
746 buffer.write(html);
747 }
748 }
749
750 class CodeLineHtmlPart implements HtmlPart {
751 final CodeLine line;
752
753 CodeLineHtmlPart(this.line);
754
755 @override
756 void printHtmlOn(StringBuffer buffer, [int lineNoWidth]) {
757 line.printHtmlOn(buffer, lineNoWidth);
758 }
759 }
760
761 /// Convert [parts] to an HTML string while checking invariants for [lines].
762 String htmlPartsToString(List<HtmlPart> parts, List<CodeLine> lines) {
763 int lineNoWidth;
764 if (lines.isNotEmpty) {
765 lineNoWidth = '${lines.last.lineNo + 1}'.length;
766 }
767 StringBuffer buffer = new StringBuffer();
768 int expectedLineNo = 0;
769 for (HtmlPart part in parts) {
770 if (part is CodeLineHtmlPart) {
771 if (part.line.lineNo != expectedLineNo) {
772 print('Expected line no $expectedLineNo, found ${part.line.lineNo}');
773 if (part.line.lineNo < expectedLineNo) {
774 print('Duplicate lines:');
775 int index = part.line.lineNo;
776 while (index <= expectedLineNo) {
777 print(lines[index++].code);
778 }
779 } else {
780 print('Missing lines:');
781 int index = expectedLineNo;
782 while (index <= part.line.lineNo) {
783 print(lines[index++].code);
784 }
785 }
786 expectedLineNo = part.line.lineNo;
787 }
788 expectedLineNo++;
789 part.printHtmlOn(buffer, lineNoWidth);
790 } else {
791 part.printHtmlOn(buffer);
792 }
793 }
794 return buffer.toString();
795 }
796
797 class CodeLinesResult {
798 final List<CodeLine> codeLines;
799 final Coverage coverage;
800
801 CodeLinesResult(this.codeLines, this.coverage);
802 }
803
804 /// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
805 Future<CodeLinesResult> computeCodeLines(
806 List<String> options,
807 String filename) async {
808 SourceMapProcessor processor = new SourceMapProcessor(filename);
809 List<SourceMapInfo> sourceMapInfoList =
810 await processor.process(options, perElement: false);
811
812 const int WITH_SOURCE_INFO = 0;
813 const int WITHOUT_SOURCE_INFO = 1;
814 const int ADDITIONAL_SOURCE_INFO = 2;
815
816 for (SourceMapInfo info in sourceMapInfoList) {
817 if (info.element != null) continue;
818
819 List<CodeLine> codeLines;
820 Coverage coverage = new Coverage();
821 List<Annotation> annotations = <Annotation>[];
822 String code = info.code;
823 TraceGraph graph = createTraceGraph(info, coverage);
824 Set<js.Node> mappedNodes = new Set<js.Node>();
825 for (TraceStep step in graph.steps) {
826 int offset;
827 if (options.contains(USE_NEW_SOURCE_INFO)) {
828 offset = step.offset.codeOffset;
829 } else {
830 offset = info.jsCodePositions[step.node].startPosition;
831 }
832 if (offset != null) {
833 int id = step.sourceLocation != null
834 ? WITH_SOURCE_INFO : WITHOUT_SOURCE_INFO;
835 annotations.add(
836 new Annotation(id, offset, null));
837 }
838 }
839 if (!options.contains(USE_NEW_SOURCE_INFO)) {
840 for (js.Node node in info.nodeMap.nodes) {
841 if (!mappedNodes.contains(node)) {
842 int offset = info.jsCodePositions[node].startPosition;
843 annotations.add(
844 new Annotation(ADDITIONAL_SOURCE_INFO, offset, null));
845 }
846 }
847 }
848 codeLines = convertAnnotatedCodeToCodeLines(
849 code,
850 annotations,
851 colorScheme: new CustomColorScheme(
852 single: (int id) {
853 if (id == WITH_SOURCE_INFO) {
854 return WITH_SOURCE_INFO_STYLE;
855 } else if (id == ADDITIONAL_SOURCE_INFO) {
856 return ADDITIONAL_SOURCE_INFO_STYLE;
857 }
858 return WITHOUT_SOURCE_INFO_STYLE;
859 },
860 multi: (List ids) {
861 if (ids.contains(WITH_SOURCE_INFO)) {
862 return WITH_SOURCE_INFO_STYLE;
863 } else if (ids.contains(ADDITIONAL_SOURCE_INFO)) {
864 return ADDITIONAL_SOURCE_INFO_STYLE;
865 }
866 return WITHOUT_SOURCE_INFO_STYLE;
867 }
868 ));
869 return new CodeLinesResult(codeLines, coverage);
870 }
871 }
OLDNEW
« 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