| Index: sdk/lib/_internal/compiler/implementation/source_file.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/source_file.dart b/sdk/lib/_internal/compiler/implementation/source_file.dart
|
| index 3e7d2b8a37856e63a45226e3e3e9f077f2fff207..66e9e3478922ef8a81725d4337d1a9c1e585dafa 100644
|
| --- a/sdk/lib/_internal/compiler/implementation/source_file.dart
|
| +++ b/sdk/lib/_internal/compiler/implementation/source_file.dart
|
| @@ -5,42 +5,83 @@
|
| library source_file;
|
|
|
| import 'dart:math';
|
| +import 'dart:convert' show UTF8;
|
|
|
| /**
|
| - * Represents a file of source code.
|
| + * Represents a file of source code. The content can be either a [String] or
|
| + * a UTF-8 encoded [List<int>] of bytes.
|
| */
|
| -class SourceFile {
|
| +abstract class SourceFile {
|
|
|
| /** The name of the file. */
|
| final String filename;
|
|
|
| - /** The text content of the file. */
|
| - final String text;
|
| + SourceFile(this.filename);
|
| +
|
| + /** The text content of the file represented as a String. */
|
| + String slowText();
|
|
|
| - List<int> _lineStarts;
|
| + /** The content of the file represented as a UTF-8 encoded [List<int>]. */
|
| + List<int> slowUtf8Bytes();
|
|
|
| - SourceFile(this.filename, this.text);
|
| + /**
|
| + * The length of the string representation of this source file, i.e.,
|
| + * equivalent to [:slowText().length:], but faster.
|
| + */
|
| + int get length;
|
|
|
| + /**
|
| + * Sets the string length of this source file. For source files based on UTF-8
|
| + * byte arrays, the string length is computed and assigned by the scanner.
|
| + */
|
| + set length(v);
|
| +
|
| + /**
|
| + * A map from line numbers to offsets in the string text representation of
|
| + * this source file.
|
| + */
|
| List<int> get lineStarts {
|
| - if (_lineStarts == null) {
|
| - var starts = [0];
|
| - var index = 0;
|
| - while (index < text.length) {
|
| - index = text.indexOf('\n', index) + 1;
|
| - if (index <= 0) break;
|
| - starts.add(index);
|
| - }
|
| - starts.add(text.length + 1);
|
| - _lineStarts = starts;
|
| + if (lineStartsCache == null) {
|
| + // When reporting errors during scanning, the line numbers are not yet
|
| + // available and need to be computed using this slow path.
|
| + lineStartsCache = lineStartsFromString(slowText());
|
| }
|
| - return _lineStarts;
|
| + return lineStartsCache;
|
| }
|
|
|
| + /**
|
| + * Sets the line numbers map for this source file. This map is computed and
|
| + * assigned by the scanner, avoiding a separate traversal of the source file.
|
| + *
|
| + * The map contains one additional entry at the end of the file, as if the
|
| + * source file had one more empty line at the end. This simplifies the binary
|
| + * search in [getLine].
|
| + */
|
| + set lineStarts(v) => lineStartsCache = v;
|
| +
|
| + List<int> lineStartsCache;
|
| +
|
| + List<int> lineStartsFromString(String text) {
|
| + var starts = [0];
|
| + var index = 0;
|
| + while (index < text.length) {
|
| + index = text.indexOf('\n', index) + 1;
|
| + if (index <= 0) break;
|
| + starts.add(index);
|
| + }
|
| + starts.add(text.length + 1); // One additional line start at the end.
|
| + return starts;
|
| + }
|
| +
|
| + /**
|
| + * Returns the line number for the offset [position] in the string
|
| + * representation of this source file.
|
| + */
|
| int getLine(int position) {
|
| List<int> starts = lineStarts;
|
| if (position < 0 || starts.last <= position) {
|
| throw 'bad position #$position in file $filename with '
|
| - 'length ${text.length}.';
|
| + 'length ${length}.';
|
| }
|
| int first = 0;
|
| int count = starts.length;
|
| @@ -58,10 +99,16 @@ class SourceFile {
|
| return first;
|
| }
|
|
|
| + /**
|
| + * Returns the column number for the offset [position] in the string
|
| + * representation of this source file.
|
| + */
|
| int getColumn(int line, int position) {
|
| return position - lineStarts[line];
|
| }
|
|
|
| + String slowSubstring(int start, int end);
|
| +
|
| /**
|
| * Create a pretty string representation from a character position
|
| * in the file.
|
| @@ -75,12 +122,12 @@ class SourceFile {
|
| '${filename}:${line + 1}:${column + 1}: $message');
|
| if (includeText) {
|
| buf.write('\n');
|
| - var textLine;
|
| + String textLine;
|
| // +1 for 0-indexing, +1 again to avoid the last line of the file
|
| - if ((line + 2) < _lineStarts.length) {
|
| - textLine = text.substring(_lineStarts[line], _lineStarts[line+1]);
|
| + if ((line + 2) < lineStarts.length) {
|
| + textLine = slowSubstring(lineStarts[line], lineStarts[line+1]);
|
| } else {
|
| - textLine = '${text.substring(_lineStarts[line])}\n';
|
| + textLine = '${slowSubstring(lineStarts[line], length)}\n';
|
| }
|
|
|
| int toColumn = min(column + (end-start), textLine.length);
|
| @@ -101,3 +148,47 @@ class SourceFile {
|
| return buf.toString();
|
| }
|
| }
|
| +
|
| +class Utf8BytesSourceFile extends SourceFile {
|
| +
|
| + /** The UTF-8 encoded content of the source file. */
|
| + final List<int> content;
|
| +
|
| + Utf8BytesSourceFile(String filename, this.content) : super(filename);
|
| +
|
| + String slowText() => UTF8.decode(content);
|
| +
|
| + List<int> slowUtf8Bytes() => content;
|
| +
|
| + String slowSubstring(int start, int end) {
|
| + // TODO(lry), to make this faster, the scanner could record the UTF-8 slack
|
| + // for all positions of the source text. We could use [:content.sublist:].
|
| + return slowText().substring(start, end);
|
| + }
|
| +
|
| + int get length {
|
| + if (lengthCache == -1) {
|
| + // During scanning the length is not yet assigned, so we use a slow path.
|
| + length = slowText().length;
|
| + }
|
| + return lengthCache;
|
| + }
|
| + set length(v) => lengthCache = v;
|
| + int lengthCache = -1;
|
| +}
|
| +
|
| +class StringSourceFile extends SourceFile {
|
| +
|
| + final String text;
|
| +
|
| + StringSourceFile(String filename, this.text) : super(filename);
|
| +
|
| + int get length => text.length;
|
| + set length(v) { }
|
| +
|
| + String slowText() => text;
|
| +
|
| + List<int> slowUtf8Bytes() => UTF8.encode(text);
|
| +
|
| + String slowSubstring(int start, int end) => text.substring(start, end);
|
| +}
|
|
|