OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 /// Contains the top-level function to parse source maps version 3. | 5 /// Contains the top-level function to parse source maps version 3. |
6 library source_maps.parser; | 6 library source_maps.parser; |
7 | 7 |
8 import 'dart:collection'; | |
8 import 'dart:convert'; | 9 import 'dart:convert'; |
9 | 10 |
11 import 'builder.dart'; | |
10 import 'span.dart'; | 12 import 'span.dart'; |
11 import 'src/utils.dart'; | 13 import 'src/utils.dart'; |
12 import 'src/vlq.dart'; | 14 import 'src/vlq.dart'; |
13 | 15 |
14 /// Parses a source map directly from a json string. | 16 /// Parses a source map directly from a json string. |
15 // TODO(sigmund): evaluate whether other maps should have the json parsed, or | 17 // TODO(sigmund): evaluate whether other maps should have the json parsed, or |
16 // the string represenation. | 18 // the string represenation. |
17 Mapping parse(String jsonMap, {Map<String, Map> otherMaps}) => | 19 Mapping parse(String jsonMap, {Map<String, Map> otherMaps}) => |
18 parseJson(JSON.decode(jsonMap), otherMaps: otherMaps); | 20 parseJson(JSON.decode(jsonMap), otherMaps: otherMaps); |
19 | 21 |
(...skipping 14 matching lines...) Expand all Loading... | |
34 map.containsKey('names')) { | 36 map.containsKey('names')) { |
35 throw new FormatException('map containing "sections" ' | 37 throw new FormatException('map containing "sections" ' |
36 'cannot contain "mappings", "sources", or "names".'); | 38 'cannot contain "mappings", "sources", or "names".'); |
37 } | 39 } |
38 return new MultiSectionMapping.fromJson(map['sections'], otherMaps); | 40 return new MultiSectionMapping.fromJson(map['sections'], otherMaps); |
39 } | 41 } |
40 return new SingleMapping.fromJson(map); | 42 return new SingleMapping.fromJson(map); |
41 } | 43 } |
42 | 44 |
43 | 45 |
44 /// A mapping parsed our of a source map. | 46 /// A mapping parsed out of a source map. |
45 abstract class Mapping { | 47 abstract class Mapping { |
46 Span spanFor(int line, int column, {Map<String, SourceFile> files}); | 48 Span spanFor(int line, int column, {Map<String, SourceFile> files}); |
47 | 49 |
48 Span spanForLocation(Location loc, {Map<String, SourceFile> files}) { | 50 Span spanForLocation(Location loc, {Map<String, SourceFile> files}) { |
49 return spanFor(loc.line, loc.column, files: files); | 51 return spanFor(loc.line, loc.column, files: files); |
50 } | 52 } |
51 } | 53 } |
52 | 54 |
53 /// A meta-level map containing sections. | 55 /// A meta-level map containing sections. |
54 class MultiSectionMapping extends Mapping { | 56 class MultiSectionMapping extends Mapping { |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 ..write(':') | 126 ..write(':') |
125 ..write(_maps[i]) | 127 ..write(_maps[i]) |
126 ..write(')'); | 128 ..write(')'); |
127 } | 129 } |
128 buff.write(']'); | 130 buff.write(']'); |
129 return buff.toString(); | 131 return buff.toString(); |
130 } | 132 } |
131 } | 133 } |
132 | 134 |
133 /// A map containing direct source mappings. | 135 /// A map containing direct source mappings. |
134 // TODO(sigmund): integrate mapping and sourcemap builder? | 136 // TODO(sigmund): integrate mapping and sourcemap builder? |
Siggi Cherem (dart-lang)
2014/07/08 00:31:00
delete TODO :-)
tjblasi
2014/07/08 16:36:19
Done.
| |
135 class SingleMapping extends Mapping { | 137 class SingleMapping extends Mapping { |
136 /// Url of the target file. | 138 /// Url of the target file. |
137 final String targetUrl; | 139 final String targetUrl; |
138 | 140 |
139 /// Source urls used in the mapping, indexed by id. | 141 /// Source urls used in the mapping, indexed by id. |
140 final List<String> urls; | 142 final List<String> urls; |
141 | 143 |
142 /// Source names used in the mapping, indexed by id. | 144 /// Source names used in the mapping, indexed by id. |
143 final List<String> names; | 145 final List<String> names; |
144 | 146 |
145 /// Entries indicating the beginning of each span. | 147 /// Entries indicating the beginning of each span. |
146 final List<TargetLineEntry> lines = <TargetLineEntry>[]; | 148 final List<TargetLineEntry> lines = <TargetLineEntry>[]; |
147 | 149 |
150 SingleMapping._internal(this.targetUrl, this.urls, this.names); | |
151 | |
152 factory SingleMapping.fromBuilder(SourceMapBuilder builder, [String fileUrl]) { | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:01
how about switching to take a list of entries inst
tjblasi
2014/07/08 16:36:20
Done.
| |
153 List<Entry> sourceEntries = builder.entries; | |
154 List<TargetLineEntry> lines = []; | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:01
style nit: use 'var' for local variables
tjblasi
2014/07/08 16:36:20
Done.
| |
155 | |
156 // Indices associated with file urls that will be part of the source map. We | |
157 // use a linked hash-map so that `_urls.keys[_urls[u]] == u` | |
158 Map<String, int> urls = new LinkedHashMap<String, int>(); | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:00
same here and below (var urls = ...)
tjblasi
2014/07/08 16:36:20
Done.
| |
159 | |
160 // Indices associated with identifiers that will be part of the source map. | |
161 // We use a linked hash-map so that `_names.keys[_names[n]] == n` | |
162 Map<String, int> names = new LinkedHashMap<String, int>(); | |
163 | |
164 // The encoding needs to be sorted by the target offsets. | |
165 sourceEntries.sort(); | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:00
since sorting modifies the list in-place, we might
tjblasi
2014/07/08 16:36:20
Done.
| |
166 for (var sourceEntry in sourceEntries) { | |
167 if (lines.isEmpty || sourceEntry.target.line > lines.last.line) { | |
168 lines.add(new TargetLineEntry(sourceEntry.target.line, [])); | |
169 } | |
170 | |
171 if (sourceEntry.source == null) { | |
172 lines.last.entries.add(new TargetEntry(sourceEntry.target.column)); | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:01
since `lines.last` can be expensive, maybe we coul
tjblasi
2014/07/08 16:36:20
Done.
| |
173 } else { | |
174 var urlId = urls.putIfAbsent(sourceEntry.source.sourceUrl, () => urls.le ngth); | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:00
nit: 80 columns (here and elsewhere)
tjblasi
2014/07/08 16:36:20
Done.
| |
175 var srcNameId = sourceEntry.identifierName != null ? | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:01
minor nit: maybe swapping this branch might make i
tjblasi
2014/07/08 16:36:20
Done.
| |
176 names.putIfAbsent(sourceEntry.identifierName, () => names.length) : null; | |
177 lines.last.entries.add(new TargetEntry( | |
178 sourceEntry.target.column, | |
179 urlId, | |
180 sourceEntry.source.line, | |
181 sourceEntry.source.column, | |
182 srcNameId)); | |
183 } | |
184 } | |
185 var mapping = new SingleMapping._internal(fileUrl, urls.keys.toList(), names .keys.toList()); | |
186 mapping.lines.addAll(lines); | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:01
nit: maybe move the default initialization above t
tjblasi
2014/07/08 16:36:20
Done.
| |
187 return mapping; | |
188 } | |
189 | |
148 SingleMapping.fromJson(Map map) | 190 SingleMapping.fromJson(Map map) |
149 : targetUrl = map['file'], | 191 : targetUrl = map['file'], |
150 // TODO(sigmund): add support for 'sourceRoot' | 192 // TODO(sigmund): add support for 'sourceRoot' |
151 urls = map['sources'], | 193 urls = map['sources'], |
152 names = map['names'] { | 194 names = map['names'] { |
153 int line = 0; | 195 int line = 0; |
154 int column = 0; | 196 int column = 0; |
155 int srcUrlId = 0; | 197 int srcUrlId = 0; |
156 int srcLine = 0; | 198 int srcLine = 0; |
157 int srcColumn = 0; | 199 int srcColumn = 0; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
208 srcNameId)); | 250 srcNameId)); |
209 } | 251 } |
210 } | 252 } |
211 if (tokenizer.nextKind.isNewSegment) tokenizer._consumeNewSegment(); | 253 if (tokenizer.nextKind.isNewSegment) tokenizer._consumeNewSegment(); |
212 } | 254 } |
213 if (!entries.isEmpty) { | 255 if (!entries.isEmpty) { |
214 lines.add(new TargetLineEntry(line, entries)); | 256 lines.add(new TargetLineEntry(line, entries)); |
215 } | 257 } |
216 } | 258 } |
217 | 259 |
260 /// Encodes the Mapping mappings as a json map. | |
261 Map toMap() { | |
Siggi Cherem (dart-lang)
2014/07/08 00:31:00
suggestion: maybe rename as toJson (to make it con
tjblasi
2014/07/08 16:36:20
Done.
| |
262 var buff = new StringBuffer(); | |
263 var line = 0; | |
264 var column = 0; | |
265 var srcLine = 0; | |
266 var srcColumn = 0; | |
267 var srcUrlId = 0; | |
268 var srcNameId = 0; | |
269 var first = true; | |
270 | |
271 for (var entry in lines) { | |
272 int nextLine = entry.line; | |
273 if (nextLine > line) { | |
274 for (int i = line; i < nextLine; ++i) { | |
275 buff.write(';'); | |
276 } | |
277 line = nextLine; | |
278 column = 0; | |
279 first = true; | |
280 } | |
281 | |
282 for (var segment in entry.entries) { | |
283 if (!first) buff.write(','); | |
284 first = false; | |
285 column = _append(buff, column, segment.column); | |
286 | |
287 // Encoding can be just the column offset if there is no source | |
288 // information. | |
289 var newUrlId = segment.sourceUrlId; | |
290 if (newUrlId == null) continue; | |
291 srcUrlId = _append(buff, srcUrlId, newUrlId); | |
292 srcLine = _append(buff, srcLine, segment.sourceLine); | |
293 srcColumn = _append(buff, srcColumn, segment.sourceColumn); | |
294 | |
295 if (segment.sourceNameId == null) continue; | |
296 srcNameId = _append(buff, srcNameId, segment.sourceNameId); | |
297 } | |
298 } | |
299 | |
300 var result = { | |
301 'version': 3, | |
302 'sourceRoot': '', | |
303 'sources': urls, | |
304 'names' : names, | |
305 'mappings' : buff.toString() | |
306 }; | |
307 if (targetUrl != null) { | |
308 result['file'] = targetUrl; | |
309 } | |
310 return result; | |
311 } | |
312 | |
313 /// Appends to [buff] a VLQ encoding of [newValue] using the difference | |
314 /// between [oldValue] and [newValue] | |
315 static int _append(StringBuffer buff, int oldValue, int newValue) { | |
316 buff.writeAll(encodeVlq(newValue - oldValue)); | |
317 return newValue; | |
318 } | |
319 | |
218 _segmentError(int seen, int line) => new StateError( | 320 _segmentError(int seen, int line) => new StateError( |
219 'Invalid entry in sourcemap, expected 1, 4, or 5' | 321 'Invalid entry in sourcemap, expected 1, 4, or 5' |
220 ' values, but got $seen.\ntargeturl: $targetUrl, line: $line'); | 322 ' values, but got $seen.\ntargeturl: $targetUrl, line: $line'); |
221 | 323 |
222 /// Returns [TargetLineEntry] which includes the location in the target [line] | 324 /// Returns [TargetLineEntry] which includes the location in the target [line] |
223 /// number. In particular, the resulting entry is the last entry whose line | 325 /// number. In particular, the resulting entry is the last entry whose line |
224 /// number is lower or equal to [line]. | 326 /// number is lower or equal to [line]. |
225 TargetLineEntry _findLine(int line) { | 327 TargetLineEntry _findLine(int line) { |
226 int index = binarySearch(lines, (e) => e.line > line); | 328 int index = binarySearch(lines, (e) => e.line > line); |
227 return (index <= 0) ? null : lines[index - 1]; | 329 return (index <= 0) ? null : lines[index - 1]; |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
381 static const _TokenKind EOF = const _TokenKind(isEof: true); | 483 static const _TokenKind EOF = const _TokenKind(isEof: true); |
382 static const _TokenKind VALUE = const _TokenKind(); | 484 static const _TokenKind VALUE = const _TokenKind(); |
383 final bool isNewLine; | 485 final bool isNewLine; |
384 final bool isNewSegment; | 486 final bool isNewSegment; |
385 final bool isEof; | 487 final bool isEof; |
386 bool get isValue => !isNewLine && !isNewSegment && !isEof; | 488 bool get isValue => !isNewLine && !isNewSegment && !isEof; |
387 | 489 |
388 const _TokenKind( | 490 const _TokenKind( |
389 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); | 491 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); |
390 } | 492 } |
OLD | NEW |