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

Side by Side Diff: tests/compiler/dart2js/sourcemaps/diff_view.dart

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

Powered by Google App Engine
This is Rietveld 408576698