Use a relative path when generating unique IDs for elements (#52)

This does not affect the canonical URI for a library, but it does
ensure the IDs for libraries and their members are more stable when
using build systems that rely on temporary directories, like pkg:build

This accomplishes most of the desired behavior from
https://github.com/dart-lang/sdk/commit/7fe8659613bfbf360d57d12327b7521cd869987a#diff-49250e8c7ce346ad217448560050bf76

which was reverted in
https://github.com/dart-lang/sdk/commit/2e1c17e5c15766baa8771ad4f29ef188fec52699#diff-49250e8c7ce346ad217448560050bf76
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1bc876..9299f56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.5.13
+
+* Use a more efficient `Map` implementation for decoding existing info files.
+
+* Use a relative path when generating unique IDs for elements in non-package
+  sources.
+
 ## 0.5.12
 
 * Improved output of `dart2js_info_diff` by sorting the diffs by
diff --git a/lib/info.dart b/lib/info.dart
index 9e35f69..05ba195 100644
--- a/lib/info.dart
+++ b/lib/info.dart
@@ -73,7 +73,7 @@
         // Instead, use the content of the code.
         _id = (this as ConstantInfo).code.hashCode;
       } else {
-        _id = longName(this, useLibraryUri: true).hashCode;
+        _id = longName(this, useLibraryUri: true, forId: true).hashCode;
       }
       while (!_ids.add(_id)) {
         _id++;
diff --git a/lib/src/util.dart b/lib/src/util.dart
index a14ffbd..3ab0a64 100644
--- a/lib/src/util.dart
+++ b/lib/src/util.dart
@@ -52,7 +52,7 @@
 
 /// Provide a unique long name associated with [info].
 // TODO(sigmund): guarantee that the name is actually unique.
-String longName(Info info, {bool useLibraryUri: false}) {
+String longName(Info info, {bool useLibraryUri: false, bool forId: false}) {
   var infoPath = [];
   while (info != null) {
     infoPath.add(info);
@@ -66,7 +66,25 @@
     // assert(!first || segment is LibraryInfo);
     // (today might not be true for for closure classes).
     if (segment is LibraryInfo) {
-      sb.write(useLibraryUri ? segment.uri : segment.name);
+      // TODO(kevmoo): Remove this when dart2js can be invoked with an app-root
+      // custom URI
+      if (useLibraryUri && forId && segment.uri.isScheme('file')) {
+        assert(Uri.base.isScheme('file'));
+        var currentBase = Uri.base.path;
+        var segmentString = segment.uri.path;
+
+        // If longName is being called to calculate an element ID (forId = true)
+        // then use a relative path for the longName calculation
+        // This allows a more stable ID for cases when files are generated into
+        // temp directories – e.g. with pkg:build_web_compilers
+        if (segmentString.startsWith(currentBase)) {
+          segmentString = segmentString.substring(currentBase.length);
+        }
+
+        sb.write(segmentString);
+      } else {
+        sb.write(useLibraryUri ? segment.uri : segment.name);
+      }
       sb.write('::');
     } else {
       first = false;