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

Side by Side Diff: pkg/compiler/lib/src/io/position_information.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
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 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 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 /// Source information system mapping that attempts a semantic mapping between 5 /// Source information system mapping that attempts a semantic mapping between
6 /// offsets of JavaScript code points to offsets of Dart code points. 6 /// offsets of JavaScript code points to offsets of Dart code points.
7 7
8 library dart2js.source_information.position; 8 library dart2js.source_information.position;
9 9
10 import '../common.dart'; 10 import '../common.dart';
11 import '../elements/elements.dart' show 11 import '../elements/elements.dart' show
12 AstElement, 12 AstElement,
13 LocalElement; 13 LocalElement;
14 import '../js/js.dart' as js; 14 import '../js/js.dart' as js;
15 import '../js/js_source_mapping.dart'; 15 import '../js/js_source_mapping.dart';
16 import '../js/js_debug.dart'; 16 import '../js/js_debug.dart';
17 import '../tree/tree.dart' show 17 import '../tree/tree.dart' show
18 Node, 18 Node,
19 Send; 19 Send;
20 20
21 import 'code_output.dart' show
22 CodeBuffer;
21 import 'source_file.dart'; 23 import 'source_file.dart';
22 import 'source_information.dart'; 24 import 'source_information.dart';
23 25
24 /// [SourceInformation] that consists of an offset position into the source 26 /// [SourceInformation] that consists of an offset position into the source
25 /// code. 27 /// code.
26 class PositionSourceInformation extends SourceInformation { 28 class PositionSourceInformation extends SourceInformation {
27 @override 29 @override
28 final SourceLocation startPosition; 30 final SourceLocation startPosition;
29 31
30 @override 32 @override
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 108
107 @override 109 @override
108 SourceInformationBuilder createBuilderForContext(AstElement element) { 110 SourceInformationBuilder createBuilderForContext(AstElement element) {
109 return new PositionSourceInformationBuilder(element); 111 return new PositionSourceInformationBuilder(element);
110 } 112 }
111 113
112 @override 114 @override
113 SourceInformationProcessor createProcessor(SourceMapper mapper) { 115 SourceInformationProcessor createProcessor(SourceMapper mapper) {
114 return new PositionSourceInformationProcessor(mapper); 116 return new PositionSourceInformationProcessor(mapper);
115 } 117 }
118
119 @override
120 void onComplete() {}
121
122 @override
123 SourceInformation buildSourceMappedMarker() {
124 return const SourceMappedMarker();
125 }
126 }
127
128 /// Marker used to tag the root nodes of source-mapped code.
129 ///
130 /// This is needed to be able to distinguish JavaScript nodes that shouldn't
131 /// have source locations (like the premable) from the nodes that should
132 /// (like functions compiled from Dart code).
133 class SourceMappedMarker extends SourceInformation {
134 const SourceMappedMarker();
135
136 @override
137 String get shortText => '';
138
139 @override
140 List<SourceLocation> get sourceLocations => const <SourceLocation>[];
141
142 @override
143 SourceSpan get sourceSpan => new SourceSpan(null, null, null);
116 } 144 }
117 145
118 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. 146 /// [SourceInformationBuilder] that generates [PositionSourceInformation].
119 class PositionSourceInformationBuilder implements SourceInformationBuilder { 147 class PositionSourceInformationBuilder implements SourceInformationBuilder {
120 final SourceFile sourceFile; 148 final SourceFile sourceFile;
121 final String name; 149 final String name;
122 150
123 PositionSourceInformationBuilder(AstElement element) 151 PositionSourceInformationBuilder(AstElement element)
124 : sourceFile = element.implementation.compilationUnit.script.file, 152 : sourceFile = element.implementation.compilationUnit.script.file,
125 name = computeElementNameForSourceMaps(element); 153 name = computeElementNameForSourceMaps(element);
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 } 246 }
219 } 247 }
220 248
221 /// The start, end and closing offsets for a [js.Node]. 249 /// The start, end and closing offsets for a [js.Node].
222 class CodePosition { 250 class CodePosition {
223 final int startPosition; 251 final int startPosition;
224 final int endPosition; 252 final int endPosition;
225 final int closingPosition; 253 final int closingPosition;
226 254
227 CodePosition(this.startPosition, this.endPosition, this.closingPosition); 255 CodePosition(this.startPosition, this.endPosition, this.closingPosition);
256
257 int getPosition(CodePositionKind kind) {
258 switch (kind) {
259 case CodePositionKind.START:
260 return startPosition;
261 case CodePositionKind.END:
262 return endPosition;
263 case CodePositionKind.CLOSING:
264 return closingPosition;
265 }
266 }
267
268 String toString() {
269 return 'CodePosition(start=$startPosition,'
270 'end=$endPosition,closing=$closingPosition)';
271 }
272 }
273
274 /// A map from a [js.Node] to its [CodePosition].
275 abstract class CodePositionMap {
276 CodePosition operator [](js.Node node);
228 } 277 }
229 278
230 /// Registry for mapping [js.Node]s to their [CodePosition]. 279 /// Registry for mapping [js.Node]s to their [CodePosition].
231 class CodePositionRecorder { 280 class CodePositionRecorder implements CodePositionMap {
232 Map<js.Node, CodePosition> _codePositionMap = 281 Map<js.Node, CodePosition> _codePositionMap =
233 new Map<js.Node, CodePosition>.identity(); 282 new Map<js.Node, CodePosition>.identity();
234 283
235 void registerPositions(js.Node node, 284 void registerPositions(js.Node node,
236 int startPosition, 285 int startPosition,
237 int endPosition, 286 int endPosition,
238 int closingPosition) { 287 int closingPosition) {
239 registerCodePosition(node, 288 registerCodePosition(node,
240 new CodePosition(startPosition, endPosition, closingPosition)); 289 new CodePosition(startPosition, endPosition, closingPosition));
241 } 290 }
242 291
243 void registerCodePosition(js.Node node, CodePosition codePosition) { 292 void registerCodePosition(js.Node node, CodePosition codePosition) {
244 _codePositionMap[node] = codePosition; 293 _codePositionMap[node] = codePosition;
245 } 294 }
246 295
247 CodePosition operator [](js.Node node) => _codePositionMap[node]; 296 CodePosition operator [](js.Node node) => _codePositionMap[node];
248 } 297 }
249 298
299 /// Enum values for the part of a Dart node used for the source location offset.
250 enum SourcePositionKind { 300 enum SourcePositionKind {
301 /// The source mapping should point to the start of the Dart node.
302 ///
303 /// For instance the first '(' for the `(*)()` call and 'f' of both the
304 /// `foo()` and the `*.bar()` call:
305 ///
306 /// (foo().bar())()
307 /// ^ // the start of the `(*)()` node
308 /// ^ // the start of the `foo()` node
309 /// ^ // the start of the `*.bar()` node
310 ///
251 START, 311 START,
312
313 /// The source mapping should point an inner position of the Dart node.
314 ///
315 /// For instance the second '(' of the `(*)()` call, the 'f' of the `foo()`
316 /// call and the 'b' of the `*.bar()` call:
317 ///
318 /// (foo().bar())()
319 /// ^ // the inner position of the `(*)()` node
320 /// ^ // the inner position of the `foo()` node
321 /// ^ // the inner position of the `*.bar()` node
322 ///
323 /// For function expressions the inner position is the closing brace or the
324 /// arrow:
325 ///
326 /// foo() => () {}
327 /// ^ // the inner position of the 'foo' function
328 /// ^ // the inner position of the closure
329 ///
330 INNER,
331 }
332
333 SourceLocation getSourceLocation(
334 SourceInformation sourceInformation,
335 [SourcePositionKind sourcePositionKind = SourcePositionKind.START]) {
336 if (sourceInformation == null) return null;
337 switch (sourcePositionKind) {
338 case SourcePositionKind.START:
339 return sourceInformation.startPosition;
340 case SourcePositionKind.INNER:
341 return sourceInformation.closingPosition;
342 }
343 }
344
345 /// Enum values for the part of the JavaScript node used for the JavaScript
346 /// code offset of a source mapping.
347 enum CodePositionKind {
348 /// The source mapping is put on left-most offset of the node.
349 ///
350 /// For instance on the 'f' of a function or 'r' of a return statement:
351 ///
352 /// foo: function() { return 0; }
353 /// ^ // the function start position
354 /// ^ // the return start position
355 START,
356
357 /// The source mapping is put on the closing token.
358 ///
359 /// For instance on the '}' of a function or the ';' of a return statement:
360 ///
361 /// foo: function() { return 0; }
362 /// ^ // the function closing position
363 /// ^ // the return closing position
364 ///
252 CLOSING, 365 CLOSING,
253 END, 366
254 } 367 /// The source mapping is put at the end of the code for the node.
255 368 ///
256 enum CodePositionKind { 369 /// For instance after '}' of a function or after the ';' of a return
257 START, 370 /// statement:
258 CLOSING, 371 ///
372 /// foo: function() { return 0; }
373 /// ^ // the function end position
374 /// ^ // the return end position
375 ///
259 END, 376 END,
260 } 377 }
261 378
262 /// Processor that associates [SourceLocation]s from [SourceInformation] on 379 /// Processor that associates [SourceLocation]s from [SourceInformation] on
263 /// [js.Node]s with the target offsets in a [SourceMapper]. 380 /// [js.Node]s with the target offsets in a [SourceMapper].
264 class PositionSourceInformationProcessor 381 class PositionSourceInformationProcessor implements SourceInformationProcessor {
265 extends js.BaseVisitor implements SourceInformationProcessor { 382 final CodePositionRecorder codePositionRecorder = new CodePositionRecorder();
266 final CodePositionRecorder codePositions = new CodePositionRecorder(); 383 CodePositionMap codePositionMap;
267 final SourceMapper sourceMapper; 384 List<TraceListener> traceListeners;
268 385
269 PositionSourceInformationProcessor(this.sourceMapper); 386 PositionSourceInformationProcessor(
270 387 SourceMapper sourceMapper,
271 void process(js.Node node) { 388 [Coverage coverage]) {
272 node.accept(this); 389 codePositionMap = coverage != null
273 } 390 ? new CodePositionCoverage(codePositionRecorder, coverage)
274 391 : codePositionRecorder;
275 void visitChildren(js.Node node) { 392 traceListeners = [new PositionTraceListener(sourceMapper)];
276 node.visitChildren(this); 393 if (coverage != null) {
277 } 394 traceListeners.add(new CoverageListener(coverage));
278 395 }
279 CodePosition getCodePosition(js.Node node) { 396 }
280 return codePositions[node]; 397
281 } 398 void process(js.Node node, CodeBuffer codeBuffer) {
282 399 new JavaScriptTracer(codePositionMap, traceListeners).apply(node);
283 /// Associates [sourceInformation] with the JavaScript [node].
284 ///
285 /// The offset into the JavaScript code is computed by pulling the
286 /// [codePositionKind] from the code positions associated with
287 /// [codePositionNode].
288 ///
289 /// The mapped Dart source location is computed by pulling the
290 /// [sourcePositionKind] source location from [sourceInformation].
291 void apply(js.Node node,
292 js.Node codePositionNode,
293 CodePositionKind codePositionKind,
294 SourceInformation sourceInformation,
295 SourcePositionKind sourcePositionKind) {
296 if (sourceInformation != null) {
297 CodePosition codePosition = getCodePosition(codePositionNode);
298 // We should always have recorded the needed code positions.
299 assert(invariant(
300 NO_LOCATION_SPANNABLE,
301 codePosition != null,
302 message:
303 "Code position missing for "
304 "${nodeToString(codePositionNode)}:\n"
305 "${DebugPrinter.prettyPrint(node)}"));
306 if (codePosition == null) return;
307 int codeLocation;
308 SourceLocation sourceLocation;
309 switch (codePositionKind) {
310 case CodePositionKind.START:
311 codeLocation = codePosition.startPosition;
312 break;
313 case CodePositionKind.CLOSING:
314 codeLocation = codePosition.closingPosition;
315 break;
316 case CodePositionKind.END:
317 codeLocation = codePosition.endPosition;
318 break;
319 }
320 switch (sourcePositionKind) {
321 case SourcePositionKind.START:
322 sourceLocation = sourceInformation.startPosition;
323 break;
324 case SourcePositionKind.CLOSING:
325 sourceLocation = sourceInformation.closingPosition;
326 break;
327 case SourcePositionKind.END:
328 sourceLocation = sourceInformation.endPosition;
329 break;
330 }
331 if (codeLocation != null && sourceLocation != null) {
332 sourceMapper.register(node, codeLocation, sourceLocation);
333 }
334 }
335 }
336
337 @override
338 visitNode(js.Node node) {
339 SourceInformation sourceInformation = node.sourceInformation;
340 if (sourceInformation != null) {
341 /// Associates the left-most position of the JS code with the left-most
342 /// position of the Dart code.
343 apply(node,
344 node, CodePositionKind.START,
345 sourceInformation, SourcePositionKind.START);
346 }
347 visitChildren(node);
348 }
349
350 @override
351 visitFun(js.Fun node) {
352 SourceInformation sourceInformation = node.sourceInformation;
353 if (sourceInformation != null) {
354 /// Associates the end brace of the JavaScript function with the end brace
355 /// of the Dart function (or the `;` in case of arrow notation).
356 apply(node,
357 node, CodePositionKind.CLOSING,
358 sourceInformation, SourcePositionKind.CLOSING);
359 }
360
361 visitChildren(node);
362 }
363
364 @override
365 visitExpressionStatement(js.ExpressionStatement node) {
366 visitChildren(node);
367 }
368
369 @override
370 visitBinary(js.Binary node) {
371 visitChildren(node);
372 }
373
374 @override
375 visitAccess(js.PropertyAccess node) {
376 visitChildren(node);
377 }
378
379 @override
380 visitCall(js.Call node) {
381 SourceInformation sourceInformation = node.sourceInformation;
382 if (sourceInformation != null) {
383 if (node.target is js.PropertyAccess) {
384 js.PropertyAccess access = node.target;
385 js.Node target = access;
386 bool pureAccess = false;
387 while (target is js.PropertyAccess) {
388 js.PropertyAccess targetAccess = target;
389 if (targetAccess.receiver is js.VariableUse ||
390 targetAccess.receiver is js.This) {
391 pureAccess = true;
392 break;
393 } else {
394 target = targetAccess.receiver;
395 }
396 }
397 if (pureAccess) {
398 // a.m() this.m() a.b.c.d.m()
399 // ^ ^ ^
400 apply(
401 node,
402 node,
403 CodePositionKind.START,
404 sourceInformation,
405 SourcePositionKind.START);
406 } else {
407 // *.m() *.a.b.c.d.m()
408 // ^ ^
409 apply(
410 node,
411 access.selector,
412 CodePositionKind.START,
413 sourceInformation,
414 SourcePositionKind.CLOSING);
415 }
416 } else if (node.target is js.VariableUse) {
417 // m()
418 // ^
419 apply(
420 node,
421 node,
422 CodePositionKind.START,
423 sourceInformation,
424 SourcePositionKind.START);
425 } else if (node.target is js.Fun || node.target is js.New) {
426 // function(){}() new Function("...")()
427 // ^ ^
428 apply(
429 node,
430 node.target,
431 CodePositionKind.END,
432 sourceInformation,
433 SourcePositionKind.CLOSING);
434 } else {
435 assert(invariant(NO_LOCATION_SPANNABLE, false,
436 message: "Unexpected property access ${nodeToString(node)}:\n"
437 "${DebugPrinter.prettyPrint(node)}"));
438 // Don't know....
439 apply(
440 node,
441 node,
442 CodePositionKind.START,
443 sourceInformation,
444 SourcePositionKind.START);
445 }
446 }
447 visitChildren(node);
448 } 400 }
449 401
450 @override 402 @override
451 void onPositions(js.Node node, 403 void onPositions(js.Node node,
452 int startPosition, 404 int startPosition,
453 int endPosition, 405 int endPosition,
454 int closingPosition) { 406 int closingPosition) {
455 codePositions.registerPositions( 407 codePositionRecorder.registerPositions(
456 node, startPosition, endPosition, closingPosition); 408 node, startPosition, endPosition, closingPosition);
457 } 409 }
458 } 410 }
411
412 /// [TraceListener] that register [SourceLocation]s with a [SourceMapper].
413 class PositionTraceListener extends TraceListener {
414 final SourceMapper sourceMapper;
415
416 PositionTraceListener(this.sourceMapper);
417
418 @override
419 void onStep(js.Node node, Offset offset, StepKind kind) {
420 SourceInformation sourceInformation = node.sourceInformation;
421 if (sourceInformation == null) return;
422
423 SourcePositionKind sourcePositionKind = SourcePositionKind.START;
424 switch (kind) {
425 case StepKind.FUN:
426 sourcePositionKind = SourcePositionKind.INNER;
427 break;
428 case StepKind.CALL:
429 CallPosition callPosition =
430 CallPosition.getSemanticPositionForCall(node);
431 sourcePositionKind = callPosition.sourcePositionKind;
432 break;
433 case StepKind.NEW:
434 case StepKind.RETURN:
435 case StepKind.BREAK:
436 case StepKind.CONTINUE:
437 case StepKind.THROW:
438 case StepKind.EXPRESSION_STATEMENT:
439 case StepKind.IF_CONDITION:
440 case StepKind.FOR_INITIALIZER:
441 case StepKind.FOR_CONDITION:
442 case StepKind.FOR_UPDATE:
443 case StepKind.WHILE_CONDITION:
444 case StepKind.DO_CONDITION:
445 case StepKind.SWITCH_EXPRESSION:
446 break;
447 }
448 int codeLocation = offset.codeOffset;
449 SourceLocation sourceLocation =
450 getSourceLocation(sourceInformation, sourcePositionKind);
451 if (codeLocation != null && sourceLocation != null) {
452 sourceMapper.register(node, codeLocation, sourceLocation);
453 }
454 }
455 }
456
457 /// The position of a [js.Call] node.
458 class CallPosition {
459 final js.Node node;
460 final CodePositionKind codePositionKind;
461 final SourcePositionKind sourcePositionKind;
462
463 CallPosition(this.node, this.codePositionKind, this.sourcePositionKind);
464
465 /// Computes the [CallPosition] for [node].
466 static CallPosition getSemanticPositionForCall(js.Call node) {
467 if (node.target is js.PropertyAccess) {
468 js.PropertyAccess access = node.target;
469 js.Node target = access;
470 bool pureAccess = false;
471 while (target is js.PropertyAccess) {
472 js.PropertyAccess targetAccess = target;
473 if (targetAccess.receiver is js.VariableUse ||
474 targetAccess.receiver is js.This) {
475 pureAccess = true;
476 break;
477 } else {
478 target = targetAccess.receiver;
479 }
480 }
481 if (pureAccess) {
482 // a.m() this.m() a.b.c.d.m()
483 // ^ ^ ^
484 return new CallPosition(
485 node,
486 CodePositionKind.START,
487 SourcePositionKind.START);
488 } else {
489 // *.m() *.a.b.c.d.m()
490 // ^ ^
491 return new CallPosition(
492 access.selector,
493 CodePositionKind.START,
494 SourcePositionKind.INNER);
495 }
496 } else if (node.target is js.VariableUse) {
497 // m()
498 // ^
499 return new CallPosition(
500 node,
501 CodePositionKind.START,
502 SourcePositionKind.START);
503 } else if (node.target is js.Fun || node.target is js.New) {
504 // function(){}() new Function("...")()
505 // ^ ^
506 return new CallPosition(
507 node.target,
508 CodePositionKind.END,
509 SourcePositionKind.INNER);
510 } else if (node.target is js.Binary || node.target is js.Call) {
511 // (0,a)() m()()
512 // ^ ^
513 return new CallPosition(
514 node.target,
515 CodePositionKind.END,
516 SourcePositionKind.INNER);
517 } else {
518 assert(invariant(NO_LOCATION_SPANNABLE, false,
519 message: "Unexpected property access ${nodeToString(node)}:\n"
520 "${DebugPrinter.prettyPrint(node)}"));
521 // Don't know....
522 return new CallPosition(
523 node,
524 CodePositionKind.START,
525 SourcePositionKind.START);
526 }
527 }
528 }
529
530 class Offset {
531 /// The offset of the enclosing statement relative to the beginning of the
532 /// file.
533 ///
534 /// For instance:
535 ///
536 /// foo().bar(baz());
537 /// ^ // the statement offset of the `foo()` call
538 /// ^ // the statement offset of the `*.bar()` call
539 /// ^ // the statement offset of the `baz()` call
540 ///
541 final int statementOffset;
542
543 /// The `subexpression` offset of the step. This is the (mostly) unique
544 /// offset relative to the beginning of the file, that identifies the
545 /// current of execution.
546 ///
547 /// For instance:
548 ///
549 /// foo().bar(baz());
550 /// ^ // the subexpression offset of the `foo()` call
551 /// ^ // the subexpression offset of the `*.bar()` call
552 /// ^ // the subexpression offset of the `baz()` call
553 ///
554 /// Here, even though the JavaScript node for the `*.bar()` call contains
555 /// the `foo()` its execution is identified by the `bar` identifier more than
556 /// the foo identifier.
557 ///
558 final int codeOffset;
559
560 /// The `left-to-right` offset of the step. This is a restricted version of
561 /// [subexpressionOffset] where offset by the execution order are always
Siggi Cherem (dart-lang) 2016/01/22 19:32:58 A few notes: - [subexpressionOffset] => [codeOffs
Johnni Winther 2016/01/25 09:58:55 Done.
562 /// increasing within the same statement.
563 ///
564 /// For instance:
565 ///
566 /// foo().bar(baz());
567 /// ^ // the left-to-right offset of the `foo()` call
568 /// ^ // the left-to-right offset of the `*.bar()` call
569 /// ^ // the left-to-right offset of the `baz()` call
570 ///
571 /// Here, `baz()` is executed before `foo()` be needs to use 'f' as its best
Siggi Cherem (dart-lang) 2016/01/22 19:32:58 nit: "be needs" => "so we need to"?
Johnni Winther 2016/01/25 09:58:55 Done.
572 /// position under the restriction.
573 ///
574 final int leftToRightOffset;
Siggi Cherem (dart-lang) 2016/01/22 19:32:58 another idea for the name here: executionOrderOffs
Johnni Winther 2016/01/25 09:58:55 I find that equally confusing :(
575
576 Offset(this.statementOffset, this.leftToRightOffset, this.codeOffset);
577
578 String toString() {
579 return 'Offset[statementOffset=$statementOffset,'
580 'leftToRightOffset=$leftToRightOffset,'
581 'codeOffset=$codeOffset]';
582 }
583 }
584
585 enum BranchKind {
586 CONDITION,
587 LOOP,
588 CATCH,
589 FINALLY,
590 CASE,
591 }
592
593 enum StepKind {
594 FUN,
595 CALL,
596 NEW,
597 RETURN,
598 BREAK,
599 CONTINUE,
600 THROW,
601 EXPRESSION_STATEMENT,
602 IF_CONDITION,
603 FOR_INITIALIZER,
604 FOR_CONDITION,
605 FOR_UPDATE,
606 WHILE_CONDITION,
607 DO_CONDITION,
608 SWITCH_EXPRESSION,
609 }
610
611 /// Listener for the [JavaScriptTracer].
612 abstract class TraceListener {
613 /// Called before [root] node is procesed by the [JavaScriptTracer].
614 void onStart(js.Node root) {}
615
616 /// Called after [root] node has been procesed by the [JavaScriptTracer].
617 void onEnd(js.Node root) {}
618
619 /// Called when a branch of the given [kind] is started. [value] is provided
620 /// to distinguish true/false branches of [BranchKind.CONDITION] and cases of
621 /// [Branch.CASE].
622 void pushBranch(BranchKind kind, [value]) {}
623
624 /// Called when the current branch ends.
625 void popBranch() {}
626
627 /// Called when [node] defines a step of the given [kind] at the given
628 /// [offset] when the generated JavaScript code.
629 void onStep(js.Node node, Offset offset, StepKind kind) {}
630 }
631
632 /// Visitor that computes the [js.Node]s the are part of the JavaScript
633 /// steppable execution and thus needs source mapping locations.
634 class JavaScriptTracer extends js.BaseVisitor {
635 final CodePositionMap codePositions;
636 final List<TraceListener> listeners;
637
638 /// The steps added by subexpressions.
639 List steps = [];
640
641 /// The offset of the current statement.
642 int statementOffset;
643
644 /// The current offset in left-to-right progression.
645 int leftToRightOffset;
646
647 /// The offset of the surrounding statement, used for the first subexpression.
648 int offsetPosition;
649
650 bool active;
651
652 JavaScriptTracer(this.codePositions,
653 this.listeners,
654 {this.active: false});
655
656 void notifyStart(js.Node node) {
657 listeners.forEach((listener) => listener.onStart(node));
658 }
659
660 void notifyEnd(js.Node node) {
661 listeners.forEach((listener) => listener.onEnd(node));
662 }
663
664 void notifyPushBranch(BranchKind kind, [value]) {
665 if (active) {
666 listeners.forEach((listener) => listener.pushBranch(kind, value));
667 }
668 }
669
670 void notifyPopBranch() {
671 if (active) {
672 listeners.forEach((listener) => listener.popBranch());
673 }
674 }
675
676 void notifyStep(js.Node node, Offset offset, StepKind kind) {
677 if (active) {
678 listeners.forEach((listener) => listener.onStep(node, offset, kind));
679 }
680 }
681
682 void apply(js.Node node) {
683 notifyStart(node);
684 node.accept(this);
685 notifyEnd(node);
686 }
687
688 @override
689 visitNode(js.Node node) {
690 node.visitChildren(this);
691 }
692
693 visit(js.Node node, [BranchKind branch, value]) {
694 if (node != null) {
695 if (branch != null) {
696 notifyPushBranch(branch, value);
697 node.accept(this);
698 notifyPopBranch();
699 } else {
700 node.accept(this);
701 }
702 }
703 }
704
705 visitList(List<js.Node> nodeList) {
706 if (nodeList != null) {
707 for (js.Node node in nodeList) {
708 visit(node);
709 }
710 }
711 }
712
713 @override
714 visitFun(js.Fun node) {
715 bool activeBefore = active;
716 if (!active) {
717 active = node.sourceInformation != null;
718 }
719 visit(node.body);
720 leftToRightOffset = statementOffset =
721 getSyntaxOffset(node, kind: CodePositionKind.CLOSING);
722 Offset offset = getOffsetForNode(node, statementOffset);
723 notifyStep(node, offset, StepKind.FUN);
724 active = activeBefore;
725 }
726
727 @override
728 visitBlock(js.Block node) {
729 for (js.Statement statement in node.statements) {
730 visit(statement);
731 }
732 }
733
734 int getSyntaxOffset(js.Node node,
735 {CodePositionKind kind: CodePositionKind.START}) {
736 CodePosition codePosition = codePositions[node];
737 if (codePosition != null) {
738 return codePosition.getPosition(kind);
739 }
740 return null;
741 }
742
743 visitSubexpression(js.Node parent,
744 js.Expression child,
745 int codeOffset,
746 StepKind kind) {
747 var oldSteps = steps;
748 steps = [];
749 offsetPosition = codeOffset;
750 visit(child);
751 if (steps.isEmpty) {
752 notifyStep(parent,
753 getOffsetForNode(parent, offsetPosition),
754 kind);
755 }
756 steps = oldSteps;
757 }
758
759 @override
760 visitExpressionStatement(js.ExpressionStatement node) {
761 statementOffset = getSyntaxOffset(node);
762 visitSubexpression(
763 node, node.expression, statementOffset,
764 StepKind.EXPRESSION_STATEMENT);
765 statementOffset = null;
766 leftToRightOffset = null;
767 }
768
769 @override
770 visitEmptyStatement(js.EmptyStatement node) {}
771
772 @override
773 visitCall(js.Call node) {
774 visit(node.target);
775 int oldPosition = offsetPosition;
776 offsetPosition = null;
777 visitList(node.arguments);
778 offsetPosition = oldPosition;
779 CallPosition callPosition =
780 CallPosition.getSemanticPositionForCall(node);
781 js.Node positionNode = callPosition.node;
782 int callOffset = getSyntaxOffset(
783 positionNode, kind: callPosition.codePositionKind);
784 if (offsetPosition == null) {
785 offsetPosition = callOffset;
786 }
787 Offset offset = getOffsetForNode(positionNode, offsetPosition);
788 notifyStep(node, offset, StepKind.CALL);
789 steps.add(node);
790 offsetPosition = null;
791 }
792
793 @override
794 visitNew(js.New node) {
795 visit(node.target);
796 visitList(node.arguments);
797 notifyStep(
798 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.NEW);
799 steps.add(node);
800 offsetPosition = null;
801 }
802
803 @override
804 visitAccess(js.PropertyAccess node) {
805 visit(node.receiver);
806 visit(node.selector);
807 }
808
809 @override
810 visitVariableUse(js.VariableUse node) {}
811
812 @override
813 visitLiteralBool(js.LiteralBool node) {}
814
815 @override
816 visitLiteralString(js.LiteralString node) {}
817
818 @override
819 visitLiteralNumber(js.LiteralNumber node) {}
820
821 @override
822 visitLiteralNull(js.LiteralNull node) {}
823
824 @override
825 visitName(js.Name node) {}
826
827 @override
828 visitVariableDeclarationList(js.VariableDeclarationList node) {
829 visitList(node.declarations);
830 }
831
832 @override
833 visitVariableDeclaration(js.VariableDeclaration node) {}
834
835 @override
836 visitVariableInitialization(js.VariableInitialization node) {
837 visit(node.leftHandSide);
838 visit(node.value);
839 }
840
841 @override
842 visitAssignment(js.Assignment node) {
843 visit(node.leftHandSide);
844 visit(node.value);
845 }
846
847 @override
848 visitIf(js.If node) {
849 statementOffset = getSyntaxOffset(node);
850 visitSubexpression(node, node.condition, statementOffset,
851 StepKind.IF_CONDITION);
852 statementOffset = null;
853 visit(node.then, BranchKind.CONDITION, true);
854 visit(node.otherwise, BranchKind.CONDITION, false);
855 }
856
857 @override
858 visitFor(js.For node) {
859 int offset = statementOffset = getSyntaxOffset(node);
860 statementOffset = offset;
861 leftToRightOffset = null;
862 if (node.init != null) {
863 visitSubexpression(node, node.init, getSyntaxOffset(node),
864 StepKind.FOR_INITIALIZER);
865 }
866
867 if (node.condition != null) {
868 visitSubexpression(node, node.condition, getSyntaxOffset(node.condition),
869 StepKind.FOR_CONDITION);
870 }
871
872 notifyPushBranch(BranchKind.LOOP);
873 visit(node.body);
874
875 statementOffset = offset;
876 if (node.update != null) {
877 visitSubexpression(node, node.update, getSyntaxOffset(node.update),
878 StepKind.FOR_UPDATE);
879 }
880
881 notifyPopBranch();
882 }
883
884 @override
885 visitWhile(js.While node) {
886 statementOffset = getSyntaxOffset(node);
887 if (node.condition != null) {
888 visitSubexpression(node, node.condition, getSyntaxOffset(node.condition),
889 StepKind.WHILE_CONDITION);
890 }
891 statementOffset = null;
892 leftToRightOffset = null;
893
894 visit(node.body, BranchKind.LOOP);
895 }
896
897 @override
898 visitDo(js.Do node) {
899 statementOffset = getSyntaxOffset(node);
900 visit(node.body);
901 if (node.condition != null) {
902 visitSubexpression(node, node.condition, getSyntaxOffset(node.condition),
903 StepKind.DO_CONDITION);
904 }
905 statementOffset = null;
906 leftToRightOffset = null;
907 }
908
909 @override
910 visitBinary(js.Binary node) {
911 visit(node.left);
912 visit(node.right);
913 }
914
915 @override
916 visitThis(js.This node) {}
917
918 @override
919 visitReturn(js.Return node) {
920 statementOffset = getSyntaxOffset(node);
921 visit(node.value);
922 notifyStep(
923 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.RETURN);
924 statementOffset = null;
925 leftToRightOffset = null;
926 }
927
928 @override
929 visitThrow(js.Throw node) {
930 statementOffset = getSyntaxOffset(node);
931 visit(node.expression);
932 notifyStep(
933 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.THROW);
934 statementOffset = null;
935 leftToRightOffset = null;
936 }
937
938 @override
939 visitContinue(js.Continue node) {
940 statementOffset = getSyntaxOffset(node);
941 notifyStep(
942 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.CONTINUE);
943 statementOffset = null;
944 leftToRightOffset = null;
945 }
946
947 @override
948 visitBreak(js.Break node) {
949 statementOffset = getSyntaxOffset(node);
950 notifyStep(
951 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.BREAK);
952 statementOffset = null;
953 leftToRightOffset = null;
954 }
955
956 @override
957 visitTry(js.Try node) {
958 visit(node.body);
959 visit(node.catchPart, BranchKind.CATCH);
960 visit(node.finallyPart, BranchKind.FINALLY);
961 }
962
963 @override
964 visitCatch(js.Catch node) {
965 visit(node.body);
966 }
967
968 @override
969 visitConditional(js.Conditional node) {
970 visit(node.condition);
971 visit(node.then, BranchKind.CONDITION, true);
972 visit(node.otherwise, BranchKind.CONDITION, false);
973 }
974
975 @override
976 visitPrefix(js.Prefix node) {
977 visit(node.argument);
978 }
979
980 @override
981 visitPostfix(js.Postfix node) {
982 visit(node.argument);
983 }
984
985 @override
986 visitObjectInitializer(js.ObjectInitializer node) {
987 visitList(node.properties);
988 }
989
990 @override
991 visitProperty(js.Property node) {
992 visit(node.name);
993 visit(node.value);
994 }
995
996 @override
997 visitRegExpLiteral(js.RegExpLiteral node) {}
998
999 @override
1000 visitSwitch(js.Switch node) {
1001 statementOffset = getSyntaxOffset(node);
1002 visitSubexpression(node, node.key, getSyntaxOffset(node),
1003 StepKind.SWITCH_EXPRESSION);
1004 statementOffset = null;
1005 leftToRightOffset = null;
1006 for (int i = 0; i < node.cases.length; i++) {
1007 visit(node.cases[i], BranchKind.CASE, i);
1008 }
1009 }
1010
1011 @override
1012 visitCase(js.Case node) {
1013 visit(node.expression);
1014 visit(node.body);
1015 }
1016
1017 @override
1018 visitDefault(js.Default node) {
1019 visit(node.body);
1020 }
1021
1022 @override
1023 visitArrayInitializer(js.ArrayInitializer node) {
1024 visitList(node.elements);
1025 }
1026
1027 @override
1028 visitArrayHole(js.ArrayHole node) {}
1029
1030 @override
1031 visitLabeledStatement(js.LabeledStatement node) {
1032 statementOffset = getSyntaxOffset(node);
1033 visit(node.body);
1034 statementOffset = null;
1035 }
1036
1037 Offset getOffsetForNode(js.Node node, int codeOffset) {
1038 if (codeOffset == null) {
1039 CodePosition codePosition = codePositions[node];
1040 if (codePosition != null) {
1041 codeOffset = codePosition.startPosition;
1042 }
1043 }
1044 if (leftToRightOffset != null && leftToRightOffset < codeOffset) {
1045 leftToRightOffset = codeOffset;
1046 }
1047 if (leftToRightOffset == null) {
1048 leftToRightOffset = statementOffset;
1049 }
1050 return new Offset(statementOffset, leftToRightOffset, codeOffset);
1051 }
1052 }
1053
1054
1055 class Coverage {
1056 Set<js.Node> _nodesWithInfo = new Set<js.Node>();
1057 int _nodesWithInfoCount = 0;
1058 Set<js.Node> _nodesWithoutInfo = new Set<js.Node>();
1059 int _nodesWithoutInfoCount = 0;
1060 Map<Type, int> _nodesWithoutInfoCountByType = <Type, int>{};
1061 Set<js.Node> _nodesWithoutOffset = new Set<js.Node>();
1062 int _nodesWithoutOffsetCount = 0;
1063
1064 void registerNodeWithInfo(js.Node node) {
1065 _nodesWithInfo.add(node);
1066 }
1067
1068 void registerNodeWithoutInfo(js.Node node) {
1069 _nodesWithoutInfo.add(node);
1070 }
1071
1072 void registerNodesWithoutOffset(js.Node node) {
1073 _nodesWithoutOffset.add(node);
1074 }
1075
1076 void collapse() {
1077 _nodesWithInfoCount += _nodesWithInfo.length;
1078 _nodesWithInfo.clear();
1079 _nodesWithoutOffsetCount += _nodesWithoutOffset.length;
1080 _nodesWithoutOffset.clear();
1081
1082 _nodesWithoutInfoCount += _nodesWithoutInfo.length;
1083 for (js.Node node in _nodesWithoutInfo) {
1084 if (node is js.ExpressionStatement) {
1085 _nodesWithoutInfoCountByType.putIfAbsent(
1086 node.expression.runtimeType, () => 0);
1087 _nodesWithoutInfoCountByType[node.expression.runtimeType]++;
1088 } else {
1089 _nodesWithoutInfoCountByType.putIfAbsent(
1090 node.runtimeType, () => 0);
1091 _nodesWithoutInfoCountByType[node.runtimeType]++;
1092 }
1093 }
1094 _nodesWithoutInfo.clear();
1095 }
1096
1097 String getCoverageReport() {
1098 collapse();
1099 StringBuffer sb = new StringBuffer();
1100 int total = _nodesWithInfoCount + _nodesWithoutInfoCount;
1101 if (total > 0) {
1102 sb.write(_nodesWithoutInfoCount);
1103 sb.write('/');
1104 sb.write(total);
1105 sb.write(' (');
1106 sb.write((100.0 * _nodesWithInfoCount / total).toStringAsFixed(2));
1107 sb.write('%) nodes with info.');
1108 } else {
1109 sb.write('No nodes.');
1110 }
1111 if (_nodesWithoutOffsetCount > 0) {
1112 sb.write(' ');
1113 sb.write(_nodesWithoutOffsetCount);
1114 sb.write(' node');
1115 if (_nodesWithoutOffsetCount > 1) {
1116 sb.write('s');
1117 }
1118 sb.write(' without offset.');
1119 }
1120 if (_nodesWithoutInfoCount > 0) {
1121 sb.write('\nNodes without info (');
1122 sb.write(_nodesWithoutInfoCount);
1123 sb.write(') by runtime type:');
1124 _nodesWithoutInfoCountByType.forEach((Type type, int count) {
1125 sb.write('\n ');
1126 sb.write(count);
1127 sb.write(' ');
1128 sb.write(type);
1129 sb.write(' node');
1130 if (count > 1) {
1131 sb.write('s');
1132 }
1133 });
1134 sb.write('\n');
1135 }
1136 return sb.toString();
1137 }
1138
1139 String toString() => getCoverageReport();
1140 }
1141
1142 /// [TraceListener] that registers [onStep] callbacks with [coverage].
1143 class CoverageListener extends TraceListener {
1144 final Coverage coverage;
1145
1146 CoverageListener(this.coverage);
1147
1148 @override
1149 void onStep(js.Node node, Offset offset, StepKind kind) {
1150 SourceInformation sourceInformation = node.sourceInformation;
1151 if (sourceInformation != null) {
1152 coverage.registerNodeWithInfo(node);
1153 } else {
1154 coverage.registerNodeWithoutInfo(node);
1155 }
1156 }
1157
1158 @override
1159 void onEnd(js.Node node) {
1160 coverage.collapse();
1161 }
1162 }
1163
1164 /// [CodePositionMap] that registers calls with [Coverage].
1165 class CodePositionCoverage implements CodePositionMap {
1166 final CodePositionMap codePositions;
1167 final Coverage coverage;
1168
1169 CodePositionCoverage(this.codePositions, this.coverage);
1170
1171 @override
1172 CodePosition operator [](js.Node node) {
1173 CodePosition codePosition = codePositions[node];
1174 if (codePosition == null) {
1175 coverage.registerNodesWithoutOffset(node);
1176 }
1177 return codePosition;
1178 }
1179 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/io/code_output.dart ('k') | pkg/compiler/lib/src/io/source_information.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698