Index: pkg/compiler/lib/src/io/position_information.dart |
diff --git a/pkg/compiler/lib/src/io/position_information.dart b/pkg/compiler/lib/src/io/position_information.dart |
index 2ef185be452f55817550c3ee125510c01b23679c..896676389d84d665dc5d383c39a3c4bd332eb5f5 100644 |
--- a/pkg/compiler/lib/src/io/position_information.dart |
+++ b/pkg/compiler/lib/src/io/position_information.dart |
@@ -10,11 +10,13 @@ library dart2js.source_information.position; |
import '../common.dart'; |
import '../elements/elements.dart' show |
AstElement, |
+ FieldElement, |
LocalElement; |
import '../js/js.dart' as js; |
import '../js/js_source_mapping.dart'; |
import '../js/js_debug.dart'; |
import '../tree/tree.dart' show |
+ FunctionExpression, |
Node, |
Send; |
@@ -147,9 +149,11 @@ class SourceMappedMarker extends SourceInformation { |
class PositionSourceInformationBuilder implements SourceInformationBuilder { |
final SourceFile sourceFile; |
final String name; |
+ final AstElement element; |
PositionSourceInformationBuilder(AstElement element) |
- : sourceFile = element.implementation.compilationUnit.script.file, |
+ : this.element = element, |
+ sourceFile = element.implementation.compilationUnit.script.file, |
name = computeElementNameForSourceMaps(element); |
SourceInformation buildDeclaration(AstElement element) { |
@@ -241,6 +245,23 @@ class PositionSourceInformationBuilder implements SourceInformationBuilder { |
SourceInformation buildAssignment(Node node) => buildBegin(node); |
@override |
+ SourceInformation buildVariableDeclaration() { |
+ if (element.hasNode) { |
+ Node node = element.node; |
+ if (node is FunctionExpression) { |
+ return buildBegin(node.body); |
+ } else if (element.isField) { |
+ FieldElement field = element; |
+ if (field.initializer != null) { |
+ return buildBegin(field.initializer); |
+ } |
+ } |
+ // TODO(johnniwinther): Are there other cases? |
+ } |
+ return null; |
+ } |
+ |
+ @override |
SourceInformationBuilder forContext(AstElement element) { |
return new PositionSourceInformationBuilder(element); |
} |
@@ -409,15 +430,75 @@ class PositionSourceInformationProcessor implements SourceInformationProcessor { |
} |
} |
+/// Visitor that computes [SourceInformation] for a [js.Node] using information |
+/// attached to the node itself or alternatively from child nodes. |
+class NodeSourceInformation extends js.BaseVisitor<SourceInformation> { |
+ const NodeSourceInformation(); |
+ |
+ SourceInformation visit(js.Node node) => node?.accept(this); |
+ |
+ @override |
+ SourceInformation visitNode(js.Node node) => node.sourceInformation; |
+ |
+ @override |
+ SourceInformation visitExpressionStatement(js.ExpressionStatement node) { |
+ if (node.sourceInformation != null) { |
+ return node.sourceInformation; |
+ } |
+ return visit(node.expression); |
+ } |
+ |
+ @override |
+ SourceInformation visitVariableDeclarationList( |
+ js.VariableDeclarationList node) { |
+ if (node.sourceInformation != null) { |
+ return node.sourceInformation; |
+ } |
+ for (js.Node declaration in node.declarations) { |
+ SourceInformation sourceInformation = visit(declaration); |
+ if (sourceInformation != null) { |
+ return sourceInformation; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ @override |
+ SourceInformation visitVariableInitialization( |
+ js.VariableInitialization node) { |
+ if (node.sourceInformation != null) { |
+ return node.sourceInformation; |
+ } |
+ return visit(node.value); |
+ } |
+ |
+ @override |
+ SourceInformation visitAssignment(js.Assignment node) { |
+ if (node.sourceInformation != null) { |
+ return node.sourceInformation; |
+ } |
+ return visit(node.value); |
+ } |
+ |
+} |
+ |
+/// Mixin that add support for computing [SourceInformation] for a [js.Node]. |
+class NodeToSourceInformationMixin { |
+ SourceInformation computeSourceInformation(js.Node node) { |
+ return const NodeSourceInformation().visit(node); |
+ } |
+} |
+ |
/// [TraceListener] that register [SourceLocation]s with a [SourceMapper]. |
-class PositionTraceListener extends TraceListener { |
+class PositionTraceListener extends TraceListener with |
+ NodeToSourceInformationMixin { |
final SourceMapper sourceMapper; |
PositionTraceListener(this.sourceMapper); |
@override |
void onStep(js.Node node, Offset offset, StepKind kind) { |
- SourceInformation sourceInformation = node.sourceInformation; |
+ SourceInformation sourceInformation = computeSourceInformation(node); |
if (sourceInformation == null) return; |
SourcePositionKind sourcePositionKind = SourcePositionKind.START; |
@@ -752,6 +833,8 @@ class JavaScriptTracer extends js.BaseVisitor { |
notifyStep(parent, |
getOffsetForNode(parent, offsetPosition), |
kind); |
+ // The [offsetPosition] should only be used by the first subexpression. |
+ offsetPosition = null; |
} |
steps = oldSteps; |
} |
@@ -782,6 +865,7 @@ class JavaScriptTracer extends js.BaseVisitor { |
int callOffset = getSyntaxOffset( |
positionNode, kind: callPosition.codePositionKind); |
if (offsetPosition == null) { |
+ // Use the call offset if this is not the first subexpression. |
offsetPosition = callOffset; |
} |
Offset offset = getOffsetForNode(positionNode, offsetPosition); |
@@ -794,8 +878,12 @@ class JavaScriptTracer extends js.BaseVisitor { |
visitNew(js.New node) { |
visit(node.target); |
visitList(node.arguments); |
+ if (offsetPosition == null) { |
+ // Use the syntax offset if this is not the first subexpression. |
+ offsetPosition = getSyntaxOffset(node); |
+ } |
notifyStep( |
- node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.NEW); |
+ node, getOffsetForNode(node, offsetPosition), StepKind.NEW); |
steps.add(node); |
offsetPosition = null; |
} |
@@ -885,7 +973,7 @@ class JavaScriptTracer extends js.BaseVisitor { |
visitWhile(js.While node) { |
statementOffset = getSyntaxOffset(node); |
if (node.condition != null) { |
- visitSubexpression(node, node.condition, getSyntaxOffset(node.condition), |
+ visitSubexpression(node, node.condition, getSyntaxOffset(node), |
StepKind.WHILE_CONDITION); |
} |
statementOffset = null; |
@@ -928,6 +1016,8 @@ class JavaScriptTracer extends js.BaseVisitor { |
@override |
visitThrow(js.Throw node) { |
statementOffset = getSyntaxOffset(node); |
+ // Do not use [offsetPosition] for the subexpression. |
+ offsetPosition = null; |
visit(node.expression); |
notifyStep( |
node, getOffsetForNode(node, getSyntaxOffset(node)), StepKind.THROW); |
@@ -1099,7 +1189,7 @@ class Coverage { |
StringBuffer sb = new StringBuffer(); |
int total = _nodesWithInfoCount + _nodesWithoutInfoCount; |
if (total > 0) { |
- sb.write(_nodesWithoutInfoCount); |
+ sb.write(_nodesWithInfoCount); |
sb.write('/'); |
sb.write(total); |
sb.write(' ('); |
@@ -1121,7 +1211,14 @@ class Coverage { |
sb.write('\nNodes without info ('); |
sb.write(_nodesWithoutInfoCount); |
sb.write(') by runtime type:'); |
- _nodesWithoutInfoCountByType.forEach((Type type, int count) { |
+ List<Type> types = _nodesWithoutInfoCountByType.keys.toList(); |
+ types.sort((a, b) { |
+ return -_nodesWithoutInfoCountByType[a].compareTo( |
+ _nodesWithoutInfoCountByType[b]); |
+ }); |
+ |
+ types.forEach((Type type) { |
+ int count = _nodesWithoutInfoCountByType[type]; |
sb.write('\n '); |
sb.write(count); |
sb.write(' '); |
@@ -1140,14 +1237,14 @@ class Coverage { |
} |
/// [TraceListener] that registers [onStep] callbacks with [coverage]. |
-class CoverageListener extends TraceListener { |
+class CoverageListener extends TraceListener with NodeToSourceInformationMixin { |
final Coverage coverage; |
CoverageListener(this.coverage); |
@override |
void onStep(js.Node node, Offset offset, StepKind kind) { |
- SourceInformation sourceInformation = node.sourceInformation; |
+ SourceInformation sourceInformation = computeSourceInformation(node); |
if (sourceInformation != null) { |
coverage.registerNodeWithInfo(node); |
} else { |