Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1308)

Unified Diff: mojo/public/dart/third_party/source_maps/lib/parser.dart

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mojo/public/dart/third_party/source_maps/lib/parser.dart
diff --git a/mojo/public/dart/third_party/source_maps/lib/parser.dart b/mojo/public/dart/third_party/source_maps/lib/parser.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a9fcff57840ec6d4cef109b24770db20c16a9440
--- /dev/null
+++ b/mojo/public/dart/third_party/source_maps/lib/parser.dart
@@ -0,0 +1,531 @@
+// 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.
+///
+/// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
+/// the source map file itself. If it's passed, any URLs in the source
+/// map will be interpreted as relative to this URL when generating spans.
+// 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, mapUrl}) =>
+ parseJson(JSON.decode(jsonMap), otherMaps: otherMaps, mapUrl: mapUrl);
+
+/// Parses a source map directly from a json map object.
+///
+/// [mapUrl], which may be either a [String] or a [Uri], indicates the URL of
+/// the source map file itself. If it's passed, any URLs in the source
+/// map will be interpreted as relative to this URL when generating spans.
+Mapping parseJson(Map map, {Map<String, Map> otherMaps, mapUrl}) {
+ 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,
+ mapUrl: mapUrl);
+ }
+ return new SingleMapping.fromJson(map, mapUrl: mapUrl);
+}
+
+
+/// 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,
+ {mapUrl}) {
+ 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, mapUrl: url));
+ } else if (map != null) {
+ _maps.add(parseJson(map, otherMaps: otherMaps, mapUrl: mapUrl));
+ } 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 prepended to all entries in [urls].
+ String sourceRoot;
+
+ final Uri _mapUrl;
+
+ SingleMapping._(this.targetUrl, this.urls, this.names, this.lines)
+ : _mapUrl = null;
+
+ 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, {mapUrl})
+ : targetUrl = map['file'],
+ urls = map['sources'],
+ names = map['names'],
+ sourceRoot = map['sourceRoot'],
+ lines = <TargetLineEntry>[],
+ _mapUrl = mapUrl is String ? Uri.parse(mapUrl) : mapUrl {
+ 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: _mapUrl == null ? url : _mapUrl.resolve(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('');
+ buff.write(current == null ? '' : current);
+ buff.write('');
+ 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});
+}
« no previous file with comments | « mojo/public/dart/third_party/source_maps/lib/builder.dart ('k') | mojo/public/dart/third_party/source_maps/lib/printer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698