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:json' as json; | 8 import 'dart:json' as json; |
9 | 9 |
10 import 'span.dart'; | 10 import 'span.dart'; |
11 import 'src/utils.dart'; | 11 import 'src/utils.dart'; |
12 import 'src/vlq.dart'; | 12 import 'src/vlq.dart'; |
13 | 13 |
14 /// Parses a source map directly from a json string. | 14 /// Parses a source map directly from a json string. |
15 // TODO(sigmund): evaluate whether other maps should have the json parsed, or | 15 // TODO(sigmund): evaluate whether other maps should have the json parsed, or |
16 // the string represenation. | 16 // the string represenation. |
17 Mapping parse(String jsonMap, {Map<String, Map> otherMaps}) => | 17 Mapping parse(String jsonMap, {Map<String, Map> otherMaps}) => |
18 parseJson(json.parse(jsonMap), otherMaps: otherMaps); | 18 parseJson(json.parse(jsonMap), otherMaps: otherMaps); |
19 | 19 |
20 /// Parses a source map directly from a json map object. | 20 /// Parses a source map directly from a json map object. |
21 Mapping parseJson(Map map, {Map<String, Map> otherMaps}) { | 21 Mapping parseJson(Map map, {Map<String, Map> otherMaps}) { |
22 if (map['version'] != 3) { | 22 if (map['version'] != 3) { |
23 throw new ArgumentError( | 23 throw new ArgumentError( |
24 'unexpected source map version: ${map["version"]}. ' | 24 'unexpected source map version: ${map["version"]}. ' |
25 'Only version 3 is supported.'); | 25 'Only version 3 is supported.'); |
26 } | 26 } |
27 | 27 |
28 // TODO(sigmund): relax this? dart2js doesn't generate the file entry. | |
29 if (!map.containsKey('file')) { | 28 if (!map.containsKey('file')) { |
30 throw new ArgumentError('missing "file" in source map'); | 29 print('warning: missing "file" entry in source map'); |
31 } | 30 } |
32 | 31 |
33 if (map.containsKey('sections')) { | 32 if (map.containsKey('sections')) { |
34 if (map.containsKey('mappings') || map.containsKey('sources') || | 33 if (map.containsKey('mappings') || map.containsKey('sources') || |
35 map.containsKey('names')) { | 34 map.containsKey('names')) { |
36 throw new FormatException('map containing "sections" ' | 35 throw new FormatException('map containing "sections" ' |
37 'cannot contain "mappings", "sources", or "names".'); | 36 'cannot contain "mappings", "sources", or "names".'); |
38 } | 37 } |
39 return new MultiSectionMapping.fromJson(map['sections'], otherMaps); | 38 return new MultiSectionMapping.fromJson(map['sections'], otherMaps); |
40 } | 39 } |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 // following order: | 178 // following order: |
180 // 0: the starting column in the current line of the generated file | 179 // 0: the starting column in the current line of the generated file |
181 // 1: the id of the original source file | 180 // 1: the id of the original source file |
182 // 2: the starting line in the original source | 181 // 2: the starting line in the original source |
183 // 3: the starting column in the original source | 182 // 3: the starting column in the original source |
184 // 4: the id of the original symbol name | 183 // 4: the id of the original symbol name |
185 // The values are relative to the previous encountered values. | 184 // The values are relative to the previous encountered values. |
186 if (tokenizer.nextKind.isNewSegment) throw _segmentError(0, line); | 185 if (tokenizer.nextKind.isNewSegment) throw _segmentError(0, line); |
187 column += tokenizer._consumeValue(); | 186 column += tokenizer._consumeValue(); |
188 if (!tokenizer.nextKind.isValue) { | 187 if (!tokenizer.nextKind.isValue) { |
189 entries.add(new TargetEntry(column)); | 188 entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn)); |
190 } else { | 189 } else { |
191 srcUrlId += tokenizer._consumeValue(); | 190 srcUrlId += tokenizer._consumeValue(); |
192 if (srcUrlId >= urls.length) { | 191 if (srcUrlId >= urls.length) { |
193 throw new StateError( | 192 throw new StateError( |
194 'Invalid source url id. $targetUrl, $line, $srcUrlId'); | 193 'Invalid source url id. $targetUrl, $line, $srcUrlId'); |
195 } | 194 } |
196 if (!tokenizer.nextKind.isValue) throw _segmentError(2, line); | 195 if (!tokenizer.nextKind.isValue) throw _segmentError(2, line); |
197 srcLine += tokenizer._consumeValue(); | 196 srcLine += tokenizer._consumeValue(); |
198 if (!tokenizer.nextKind.isValue) throw _segmentError(3, line); | 197 if (!tokenizer.nextKind.isValue) throw _segmentError(3, line); |
199 srcColumn += tokenizer._consumeValue(); | 198 srcColumn += tokenizer._consumeValue(); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 /// the very last entry on that line. | 234 /// the very last entry on that line. |
236 TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) { | 235 TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) { |
237 if (lineEntry == null || lineEntry.entries.length == 0) return null; | 236 if (lineEntry == null || lineEntry.entries.length == 0) return null; |
238 if (lineEntry.line != line) return lineEntry.entries.last; | 237 if (lineEntry.line != line) return lineEntry.entries.last; |
239 var entries = lineEntry.entries; | 238 var entries = lineEntry.entries; |
240 int index = binarySearch(entries, (e) => e.column > column); | 239 int index = binarySearch(entries, (e) => e.column > column); |
241 return (index <= 0) ? null : entries[index - 1]; | 240 return (index <= 0) ? null : entries[index - 1]; |
242 } | 241 } |
243 | 242 |
244 Span spanFor(int line, int column, {Map<String, SourceFile> files}) { | 243 Span spanFor(int line, int column, {Map<String, SourceFile> files}) { |
245 var lineEntry = _findLine(line); | |
246 var entry = _findColumn(line, column, _findLine(line)); | 244 var entry = _findColumn(line, column, _findLine(line)); |
247 if (entry == null) return null; | 245 if (entry == null) return null; |
248 var url = urls[entry.sourceUrlId]; | 246 var url = urls[entry.sourceUrlId]; |
249 if (files != null && files[url] != null) { | 247 if (files != null && files[url] != null) { |
250 var file = files[url]; | 248 var file = files[url]; |
251 var start = file.getOffset(entry.sourceLine, entry.sourceColumn); | 249 var start = file.getOffset(entry.sourceLine, entry.sourceColumn); |
252 if (entry.sourceNameId != null) { | 250 if (entry.sourceNameId != null) { |
253 var text = names[entry.sourceNameId]; | 251 var text = names[entry.sourceNameId]; |
254 return new FileSpan(files[url], start, start + text.length, true); | 252 return new FileSpan(files[url], start, start + text.length, true); |
255 } else { | 253 } else { |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 String toString() => '$runtimeType: $line $entries'; | 314 String toString() => '$runtimeType: $line $entries'; |
317 } | 315 } |
318 | 316 |
319 /// A target segment entry read from a source map | 317 /// A target segment entry read from a source map |
320 class TargetEntry { | 318 class TargetEntry { |
321 final int column; | 319 final int column; |
322 final int sourceUrlId; | 320 final int sourceUrlId; |
323 final int sourceLine; | 321 final int sourceLine; |
324 final int sourceColumn; | 322 final int sourceColumn; |
325 final int sourceNameId; | 323 final int sourceNameId; |
326 TargetEntry(this.column, [this.sourceUrlId, this.sourceLine, | 324 |
327 this.sourceColumn, this.sourceNameId]); | 325 TargetEntry(this.column, this.sourceUrlId, this.sourceLine, |
| 326 this.sourceColumn, [this.sourceNameId]); |
328 | 327 |
329 String toString() => '$runtimeType: ' | 328 String toString() => '$runtimeType: ' |
330 '($column, $sourceUrlId, $sourceLine, $sourceColumn, $sourceNameId)'; | 329 '($column, $sourceUrlId, $sourceLine, $sourceColumn, $sourceNameId)'; |
331 } | 330 } |
332 | 331 |
333 /** A character iterator over a string that can peek one character ahead. */ | 332 /** A character iterator over a string that can peek one character ahead. */ |
334 class _MappingTokenizer implements Iterator<String> { | 333 class _MappingTokenizer implements Iterator<String> { |
335 final String _internal; | 334 final String _internal; |
336 final int _length; | 335 final int _length; |
337 int index = -1; | 336 int index = -1; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 static const _TokenKind EOF = const _TokenKind(isEof: true); | 381 static const _TokenKind EOF = const _TokenKind(isEof: true); |
383 static const _TokenKind VALUE = const _TokenKind(); | 382 static const _TokenKind VALUE = const _TokenKind(); |
384 final bool isNewLine; | 383 final bool isNewLine; |
385 final bool isNewSegment; | 384 final bool isNewSegment; |
386 final bool isEof; | 385 final bool isEof; |
387 bool get isValue => !isNewLine && !isNewSegment && !isEof; | 386 bool get isValue => !isNewLine && !isNewSegment && !isEof; |
388 | 387 |
389 const _TokenKind( | 388 const _TokenKind( |
390 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); | 389 {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); |
391 } | 390 } |
OLD | NEW |