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 |