OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library sourcemap.diff_view; | 5 library sourcemap.diff_view; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 | 10 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 } | 83 } |
84 | 84 |
85 SourceFileManager sourceFileManager = new IOSourceFileManager(Uri.base); | 85 SourceFileManager sourceFileManager = new IOSourceFileManager(Uri.base); |
86 List<AnnotatedOutput> outputs = <AnnotatedOutput>[]; | 86 List<AnnotatedOutput> outputs = <AnnotatedOutput>[]; |
87 for (int i = 0; i < 2; i++) { | 87 for (int i = 0; i < 2; i++) { |
88 AnnotatedOutput output; | 88 AnnotatedOutput output; |
89 if (loadFrom.containsKey(i)) { | 89 if (loadFrom.containsKey(i)) { |
90 output = AnnotatedOutput.loadOutput(loadFrom[i]); | 90 output = AnnotatedOutput.loadOutput(loadFrom[i]); |
91 } else { | 91 } else { |
92 print('Compiling ${options[i].join(' ')} $filename'); | 92 print('Compiling ${options[i].join(' ')} $filename'); |
93 CodeLinesResult result = await computeCodeLines( | 93 CodeLinesResult result = await computeCodeLines(options[i], filename); |
94 options[i], filename); | |
95 OutputStructure structure = OutputStructure.parse(result.codeLines); | 94 OutputStructure structure = OutputStructure.parse(result.codeLines); |
96 computeEntityCodeSources(result, structure); | 95 computeEntityCodeSources(result, structure); |
97 output = new AnnotatedOutput( | 96 output = new AnnotatedOutput( |
98 filename, | 97 filename, options[i], structure, result.coverage.getCoverageReport()); |
99 options[i], | |
100 structure, | |
101 result.coverage.getCoverageReport()); | |
102 } | 98 } |
103 if (saveTo.containsKey(i)) { | 99 if (saveTo.containsKey(i)) { |
104 AnnotatedOutput.saveOutput(output, saveTo[i]); | 100 AnnotatedOutput.saveOutput(output, saveTo[i]); |
105 } | 101 } |
106 outputs.add(output); | 102 outputs.add(output); |
107 } | 103 } |
108 | 104 |
109 List<DiffBlock> blocks = createDiffBlocks( | 105 List<DiffBlock> blocks = createDiffBlocks( |
110 outputs.map((o) => o.structure).toList(), | 106 outputs.map((o) => o.structure).toList(), sourceFileManager); |
111 sourceFileManager); | |
112 | 107 |
113 outputDiffView( | 108 outputDiffView(out, outputs, blocks, |
114 out, outputs, blocks, | 109 showMarkers: showAnnotations, showSourceMapped: showAnnotations); |
115 showMarkers: showAnnotations, | |
116 showSourceMapped: showAnnotations); | |
117 } | 110 } |
118 | 111 |
119 /// Attaches [CodeSource]s to the entities in [structure] using the | 112 /// Attaches [CodeSource]s to the entities in [structure] using the |
120 /// element-to-offset in [result]. | 113 /// element-to-offset in [result]. |
121 void computeEntityCodeSources( | 114 void computeEntityCodeSources( |
122 CodeLinesResult result, OutputStructure structure) { | 115 CodeLinesResult result, OutputStructure structure) { |
123 result.elementMap.forEach((int line, Element element) { | 116 result.elementMap.forEach((int line, Element element) { |
124 OutputEntity entity = structure.getEntityForLine(line); | 117 OutputEntity entity = structure.getEntityForLine(line); |
125 if (entity != null) { | 118 if (entity != null) { |
126 entity.codeSource = codeSourceFromElement(element); | 119 entity.codeSource = codeSourceFromElement(element); |
127 } | 120 } |
128 }); | 121 }); |
129 } | 122 } |
130 | 123 |
131 class CodeLineAnnotationJsonStrategy implements JsonStrategy { | 124 class CodeLineAnnotationJsonStrategy implements JsonStrategy { |
132 const CodeLineAnnotationJsonStrategy(); | 125 const CodeLineAnnotationJsonStrategy(); |
133 | 126 |
134 Map encodeAnnotation(Annotation annotation) { | 127 Map encodeAnnotation(Annotation annotation) { |
135 CodeLineAnnotation data = annotation.data; | 128 CodeLineAnnotation data = annotation.data; |
136 return { | 129 return { |
137 'id': annotation.id, | 130 'id': annotation.id, |
138 'codeOffset': annotation.codeOffset, | 131 'codeOffset': annotation.codeOffset, |
139 'title': annotation.title, | 132 'title': annotation.title, |
140 'data': data.toJson(this), | 133 'data': data.toJson(this), |
141 }; | 134 }; |
142 } | 135 } |
143 | 136 |
144 Annotation decodeAnnotation(Map json) { | 137 Annotation decodeAnnotation(Map json) { |
145 return new Annotation( | 138 return new Annotation(json['id'], json['codeOffset'], json['title'], |
146 json['id'], | |
147 json['codeOffset'], | |
148 json['title'], | |
149 data: CodeLineAnnotation.fromJson(json['data'], this)); | 139 data: CodeLineAnnotation.fromJson(json['data'], this)); |
150 } | 140 } |
151 | 141 |
152 @override | 142 @override |
153 decodeLineAnnotation(json) { | 143 decodeLineAnnotation(json) { |
154 if (json != null) { | 144 if (json != null) { |
155 return CodeSource.fromJson(json); | 145 return CodeSource.fromJson(json); |
156 } | 146 } |
157 return null; | 147 return null; |
158 } | 148 } |
(...skipping 30 matching lines...) Expand all Loading... |
189 static AnnotatedOutput fromJson(Map json) { | 179 static AnnotatedOutput fromJson(Map json) { |
190 String filename = json['filename']; | 180 String filename = json['filename']; |
191 List<String> options = json['options']; | 181 List<String> options = json['options']; |
192 OutputStructure structure = OutputStructure.fromJson( | 182 OutputStructure structure = OutputStructure.fromJson( |
193 json['structure'], const CodeLineAnnotationJsonStrategy()); | 183 json['structure'], const CodeLineAnnotationJsonStrategy()); |
194 String coverage = json['coverage']; | 184 String coverage = json['coverage']; |
195 return new AnnotatedOutput(filename, options, structure, coverage); | 185 return new AnnotatedOutput(filename, options, structure, coverage); |
196 } | 186 } |
197 | 187 |
198 static AnnotatedOutput loadOutput(filename) { | 188 static AnnotatedOutput loadOutput(filename) { |
199 AnnotatedOutput output = AnnotatedOutput.fromJson( | 189 AnnotatedOutput output = AnnotatedOutput |
200 JSON.decode(new File(filename).readAsStringSync())); | 190 .fromJson(JSON.decode(new File(filename).readAsStringSync())); |
201 print('Output loaded from $filename'); | 191 print('Output loaded from $filename'); |
202 return output; | 192 return output; |
203 } | 193 } |
204 | 194 |
205 static void saveOutput(AnnotatedOutput output, String filename) { | 195 static void saveOutput(AnnotatedOutput output, String filename) { |
206 if (filename != null) { | 196 if (filename != null) { |
207 new File(filename).writeAsStringSync( | 197 new File(filename).writeAsStringSync( |
208 const JsonEncoder.withIndent(' ').convert(output.toJson())); | 198 const JsonEncoder.withIndent(' ').convert(output.toJson())); |
209 print('Output saved in $filename'); | 199 print('Output saved in $filename'); |
210 } | 200 } |
211 } | 201 } |
212 } | 202 } |
213 | 203 |
214 void outputDiffView( | 204 void outputDiffView( |
215 String out, | 205 String out, List<AnnotatedOutput> outputs, List<DiffBlock> blocks, |
216 List<AnnotatedOutput> outputs, | 206 {bool showMarkers: true, bool showSourceMapped: true}) { |
217 List<DiffBlock> blocks, | |
218 {bool showMarkers: true, | |
219 bool showSourceMapped: true}) { | |
220 assert(outputs[0].filename == outputs[1].filename); | 207 assert(outputs[0].filename == outputs[1].filename); |
221 bool usePre = true; | 208 bool usePre = true; |
222 | 209 |
223 StringBuffer sb = new StringBuffer(); | 210 StringBuffer sb = new StringBuffer(); |
224 sb.write(''' | 211 sb.write(''' |
225 <html> | 212 <html> |
226 <head> | 213 <head> |
227 <title>Diff for ${outputs[0].filename}</title> | 214 <title>Diff for ${outputs[0].filename}</title> |
228 <style> | 215 <style> |
229 .${ClassNames.lineNumber} { | 216 .${ClassNames.lineNumber} { |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 sb.write(''' | 459 sb.write(''' |
473 <span class="${ClassNames.sourceMappingIndex(i)}"> </span>'''); | 460 <span class="${ClassNames.sourceMappingIndex(i)}"> </span>'''); |
474 } | 461 } |
475 sb.write(''' | 462 sb.write(''' |
476 <span title="JavaScript offsets and their corresponding Dart Code offset | 463 <span title="JavaScript offsets and their corresponding Dart Code offset |
477 as mapped through source-maps."> | 464 as mapped through source-maps."> |
478 mapped source locations</span> | 465 mapped source locations</span> |
479 </span> | 466 </span> |
480 '''); | 467 '''); |
481 | 468 |
482 | |
483 /// Marker to alternate output colors. | 469 /// Marker to alternate output colors. |
484 bool alternating = false; | 470 bool alternating = false; |
485 | 471 |
486 List<HtmlPrintContext> printContexts = <HtmlPrintContext>[]; | 472 List<HtmlPrintContext> printContexts = <HtmlPrintContext>[]; |
487 for (int i = 0; i < 2; i++) { | 473 for (int i = 0; i < 2; i++) { |
488 int lineNoWidth; | 474 int lineNoWidth; |
489 if (outputs[i].codeLines.isNotEmpty) { | 475 if (outputs[i].codeLines.isNotEmpty) { |
490 lineNoWidth = '${outputs[i].codeLines.last.lineNo + 1}'.length; | 476 lineNoWidth = '${outputs[i].codeLines.last.lineNo + 1}'.length; |
491 } | 477 } |
492 printContexts.add(new HtmlPrintContext( | 478 printContexts.add(new HtmlPrintContext( |
493 lineNoWidth: lineNoWidth, | 479 lineNoWidth: lineNoWidth, |
494 getAnnotationData: getAnnotationData, | 480 getAnnotationData: getAnnotationData, |
495 getLineData: getLineData)); | 481 getLineData: getLineData)); |
496 } | 482 } |
497 | 483 |
498 Set<DiffColumn> allColumns = new Set<DiffColumn>(); | 484 Set<DiffColumn> allColumns = new Set<DiffColumn>(); |
499 for (DiffBlock block in blocks) { | 485 for (DiffBlock block in blocks) { |
500 allColumns.addAll(block.columns); | 486 allColumns.addAll(block.columns); |
501 } | 487 } |
502 | 488 |
503 List<DiffColumn> columns = [column_js0, column_js1, column_dart] | 489 List<DiffColumn> columns = [column_js0, column_js1, column_dart] |
504 .where((c) => allColumns.contains(c)).toList(); | 490 .where((c) => allColumns.contains(c)) |
| 491 .toList(); |
505 | 492 |
506 sb.write(''' | 493 sb.write(''' |
507 </div> | 494 </div> |
508 <table class="${ClassNames.headerTable}"><tr>'''); | 495 <table class="${ClassNames.headerTable}"><tr>'''); |
509 for (DiffColumn column in columns) { | 496 for (DiffColumn column in columns) { |
510 sb.write(''' | 497 sb.write(''' |
511 <td class="${ClassNames.headerColumn} ${ClassNames.column(column)}">'''); | 498 <td class="${ClassNames.headerColumn} ${ClassNames.column(column)}">'''); |
512 if (column.type == 'js') { | 499 if (column.type == 'js') { |
513 sb.write('''[${outputs[column.index].options.join(',')}]'''); | 500 sb.write('''[${outputs[column.index].options.join(',')}]'''); |
514 } else { | 501 } else { |
(...skipping 13 matching lines...) Expand all Loading... |
528 switch (block.kind) { | 515 switch (block.kind) { |
529 case DiffKind.UNMATCHED: | 516 case DiffKind.UNMATCHED: |
530 className = '${ClassNames.cell}'; | 517 className = '${ClassNames.cell}'; |
531 break; | 518 break; |
532 case DiffKind.MATCHING: | 519 case DiffKind.MATCHING: |
533 className = | 520 className = |
534 '${ClassNames.cell} ${ClassNames.corresponding(alternating)}'; | 521 '${ClassNames.cell} ${ClassNames.corresponding(alternating)}'; |
535 alternating = !alternating; | 522 alternating = !alternating; |
536 break; | 523 break; |
537 case DiffKind.IDENTICAL: | 524 case DiffKind.IDENTICAL: |
538 className = | 525 className = '${ClassNames.cell} ${ClassNames.identical(alternating)}'; |
539 '${ClassNames.cell} ${ClassNames.identical(alternating)}'; | |
540 alternating = !alternating; | 526 alternating = !alternating; |
541 break; | 527 break; |
542 } | 528 } |
543 sb.write('<tr>'); | 529 sb.write('<tr>'); |
544 for (DiffColumn column in columns) { | 530 for (DiffColumn column in columns) { |
545 sb.write('''<td class="$className ${ClassNames.column(column)}">'''); | 531 sb.write('''<td class="$className ${ClassNames.column(column)}">'''); |
546 HtmlPrintContext context = new HtmlPrintContext( | 532 HtmlPrintContext context = new HtmlPrintContext( |
547 lineNoWidth: 4, | 533 lineNoWidth: 4, |
548 includeAnnotation: (Annotation annotation) { | 534 includeAnnotation: (Annotation annotation) { |
549 CodeLineAnnotation data = annotation.data; | 535 CodeLineAnnotation data = annotation.data; |
550 return data.annotationType == AnnotationType.WITH_SOURCE_INFO || | 536 return data.annotationType == AnnotationType.WITH_SOURCE_INFO || |
551 data.annotationType == AnnotationType.ADDITIONAL_SOURCE_INFO; | 537 data.annotationType == AnnotationType.ADDITIONAL_SOURCE_INFO; |
552 }, | 538 }, |
553 getAnnotationData: getAnnotationData, | 539 getAnnotationData: getAnnotationData, |
554 getLineData: getLineData); | 540 getLineData: getLineData); |
555 if (column.type == 'js') { | 541 if (column.type == 'js') { |
556 context = printContexts[column.index]; | 542 context = printContexts[column.index]; |
557 } | 543 } |
558 block.printHtmlOn(column, sb, context); | 544 block.printHtmlOn(column, sb, context); |
559 sb.write('''</td>'''); | 545 sb.write('''</td>'''); |
560 } | 546 } |
561 sb.write('</tr>'); | 547 sb.write('</tr>'); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
616 print('Diff generated in $out'); | 602 print('Diff generated in $out'); |
617 } | 603 } |
618 | 604 |
619 class CodeLinesResult { | 605 class CodeLinesResult { |
620 final List<CodeLine> codeLines; | 606 final List<CodeLine> codeLines; |
621 final Coverage coverage; | 607 final Coverage coverage; |
622 final Map<int, Element> elementMap; | 608 final Map<int, Element> elementMap; |
623 final SourceFileManager sourceFileManager; | 609 final SourceFileManager sourceFileManager; |
624 final CodeSources codeSources; | 610 final CodeSources codeSources; |
625 | 611 |
626 CodeLinesResult( | 612 CodeLinesResult(this.codeLines, this.coverage, this.elementMap, |
627 this.codeLines, | 613 this.sourceFileManager, this.codeSources); |
628 this.coverage, | |
629 this.elementMap, | |
630 this.sourceFileManager, | |
631 this.codeSources); | |
632 } | 614 } |
633 | 615 |
634 class CodeSources { | 616 class CodeSources { |
635 Map<Element, CodeSource> codeSourceMap = <Element, CodeSource>{}; | 617 Map<Element, CodeSource> codeSourceMap = <Element, CodeSource>{}; |
636 Map<Uri, Map<Interval, CodeSource>> uriCodeSourceMap = | 618 Map<Uri, Map<Interval, CodeSource>> uriCodeSourceMap = |
637 <Uri, Map<Interval, CodeSource>>{}; | 619 <Uri, Map<Interval, CodeSource>>{}; |
638 | 620 |
639 CodeSources( | 621 CodeSources(SourceMapProcessor processor, SourceMaps sourceMaps) { |
640 SourceMapProcessor processor, | |
641 SourceMaps sourceMaps) { | |
642 | |
643 CodeSource computeCodeSource(Element element) { | 622 CodeSource computeCodeSource(Element element) { |
644 return codeSourceMap.putIfAbsent(element, () { | 623 return codeSourceMap.putIfAbsent(element, () { |
645 CodeSource codeSource = codeSourceFromElement(element); | 624 CodeSource codeSource = codeSourceFromElement(element); |
646 if (codeSource.begin != null) { | 625 if (codeSource.begin != null) { |
647 Interval interval = new Interval(codeSource.begin, codeSource.end); | 626 Interval interval = new Interval(codeSource.begin, codeSource.end); |
648 Map<Interval, CodeSource> intervals = | 627 Map<Interval, CodeSource> intervals = |
649 uriCodeSourceMap[codeSource.uri]; | 628 uriCodeSourceMap[codeSource.uri]; |
650 if (intervals == null) { | 629 if (intervals == null) { |
651 intervals = <Interval, CodeSource>{}; | 630 intervals = <Interval, CodeSource>{}; |
652 uriCodeSourceMap[codeSource.uri] = intervals; | 631 uriCodeSourceMap[codeSource.uri] = intervals; |
653 } else { | 632 } else { |
654 for (Interval existingInterval in intervals.keys.toList()) { | 633 for (Interval existingInterval in intervals.keys.toList()) { |
655 if (existingInterval.contains(interval.from)) { | 634 if (existingInterval.contains(interval.from)) { |
656 CodeSource existingCodeSource = intervals[existingInterval]; | 635 CodeSource existingCodeSource = intervals[existingInterval]; |
657 intervals.remove(existingInterval); | 636 intervals.remove(existingInterval); |
658 if (existingInterval.from < interval.from) { | 637 if (existingInterval.from < interval.from) { |
659 Interval preInterval = new Interval( | 638 Interval preInterval = |
660 existingInterval.from, interval.from); | 639 new Interval(existingInterval.from, interval.from); |
661 intervals[preInterval] = existingCodeSource; | 640 intervals[preInterval] = existingCodeSource; |
662 } | 641 } |
663 if (interval.to < existingInterval.to) { | 642 if (interval.to < existingInterval.to) { |
664 Interval postInterval = new Interval( | 643 Interval postInterval = |
665 interval.to, existingInterval.to); | 644 new Interval(interval.to, existingInterval.to); |
666 intervals[postInterval] = existingCodeSource; | 645 intervals[postInterval] = existingCodeSource; |
667 } | 646 } |
668 } | 647 } |
669 } | 648 } |
670 } | 649 } |
671 intervals[interval] = codeSource; | 650 intervals[interval] = codeSource; |
672 } | 651 } |
673 if (element is ClassElement) { | 652 if (element is ClassElement) { |
674 element.forEachLocalMember((Element member) { | 653 element.forEachLocalMember((Element member) { |
675 codeSource.members.add(computeCodeSource(member)); | 654 codeSource.members.add(computeCodeSource(member)); |
676 }); | 655 }); |
677 element.implementation.forEachLocalMember((Element member) { | 656 element.implementation.forEachLocalMember((Element member) { |
678 codeSource.members.add(computeCodeSource(member)); | 657 codeSource.members.add(computeCodeSource(member)); |
679 }); | 658 }); |
680 } else if (element is MemberElement) { | 659 } else if (element is MemberElement) { |
681 element.nestedClosures.forEach((Element closure) { | 660 element.nestedClosures.forEach((Element closure) { |
682 codeSource.members.add(computeCodeSource(closure)); | 661 codeSource.members.add(computeCodeSource(closure)); |
683 }); | 662 }); |
684 } | 663 } |
685 return codeSource; | 664 return codeSource; |
686 }); | 665 }); |
687 } | 666 } |
688 | 667 |
689 for (LibraryElement library in | 668 for (LibraryElement library |
690 sourceMaps.compiler.libraryLoader.libraries) { | 669 in sourceMaps.compiler.libraryLoader.libraries) { |
691 library.forEachLocalMember(computeCodeSource); | 670 library.forEachLocalMember(computeCodeSource); |
692 library.implementation.forEachLocalMember(computeCodeSource); | 671 library.implementation.forEachLocalMember(computeCodeSource); |
693 } | 672 } |
694 | 673 |
695 uriCodeSourceMap.forEach((Uri uri, Map<Interval, CodeSource> intervals) { | 674 uriCodeSourceMap.forEach((Uri uri, Map<Interval, CodeSource> intervals) { |
696 List<Interval> sortedKeys = intervals.keys.toList()..sort( | 675 List<Interval> sortedKeys = intervals.keys.toList() |
697 (i1, i2) => i1.from.compareTo(i2.from)); | 676 ..sort((i1, i2) => i1.from.compareTo(i2.from)); |
698 Map<Interval, CodeSource> sortedintervals = <Interval, CodeSource>{}; | 677 Map<Interval, CodeSource> sortedintervals = <Interval, CodeSource>{}; |
699 sortedKeys.forEach((Interval interval) { | 678 sortedKeys.forEach((Interval interval) { |
700 sortedintervals[interval] = intervals[interval]; | 679 sortedintervals[interval] = intervals[interval]; |
701 }); | 680 }); |
702 uriCodeSourceMap[uri] = sortedintervals; | 681 uriCodeSourceMap[uri] = sortedintervals; |
703 }); | 682 }); |
704 } | 683 } |
705 | 684 |
706 CodeSource sourceLocationToCodeSource(SourceLocation sourceLocation) { | 685 CodeSource sourceLocationToCodeSource(SourceLocation sourceLocation) { |
707 Map<Interval, CodeSource> intervals = | 686 Map<Interval, CodeSource> intervals = |
708 uriCodeSourceMap[sourceLocation.sourceUri]; | 687 uriCodeSourceMap[sourceLocation.sourceUri]; |
709 if (intervals == null) { | 688 if (intervals == null) { |
710 print('No code source for $sourceLocation(${sourceLocation.offset})'); | 689 print('No code source for $sourceLocation(${sourceLocation.offset})'); |
711 print(' -- no intervals for ${sourceLocation.sourceUri}'); | 690 print(' -- no intervals for ${sourceLocation.sourceUri}'); |
712 return null; | 691 return null; |
713 } | 692 } |
714 for (Interval interval in intervals.keys) { | 693 for (Interval interval in intervals.keys) { |
715 if (interval.contains(sourceLocation.offset)) { | 694 if (interval.contains(sourceLocation.offset)) { |
716 return intervals[interval]; | 695 return intervals[interval]; |
717 } | 696 } |
718 } | 697 } |
719 print('No code source for $sourceLocation(${sourceLocation.offset})'); | 698 print('No code source for $sourceLocation(${sourceLocation.offset})'); |
720 intervals.forEach((k, v) => print(' $k: ${v.name}')); | 699 intervals.forEach((k, v) => print(' $k: ${v.name}')); |
721 return null; | 700 return null; |
722 } | 701 } |
723 } | 702 } |
724 | 703 |
725 /// Compute [CodeLine]s and [Coverage] for [filename] using the given [options]. | 704 /// Compute [CodeLine]s and [Coverage] for [filename] using the given [options]. |
726 Future<CodeLinesResult> computeCodeLines( | 705 Future<CodeLinesResult> computeCodeLines( |
727 List<String> options, | 706 List<String> options, String filename) async { |
728 String filename) async { | |
729 SourceMapProcessor processor = new SourceMapProcessor(filename); | 707 SourceMapProcessor processor = new SourceMapProcessor(filename); |
730 SourceMaps sourceMaps = | 708 SourceMaps sourceMaps = |
731 await processor.process(options, perElement: true, forMain: true); | 709 await processor.process(options, perElement: true, forMain: true); |
732 | 710 |
733 CodeSources codeSources = new CodeSources(processor, sourceMaps); | 711 CodeSources codeSources = new CodeSources(processor, sourceMaps); |
734 | 712 |
735 SourceMapInfo info = sourceMaps.mainSourceMapInfo; | 713 SourceMapInfo info = sourceMaps.mainSourceMapInfo; |
736 | 714 |
737 int nextAnnotationId = 0; | 715 int nextAnnotationId = 0; |
738 List<CodeLine> codeLines; | 716 List<CodeLine> codeLines; |
739 Coverage coverage = new Coverage(); | 717 Coverage coverage = new Coverage(); |
740 Map<int, List<CodeLineAnnotation>> codeLineAnnotationMap = | 718 Map<int, List<CodeLineAnnotation>> codeLineAnnotationMap = |
741 <int, List<CodeLineAnnotation>>{}; | 719 <int, List<CodeLineAnnotation>>{}; |
742 | 720 |
743 /// Create a [CodeLineAnnotation] for [codeOffset]. | 721 /// Create a [CodeLineAnnotation] for [codeOffset]. |
744 void addCodeLineAnnotation( | 722 void addCodeLineAnnotation( |
745 {AnnotationType annotationType, | 723 {AnnotationType annotationType, |
746 int codeOffset, | 724 int codeOffset, |
747 List<SourceLocation> locations: const <SourceLocation>[], | 725 List<SourceLocation> locations: const <SourceLocation>[], |
748 String stepInfo}) { | 726 String stepInfo}) { |
749 if (annotationType == AnnotationType.WITHOUT_SOURCE_INFO || | 727 if (annotationType == AnnotationType.WITHOUT_SOURCE_INFO || |
750 annotationType == AnnotationType.UNUSED_SOURCE_INFO) { | 728 annotationType == AnnotationType.UNUSED_SOURCE_INFO) { |
751 locations = []; | 729 locations = []; |
752 } | 730 } |
753 List<CodeLocation> codeLocations = locations | 731 List<CodeLocation> codeLocations = locations |
754 .map((l) => new CodeLocation(l.sourceUri, l.sourceName, l.offset)) | 732 .map((l) => new CodeLocation(l.sourceUri, l.sourceName, l.offset)) |
755 .toList(); | 733 .toList(); |
756 List<CodeSource> codeSourceList = locations | 734 List<CodeSource> codeSourceList = locations |
757 .map(codeSources.sourceLocationToCodeSource) | 735 .map(codeSources.sourceLocationToCodeSource) |
758 .where((c) => c != null) | 736 .where((c) => c != null) |
759 .toList(); | 737 .toList(); |
760 CodeLineAnnotation data = new CodeLineAnnotation( | 738 CodeLineAnnotation data = new CodeLineAnnotation( |
761 annotationId: nextAnnotationId++, | 739 annotationId: nextAnnotationId++, |
762 annotationType: annotationType, | 740 annotationType: annotationType, |
763 codeLocations: codeLocations, | 741 codeLocations: codeLocations, |
764 codeSources: codeSourceList, | 742 codeSources: codeSourceList, |
765 stepInfo: stepInfo); | 743 stepInfo: stepInfo); |
766 codeLineAnnotationMap.putIfAbsent( | 744 codeLineAnnotationMap |
767 codeOffset, () => <CodeLineAnnotation>[]).add(data); | 745 .putIfAbsent(codeOffset, () => <CodeLineAnnotation>[]) |
| 746 .add(data); |
768 } | 747 } |
769 | 748 |
770 String code = info.code; | 749 String code = info.code; |
771 TraceGraph graph = createTraceGraph(info, coverage); | 750 TraceGraph graph = createTraceGraph(info, coverage); |
772 | 751 |
773 Set<js.Node> mappedNodes = new Set<js.Node>(); | 752 Set<js.Node> mappedNodes = new Set<js.Node>(); |
774 | 753 |
775 /// Add an annotation for [codeOffset] pointing to [locations]. | 754 /// Add an annotation for [codeOffset] pointing to [locations]. |
776 void addSourceLocations( | 755 void addSourceLocations( |
777 {AnnotationType annotationType, | 756 {AnnotationType annotationType, |
778 int codeOffset, | 757 int codeOffset, |
779 List<SourceLocation> locations, | 758 List<SourceLocation> locations, |
780 String stepInfo}) { | 759 String stepInfo}) { |
781 locations = locations.where((l) => l != null).toList(); | 760 locations = locations.where((l) => l != null).toList(); |
782 addCodeLineAnnotation( | 761 addCodeLineAnnotation( |
783 annotationType: annotationType, | 762 annotationType: annotationType, |
784 codeOffset: codeOffset, | 763 codeOffset: codeOffset, |
785 stepInfo: stepInfo, | 764 stepInfo: stepInfo, |
786 locations: locations); | 765 locations: locations); |
787 } | 766 } |
788 | 767 |
789 /// Add annotations for all mappings created for [node]. | 768 /// Add annotations for all mappings created for [node]. |
790 bool addSourceLocationsForNode( | 769 bool addSourceLocationsForNode( |
791 {AnnotationType annotationType, | 770 {AnnotationType annotationType, js.Node node, String stepInfo}) { |
792 js.Node node, | |
793 String stepInfo}) { | |
794 Map<int, List<SourceLocation>> locations = info.nodeMap[node]; | 771 Map<int, List<SourceLocation>> locations = info.nodeMap[node]; |
795 if (locations == null || locations.isEmpty) { | 772 if (locations == null || locations.isEmpty) { |
796 return false; | 773 return false; |
797 } | 774 } |
798 locations.forEach((int offset, List<SourceLocation> locations) { | 775 locations.forEach((int offset, List<SourceLocation> locations) { |
799 addSourceLocations( | 776 addSourceLocations( |
800 annotationType: annotationType, | 777 annotationType: annotationType, |
801 codeOffset: offset, | 778 codeOffset: offset, |
802 locations: locations, | 779 locations: locations, |
803 stepInfo: stepInfo); | 780 stepInfo: stepInfo); |
(...skipping 22 matching lines...) Expand all Loading... |
826 codeOffset: offset, | 803 codeOffset: offset, |
827 stepInfo: stepInfo); | 804 stepInfo: stepInfo); |
828 } | 805 } |
829 } | 806 } |
830 } | 807 } |
831 | 808 |
832 // Add additional annotations for mappings created for particular nodes. | 809 // Add additional annotations for mappings created for particular nodes. |
833 for (js.Node node in info.nodeMap.nodes) { | 810 for (js.Node node in info.nodeMap.nodes) { |
834 if (!mappedNodes.contains(node)) { | 811 if (!mappedNodes.contains(node)) { |
835 addSourceLocationsForNode( | 812 addSourceLocationsForNode( |
836 annotationType: AnnotationType.ADDITIONAL_SOURCE_INFO, | 813 annotationType: AnnotationType.ADDITIONAL_SOURCE_INFO, node: node); |
837 node: node); | |
838 } | 814 } |
839 } | 815 } |
840 | 816 |
841 // Add annotations for unused source information associated with nodes. | 817 // Add annotations for unused source information associated with nodes. |
842 SourceLocationCollector collector = new SourceLocationCollector(); | 818 SourceLocationCollector collector = new SourceLocationCollector(); |
843 info.node.accept(collector); | 819 info.node.accept(collector); |
844 collector.sourceLocations.forEach( | 820 collector.sourceLocations |
845 (js.Node node, List<SourceLocation> locations) { | 821 .forEach((js.Node node, List<SourceLocation> locations) { |
846 if (!mappedNodes.contains(node)) { | 822 if (!mappedNodes.contains(node)) { |
847 int offset = info.jsCodePositions[node].startPosition; | 823 int offset = info.jsCodePositions[node].startPosition; |
848 addSourceLocations( | 824 addSourceLocations( |
849 annotationType: AnnotationType.UNUSED_SOURCE_INFO, | 825 annotationType: AnnotationType.UNUSED_SOURCE_INFO, |
850 codeOffset: offset, | 826 codeOffset: offset, |
851 locations: locations); | 827 locations: locations); |
852 } | 828 } |
853 }); | 829 }); |
854 | 830 |
855 // Assign consecutive ids to source mappings. | 831 // Assign consecutive ids to source mappings. |
856 int nextSourceMappedLocationIndex = 0; | 832 int nextSourceMappedLocationIndex = 0; |
857 List<Annotation> annotations = <Annotation>[]; | 833 List<Annotation> annotations = <Annotation>[]; |
858 for (int codeOffset in codeLineAnnotationMap.keys.toList()..sort()) { | 834 for (int codeOffset in codeLineAnnotationMap.keys.toList()..sort()) { |
859 bool hasSourceMappedLocation = false; | 835 bool hasSourceMappedLocation = false; |
860 for (CodeLineAnnotation data in codeLineAnnotationMap[codeOffset]) { | 836 for (CodeLineAnnotation data in codeLineAnnotationMap[codeOffset]) { |
861 if (data.annotationType.isSourceMapped) { | 837 if (data.annotationType.isSourceMapped) { |
862 data.sourceMappingIndex = nextSourceMappedLocationIndex; | 838 data.sourceMappingIndex = nextSourceMappedLocationIndex; |
863 hasSourceMappedLocation = true; | 839 hasSourceMappedLocation = true; |
864 } | 840 } |
865 annotations.add(new Annotation( | 841 annotations.add(new Annotation( |
866 data.annotationType.index, | 842 data.annotationType.index, codeOffset, 'id=${data.annotationId}', |
867 codeOffset, | |
868 'id=${data.annotationId}', | |
869 data: data)); | 843 data: data)); |
870 } | 844 } |
871 if (hasSourceMappedLocation) { | 845 if (hasSourceMappedLocation) { |
872 nextSourceMappedLocationIndex++; | 846 nextSourceMappedLocationIndex++; |
873 } | 847 } |
874 } | 848 } |
875 | 849 |
876 // Associate JavaScript offsets with [Element]s. | 850 // Associate JavaScript offsets with [Element]s. |
877 StringSourceFile sourceFile = new StringSourceFile.fromName(filename, code); | 851 StringSourceFile sourceFile = new StringSourceFile.fromName(filename, code); |
878 Map<int, Element> elementMap = <int, Element>{}; | 852 Map<int, Element> elementMap = <int, Element>{}; |
879 sourceMaps.elementSourceMapInfos.forEach( | 853 sourceMaps.elementSourceMapInfos |
880 (Element element, SourceMapInfo info) { | 854 .forEach((Element element, SourceMapInfo info) { |
881 CodePosition position = info.jsCodePositions[info.node]; | 855 CodePosition position = info.jsCodePositions[info.node]; |
882 elementMap[sourceFile.getLine(position.startPosition)] = element; | 856 elementMap[sourceFile.getLine(position.startPosition)] = element; |
883 }); | 857 }); |
884 | 858 |
885 codeLines = convertAnnotatedCodeToCodeLines(code, annotations); | 859 codeLines = convertAnnotatedCodeToCodeLines(code, annotations); |
886 return new CodeLinesResult( | 860 return new CodeLinesResult(codeLines, coverage, elementMap, |
887 codeLines, coverage, elementMap, | 861 sourceMaps.sourceFileManager, codeSources); |
888 sourceMaps.sourceFileManager, | |
889 codeSources); | |
890 } | 862 } |
891 | 863 |
892 /// Visitor that computes a map from [js.Node]s to all attached source | 864 /// Visitor that computes a map from [js.Node]s to all attached source |
893 /// locations. | 865 /// locations. |
894 class SourceLocationCollector extends js.BaseVisitor { | 866 class SourceLocationCollector extends js.BaseVisitor { |
895 Map<js.Node, List<SourceLocation>> sourceLocations = | 867 Map<js.Node, List<SourceLocation>> sourceLocations = |
896 <js.Node, List<SourceLocation>>{}; | 868 <js.Node, List<SourceLocation>>{}; |
897 | 869 |
898 @override | 870 @override |
899 visitNode(js.Node node) { | 871 visitNode(js.Node node) { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
933 } | 905 } |
934 return new CodeSource(kind, uri, name, begin, end); | 906 return new CodeSource(kind, uri, name, begin, end); |
935 } | 907 } |
936 | 908 |
937 /// Create [LineData] that colors line numbers according to the [CodeSource]s | 909 /// Create [LineData] that colors line numbers according to the [CodeSource]s |
938 /// origin if available. | 910 /// origin if available. |
939 LineData getLineData(CodeSource lineAnnotation) { | 911 LineData getLineData(CodeSource lineAnnotation) { |
940 if (lineAnnotation != null) { | 912 if (lineAnnotation != null) { |
941 return new LineData( | 913 return new LineData( |
942 lineClass: ClassNames.line, | 914 lineClass: ClassNames.line, |
943 lineNumberClass: | 915 lineNumberClass: '${ClassNames.lineNumber} ' |
944 '${ClassNames.lineNumber} ' | 916 '${ClassNames.colored(lineAnnotation.hashCode % 4)}'); |
945 '${ClassNames.colored(lineAnnotation.hashCode % 4)}'); | |
946 } | 917 } |
947 return new LineData( | 918 return new LineData( |
948 lineClass: ClassNames.line, | 919 lineClass: ClassNames.line, lineNumberClass: ClassNames.lineNumber); |
949 lineNumberClass: ClassNames.lineNumber); | |
950 } | 920 } |
951 | 921 |
952 AnnotationData getAnnotationData(Iterable<Annotation> annotations, | 922 AnnotationData getAnnotationData(Iterable<Annotation> annotations, |
953 {bool forSpan}) { | 923 {bool forSpan}) { |
954 for (Annotation annotation in annotations) { | 924 for (Annotation annotation in annotations) { |
955 CodeLineAnnotation data = annotation.data; | 925 CodeLineAnnotation data = annotation.data; |
956 if (data.annotationType.isSourceMapped) { | 926 if (data.annotationType.isSourceMapped) { |
957 if (forSpan) { | 927 if (forSpan) { |
958 int index = data.sourceMappingIndex; | 928 int index = data.sourceMappingIndex; |
959 return new AnnotationData( | 929 return new AnnotationData(tag: 'span', properties: { |
960 tag: 'span', | 930 'class': '${ClassNames.sourceMapping} ' |
961 properties: { | 931 '${ClassNames.sourceMappingIndex(index % HUE_COUNT)}', |
962 'class': | 932 'title': 'index=$index', |
963 '${ClassNames.sourceMapping} ' | 933 }); |
964 '${ClassNames.sourceMappingIndex(index % HUE_COUNT)}', | |
965 'title': 'index=$index', | |
966 }); | |
967 } else { | 934 } else { |
968 return new AnnotationData( | 935 return new AnnotationData(tag: 'span', properties: { |
969 tag: 'span', | 936 'title': annotation.title, |
970 properties: { | 937 'class': '${ClassNames.marker} ' |
971 'title': annotation.title, | 938 '${data.annotationType.className}' |
972 'class': '${ClassNames.marker} ' | 939 }); |
973 '${data.annotationType.className}'}); | |
974 } | 940 } |
975 } | 941 } |
976 } | 942 } |
977 if (forSpan) return null; | 943 if (forSpan) return null; |
978 for (Annotation annotation in annotations) { | 944 for (Annotation annotation in annotations) { |
979 CodeLineAnnotation data = annotation.data; | 945 CodeLineAnnotation data = annotation.data; |
980 if (data.annotationType == AnnotationType.UNUSED_SOURCE_INFO) { | 946 if (data.annotationType == AnnotationType.UNUSED_SOURCE_INFO) { |
981 return new AnnotationData( | 947 return new AnnotationData(tag: 'span', properties: { |
982 tag: 'span', | 948 'title': annotation.title, |
983 properties: { | 949 'class': '${ClassNames.marker} ' |
984 'title': annotation.title, | 950 '${data.annotationType.className}' |
985 'class': '${ClassNames.marker} ' | 951 }); |
986 '${data.annotationType.className}'}); | |
987 } | 952 } |
988 } | 953 } |
989 for (Annotation annotation in annotations) { | 954 for (Annotation annotation in annotations) { |
990 CodeLineAnnotation data = annotation.data; | 955 CodeLineAnnotation data = annotation.data; |
991 if (data.annotationType == AnnotationType.WITHOUT_SOURCE_INFO) { | 956 if (data.annotationType == AnnotationType.WITHOUT_SOURCE_INFO) { |
992 return new AnnotationData( | 957 return new AnnotationData(tag: 'span', properties: { |
993 tag: 'span', | 958 'title': annotation.title, |
994 properties: { | 959 'class': '${ClassNames.marker} ' |
995 'title': annotation.title, | 960 '${data.annotationType.className}' |
996 'class': '${ClassNames.marker} ' | 961 }); |
997 '${data.annotationType.className}'}); | |
998 } | 962 } |
999 } | 963 } |
1000 return null; | 964 return null; |
1001 } | 965 } |
OLD | NEW |