Chromium Code Reviews| 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..449776d866f3603555510d2ad3128f1756ac9d11 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,26 @@ class PositionSourceInformationBuilder implements SourceInformationBuilder { |
| SourceInformation buildAssignment(Node node) => buildBegin(node); |
| @override |
| + SourceInformation buildVariableDeclaration() { |
| + if (element.hasNode) { |
| + Node node = element.node; |
| + FunctionExpression functionExpression = node.asFunctionExpression(); |
| + 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
|
| + return buildBegin(functionExpression.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 +433,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 +836,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 +868,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 +881,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 +976,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 +1019,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 +1192,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 +1214,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 +1240,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 { |