Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 FieldElement, | |
| 13 LocalElement; | 14 LocalElement; |
| 14 import '../js/js.dart' as js; | 15 import '../js/js.dart' as js; |
| 15 import '../js/js_source_mapping.dart'; | 16 import '../js/js_source_mapping.dart'; |
| 16 import '../js/js_debug.dart'; | 17 import '../js/js_debug.dart'; |
| 17 import '../tree/tree.dart' show | 18 import '../tree/tree.dart' show |
| 19 FunctionExpression, | |
| 18 Node, | 20 Node, |
| 19 Send; | 21 Send; |
| 20 | 22 |
| 21 import 'code_output.dart' show | 23 import 'code_output.dart' show |
| 22 CodeBuffer; | 24 CodeBuffer; |
| 23 import 'source_file.dart'; | 25 import 'source_file.dart'; |
| 24 import 'source_information.dart'; | 26 import 'source_information.dart'; |
| 25 | 27 |
| 26 /// [SourceInformation] that consists of an offset position into the source | 28 /// [SourceInformation] that consists of an offset position into the source |
| 27 /// code. | 29 /// code. |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 List<SourceLocation> get sourceLocations => const <SourceLocation>[]; | 142 List<SourceLocation> get sourceLocations => const <SourceLocation>[]; |
| 141 | 143 |
| 142 @override | 144 @override |
| 143 SourceSpan get sourceSpan => new SourceSpan(null, null, null); | 145 SourceSpan get sourceSpan => new SourceSpan(null, null, null); |
| 144 } | 146 } |
| 145 | 147 |
| 146 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. | 148 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. |
| 147 class PositionSourceInformationBuilder implements SourceInformationBuilder { | 149 class PositionSourceInformationBuilder implements SourceInformationBuilder { |
| 148 final SourceFile sourceFile; | 150 final SourceFile sourceFile; |
| 149 final String name; | 151 final String name; |
| 152 final AstElement element; | |
| 150 | 153 |
| 151 PositionSourceInformationBuilder(AstElement element) | 154 PositionSourceInformationBuilder(AstElement element) |
| 152 : sourceFile = element.implementation.compilationUnit.script.file, | 155 : this.element = element, |
| 156 sourceFile = element.implementation.compilationUnit.script.file, | |
| 153 name = computeElementNameForSourceMaps(element); | 157 name = computeElementNameForSourceMaps(element); |
| 154 | 158 |
| 155 SourceInformation buildDeclaration(AstElement element) { | 159 SourceInformation buildDeclaration(AstElement element) { |
| 156 if (element.isSynthesized) { | 160 if (element.isSynthesized) { |
| 157 return new PositionSourceInformation( | 161 return new PositionSourceInformation( |
| 158 new OffsetSourceLocation( | 162 new OffsetSourceLocation( |
| 159 sourceFile, element.position.charOffset, name)); | 163 sourceFile, element.position.charOffset, name)); |
| 160 } else { | 164 } else { |
| 161 return new PositionSourceInformation( | 165 return new PositionSourceInformation( |
| 162 null, | 166 null, |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 234 @override | 238 @override |
| 235 SourceInformation buildIf(Node node) => buildBegin(node); | 239 SourceInformation buildIf(Node node) => buildBegin(node); |
| 236 | 240 |
| 237 @override | 241 @override |
| 238 SourceInformation buildThrow(Node node) => buildBegin(node); | 242 SourceInformation buildThrow(Node node) => buildBegin(node); |
| 239 | 243 |
| 240 @override | 244 @override |
| 241 SourceInformation buildAssignment(Node node) => buildBegin(node); | 245 SourceInformation buildAssignment(Node node) => buildBegin(node); |
| 242 | 246 |
| 243 @override | 247 @override |
| 248 SourceInformation buildVariableDeclaration() { | |
| 249 if (element.hasNode) { | |
| 250 Node node = element.node; | |
| 251 FunctionExpression functionExpression = node.asFunctionExpression(); | |
| 252 if (functionExpression != null) { | |
|
Siggi Cherem (dart-lang)
2016/02/08 22:31:53
nit: I'd remove the call to `asFunctionExpression`
Johnni Winther
2016/02/10 09:19:27
Done for FunctionExpression. Type promotion is not
| |
| 253 return buildBegin(functionExpression.body); | |
| 254 } else { | |
| 255 if (element.isField) { | |
| 256 FieldElement field = element; | |
| 257 if (field.initializer != null) { | |
| 258 return buildBegin(field.initializer); | |
| 259 } | |
| 260 } | |
| 261 // TODO(johnniwinther): Are there other cases? | |
| 262 } | |
| 263 } | |
| 264 return null; | |
| 265 } | |
| 266 | |
| 267 @override | |
| 244 SourceInformationBuilder forContext(AstElement element) { | 268 SourceInformationBuilder forContext(AstElement element) { |
| 245 return new PositionSourceInformationBuilder(element); | 269 return new PositionSourceInformationBuilder(element); |
| 246 } | 270 } |
| 247 } | 271 } |
| 248 | 272 |
| 249 /// The start, end and closing offsets for a [js.Node]. | 273 /// The start, end and closing offsets for a [js.Node]. |
| 250 class CodePosition { | 274 class CodePosition { |
| 251 final int startPosition; | 275 final int startPosition; |
| 252 final int endPosition; | 276 final int endPosition; |
| 253 final int closingPosition; | 277 final int closingPosition; |
| (...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 402 @override | 426 @override |
| 403 void onPositions(js.Node node, | 427 void onPositions(js.Node node, |
| 404 int startPosition, | 428 int startPosition, |
| 405 int endPosition, | 429 int endPosition, |
| 406 int closingPosition) { | 430 int closingPosition) { |
| 407 codePositionRecorder.registerPositions( | 431 codePositionRecorder.registerPositions( |
| 408 node, startPosition, endPosition, closingPosition); | 432 node, startPosition, endPosition, closingPosition); |
| 409 } | 433 } |
| 410 } | 434 } |
| 411 | 435 |
| 436 /// Visitor that computes [SourceInformation] for a [js.Node] using information | |
| 437 /// attached to the node itself or alternatively from child nodes. | |
| 438 class NodeSourceInformation extends js.BaseVisitor<SourceInformation> { | |
| 439 const NodeSourceInformation(); | |
| 440 | |
| 441 SourceInformation visit(js.Node node) => node?.accept(this); | |
| 442 | |
| 443 @override | |
| 444 SourceInformation visitNode(js.Node node) => node.sourceInformation; | |
| 445 | |
| 446 @override | |
| 447 SourceInformation visitExpressionStatement(js.ExpressionStatement node) { | |
| 448 if (node.sourceInformation != null) { | |
| 449 return node.sourceInformation; | |
| 450 } | |
| 451 return visit(node.expression); | |
| 452 } | |
| 453 | |
| 454 @override | |
| 455 SourceInformation visitVariableDeclarationList( | |
| 456 js.VariableDeclarationList node) { | |
| 457 if (node.sourceInformation != null) { | |
| 458 return node.sourceInformation; | |
| 459 } | |
| 460 for (js.Node declaration in node.declarations) { | |
| 461 SourceInformation sourceInformation = visit(declaration); | |
| 462 if (sourceInformation != null) { | |
| 463 return sourceInformation; | |
| 464 } | |
| 465 } | |
| 466 return null; | |
| 467 } | |
| 468 | |
| 469 @override | |
| 470 SourceInformation visitVariableInitialization( | |
| 471 js.VariableInitialization node) { | |
| 472 if (node.sourceInformation != null) { | |
| 473 return node.sourceInformation; | |
| 474 } | |
| 475 return visit(node.value); | |
| 476 } | |
| 477 | |
| 478 @override | |
| 479 SourceInformation visitAssignment(js.Assignment node) { | |
| 480 if (node.sourceInformation != null) { | |
| 481 return node.sourceInformation; | |
| 482 } | |
| 483 return visit(node.value); | |
| 484 } | |
| 485 | |
| 486 } | |
| 487 | |
| 488 /// Mixin that add support for computing [SourceInformation] for a [js.Node]. | |
| 489 class NodeToSourceInformationMixin { | |
| 490 SourceInformation computeSourceInformation(js.Node node) { | |
| 491 return const NodeSourceInformation().visit(node); | |
| 492 } | |
| 493 } | |
| 494 | |
| 412 /// [TraceListener] that register [SourceLocation]s with a [SourceMapper]. | 495 /// [TraceListener] that register [SourceLocation]s with a [SourceMapper]. |
| 413 class PositionTraceListener extends TraceListener { | 496 class PositionTraceListener extends TraceListener with |
| 497 NodeToSourceInformationMixin { | |
| 414 final SourceMapper sourceMapper; | 498 final SourceMapper sourceMapper; |
| 415 | 499 |
| 416 PositionTraceListener(this.sourceMapper); | 500 PositionTraceListener(this.sourceMapper); |
| 417 | 501 |
| 418 @override | 502 @override |
| 419 void onStep(js.Node node, Offset offset, StepKind kind) { | 503 void onStep(js.Node node, Offset offset, StepKind kind) { |
| 420 SourceInformation sourceInformation = node.sourceInformation; | 504 SourceInformation sourceInformation = computeSourceInformation(node); |
| 421 if (sourceInformation == null) return; | 505 if (sourceInformation == null) return; |
| 422 | 506 |
| 423 SourcePositionKind sourcePositionKind = SourcePositionKind.START; | 507 SourcePositionKind sourcePositionKind = SourcePositionKind.START; |
| 424 switch (kind) { | 508 switch (kind) { |
| 425 case StepKind.FUN: | 509 case StepKind.FUN: |
| 426 sourcePositionKind = SourcePositionKind.INNER; | 510 sourcePositionKind = SourcePositionKind.INNER; |
| 427 break; | 511 break; |
| 428 case StepKind.CALL: | 512 case StepKind.CALL: |
| 429 CallPosition callPosition = | 513 CallPosition callPosition = |
| 430 CallPosition.getSemanticPositionForCall(node); | 514 CallPosition.getSemanticPositionForCall(node); |
| (...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 745 int codeOffset, | 829 int codeOffset, |
| 746 StepKind kind) { | 830 StepKind kind) { |
| 747 var oldSteps = steps; | 831 var oldSteps = steps; |
| 748 steps = []; | 832 steps = []; |
| 749 offsetPosition = codeOffset; | 833 offsetPosition = codeOffset; |
| 750 visit(child); | 834 visit(child); |
| 751 if (steps.isEmpty) { | 835 if (steps.isEmpty) { |
| 752 notifyStep(parent, | 836 notifyStep(parent, |
| 753 getOffsetForNode(parent, offsetPosition), | 837 getOffsetForNode(parent, offsetPosition), |
| 754 kind); | 838 kind); |
| 839 // The [offsetPosition] should only be used by the first subexpression. | |
| 840 offsetPosition = null; | |
| 755 } | 841 } |
| 756 steps = oldSteps; | 842 steps = oldSteps; |
| 757 } | 843 } |
| 758 | 844 |
| 759 @override | 845 @override |
| 760 visitExpressionStatement(js.ExpressionStatement node) { | 846 visitExpressionStatement(js.ExpressionStatement node) { |
| 761 statementOffset = getSyntaxOffset(node); | 847 statementOffset = getSyntaxOffset(node); |
| 762 visitSubexpression( | 848 visitSubexpression( |
| 763 node, node.expression, statementOffset, | 849 node, node.expression, statementOffset, |
| 764 StepKind.EXPRESSION_STATEMENT); | 850 StepKind.EXPRESSION_STATEMENT); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 775 int oldPosition = offsetPosition; | 861 int oldPosition = offsetPosition; |
| 776 offsetPosition = null; | 862 offsetPosition = null; |
| 777 visitList(node.arguments); | 863 visitList(node.arguments); |
| 778 offsetPosition = oldPosition; | 864 offsetPosition = oldPosition; |
| 779 CallPosition callPosition = | 865 CallPosition callPosition = |
| 780 CallPosition.getSemanticPositionForCall(node); | 866 CallPosition.getSemanticPositionForCall(node); |
| 781 js.Node positionNode = callPosition.node; | 867 js.Node positionNode = callPosition.node; |
| 782 int callOffset = getSyntaxOffset( | 868 int callOffset = getSyntaxOffset( |
| 783 positionNode, kind: callPosition.codePositionKind); | 869 positionNode, kind: callPosition.codePositionKind); |
| 784 if (offsetPosition == null) { | 870 if (offsetPosition == null) { |
| 871 // Use the call offset if this is not the first subexpression. | |
| 785 offsetPosition = callOffset; | 872 offsetPosition = callOffset; |
| 786 } | 873 } |
| 787 Offset offset = getOffsetForNode(positionNode, offsetPosition); | 874 Offset offset = getOffsetForNode(positionNode, offsetPosition); |
| 788 notifyStep(node, offset, StepKind.CALL); | 875 notifyStep(node, offset, StepKind.CALL); |
| 789 steps.add(node); | 876 steps.add(node); |
| 790 offsetPosition = null; | 877 offsetPosition = null; |
| 791 } | 878 } |
| 792 | 879 |
| 793 @override | 880 @override |
| 794 visitNew(js.New node) { | 881 visitNew(js.New node) { |
| 795 visit(node.target); | 882 visit(node.target); |
| 796 visitList(node.arguments); | 883 visitList(node.arguments); |
| 884 if (offsetPosition == null) { | |
| 885 // Use the syntax offset if this is not the first subexpression. | |
| 886 offsetPosition = getSyntaxOffset(node); | |
| 887 } | |
| 797 notifyStep( | 888 notifyStep( |
| 798 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.NEW); | 889 node, getOffsetForNode(node, offsetPosition), StepKind.NEW); |
| 799 steps.add(node); | 890 steps.add(node); |
| 800 offsetPosition = null; | 891 offsetPosition = null; |
| 801 } | 892 } |
| 802 | 893 |
| 803 @override | 894 @override |
| 804 visitAccess(js.PropertyAccess node) { | 895 visitAccess(js.PropertyAccess node) { |
| 805 visit(node.receiver); | 896 visit(node.receiver); |
| 806 visit(node.selector); | 897 visit(node.selector); |
| 807 } | 898 } |
| 808 | 899 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 878 StepKind.FOR_UPDATE); | 969 StepKind.FOR_UPDATE); |
| 879 } | 970 } |
| 880 | 971 |
| 881 notifyPopBranch(); | 972 notifyPopBranch(); |
| 882 } | 973 } |
| 883 | 974 |
| 884 @override | 975 @override |
| 885 visitWhile(js.While node) { | 976 visitWhile(js.While node) { |
| 886 statementOffset = getSyntaxOffset(node); | 977 statementOffset = getSyntaxOffset(node); |
| 887 if (node.condition != null) { | 978 if (node.condition != null) { |
| 888 visitSubexpression(node, node.condition, getSyntaxOffset(node.condition), | 979 visitSubexpression(node, node.condition, getSyntaxOffset(node), |
| 889 StepKind.WHILE_CONDITION); | 980 StepKind.WHILE_CONDITION); |
| 890 } | 981 } |
| 891 statementOffset = null; | 982 statementOffset = null; |
| 892 leftToRightOffset = null; | 983 leftToRightOffset = null; |
| 893 | 984 |
| 894 visit(node.body, BranchKind.LOOP); | 985 visit(node.body, BranchKind.LOOP); |
| 895 } | 986 } |
| 896 | 987 |
| 897 @override | 988 @override |
| 898 visitDo(js.Do node) { | 989 visitDo(js.Do node) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 921 visit(node.value); | 1012 visit(node.value); |
| 922 notifyStep( | 1013 notifyStep( |
| 923 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.RETURN); | 1014 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.RETURN); |
| 924 statementOffset = null; | 1015 statementOffset = null; |
| 925 leftToRightOffset = null; | 1016 leftToRightOffset = null; |
| 926 } | 1017 } |
| 927 | 1018 |
| 928 @override | 1019 @override |
| 929 visitThrow(js.Throw node) { | 1020 visitThrow(js.Throw node) { |
| 930 statementOffset = getSyntaxOffset(node); | 1021 statementOffset = getSyntaxOffset(node); |
| 1022 // Do not use [offsetPosition] for the subexpression. | |
| 1023 offsetPosition = null; | |
| 931 visit(node.expression); | 1024 visit(node.expression); |
| 932 notifyStep( | 1025 notifyStep( |
| 933 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.THROW); | 1026 node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.THROW); |
| 934 statementOffset = null; | 1027 statementOffset = null; |
| 935 leftToRightOffset = null; | 1028 leftToRightOffset = null; |
| 936 } | 1029 } |
| 937 | 1030 |
| 938 @override | 1031 @override |
| 939 visitContinue(js.Continue node) { | 1032 visitContinue(js.Continue node) { |
| 940 statementOffset = getSyntaxOffset(node); | 1033 statementOffset = getSyntaxOffset(node); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1092 } | 1185 } |
| 1093 } | 1186 } |
| 1094 _nodesWithoutInfo.clear(); | 1187 _nodesWithoutInfo.clear(); |
| 1095 } | 1188 } |
| 1096 | 1189 |
| 1097 String getCoverageReport() { | 1190 String getCoverageReport() { |
| 1098 collapse(); | 1191 collapse(); |
| 1099 StringBuffer sb = new StringBuffer(); | 1192 StringBuffer sb = new StringBuffer(); |
| 1100 int total = _nodesWithInfoCount + _nodesWithoutInfoCount; | 1193 int total = _nodesWithInfoCount + _nodesWithoutInfoCount; |
| 1101 if (total > 0) { | 1194 if (total > 0) { |
| 1102 sb.write(_nodesWithoutInfoCount); | 1195 sb.write(_nodesWithInfoCount); |
| 1103 sb.write('/'); | 1196 sb.write('/'); |
| 1104 sb.write(total); | 1197 sb.write(total); |
| 1105 sb.write(' ('); | 1198 sb.write(' ('); |
| 1106 sb.write((100.0 * _nodesWithInfoCount / total).toStringAsFixed(2)); | 1199 sb.write((100.0 * _nodesWithInfoCount / total).toStringAsFixed(2)); |
| 1107 sb.write('%) nodes with info.'); | 1200 sb.write('%) nodes with info.'); |
| 1108 } else { | 1201 } else { |
| 1109 sb.write('No nodes.'); | 1202 sb.write('No nodes.'); |
| 1110 } | 1203 } |
| 1111 if (_nodesWithoutOffsetCount > 0) { | 1204 if (_nodesWithoutOffsetCount > 0) { |
| 1112 sb.write(' '); | 1205 sb.write(' '); |
| 1113 sb.write(_nodesWithoutOffsetCount); | 1206 sb.write(_nodesWithoutOffsetCount); |
| 1114 sb.write(' node'); | 1207 sb.write(' node'); |
| 1115 if (_nodesWithoutOffsetCount > 1) { | 1208 if (_nodesWithoutOffsetCount > 1) { |
| 1116 sb.write('s'); | 1209 sb.write('s'); |
| 1117 } | 1210 } |
| 1118 sb.write(' without offset.'); | 1211 sb.write(' without offset.'); |
| 1119 } | 1212 } |
| 1120 if (_nodesWithoutInfoCount > 0) { | 1213 if (_nodesWithoutInfoCount > 0) { |
| 1121 sb.write('\nNodes without info ('); | 1214 sb.write('\nNodes without info ('); |
| 1122 sb.write(_nodesWithoutInfoCount); | 1215 sb.write(_nodesWithoutInfoCount); |
| 1123 sb.write(') by runtime type:'); | 1216 sb.write(') by runtime type:'); |
| 1124 _nodesWithoutInfoCountByType.forEach((Type type, int count) { | 1217 List<Type> types = _nodesWithoutInfoCountByType.keys.toList(); |
| 1218 types.sort((a, b) { | |
| 1219 return -_nodesWithoutInfoCountByType[a].compareTo( | |
| 1220 _nodesWithoutInfoCountByType[b]); | |
| 1221 }); | |
| 1222 | |
| 1223 types.forEach((Type type) { | |
| 1224 int count = _nodesWithoutInfoCountByType[type]; | |
| 1125 sb.write('\n '); | 1225 sb.write('\n '); |
| 1126 sb.write(count); | 1226 sb.write(count); |
| 1127 sb.write(' '); | 1227 sb.write(' '); |
| 1128 sb.write(type); | 1228 sb.write(type); |
| 1129 sb.write(' node'); | 1229 sb.write(' node'); |
| 1130 if (count > 1) { | 1230 if (count > 1) { |
| 1131 sb.write('s'); | 1231 sb.write('s'); |
| 1132 } | 1232 } |
| 1133 }); | 1233 }); |
| 1134 sb.write('\n'); | 1234 sb.write('\n'); |
| 1135 } | 1235 } |
| 1136 return sb.toString(); | 1236 return sb.toString(); |
| 1137 } | 1237 } |
| 1138 | 1238 |
| 1139 String toString() => getCoverageReport(); | 1239 String toString() => getCoverageReport(); |
| 1140 } | 1240 } |
| 1141 | 1241 |
| 1142 /// [TraceListener] that registers [onStep] callbacks with [coverage]. | 1242 /// [TraceListener] that registers [onStep] callbacks with [coverage]. |
| 1143 class CoverageListener extends TraceListener { | 1243 class CoverageListener extends TraceListener with NodeToSourceInformationMixin { |
| 1144 final Coverage coverage; | 1244 final Coverage coverage; |
| 1145 | 1245 |
| 1146 CoverageListener(this.coverage); | 1246 CoverageListener(this.coverage); |
| 1147 | 1247 |
| 1148 @override | 1248 @override |
| 1149 void onStep(js.Node node, Offset offset, StepKind kind) { | 1249 void onStep(js.Node node, Offset offset, StepKind kind) { |
| 1150 SourceInformation sourceInformation = node.sourceInformation; | 1250 SourceInformation sourceInformation = computeSourceInformation(node); |
| 1151 if (sourceInformation != null) { | 1251 if (sourceInformation != null) { |
| 1152 coverage.registerNodeWithInfo(node); | 1252 coverage.registerNodeWithInfo(node); |
| 1153 } else { | 1253 } else { |
| 1154 coverage.registerNodeWithoutInfo(node); | 1254 coverage.registerNodeWithoutInfo(node); |
| 1155 } | 1255 } |
| 1156 } | 1256 } |
| 1157 | 1257 |
| 1158 @override | 1258 @override |
| 1159 void onEnd(js.Node node) { | 1259 void onEnd(js.Node node) { |
| 1160 coverage.collapse(); | 1260 coverage.collapse(); |
| 1161 } | 1261 } |
| 1162 } | 1262 } |
| 1163 | 1263 |
| 1164 /// [CodePositionMap] that registers calls with [Coverage]. | 1264 /// [CodePositionMap] that registers calls with [Coverage]. |
| 1165 class CodePositionCoverage implements CodePositionMap { | 1265 class CodePositionCoverage implements CodePositionMap { |
| 1166 final CodePositionMap codePositions; | 1266 final CodePositionMap codePositions; |
| 1167 final Coverage coverage; | 1267 final Coverage coverage; |
| 1168 | 1268 |
| 1169 CodePositionCoverage(this.codePositions, this.coverage); | 1269 CodePositionCoverage(this.codePositions, this.coverage); |
| 1170 | 1270 |
| 1171 @override | 1271 @override |
| 1172 CodePosition operator [](js.Node node) { | 1272 CodePosition operator [](js.Node node) { |
| 1173 CodePosition codePosition = codePositions[node]; | 1273 CodePosition codePosition = codePositions[node]; |
| 1174 if (codePosition == null) { | 1274 if (codePosition == null) { |
| 1175 coverage.registerNodesWithoutOffset(node); | 1275 coverage.registerNodesWithoutOffset(node); |
| 1176 } | 1276 } |
| 1177 return codePosition; | 1277 return codePosition; |
| 1178 } | 1278 } |
| 1179 } | 1279 } |
| OLD | NEW |