Avoid instantiating FileLocations where possible in source_span.

R=rnystrom@google.com

Review URL: https://codereview.chromium.org//754463002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/source_span@41921 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5190bc..f057e9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+# 1.0.2
+
+* Avoid unintentionally allocating extra objects for internal `FileSpan`
+  operations.
+
+* Ensure that `SourceSpan.operator==` works on arbitrary `Object`s.
+
 # 1.0.1
 
 * Use a more compact internal representation for `FileSpan`.
diff --git a/lib/src/file.dart b/lib/src/file.dart
index 14aa226..ed5f6a8 100644
--- a/lib/src/file.dart
+++ b/lib/src/file.dart
@@ -199,10 +199,11 @@
   /// objects.
   final int _end;
 
+  Uri get sourceUrl => file.url;
+  int get length => _end - _start;
   FileLocation get start => new FileLocation._(file, _start);
   FileLocation get end => new FileLocation._(file, _end);
-
-  String get text => file.getText(start.offset, end.offset);
+  String get text => file.getText(_start, _end);
 
   FileSpan._(this.file, this._start, this._end) {
     if (_end < _start) {
@@ -215,20 +216,37 @@
     }
   }
 
+  int compareTo(SourceSpan other) {
+    if (other is! FileSpan) return super.compareTo(other);
+
+    FileSpan otherFile = other;
+    var result = _start.compareTo(otherFile._start);
+    return result == 0 ? _end.compareTo(otherFile._end) : result;
+  }
+
   SourceSpan union(SourceSpan other) {
     if (other is! FileSpan) return super.union(other);
 
     var span = expand(other);
-    var beginSpan = span.start == this.start ? this : other;
-    var endSpan = span.end == this.end ? this : other;
+    var beginSpan = span._start == _start ? this : other;
+    var endSpan = span._end == _end ? this : other;
 
-    if (beginSpan.end.compareTo(endSpan.start) < 0) {
+    if (beginSpan._end < endSpan._start) {
       throw new ArgumentError("Spans $this and $other are disjoint.");
     }
 
     return span;
   }
 
+  bool operator ==(other) {
+    if (other is! FileSpan) return super == other;
+    return _start == other._start && _end == other._end &&
+        sourceUrl == other.sourceUrl;
+  }
+
+  int get hashCode => _start.hashCode + 5 * _end.hashCode +
+      7 * sourceUrl.hashCode;
+
   /// Returns a new span that covers both [this] and [other].
   ///
   /// Unlike [union], [other] may be disjoint from [this]. If it is, the text
@@ -241,7 +259,7 @@
 
     var start = math.min(this._start, other._start);
     var end = math.max(this._end, other._end);
-    return new FileSpan._(file, start, end);    
+    return new FileSpan._(file, start, end);
   }
 
   String message(String message, {color}) {
diff --git a/lib/src/span_mixin.dart b/lib/src/span_mixin.dart
index 95a720a..716e6e0 100644
--- a/lib/src/span_mixin.dart
+++ b/lib/src/span_mixin.dart
@@ -21,8 +21,8 @@
   int get length => end.offset - start.offset;
 
   int compareTo(SourceSpan other) {
-    int d = start.compareTo(other.start);
-    return d == 0 ? end.compareTo(other.end) : d;
+    var result = start.compareTo(other.start);
+    return result == 0 ? end.compareTo(other.end) : result;
   }
 
   SourceSpan union(SourceSpan other) {
@@ -65,7 +65,7 @@
     return buffer.toString();
   }
 
-  bool operator ==(SourceSpan other) =>
+  bool operator ==(other) => other is SourceSpan &&
       start == other.start && end == other.end;
 
   int get hashCode => start.hashCode + (31 * end.hashCode);
diff --git a/pubspec.yaml b/pubspec.yaml
index ae1bfcf..5c166fc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: source_span
 
-version: 1.0.1
+version: 1.0.2
 author: Dart Team <misc@dartlang.org>
 description: A library for identifying source spans and locations.
 homepage: http://www.dartlang.org