OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 /// A library with code coverage models. | |
6 library runtime.coverage.model; | |
7 | |
8 import 'dart:collection' show SplayTreeMap; | |
9 | |
10 import 'package:analyzer_experimental/src/generated/source.dart' show Source, So
urceRange; | |
11 import 'package:analyzer_experimental/src/generated/ast.dart' show ASTNode; | |
12 | |
13 import 'utils.dart'; | |
14 | |
15 | |
16 /// Contains information about the application. | |
17 class AppInfo { | |
18 final nodeStack = new List<NodeInfo>(); | |
19 final units = new List<UnitInfo>(); | |
20 final pathToFile = new Map<String, UnitInfo>(); | |
21 NodeInfo currentNode; | |
22 int nextId = 0; | |
23 | |
24 void enterUnit(String path, String content) { | |
25 var unit = new UnitInfo(this, path, content); | |
26 units.add(unit); | |
27 currentNode = unit; | |
28 } | |
29 | |
30 void enter(String kind, String name) { | |
31 nodeStack.add(currentNode); | |
32 currentNode = new NodeInfo(this, currentNode, kind, name); | |
33 } | |
34 | |
35 void leave() { | |
36 currentNode = nodeStack.removeLast(); | |
37 } | |
38 | |
39 int addNode(ASTNode node) { | |
40 return currentNode.addNode(node); | |
41 } | |
42 | |
43 void write(StringSink sink, Set<int> executedIds) { | |
44 sink.writeln('{'); | |
45 units.fold(null, (prev, unit) { | |
46 if (prev != null) sink.writeln(','); | |
47 return unit..write(sink, executedIds, ' '); | |
48 }); | |
49 sink.writeln(); | |
50 sink.writeln('}'); | |
51 } | |
52 } | |
53 | |
54 /// Information about some node - unit, class, method, function. | |
55 class NodeInfo { | |
56 final AppInfo appInfo; | |
57 final NodeInfo parent; | |
58 final String kind; | |
59 final String name; | |
60 final idToRange = new SplayTreeMap<int, SourceRange>(); | |
61 final children = <NodeInfo>[]; | |
62 | |
63 NodeInfo(this.appInfo, this.parent, this.kind, this.name) { | |
64 if (parent != null) { | |
65 parent.children.add(this); | |
66 } | |
67 } | |
68 | |
69 int addNode(ASTNode node) { | |
70 var id = appInfo.nextId++; | |
71 var range = new SourceRange(node.offset, node.length); | |
72 idToRange[id] = range; | |
73 return id; | |
74 } | |
75 | |
76 void write(StringSink sink, Set<int> executedIds, String prefix) { | |
77 sink.writeln('$prefix"$name": {'); | |
78 // Kind. | |
79 sink.writeln('$prefix "kind": "$kind",'); | |
80 // Print children. | |
81 if (children.isNotEmpty) { | |
82 sink.writeln('$prefix "children": {'); | |
83 children.fold(null, (prev, child) { | |
84 if (prev != null) sink.writeln(','); | |
85 return child..write(sink, executedIds, '$prefix '); | |
86 }); | |
87 sink.writeln(); | |
88 sink.writeln('$prefix }'); | |
89 } | |
90 // Print source and line ranges. | |
91 if (children.isEmpty) { | |
92 sink.write('${prefix} "ranges": ['); | |
93 var rangePrinter = new RangePrinter(unit, sink, executedIds); | |
94 idToRange.forEach(rangePrinter.handle); | |
95 rangePrinter.printRange(); | |
96 sink.writeln(']'); | |
97 } | |
98 // Close this node. | |
99 sink.write('$prefix}'); | |
100 } | |
101 | |
102 UnitInfo get unit => parent.unit; | |
103 } | |
104 | |
105 /// Helper for printing merged source/line intervals. | |
106 class RangePrinter { | |
107 final UnitInfo unit; | |
108 final StringSink sink; | |
109 final Set<int> executedIds; | |
110 | |
111 bool first = true; | |
112 int startId = -1; | |
113 int startOffset = -1; | |
114 int endId = -1; | |
115 int endOffset = -1; | |
116 | |
117 RangePrinter(this.unit, this.sink, this.executedIds); | |
118 | |
119 handle(int id, SourceRange range) { | |
120 if (executedIds.contains(id)) { | |
121 printRange(); | |
122 } else { | |
123 if (endId == id - 1) { | |
124 endId = id; | |
125 endOffset = range.end; | |
126 } else { | |
127 startId = id; | |
128 endId = id; | |
129 startOffset = range.offset; | |
130 endOffset = range.end; | |
131 } | |
132 } | |
133 } | |
134 | |
135 void printRange() { | |
136 if (endId == -1) return; | |
137 printSeparator(); | |
138 var startLine = unit.getLine(startOffset); | |
139 var endLine = unit.getLine(endOffset); | |
140 sink.write('$startOffset,$endOffset,$startLine,$endLine'); | |
141 startId = startOffset = startLine = -1; | |
142 endId = endOffset = endLine = -1; | |
143 } | |
144 | |
145 void printSeparator() { | |
146 if (first) { | |
147 first = false; | |
148 } else { | |
149 sink.write(', '); | |
150 } | |
151 } | |
152 } | |
153 | |
154 /// Contains information about the single unit of the application. | |
155 class UnitInfo extends NodeInfo { | |
156 List<int> lineOffsets; | |
157 | |
158 UnitInfo(AppInfo appInfo, String path, String content) | |
159 : super(appInfo, null, 'unit', path) { | |
160 lineOffsets = getLineOffsets(content); | |
161 } | |
162 | |
163 UnitInfo get unit => this; | |
164 | |
165 int getLine(int offset) { | |
166 return binarySearch(lineOffsets, (x) => x >= offset); | |
167 } | |
168 } | |
OLD | NEW |