LCOV - code coverage report
Current view: top level - source_maps-0.10.4/lib - parser.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 1 252 0.4 %
Date: 2017-10-10 20:17:03 Functions: 0 0 -

          Line data    Source code
       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
       3             : // BSD-style license that can be found in the LICENSE file.
       4             : 
       5             : /// Contains the top-level function to parse source maps version 3.
       6             : library source_maps.parser;
       7             : 
       8             : import 'dart:collection';
       9             : import 'dart:convert';
      10             : 
      11             : import 'package:source_span/source_span.dart';
      12             : 
      13             : import 'builder.dart' as builder;
      14             : import 'src/source_map_span.dart';
      15             : import 'src/utils.dart';
      16             : import 'src/vlq.dart';
      17             : 
      18             : /// Parses a source map directly from a json string.
      19             : ///
      20             : /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
      21             : /// the source map file itself. If it's passed, any URLs in the source
      22             : /// map will be interpreted as relative to this URL when generating spans.
      23             : // TODO(sigmund): evaluate whether other maps should have the json parsed, or
      24             : // the string represenation.
      25             : // TODO(tjblasi): Ignore the first line of [jsonMap] if the JSON safety string
      26             : // `)]}'` begins the string representation of the map.
      27             : Mapping parse(String jsonMap, {Map<String, Map> otherMaps, mapUrl}) =>
      28           0 :     parseJson(JSON.decode(jsonMap), otherMaps: otherMaps, mapUrl: mapUrl);
      29             : 
      30             : /// Parses a source map or source map bundle directly from a json string.
      31             : ///
      32             : /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
      33             : /// the source map file itself. If it's passed, any URLs in the source
      34             : /// map will be interpreted as relative to this URL when generating spans.
      35             : Mapping parseExtended(String jsonMap, {Map<String, Map> otherMaps, mapUrl}) =>
      36           0 :     parseJsonExtended(JSON.decode(jsonMap),
      37             :         otherMaps: otherMaps, mapUrl: mapUrl);
      38             : 
      39             : /// Parses a source map or source map bundle.
      40             : ///
      41             : /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
      42             : /// the source map file itself. If it's passed, any URLs in the source
      43             : /// map will be interpreted as relative to this URL when generating spans.
      44             : Mapping parseJsonExtended(/*List|Map*/ json,
      45             :     {Map<String, Map> otherMaps, mapUrl}) {
      46           0 :   if (json is List) {
      47           0 :     return new MappingBundle.fromJson(json, mapUrl: mapUrl);
      48             :   }
      49           0 :   return parseJson(json as Map);
      50             : }
      51             : 
      52             : /// Parses a source map
      53             : ///
      54             : /// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
      55             : /// the source map file itself. If it's passed, any URLs in the source
      56             : /// map will be interpreted as relative to this URL when generating spans.
      57             : Mapping parseJson(Map map, {Map<String, Map> otherMaps, mapUrl}) {
      58           0 :   if (map['version'] != 3) {
      59           0 :     throw new ArgumentError('unexpected source map version: ${map["version"]}. '
      60           0 :         'Only version 3 is supported.');
      61             :   }
      62             : 
      63           0 :   if (map.containsKey('sections')) {
      64           0 :     if (map.containsKey('mappings') ||
      65           0 :         map.containsKey('sources') ||
      66           0 :         map.containsKey('names')) {
      67           0 :       throw new FormatException('map containing "sections" '
      68             :           'cannot contain "mappings", "sources", or "names".');
      69             :     }
      70           0 :     return new MultiSectionMapping.fromJson(map['sections'], otherMaps,
      71             :         mapUrl: mapUrl);
      72             :   }
      73           0 :   return new SingleMapping.fromJson(map, mapUrl: mapUrl);
      74             : }
      75             : 
      76             : /// A mapping parsed out of a source map.
      77             : abstract class Mapping {
      78             :   /// Returns the span associated with [line] and [column].
      79             :   ///
      80             :   /// [uri] is the optional location of the output file to find the span for
      81             :   /// to disambiguate cases where a mapping may have different mappings for
      82             :   /// different output files.
      83             :   SourceMapSpan spanFor(int line, int column,
      84             :       {Map<String, SourceFile> files, String uri});
      85             : 
      86             :   /// Returns the span associated with [location].
      87             :   SourceMapSpan spanForLocation(SourceLocation location,
      88             :       {Map<String, SourceFile> files}) {
      89           0 :     return spanFor(location.line, location.column,
      90           0 :         uri: location.sourceUrl?.toString(), files: files);
      91             :   }
      92             : }
      93             : 
      94             : /// A meta-level map containing sections.
      95             : class MultiSectionMapping extends Mapping {
      96             :   /// For each section, the start line offset.
      97             :   final List<int> _lineStart = <int>[];
      98             : 
      99             :   /// For each section, the start column offset.
     100             :   final List<int> _columnStart = <int>[];
     101             : 
     102             :   /// For each section, the actual source map information, which is not adjusted
     103             :   /// for offsets.
     104             :   final List<Mapping> _maps = <Mapping>[];
     105             : 
     106             :   /// Creates a section mapping from json.
     107             :   MultiSectionMapping.fromJson(List sections, Map<String, Map> otherMaps,
     108           0 :       {mapUrl}) {
     109           0 :     for (var section in sections) {
     110           0 :       var offset = section['offset'];
     111           0 :       if (offset == null) throw new FormatException('section missing offset');
     112             : 
     113           0 :       var line = section['offset']['line'];
     114           0 :       if (line == null) throw new FormatException('offset missing line');
     115             : 
     116           0 :       var column = section['offset']['column'];
     117           0 :       if (column == null) throw new FormatException('offset missing column');
     118             : 
     119           0 :       _lineStart.add(line);
     120           0 :       _columnStart.add(column);
     121             : 
     122           0 :       var url = section['url'];
     123           0 :       var map = section['map'];
     124             : 
     125             :       if (url != null && map != null) {
     126           0 :         throw new FormatException("section can't use both url and map entries");
     127             :       } else if (url != null) {
     128           0 :         if (otherMaps == null || otherMaps[url] == null) {
     129           0 :           throw new FormatException(
     130             :               'section contains refers to $url, but no map was '
     131           0 :               'given for it. Make sure a map is passed in "otherMaps"');
     132             :         }
     133           0 :         _maps.add(parseJson(otherMaps[url], otherMaps: otherMaps, mapUrl: url));
     134             :       } else if (map != null) {
     135           0 :         _maps.add(parseJson(map, otherMaps: otherMaps, mapUrl: mapUrl));
     136             :       } else {
     137           0 :         throw new FormatException('section missing url or map');
     138             :       }
     139             :     }
     140           0 :     if (_lineStart.length == 0) {
     141           0 :       throw new FormatException('expected at least one section');
     142             :     }
     143             :   }
     144             : 
     145             :   int _indexFor(line, column) {
     146           0 :     for (int i = 0; i < _lineStart.length; i++) {
     147           0 :       if (line < _lineStart[i]) return i - 1;
     148           0 :       if (line == _lineStart[i] && column < _columnStart[i]) return i - 1;
     149             :     }
     150           0 :     return _lineStart.length - 1;
     151             :   }
     152             : 
     153             :   SourceMapSpan spanFor(int line, int column,
     154             :       {Map<String, SourceFile> files, String uri}) {
     155             :     // TODO(jacobr): perhaps verify that targetUrl matches the actual uri
     156             :     // or at least ends in the same file name.
     157           0 :     int index = _indexFor(line, column);
     158           0 :     return _maps[index].spanFor(
     159           0 :         line - _lineStart[index], column - _columnStart[index],
     160             :         files: files);
     161             :   }
     162             : 
     163             :   String toString() {
     164           0 :     var buff = new StringBuffer("$runtimeType : [");
     165           0 :     for (int i = 0; i < _lineStart.length; i++) {
     166             :       buff
     167           0 :         ..write('(')
     168           0 :         ..write(_lineStart[i])
     169           0 :         ..write(',')
     170           0 :         ..write(_columnStart[i])
     171           0 :         ..write(':')
     172           0 :         ..write(_maps[i])
     173           0 :         ..write(')');
     174             :     }
     175           0 :     buff.write(']');
     176           0 :     return buff.toString();
     177             :   }
     178             : }
     179             : 
     180             : class MappingBundle extends Mapping {
     181             :   Map<String, SingleMapping> _mappings = {};
     182             : 
     183           0 :   MappingBundle() {}
     184             : 
     185           0 :   MappingBundle.fromJson(List json, {String mapUrl}) {
     186           0 :     for (var map in json) {
     187           0 :       addMapping(parseJson(map, mapUrl: mapUrl) as SingleMapping);
     188             :     }
     189             :   }
     190             : 
     191             :   addMapping(SingleMapping mapping) {
     192             :     // TODO(jacobr): verify that targetUrl is valid uri instead of a windows
     193             :     // path.
     194           0 :     _mappings[mapping.targetUrl] = mapping;
     195             :   }
     196             : 
     197             :   /// Encodes the Mapping mappings as a json map.
     198           0 :   List toJson() => _mappings.values.map((v) => v.toJson()).toList();
     199             : 
     200             :   String toString() {
     201           0 :     var buff = new StringBuffer();
     202           0 :     for (var map in _mappings.values) {
     203           0 :       buff.write(map.toString());
     204             :     }
     205           0 :     return buff.toString();
     206             :   }
     207             : 
     208           0 :   bool containsMapping(String url) => _mappings.containsKey(url);
     209             : 
     210             :   SourceMapSpan spanFor(int line, int column,
     211             :       {Map<String, SourceFile> files, String uri}) {
     212             :     if (uri == null) {
     213           0 :       throw new ArgumentError.notNull('uri');
     214             :     }
     215             : 
     216             :     // Find the longest suffix of the uri that matches the sourcemap
     217             :     // where the suffix starts after a path segment boundary.
     218             :     // We consider ":" and "/" as path segment boundaries so that
     219             :     // "package:" uris can be handled with minimal special casing. Having a
     220             :     // few false positive path segment boundaries is not a significant issue
     221             :     // as we prefer the longest matching prefix.
     222             :     // Using package:path `path.split` to find path segment boundaries would
     223             :     // not generate all of the path segment boundaries we want for "package:"
     224             :     // urls as "package:package_name" would be one path segment when we want
     225             :     // "package" and "package_name" to be sepearate path segments.
     226             : 
     227             :     bool onBoundary = true;
     228           0 :     var separatorCodeUnits = ['/'.codeUnitAt(0), ':'.codeUnitAt(0)];
     229           0 :     for (var i = 0; i < uri.length; ++i) {
     230             :       if (onBoundary) {
     231           0 :         var candidate = uri.substring(i);
     232           0 :         if (_mappings.containsKey(candidate)) {
     233           0 :           return _mappings[candidate]
     234           0 :               .spanFor(line, column, files: files, uri: candidate);
     235             :         }
     236             :       }
     237           0 :       onBoundary = separatorCodeUnits.contains(uri.codeUnitAt(i));
     238             :     }
     239             : 
     240             :     // Note: when there is no source map for an uri, this behaves like an
     241             :     // identity function, returning the requested location as the result.
     242             : 
     243             :     // Create a mock offset for the output location. We compute it in terms
     244             :     // of the input line and column to minimize the chances that two different
     245             :     // line and column locations are mapped to the same offset.
     246           0 :     var offset = line * 1000000 + column;
     247           0 :     var location = new SourceLocation(offset,
     248           0 :         line: line, column: column, sourceUrl: Uri.parse(uri));
     249           0 :     return new SourceMapSpan(location, location, "");
     250             :   }
     251             : }
     252             : 
     253             : /// A map containing direct source mappings.
     254             : class SingleMapping extends Mapping {
     255             :   /// Source urls used in the mapping, indexed by id.
     256             :   final List<String> urls;
     257             : 
     258             :   /// Source names used in the mapping, indexed by id.
     259             :   final List<String> names;
     260             : 
     261             :   /// Entries indicating the beginning of each span.
     262             :   final List<TargetLineEntry> lines;
     263             : 
     264             :   /// Url of the target file.
     265             :   String targetUrl;
     266             : 
     267             :   /// Source root prepended to all entries in [urls].
     268             :   String sourceRoot;
     269             : 
     270             :   final Uri _mapUrl;
     271             : 
     272             :   SingleMapping._(this.targetUrl, this.urls, this.names, this.lines)
     273           0 :       : _mapUrl = null;
     274             : 
     275             :   factory SingleMapping.fromEntries(Iterable<builder.Entry> entries,
     276             :       [String fileUrl]) {
     277             :     // The entries needs to be sorted by the target offsets.
     278           0 :     var sourceEntries = new List.from(entries)..sort();
     279           0 :     var lines = <TargetLineEntry>[];
     280             : 
     281             :     // Indices associated with file urls that will be part of the source map. We
     282             :     // use a linked hash-map so that `_urls.keys[_urls[u]] == u`
     283           0 :     var urls = new LinkedHashMap<String, int>();
     284             : 
     285             :     // Indices associated with identifiers that will be part of the source map.
     286             :     // We use a linked hash-map so that `_names.keys[_names[n]] == n`
     287           0 :     var names = new LinkedHashMap<String, int>();
     288             : 
     289             :     var lineNum;
     290             :     List<TargetEntry> targetEntries;
     291           0 :     for (var sourceEntry in sourceEntries) {
     292           0 :       if (lineNum == null || sourceEntry.target.line > lineNum) {
     293           0 :         lineNum = sourceEntry.target.line;
     294           0 :         targetEntries = <TargetEntry>[];
     295           0 :         lines.add(new TargetLineEntry(lineNum, targetEntries));
     296             :       }
     297             : 
     298           0 :       if (sourceEntry.source == null) {
     299           0 :         targetEntries.add(new TargetEntry(sourceEntry.target.column));
     300             :       } else {
     301           0 :         var sourceUrl = sourceEntry.source.sourceUrl;
     302           0 :         var urlId = urls.putIfAbsent(
     303           0 :             sourceUrl == null ? '' : sourceUrl.toString(), () => urls.length);
     304           0 :         var srcNameId = sourceEntry.identifierName == null
     305             :             ? null
     306           0 :             : names.putIfAbsent(sourceEntry.identifierName, () => names.length);
     307           0 :         targetEntries.add(new TargetEntry(sourceEntry.target.column, urlId,
     308           0 :             sourceEntry.source.line, sourceEntry.source.column, srcNameId));
     309             :       }
     310             :     }
     311           0 :     return new SingleMapping._(
     312           0 :         fileUrl, urls.keys.toList(), names.keys.toList(), lines);
     313             :   }
     314             : 
     315             :   SingleMapping.fromJson(Map map, {mapUrl})
     316           0 :       : targetUrl = map['file'],
     317           0 :         urls = new List<String>.from(map['sources']),
     318           0 :         names = new List<String>.from(map['names']),
     319           0 :         sourceRoot = map['sourceRoot'],
     320           0 :         lines = <TargetLineEntry>[],
     321           0 :         _mapUrl = mapUrl is String ? Uri.parse(mapUrl) : mapUrl {
     322             :     int line = 0;
     323             :     int column = 0;
     324             :     int srcUrlId = 0;
     325             :     int srcLine = 0;
     326             :     int srcColumn = 0;
     327             :     int srcNameId = 0;
     328           0 :     var tokenizer = new _MappingTokenizer(map['mappings']);
     329           0 :     var entries = <TargetEntry>[];
     330             : 
     331           0 :     while (tokenizer.hasTokens) {
     332           0 :       if (tokenizer.nextKind.isNewLine) {
     333           0 :         if (!entries.isEmpty) {
     334           0 :           lines.add(new TargetLineEntry(line, entries));
     335           0 :           entries = <TargetEntry>[];
     336             :         }
     337           0 :         line++;
     338             :         column = 0;
     339           0 :         tokenizer._consumeNewLine();
     340             :         continue;
     341             :       }
     342             : 
     343             :       // Decode the next entry, using the previous encountered values to
     344             :       // decode the relative values.
     345             :       //
     346             :       // We expect 1, 4, or 5 values. If present, values are expected in the
     347             :       // following order:
     348             :       //   0: the starting column in the current line of the generated file
     349             :       //   1: the id of the original source file
     350             :       //   2: the starting line in the original source
     351             :       //   3: the starting column in the original source
     352             :       //   4: the id of the original symbol name
     353             :       // The values are relative to the previous encountered values.
     354           0 :       if (tokenizer.nextKind.isNewSegment) throw _segmentError(0, line);
     355           0 :       column += tokenizer._consumeValue();
     356           0 :       if (!tokenizer.nextKind.isValue) {
     357           0 :         entries.add(new TargetEntry(column));
     358             :       } else {
     359           0 :         srcUrlId += tokenizer._consumeValue();
     360           0 :         if (srcUrlId >= urls.length) {
     361           0 :           throw new StateError(
     362           0 :               'Invalid source url id. $targetUrl, $line, $srcUrlId');
     363             :         }
     364           0 :         if (!tokenizer.nextKind.isValue) throw _segmentError(2, line);
     365           0 :         srcLine += tokenizer._consumeValue();
     366           0 :         if (!tokenizer.nextKind.isValue) throw _segmentError(3, line);
     367           0 :         srcColumn += tokenizer._consumeValue();
     368           0 :         if (!tokenizer.nextKind.isValue) {
     369           0 :           entries.add(new TargetEntry(column, srcUrlId, srcLine, srcColumn));
     370             :         } else {
     371           0 :           srcNameId += tokenizer._consumeValue();
     372           0 :           if (srcNameId >= names.length) {
     373           0 :             throw new StateError(
     374           0 :                 'Invalid name id: $targetUrl, $line, $srcNameId');
     375             :           }
     376           0 :           entries.add(
     377           0 :               new TargetEntry(column, srcUrlId, srcLine, srcColumn, srcNameId));
     378             :         }
     379             :       }
     380           0 :       if (tokenizer.nextKind.isNewSegment) tokenizer._consumeNewSegment();
     381             :     }
     382           0 :     if (!entries.isEmpty) {
     383           0 :       lines.add(new TargetLineEntry(line, entries));
     384             :     }
     385             :   }
     386             : 
     387             :   /// Encodes the Mapping mappings as a json map.
     388             :   Map toJson() {
     389           0 :     var buff = new StringBuffer();
     390             :     var line = 0;
     391             :     var column = 0;
     392             :     var srcLine = 0;
     393             :     var srcColumn = 0;
     394             :     var srcUrlId = 0;
     395             :     var srcNameId = 0;
     396             :     var first = true;
     397             : 
     398           0 :     for (var entry in lines) {
     399           0 :       int nextLine = entry.line;
     400           0 :       if (nextLine > line) {
     401           0 :         for (int i = line; i < nextLine; ++i) {
     402           0 :           buff.write(';');
     403             :         }
     404             :         line = nextLine;
     405             :         column = 0;
     406             :         first = true;
     407             :       }
     408             : 
     409           0 :       for (var segment in entry.entries) {
     410           0 :         if (!first) buff.write(',');
     411             :         first = false;
     412           0 :         column = _append(buff, column, segment.column);
     413             : 
     414             :         // Encoding can be just the column offset if there is no source
     415             :         // information.
     416           0 :         var newUrlId = segment.sourceUrlId;
     417             :         if (newUrlId == null) continue;
     418           0 :         srcUrlId = _append(buff, srcUrlId, newUrlId);
     419           0 :         srcLine = _append(buff, srcLine, segment.sourceLine);
     420           0 :         srcColumn = _append(buff, srcColumn, segment.sourceColumn);
     421             : 
     422           0 :         if (segment.sourceNameId == null) continue;
     423           0 :         srcNameId = _append(buff, srcNameId, segment.sourceNameId);
     424             :       }
     425             :     }
     426             : 
     427           0 :     var result = {
     428             :       'version': 3,
     429           0 :       'sourceRoot': sourceRoot == null ? '' : sourceRoot,
     430           0 :       'sources': urls,
     431           0 :       'names': names,
     432           0 :       'mappings': buff.toString()
     433             :     };
     434           0 :     if (targetUrl != null) {
     435           0 :       result['file'] = targetUrl;
     436             :     }
     437             :     return result;
     438             :   }
     439             : 
     440             :   /// Appends to [buff] a VLQ encoding of [newValue] using the difference
     441             :   /// between [oldValue] and [newValue]
     442             :   static int _append(StringBuffer buff, int oldValue, int newValue) {
     443           0 :     buff.writeAll(encodeVlq(newValue - oldValue));
     444             :     return newValue;
     445             :   }
     446             : 
     447             :   _segmentError(int seen, int line) =>
     448           0 :       new StateError('Invalid entry in sourcemap, expected 1, 4, or 5'
     449           0 :           ' values, but got $seen.\ntargeturl: $targetUrl, line: $line');
     450             : 
     451             :   /// Returns [TargetLineEntry] which includes the location in the target [line]
     452             :   /// number. In particular, the resulting entry is the last entry whose line
     453             :   /// number is lower or equal to [line].
     454             :   TargetLineEntry _findLine(int line) {
     455           0 :     int index = binarySearch(lines, (e) => e.line > line);
     456           0 :     return (index <= 0) ? null : lines[index - 1];
     457             :   }
     458             : 
     459             :   /// Returns [TargetEntry] which includes the location denoted by
     460             :   /// [line], [column]. If [lineEntry] corresponds to [line], then this will be
     461             :   /// the last entry whose column is lower or equal than [column]. If
     462             :   /// [lineEntry] corresponds to a line prior to [line], then the result will be
     463             :   /// the very last entry on that line.
     464             :   TargetEntry _findColumn(int line, int column, TargetLineEntry lineEntry) {
     465           0 :     if (lineEntry == null || lineEntry.entries.length == 0) return null;
     466           0 :     if (lineEntry.line != line) return lineEntry.entries.last;
     467           0 :     var entries = lineEntry.entries;
     468           0 :     int index = binarySearch(entries, (e) => e.column > column);
     469           0 :     return (index <= 0) ? null : entries[index - 1];
     470             :   }
     471             : 
     472             :   SourceMapSpan spanFor(int line, int column,
     473             :       {Map<String, SourceFile> files, String uri}) {
     474           0 :     var entry = _findColumn(line, column, _findLine(line));
     475           0 :     if (entry == null || entry.sourceUrlId == null) return null;
     476           0 :     var url = urls[entry.sourceUrlId];
     477           0 :     if (sourceRoot != null) {
     478           0 :       url = '${sourceRoot}${url}';
     479             :     }
     480           0 :     if (files != null && files[url] != null) {
     481           0 :       var file = files[url];
     482           0 :       var start = file.getOffset(entry.sourceLine, entry.sourceColumn);
     483           0 :       if (entry.sourceNameId != null) {
     484           0 :         var text = names[entry.sourceNameId];
     485           0 :         return new SourceMapFileSpan(
     486           0 :             files[url].span(start, start + text.length),
     487             :             isIdentifier: true);
     488             :       } else {
     489           0 :         return new SourceMapFileSpan(files[url].location(start).pointSpan());
     490             :       }
     491             :     } else {
     492           0 :       var start = new SourceLocation(0,
     493           0 :           sourceUrl: _mapUrl == null ? url : _mapUrl.resolve(url),
     494           0 :           line: entry.sourceLine,
     495           0 :           column: entry.sourceColumn);
     496             : 
     497             :       // Offset and other context is not available.
     498           0 :       if (entry.sourceNameId != null) {
     499           0 :         return new SourceMapSpan.identifier(start, names[entry.sourceNameId]);
     500             :       } else {
     501           0 :         return new SourceMapSpan(start, start, '');
     502             :       }
     503             :     }
     504             :   }
     505             : 
     506             :   String toString() {
     507           0 :     return (new StringBuffer("$runtimeType : [")
     508           0 :           ..write('targetUrl: ')
     509           0 :           ..write(targetUrl)
     510           0 :           ..write(', sourceRoot: ')
     511           0 :           ..write(sourceRoot)
     512           0 :           ..write(', urls: ')
     513           0 :           ..write(urls)
     514           0 :           ..write(', names: ')
     515           0 :           ..write(names)
     516           0 :           ..write(', lines: ')
     517           0 :           ..write(lines)
     518           0 :           ..write(']'))
     519           0 :         .toString();
     520             :   }
     521             : 
     522             :   String get debugString {
     523           0 :     var buff = new StringBuffer();
     524           0 :     for (var lineEntry in lines) {
     525           0 :       var line = lineEntry.line;
     526           0 :       for (var entry in lineEntry.entries) {
     527             :         buff
     528           0 :           ..write(targetUrl)
     529           0 :           ..write(': ')
     530           0 :           ..write(line)
     531           0 :           ..write(':')
     532           0 :           ..write(entry.column);
     533           0 :         if (entry.sourceUrlId != null) {
     534             :           buff
     535           0 :             ..write('   -->   ')
     536           0 :             ..write(sourceRoot)
     537           0 :             ..write(urls[entry.sourceUrlId])
     538           0 :             ..write(': ')
     539           0 :             ..write(entry.sourceLine)
     540           0 :             ..write(':')
     541           0 :             ..write(entry.sourceColumn);
     542             :         }
     543           0 :         if (entry.sourceNameId != null) {
     544           0 :           buff..write(' (')..write(names[entry.sourceNameId])..write(')');
     545             :         }
     546           0 :         buff.write('\n');
     547             :       }
     548             :     }
     549           0 :     return buff.toString();
     550             :   }
     551             : }
     552             : 
     553             : /// A line entry read from a source map.
     554             : class TargetLineEntry {
     555             :   final int line;
     556             :   List<TargetEntry> entries;
     557           0 :   TargetLineEntry(this.line, this.entries);
     558             : 
     559           0 :   String toString() => '$runtimeType: $line $entries';
     560             : }
     561             : 
     562             : /// A target segment entry read from a source map
     563             : class TargetEntry {
     564             :   final int column;
     565             :   final int sourceUrlId;
     566             :   final int sourceLine;
     567             :   final int sourceColumn;
     568             :   final int sourceNameId;
     569             : 
     570             :   TargetEntry(this.column,
     571             :       [this.sourceUrlId,
     572             :       this.sourceLine,
     573             :       this.sourceColumn,
     574           0 :       this.sourceNameId]);
     575             : 
     576           0 :   String toString() => '$runtimeType: '
     577           0 :       '($column, $sourceUrlId, $sourceLine, $sourceColumn, $sourceNameId)';
     578             : }
     579             : 
     580             : /** A character iterator over a string that can peek one character ahead. */
     581             : class _MappingTokenizer implements Iterator<String> {
     582             :   final String _internal;
     583             :   final int _length;
     584             :   int index = -1;
     585             :   _MappingTokenizer(String internal)
     586             :       : _internal = internal,
     587           0 :         _length = internal.length;
     588             : 
     589             :   // Iterator API is used by decodeVlq to consume VLQ entries.
     590           0 :   bool moveNext() => ++index < _length;
     591             :   String get current =>
     592           0 :       (index >= 0 && index < _length) ? _internal[index] : null;
     593             : 
     594           0 :   bool get hasTokens => index < _length - 1 && _length > 0;
     595             : 
     596             :   _TokenKind get nextKind {
     597           0 :     if (!hasTokens) return _TokenKind.EOF;
     598           0 :     var next = _internal[index + 1];
     599           0 :     if (next == ';') return _TokenKind.LINE;
     600           0 :     if (next == ',') return _TokenKind.SEGMENT;
     601             :     return _TokenKind.VALUE;
     602             :   }
     603             : 
     604           0 :   int _consumeValue() => decodeVlq(this);
     605             :   void _consumeNewLine() {
     606           0 :     ++index;
     607             :   }
     608             : 
     609             :   void _consumeNewSegment() {
     610           0 :     ++index;
     611             :   }
     612             : 
     613             :   // Print the state of the iterator, with colors indicating the current
     614             :   // position.
     615             :   String toString() {
     616           0 :     var buff = new StringBuffer();
     617           0 :     for (int i = 0; i < index; i++) {
     618           0 :       buff.write(_internal[i]);
     619             :     }
     620           0 :     buff.write('');
     621           0 :     buff.write(current == null ? '' : current);
     622           0 :     buff.write('');
     623           0 :     for (int i = index + 1; i < _internal.length; i++) {
     624           0 :       buff.write(_internal[i]);
     625             :     }
     626           0 :     buff.write(' ($index)');
     627           0 :     return buff.toString();
     628             :   }
     629             : }
     630             : 
     631             : class _TokenKind {
     632             :   static const _TokenKind LINE = const _TokenKind(isNewLine: true);
     633             :   static const _TokenKind SEGMENT = const _TokenKind(isNewSegment: true);
     634             :   static const _TokenKind EOF = const _TokenKind(isEof: true);
     635             :   static const _TokenKind VALUE = const _TokenKind();
     636             :   final bool isNewLine;
     637             :   final bool isNewSegment;
     638             :   final bool isEof;
     639           0 :   bool get isValue => !isNewLine && !isNewSegment && !isEof;
     640             : 
     641             :   const _TokenKind(
     642           5 :       {this.isNewLine: false, this.isNewSegment: false, this.isEof: false});
     643             : }

Generated by: LCOV version 1.13