Index: pkg/source_maps/lib/parser.dart |
diff --git a/pkg/source_maps/lib/parser.dart b/pkg/source_maps/lib/parser.dart |
deleted file mode 100644 |
index 369c27d0ad7a0cc8fa0f557cfa893cba17aaf290..0000000000000000000000000000000000000000 |
--- a/pkg/source_maps/lib/parser.dart |
+++ /dev/null |
@@ -1,514 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/// Contains the top-level function to parse source maps version 3. |
-library source_maps.parser; |
- |
-import 'dart:collection'; |
-import 'dart:convert'; |
- |
-import 'package:source_span/source_span.dart'; |
- |
-import 'builder.dart' as builder; |
-import 'src/source_map_span.dart'; |
-import 'src/utils.dart'; |
-import 'src/vlq.dart'; |
- |
-/// Parses a source map directly from a json string. |
-// TODO(sigmund): evaluate whether other maps should have the json parsed, or |
-// the string represenation. |
-// TODO(tjblasi): Ignore the first line of [jsonMap] if the JSON safety string |
-// `)]}'` begins the string representation of the map. |
-Mapping parse(String jsonMap, {Map<String, Map> otherMaps}) => |
- parseJson(JSON.decode(jsonMap), otherMaps: otherMaps); |
- |
-/// Parses a source map directly from a json map object. |
-Mapping parseJson(Map map, {Map<String, Map> otherMaps}) { |
- if (map['version'] != 3) { |
- throw new ArgumentError( |
- 'unexpected source map version: ${map["version"]}. ' |
- 'Only version 3 is supported.'); |
- } |
- |
- if (map.containsKey('sections')) { |
- if (map.containsKey('mappings') || map.containsKey('sources') || |
- map.containsKey('names')) { |
- throw new FormatException('map containing "sections" ' |
- 'cannot contain "mappings", "sources", or "names".'); |
- } |
- return new MultiSectionMapping.fromJson(map['sections'], otherMaps); |
- } |
- return new SingleMapping.fromJson(map); |
-} |
- |
- |
-/// A mapping parsed out of a source map. |
-abstract class Mapping { |
- /// Returns the span associated with [line] and [column]. |
- SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}); |
- |
- /// Returns the span associated with [location]. |
- SourceMapSpan spanForLocation(SourceLocation location, |
- {Map<String, SourceFile> files}) { |
- return spanFor(location.line, location.column, files: files); |
- } |
-} |
- |
-/// A meta-level map containing sections. |
-class MultiSectionMapping extends Mapping { |
- /// For each section, the start line offset. |
- final List<int> _lineStart = <int>[]; |
- |
- /// For each section, the start column offset. |
- final List<int> _columnStart = <int>[]; |
- |
- /// For each section, the actual source map information, which is not adjusted |
- /// for offsets. |
- final List<Mapping> _maps = <Mapping>[]; |
- |
- /// Creates a section mapping from json. |
- MultiSectionMapping.fromJson(List sections, Map<String, Map> otherMaps) { |
- for (var section in sections) { |
- var offset = section['offset']; |
- if (offset == null) throw new FormatException('section missing offset'); |
- |
- var line = section['offset']['line']; |
- if (line == null) throw new FormatException('offset missing line'); |
- |
- var column = section['offset']['column']; |
- if (column == null) throw new FormatException('offset missing column'); |
- |
- _lineStart.add(line); |
- _columnStart.add(column); |
- |
- var url = section['url']; |
- var map = section['map']; |
- |
- if (url != null && map != null) { |
- throw new FormatException("section can't use both url and map entries"); |
- } else if (url != null) { |
- if (otherMaps == null || otherMaps[url] == null) { |
- throw new FormatException( |
- 'section contains refers to $url, but no map was ' |
- 'given for it. Make sure a map is passed in "otherMaps"'); |
- } |
- _maps.add(parseJson(otherMaps[url], otherMaps: otherMaps)); |
- } else if (map != null) { |
- _maps.add(parseJson(map, otherMaps: otherMaps)); |
- } else { |
- throw new FormatException('section missing url or map'); |
- } |
- } |
- if (_lineStart.length == 0) { |
- throw new FormatException('expected at least one section'); |
- } |
- } |
- |
- int _indexFor(line, column) { |
- for(int i = 0; i < _lineStart.length; i++) { |
- if (line < _lineStart[i]) return i - 1; |
- if (line == _lineStart[i] && column < _columnStart[i]) return i - 1; |
- } |
- return _lineStart.length - 1; |
- } |
- |
- SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) { |
- int index = _indexFor(line, column); |
- return _maps[index].spanFor( |
- line - _lineStart[index], column - _columnStart[index], files: files); |
- } |
- |
- String toString() { |
- var buff = new StringBuffer("$runtimeType : ["); |
- for (int i = 0; i < _lineStart.length; i++) { |
- buff..write('(') |
- ..write(_lineStart[i]) |
- ..write(',') |
- ..write(_columnStart[i]) |
- ..write(':') |
- ..write(_maps[i]) |
- ..write(')'); |
- } |
- buff.write(']'); |
- return buff.toString(); |
- } |
-} |
- |
-/// A map containing direct source mappings. |
-class SingleMapping extends Mapping { |
- /// Source urls used in the mapping, indexed by id. |
- final List<String> urls; |
- |
- /// Source names used in the mapping, indexed by id. |
- final List<String> names; |
- |
- /// Entries indicating the beginning of each span. |
- final List<TargetLineEntry> lines; |
- |
- /// Url of the target file. |
- String targetUrl; |
- |
- /// Source root appended to the start of all entries in [urls]. |
- String sourceRoot; |
- |
- SingleMapping._(this.targetUrl, this.urls, this.names, this.lines); |
- |
- factory SingleMapping.fromEntries( |
- Iterable<builder.Entry> entries, [String fileUrl]) { |
- // The entries needs to be sorted by the target offsets. |
- var sourceEntries = new List.from(entries)..sort(); |
- var lines = <TargetLineEntry>[]; |
- |
- // Indices associated with file urls that will be part of the source map. We |
- // use a linked hash-map so that `_urls.keys[_urls[u]] == u` |
- var urls = new LinkedHashMap<String, int>(); |
- |
- // Indices associated with identifiers that will be part of the source map. |
- // We use a linked hash-map so that `_names.keys[_names[n]] == n` |
- var names = new LinkedHashMap<String, int>(); |
- |
- var lineNum; |
- var targetEntries; |
- for (var sourceEntry in sourceEntries) { |
- if (lineNum == null || sourceEntry.target.line > lineNum) { |
- lineNum = sourceEntry.target.line; |
- targetEntries = <TargetEntry>[]; |
- lines.add(new TargetLineEntry(lineNum, targetEntries)); |
- } |
- |
- if (sourceEntry.source == null) { |
- targetEntries.add(new TargetEntry(sourceEntry.target.column)); |
- } else { |
- var sourceUrl = sourceEntry.source.sourceUrl; |
- var urlId = urls.putIfAbsent( |
- sourceUrl == null ? '' : sourceUrl.toString(), () => urls.length); |
- var srcNameId = sourceEntry.identifierName == null ? null : |
- names.putIfAbsent(sourceEntry.identifierName, () => names.length); |
- targetEntries.add(new TargetEntry( |
- sourceEntry.target.column, |
- urlId, |
- sourceEntry.source.line, |
- sourceEntry.source.column, |
- srcNameId)); |
- } |
- } |
- return new SingleMapping._( |
- fileUrl, urls.keys.toList(), names.keys.toList(), lines); |
- } |
- |
- SingleMapping.fromJson(Map map) |
- : targetUrl = map['file'], |
- urls = map['sources'], |
- names = map['names'], |
- sourceRoot = map['sourceRoot'], |
- lines = <TargetLineEntry>[] { |
- int line = 0; |
- int column = 0; |
- int srcUrlId = 0; |
- int srcLine = 0; |
- int srcColumn = 0; |
- int srcNameId = 0; |
- var tokenizer = new _MappingTokenizer(map['mappings']); |
- var entries = <TargetEntry>[]; |
- |
- while (tokenizer.hasTokens) { |
- if (tokenizer.nextKind.isNewLine) { |
- if (!entries.isEmpty) { |
- lines.add(new TargetLineEntry(line, entries)); |
- entries = <TargetEntry>[]; |
- } |
- line++; |
- column = 0; |
- tokenizer._consumeNewLine(); |
- continue; |
- } |
- |
- // Decode the next entry, using the previous encountered values to |
- // decode the relative values. |
- // |
- // We expect 1, 4, or 5 values. If present, values are expected in the |
- // following order: |
- // 0: the starting column in the current line of the generated file |
- // 1: the id of the original source file |
- // 2: the starting line in the original source |
- // 3: the starting column in the original source |
- // 4: the id of the original symbol name |
- // The values are relative to the previous encountered values. |
- if (tokenizer.nextKind.isNewSegment) throw _segmentError(0, line); |
- column += tokenizer._consumeValue(); |
- if (!tokenizer.nextKind.isValue) { |
- entries.add(new TargetEntry(column)); |
- } else { |
- srcUrlId += tokenizer._consumeValue(); |
- if (srcUrlId >= urls.length) { |
- throw new StateError( |
- 'Invalid source url id. $targetUrl, $line, $srcUrlId'); |
- } |
- if (!tokenizer.nextKind.isValue) throw _segmentError(2, line); |
- srcLine += tokenizer._consumeValue(); |
- if (!tokenizer.nextKind.isValue) throw _segmentError(3, line); |
- srcColumn += tokenizer._consumeValue(); |
- if (!tokenizer.nextKind.isValue) { |
- entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn)); |
- } else { |
- srcNameId += tokenizer._consumeValue(); |
- if (srcNameId >= names.length) { |
- throw new StateError( |
- 'Invalid name id: $targetUrl, $line, $srcNameId'); |
- } |
- entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn, |
- srcNameId)); |
- } |
- } |
- if (tokenizer.nextKind.isNewSegment) tokenizer._consumeNewSegment(); |
- } |
- if (!entries.isEmpty) { |
- lines.add(new TargetLineEntry(line, entries)); |
- } |
- } |
- |
- /// Encodes the Mapping mappings as a json map. |
- Map toJson() { |
- var buff = new StringBuffer(); |
- var line = 0; |
- var column = 0; |
- var srcLine = 0; |
- var srcColumn = 0; |
- var srcUrlId = 0; |
- var srcNameId = 0; |
- var first = true; |
- |
- for (var entry in lines) { |
- int nextLine = entry.line; |
- if (nextLine > line) { |
- for (int i = line; i < nextLine; ++i) { |
- buff.write(';'); |
- } |
- line = nextLine; |
- column = 0; |
- first = true; |
- } |
- |
- for (var segment in entry.entries) { |
- if (!first) buff.write(','); |
- first = false; |
- column = _append(buff, column, segment.column); |
- |
- // Encoding can be just the column offset if there is no source |
- // information. |
- var newUrlId = segment.sourceUrlId; |
- if (newUrlId == null) continue; |
- srcUrlId = _append(buff, srcUrlId, newUrlId); |
- srcLine = _append(buff, srcLine, segment.sourceLine); |
- srcColumn = _append(buff, srcColumn, segment.sourceColumn); |
- |
- if (segment.sourceNameId == null) continue; |
- srcNameId = _append(buff, srcNameId, segment.sourceNameId); |
- } |
- } |
- |
- var result = { |
- 'version': 3, |
- 'sourceRoot': sourceRoot == null ? '' : sourceRoot, |
- 'sources': urls, |
- 'names' : names, |
- 'mappings' : buff.toString() |
- }; |
- if (targetUrl != null) { |
- result['file'] = targetUrl; |
- } |
- return result; |
- } |
- |
- /// Appends to [buff] a VLQ encoding of [newValue] using the difference |
- /// between [oldValue] and [newValue] |
- static int _append(StringBuffer buff, int oldValue, int newValue) { |
- buff.writeAll(encodeVlq(newValue - oldValue)); |
- return newValue; |
- } |
- |
- _segmentError(int seen, int line) => new StateError( |
- 'Invalid entry in sourcemap, expected 1, 4, or 5' |
- ' values, but got $seen.\ntargeturl: $targetUrl, line: $line'); |
- |
- /// Returns [TargetLineEntry] which includes the location in the target [line] |
- /// number. In particular, the resulting entry is the last entry whose line |
- /// number is lower or equal to [line]. |
- TargetLineEntry _findLine(int line) { |
- int index = binarySearch(lines, (e) => e.line > line); |
- return (index <= 0) ? null : lines[index - 1]; |
- } |
- |
- /// Returns [TargetEntry] which includes the location denoted by |
- /// [line], [column]. If [lineEntry] corresponds to [line], then this will be |
- /// the last entry whose column is lower or equal than [column]. If |
- /// [lineEntry] corresponds to a line prior to [line], then the result will be |
- /// the very last entry on that line. |
- TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) { |
- if (lineEntry == null || lineEntry.entries.length == 0) return null; |
- if (lineEntry.line != line) return lineEntry.entries.last; |
- var entries = lineEntry.entries; |
- int index = binarySearch(entries, (e) => e.column > column); |
- return (index <= 0) ? null : entries[index - 1]; |
- } |
- |
- SourceMapSpan spanFor(int line, int column, {Map<String, SourceFile> files}) { |
- var entry = _findColumn(line, column, _findLine(line)); |
- if (entry == null || entry.sourceUrlId == null) return null; |
- var url = urls[entry.sourceUrlId]; |
- if (sourceRoot != null) { |
- url = '${sourceRoot}${url}'; |
- } |
- if (files != null && files[url] != null) { |
- var file = files[url]; |
- var start = file.getOffset(entry.sourceLine, entry.sourceColumn); |
- if (entry.sourceNameId != null) { |
- var text = names[entry.sourceNameId]; |
- return new SourceMapFileSpan( |
- files[url].span(start, start + text.length), |
- isIdentifier: true); |
- } else { |
- return new SourceMapFileSpan(files[url].location(start).pointSpan()); |
- } |
- } else { |
- var start = new SourceLocation(0, |
- sourceUrl: url, line: entry.sourceLine, column: entry.sourceColumn); |
- // Offset and other context is not available. |
- if (entry.sourceNameId != null) { |
- return new SourceMapSpan.identifier(start, names[entry.sourceNameId]); |
- } else { |
- return new SourceMapSpan(start, start, ''); |
- } |
- } |
- } |
- |
- String toString() { |
- return (new StringBuffer("$runtimeType : [") |
- ..write('targetUrl: ') |
- ..write(targetUrl) |
- ..write(', sourceRoot: ') |
- ..write(sourceRoot) |
- ..write(', urls: ') |
- ..write(urls) |
- ..write(', names: ') |
- ..write(names) |
- ..write(', lines: ') |
- ..write(lines) |
- ..write(']')).toString(); |
- } |
- |
- String get debugString { |
- var buff = new StringBuffer(); |
- for (var lineEntry in lines) { |
- var line = lineEntry.line; |
- for (var entry in lineEntry.entries) { |
- buff..write(targetUrl) |
- ..write(': ') |
- ..write(line) |
- ..write(':') |
- ..write(entry.column); |
- if (entry.sourceUrlId != null) { |
- buff..write(' --> ') |
- ..write(sourceRoot) |
- ..write(urls[entry.sourceUrlId]) |
- ..write(': ') |
- ..write(entry.sourceLine) |
- ..write(':') |
- ..write(entry.sourceColumn); |
- } |
- if (entry.sourceNameId != null) { |
- buff..write(' (') |
- ..write(names[entry.sourceNameId]) |
- ..write(')'); |
- } |
- buff.write('\n'); |
- } |
- } |
- return buff.toString(); |
- } |
-} |
- |
-/// A line entry read from a source map. |
-class TargetLineEntry { |
- final int line; |
- List<TargetEntry> entries; |
- TargetLineEntry(this.line, this.entries); |
- |
- String toString() => '$runtimeType: $line $entries'; |
-} |
- |
-/// A target segment entry read from a source map |
-class TargetEntry { |
- final int column; |
- final int sourceUrlId; |
- final int sourceLine; |
- final int sourceColumn; |
- final int sourceNameId; |
- |
- TargetEntry(this.column, [this.sourceUrlId, this.sourceLine, |
- this.sourceColumn, this.sourceNameId]); |
- |
- String toString() => '$runtimeType: ' |
- '($column, $sourceUrlId, $sourceLine, $sourceColumn, $sourceNameId)'; |
-} |
- |
-/** A character iterator over a string that can peek one character ahead. */ |
-class _MappingTokenizer implements Iterator<String> { |
- final String _internal; |
- final int _length; |
- int index = -1; |
- _MappingTokenizer(String internal) |
- : _internal = internal, |
- _length = internal.length; |
- |
- // Iterator API is used by decodeVlq to consume VLQ entries. |
- bool moveNext() => ++index < _length; |
- String get current => |
- (index >= 0 && index < _length) ? _internal[index] : null; |
- |
- bool get hasTokens => index < _length - 1 && _length > 0; |
- |
- _TokenKind get nextKind { |
- if (!hasTokens) return _TokenKind.EOF; |
- var next = _internal[index + 1]; |
- if (next == ';') return _TokenKind.LINE; |
- if (next == ',') return _TokenKind.SEGMENT; |
- return _TokenKind.VALUE; |
- } |
- |
- int _consumeValue() => decodeVlq(this); |
- void _consumeNewLine() { ++index; } |
- void _consumeNewSegment() { ++index; } |
- |
- // Print the state of the iterator, with colors indicating the current |
- // position. |
- String toString() { |
- var buff = new StringBuffer(); |
- for (int i = 0; i < index; i++) { |
- buff.write(_internal[i]); |
- } |
- buff.write('[31m'); |
- buff.write(current == null ? '' : current); |
- buff.write('[0m'); |
- for (int i = index + 1; i < _internal.length; i++) { |
- buff.write(_internal[i]); |
- } |
- buff.write(' ($index)'); |
- return buff.toString(); |
- } |
-} |
- |
-class _TokenKind { |
- static const _TokenKind LINE = const _TokenKind(isNewLine: true); |
- static const _TokenKind SEGMENT = const _TokenKind(isNewSegment: true); |
- static const _TokenKind EOF = const _TokenKind(isEof: true); |
- static const _TokenKind VALUE = const _TokenKind(); |
- final bool isNewLine; |
- final bool isNewSegment; |
- final bool isEof; |
- bool get isValue => !isNewLine && !isNewSegment && !isEof; |
- |
- const _TokenKind( |
- {this.isNewLine: false, this.isNewSegment: false, this.isEof: false}); |
-} |