Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: pkg/compiler/lib/src/io/source_map_builder.dart

Issue 1084573006: Refactor SourceMapBuilder to allow for post-processing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Updated cf. comment. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/compiler/lib/src/use_unused_api.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 library dart2js.source_map_builder; 5 library dart2js.source_map_builder;
6 6
7 import '../util/util.dart'; 7 import '../util/util.dart';
8 import '../util/uri_extras.dart' show relativize; 8 import '../util/uri_extras.dart' show relativize;
9 import 'line_column_provider.dart'; 9 import 'line_column_provider.dart';
10 import 'source_information.dart' show SourceLocation; 10 import 'source_information.dart' show SourceLocation;
11 11
12 class SourceMapBuilder { 12 class SourceMapBuilder {
13 static const int VLQ_BASE_SHIFT = 5;
14 static const int VLQ_BASE_MASK = (1 << 5) - 1;
15 static const int VLQ_CONTINUATION_BIT = 1 << 5;
16 static const int VLQ_CONTINUATION_MASK = 1 << 5;
17 static const String BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn'
18 'opqrstuvwxyz0123456789+/';
19 13
20 /// The URI of the source map file. 14 /// The URI of the source map file.
21 final Uri sourceMapUri; 15 final Uri sourceMapUri;
22 /// The URI of the target language file. 16 /// The URI of the target language file.
23 final Uri targetFileUri; 17 final Uri targetFileUri;
24 18
25 LineColumnProvider lineColumnProvider; 19 final LineColumnProvider lineColumnProvider;
26 List<SourceMapEntry> entries; 20 final List<SourceMapEntry> entries = new List<SourceMapEntry>();
27
28 Map<Uri, int> sourceUriMap;
29 List<Uri> sourceUriList;
30 Map<String, int> sourceNameMap;
31 List<String> sourceNameList;
32
33 int previousTargetLine;
34 int previousTargetColumn;
35 int previousSourceUriIndex;
36 int previousSourceLine;
37 int previousSourceColumn;
38 int previousSourceNameIndex;
39 bool firstEntryInLine;
40 21
41 SourceMapBuilder(this.sourceMapUri, 22 SourceMapBuilder(this.sourceMapUri,
42 this.targetFileUri, 23 this.targetFileUri,
43 this.lineColumnProvider) { 24 this.lineColumnProvider);
44 entries = new List<SourceMapEntry>();
45
46 sourceUriMap = new Map<Uri, int>();
47 sourceUriList = new List<Uri>();
48 sourceNameMap = new Map<String, int>();
49 sourceNameList = new List<String>();
50
51 previousTargetLine = 0;
52 previousTargetColumn = 0;
53 previousSourceUriIndex = 0;
54 previousSourceLine = 0;
55 previousSourceColumn = 0;
56 previousSourceNameIndex = 0;
57 firstEntryInLine = true;
58 }
59
60 resetPreviousSourceLocation() {
61 previousSourceUriIndex = 0;
62 previousSourceLine = 0;
63 previousSourceColumn = 0;
64 previousSourceNameIndex = 0;
65 }
66
67 updatePreviousSourceLocation(SourceLocation sourceLocation) {
68 previousSourceLine = sourceLocation.line;
69 previousSourceColumn = sourceLocation.column;
70 Uri sourceUri = sourceLocation.sourceUri;
71 previousSourceUriIndex = indexOf(sourceUriList, sourceUri, sourceUriMap);
72 String sourceName = sourceLocation.sourceName;
73 if (sourceName != null) {
74 previousSourceNameIndex =
75 indexOf(sourceNameList, sourceName, sourceNameMap);
76 }
77 }
78
79 bool sameAsPreviousLocation(SourceLocation sourceLocation) {
80 if (sourceLocation == null) {
81 return true;
82 }
83 int sourceUriIndex =
84 indexOf(sourceUriList, sourceLocation.sourceUri, sourceUriMap);
85 return
86 sourceUriIndex == previousSourceUriIndex &&
87 sourceLocation.line == previousSourceLine &&
88 sourceLocation.column == previousSourceColumn;
89 }
90 25
91 void addMapping(int targetOffset, SourceLocation sourceLocation) { 26 void addMapping(int targetOffset, SourceLocation sourceLocation) {
92
93 bool sameLine(int position, otherPosition) {
94 return lineColumnProvider.getLine(position) ==
95 lineColumnProvider.getLine(otherPosition);
96 }
97
98 if (!entries.isEmpty && sameLine(targetOffset, entries.last.targetOffset)) {
99 if (sameAsPreviousLocation(sourceLocation)) {
100 // The entry points to the same source location as the previous entry in
101 // the same line, hence it is not needed for the source map.
102 //
103 // TODO(zarah): Remove this check and make sure that [addMapping] is not
104 // called for this position. Instead, when consecutive lines in the
105 // generated code point to the same source location, record this and use
106 // it to generate the entries of the source map.
107 return;
108 }
109 }
110
111 if (sourceLocation != null) {
112 updatePreviousSourceLocation(sourceLocation);
113 }
114 entries.add(new SourceMapEntry(sourceLocation, targetOffset)); 27 entries.add(new SourceMapEntry(sourceLocation, targetOffset));
115 } 28 }
116 29
117 void printStringListOn(List<String> strings, StringBuffer buffer) { 30 void printStringListOn(Iterable<String> strings, StringBuffer buffer) {
118 bool first = true; 31 bool first = true;
119 buffer.write('['); 32 buffer.write('[');
120 for (String string in strings) { 33 for (String string in strings) {
121 if (!first) buffer.write(','); 34 if (!first) buffer.write(',');
122 buffer.write('"'); 35 buffer.write('"');
123 writeJsonEscapedCharsOn(string, buffer); 36 writeJsonEscapedCharsOn(string, buffer);
124 buffer.write('"'); 37 buffer.write('"');
125 first = false; 38 first = false;
126 } 39 }
127 buffer.write(']'); 40 buffer.write(']');
128 } 41 }
129 42
130 String build() { 43 String build() {
131 resetPreviousSourceLocation(); 44
45 LineColumnMap<SourceMapEntry> lineColumnMap =
46 new LineColumnMap<SourceMapEntry>();
47 Map<Uri, LineColumnMap<SourceMapEntry>> sourceLocationMap =
48 <Uri, LineColumnMap<SourceMapEntry>>{};
49 entries.forEach((SourceMapEntry sourceMapEntry) {
50 int line = lineColumnProvider.getLine(sourceMapEntry.targetOffset);
51 int column =
52 lineColumnProvider.getColumn(line, sourceMapEntry.targetOffset);
53 lineColumnMap.add(line, column, sourceMapEntry);
54
55 SourceLocation location = sourceMapEntry.sourceLocation;
56 if (location != null) {
57 LineColumnMap<SourceMapEntry> sourceLineColumnMap =
58 sourceLocationMap.putIfAbsent(location.sourceUri,
59 () => new LineColumnMap<SourceMapEntry>());
60 sourceLineColumnMap.add(location.line, location.column, sourceMapEntry);
61 }
62 });
63
64 return _build(lineColumnMap);
65 }
66
67 String _build(LineColumnMap<SourceMapEntry> lineColumnMap) {
68 IndexMap<Uri> uriMap = new IndexMap<Uri>();
69 IndexMap<String> nameMap = new IndexMap<String>();
70
71 lineColumnMap.forEachElement((SourceMapEntry entry) {
72 SourceLocation sourceLocation = entry.sourceLocation;
73 if (sourceLocation != null) {
74 uriMap.register(sourceLocation.sourceUri);
75 if (sourceLocation.sourceName != null) {
76 nameMap.register(sourceLocation.sourceName);
77 }
78 }
79 });
80
132 StringBuffer mappingsBuffer = new StringBuffer(); 81 StringBuffer mappingsBuffer = new StringBuffer();
133 entries.forEach((SourceMapEntry entry) { 82 writeEntries(lineColumnMap, uriMap, nameMap, mappingsBuffer);
134 writeEntry(entry, mappingsBuffer); 83
135 });
136 StringBuffer buffer = new StringBuffer(); 84 StringBuffer buffer = new StringBuffer();
137 buffer.write('{\n'); 85 buffer.write('{\n');
138 buffer.write(' "version": 3,\n'); 86 buffer.write(' "version": 3,\n');
139 if (sourceMapUri != null && targetFileUri != null) { 87 if (sourceMapUri != null && targetFileUri != null) {
140 buffer.write( 88 buffer.write(
141 ' "file": "${relativize(sourceMapUri, targetFileUri, false)}",\n'); 89 ' "file": "${relativize(sourceMapUri, targetFileUri, false)}",\n');
142 } 90 }
143 buffer.write(' "sourceRoot": "",\n'); 91 buffer.write(' "sourceRoot": "",\n');
144 buffer.write(' "sources": '); 92 buffer.write(' "sources": ');
145 List<String> relativeSourceUriList = <String>[]; 93 Iterable<String> relativeSourceUriList = const <String>[];
146 if (sourceMapUri != null) { 94 if (sourceMapUri != null) {
147 relativeSourceUriList = sourceUriList 95 relativeSourceUriList = uriMap.elements
148 .map((u) => relativize(sourceMapUri, u, false)) 96 .map((u) => relativize(sourceMapUri, u, false));
149 .toList();
150 } 97 }
151 printStringListOn(relativeSourceUriList, buffer); 98 printStringListOn(relativeSourceUriList, buffer);
152 buffer.write(',\n'); 99 buffer.write(',\n');
153 buffer.write(' "names": '); 100 buffer.write(' "names": ');
154 printStringListOn(sourceNameList, buffer); 101 printStringListOn(nameMap.elements, buffer);
155 buffer.write(',\n'); 102 buffer.write(',\n');
156 buffer.write(' "mappings": "'); 103 buffer.write(' "mappings": "');
157 buffer.write(mappingsBuffer); 104 buffer.write(mappingsBuffer);
158 buffer.write('"\n}\n'); 105 buffer.write('"\n}\n');
159 return buffer.toString(); 106 return buffer.toString();
160 } 107 }
161 108
162 void writeEntry(SourceMapEntry entry, StringBuffer output) { 109 void writeEntries(LineColumnMap<SourceMapEntry> entries,
163 int targetLine = lineColumnProvider.getLine(entry.targetOffset); 110 IndexMap<Uri> uriMap,
164 int targetColumn = 111 IndexMap<String> nameMap,
165 lineColumnProvider.getColumn(targetLine, entry.targetOffset); 112 StringBuffer output) {
113 SourceLocation previousSourceLocation;
114 int previousTargetLine = 0;
115 DeltaEncoder targetColumnEncoder = new DeltaEncoder();
116 bool firstEntryInLine = true;
117 DeltaEncoder sourceUriIndexEncoder = new DeltaEncoder();
118 DeltaEncoder sourceLineEncoder = new DeltaEncoder();
119 DeltaEncoder sourceColumnEncoder = new DeltaEncoder();
120 DeltaEncoder sourceNameIndexEncoder = new DeltaEncoder();
166 121
167 if (targetLine > previousTargetLine) { 122 entries.forEach((int targetLine,
168 for (int i = previousTargetLine; i < targetLine; ++i) { 123 int targetColumn,
169 output.write(';'); 124 SourceMapEntry entry) {
125 SourceLocation sourceLocation = entry.sourceLocation;
126 if (sourceLocation == previousSourceLocation) {
127 return;
170 } 128 }
171 previousTargetLine = targetLine;
172 previousTargetColumn = 0;
173 firstEntryInLine = true;
174 }
175 129
176 if (!firstEntryInLine) { 130 if (targetLine > previousTargetLine) {
177 output.write(','); 131 for (int i = previousTargetLine; i < targetLine; ++i) {
178 } 132 output.write(';');
179 firstEntryInLine = false; 133 }
134 previousTargetLine = targetLine;
135 previousSourceLocation = null;
136 targetColumnEncoder.reset();
137 firstEntryInLine = true;
138 }
180 139
181 encodeVLQ(output, targetColumn - previousTargetColumn); 140 if (!firstEntryInLine) {
182 previousTargetColumn = targetColumn; 141 output.write(',');
142 }
143 firstEntryInLine = false;
183 144
184 if (entry.sourceLocation == null) return; 145 targetColumnEncoder.encode(output, targetColumn);
185 146
186 Uri sourceUri = entry.sourceLocation.sourceUri; 147 if (sourceLocation == null) {
187 int sourceLine = entry.sourceLocation.line; 148 return;
188 int sourceColumn = entry.sourceLocation.column; 149 }
189 String sourceName = entry.sourceLocation.sourceName;
190 150
191 int sourceUriIndex = indexOf(sourceUriList, sourceUri, sourceUriMap); 151 Uri sourceUri = sourceLocation.sourceUri;
192 encodeVLQ(output, sourceUriIndex - previousSourceUriIndex); 152 sourceUriIndexEncoder.encode(output, uriMap[sourceUri]);
193 encodeVLQ(output, sourceLine - previousSourceLine); 153 sourceLineEncoder.encode(output, sourceLocation.line);
194 encodeVLQ(output, sourceColumn - previousSourceColumn); 154 sourceColumnEncoder.encode(output, sourceLocation.column);
195 155
196 if (sourceName != null) { 156 String sourceName = sourceLocation.sourceName;
197 int sourceNameIndex = indexOf(sourceNameList, sourceName, sourceNameMap); 157 if (sourceName != null) {
198 encodeVLQ(output, sourceNameIndex - previousSourceNameIndex); 158 sourceNameIndexEncoder.encode(output, nameMap[sourceName]);
199 } 159 }
200 160
201 // Update previous source location to ensure the next indices are relative 161 previousSourceLocation = sourceLocation;
202 // to those if [entry.sourceLocation]. 162 });
203 updatePreviousSourceLocation(entry.sourceLocation); 163 }
164 }
165
166 /// Encoder for value deltas in VLQ format.
167 class DeltaEncoder {
168 /// The last emitted value of the encoder.
169 int _value = 0;
170
171 /// Reset the encoder to its initial state.
172 void reset() {
173 _value = 0;
204 } 174 }
205 175
206 int indexOf(List list, value, Map<dynamic, int> map) { 176 /// Writes the VLQ of delta between [value] and the last emitted value into
207 return map.putIfAbsent(value, () { 177 /// [output] and updates the last emitted value of the encoder.
208 int index = list.length; 178 void encode(StringBuffer output, int value) {
209 list.add(value); 179 _value = encodeVLQ(output, value, _value);
210 return index;
211 });
212 } 180 }
213 181
214 static void encodeVLQ(StringBuffer output, int value) { 182 static const int VLQ_BASE_SHIFT = 5;
183 static const int VLQ_BASE_MASK = (1 << 5) - 1;
184 static const int VLQ_CONTINUATION_BIT = 1 << 5;
185 static const int VLQ_CONTINUATION_MASK = 1 << 5;
186 static const String BASE64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn'
187 'opqrstuvwxyz0123456789+/';
188
189 /// Writes the VLQ of delta between [value] and [offset] into [output] and
190 /// return [value].
191 static int encodeVLQ(StringBuffer output, int value, int offset) {
192 int delta = value - offset;
215 int signBit = 0; 193 int signBit = 0;
216 if (value < 0) { 194 if (delta < 0) {
217 signBit = 1; 195 signBit = 1;
218 value = -value; 196 delta = -delta;
219 } 197 }
220 value = (value << 1) | signBit; 198 delta = (delta << 1) | signBit;
221 do { 199 do {
222 int digit = value & VLQ_BASE_MASK; 200 int digit = delta & VLQ_BASE_MASK;
223 value >>= VLQ_BASE_SHIFT; 201 delta >>= VLQ_BASE_SHIFT;
224 if (value > 0) { 202 if (delta > 0) {
225 digit |= VLQ_CONTINUATION_BIT; 203 digit |= VLQ_CONTINUATION_BIT;
226 } 204 }
227 output.write(BASE64_DIGITS[digit]); 205 output.write(BASE64_DIGITS[digit]);
228 } while (value > 0); 206 } while (delta > 0);
207 return value;
229 } 208 }
230 } 209 }
231 210
232 class SourceMapEntry { 211 class SourceMapEntry {
233 SourceLocation sourceLocation; 212 SourceLocation sourceLocation;
234 int targetOffset; 213 int targetOffset;
235 214
236 SourceMapEntry(this.sourceLocation, this.targetOffset); 215 SourceMapEntry(this.sourceLocation, this.targetOffset);
237 } 216 }
217
218 /// Map from line/column pairs to lists of [T] elements.
219 class LineColumnMap<T> {
220 Map<int, Map<int, List<T>>> _map = <int, Map<int, List<T>>>{};
221
222 /// Returns the list of elements associated with ([line],[column]).
223 List<T> _getList(int line, int column) {
224 Map<int, List<T>> lineMap = _map.putIfAbsent(line, () => <int, List<T>>{});
225 return lineMap.putIfAbsent(column, () => <T>[]);
226 }
227
228 /// Adds [element] to the end of the list of elements associated with
229 /// ([line],[column]).
230 void add(int line, int column, T element) {
231 _getList(line, column).add(element);
232 }
233
234 /// Adds [element] to the beginning of the list of elements associated with
235 /// ([line],[column]).
236 void addFirst(int line, int column, T element) {
237 _getList(line, column).insert(0, element);
238 }
239
240 /// Calls [f] with the line number for each line with associated elements.
241 ///
242 /// [f] is called in increasing line order.
243 void forEachLine(f(int line)) {
244 List<int> lines = _map.keys.toList()..sort();
245 lines.forEach(f);
246 }
247
248 /// Returns the elements for the first the column in [line] that has
249 /// associated elements.
250 List<T> getFirstElementsInLine(int line) {
251 Map<int, List<T>> lineMap = _map[line];
252 if (lineMap == null) return null;
253 List<int> columns = lineMap.keys.toList()..sort();
254 return lineMap[columns.first];
255 }
256
257 /// Calls [f] for each column with associated elements in [line].
258 ///
259 /// [f] is called in increasing column order.
260 void forEachColumn(int line, f(int column, List<T> elements)) {
261 Map<int, List<T>> lineMap = _map[line];
262 if (lineMap != null) {
263 List<int> columns = lineMap.keys.toList()..sort();
264 columns.forEach((int column) {
265 f(column, lineMap[column]);
266 });
267 }
268 }
269
270 /// Calls [f] for each line/column/element triplet in the map.
271 ///
272 /// [f] is called in increasing line, column, element order.
273 void forEach(f(int line, int column, T element)) {
274 List<int> lines = _map.keys.toList()..sort();
275 for (int line in lines) {
276 Map<int, List<T>> lineMap = _map[line];
277 List<int> columns = lineMap.keys.toList()..sort();
278 for (int column in columns) {
279 lineMap[column].forEach((e) => f(line, column, e));
280 }
281 }
282 }
283
284 /// Calls [f] for each element associated in the map.
285 ///
286 /// [f] is called in increasing line, column, element order.
287 void forEachElement(f(T element)) {
288 forEach((line, column, element) => f(element));
289 }
290 }
291
292 /// Map from [T] elements to assigned indices.
293 class IndexMap<T> {
294 Map<T, int> map = <T, int>{};
295
296 /// Register [element] and returns its index.
297 int register(T element) {
298 return map.putIfAbsent(element, () => map.length);
299 }
300
301 /// Returns the index of [element].
302 int operator [](T element) => map[element];
303
304 /// Returns the indexed elements.
305 Iterable<T> get elements => map.keys;
306 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/use_unused_api.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698