Chromium Code Reviews| 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 |