add LibraryIdentifier

R=sigmund@google.com

Review URL: https://codereview.chromium.org//880713002
diff --git a/CHANGELOG.md b/CHANGELOG.md
index de1d786..9937cb7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.3.0-dev
+
+* Library initializers now pass a `LibraryIdentifier` to `initialize` instead of
+just a `Symbol`. This provides the package, and path to the library in addition
+to the symbol so that paths can be normalized.
+
 ## 0.2.0
 
 * `entryPoint` and `newEntryPoint` transformer options were renamed to
diff --git a/test/initialize_tracker.dart b/lib/src/initialize_tracker.dart
similarity index 100%
rename from test/initialize_tracker.dart
rename to lib/src/initialize_tracker.dart
diff --git a/lib/src/initializer.dart b/lib/src/initializer.dart
index 3ddf0cc..60a04d5 100644
--- a/lib/src/initializer.dart
+++ b/lib/src/initializer.dart
@@ -26,3 +26,23 @@
 
 /// Typedef for a custom filter function.
 typedef bool InitializerFilter(Initializer initializer);
+
+/// When annotating libraries, this is passed to the initializer.
+class LibraryIdentifier {
+  // The qualified name of the library.
+  final Symbol name;
+
+  // The package this library lives in. May be null if its the same as the root
+  // package.
+  final String package;
+
+  // The path to the library.
+  final String path;
+
+  const LibraryIdentifier(this.name, this.package, this.path);
+
+  bool operator ==(LibraryIdentifier other) =>
+      name == other.name && package == other.package && path == other.path;
+
+  String toString() => '$name: $package:$path';
+}
diff --git a/lib/src/mirror_loader.dart b/lib/src/mirror_loader.dart
index 1512ec9..f3e2689 100644
--- a/lib/src/mirror_loader.dart
+++ b/lib/src/mirror_loader.dart
@@ -145,7 +145,22 @@
         annotatedValue = (declaration.owner as ObjectMirror)
             .getField(declaration.simpleName).reflectee;
       } else if (declaration is LibraryMirror) {
-        annotatedValue = declaration.qualifiedName;
+        var package;
+        var filePath;
+        Uri uri = declaration.uri;
+        if (uri.scheme == 'file' || uri.scheme.startsWith('http')) {
+          filePath = path.url.relative(
+              uri.path, from: path.url.dirname(_root.uri.path));
+        } else if (uri.scheme == 'package') {
+          var segments = uri.pathSegments;
+          package = segments[0];
+          filePath = path.url.joinAll(segments.getRange(1, segments.length));
+        } else {
+          throw new UnsupportedError('Unsupported uri scheme ${uri.scheme} for '
+              'library ${declaration}.');
+        }
+        annotatedValue =
+            new LibraryIdentifier(declaration.qualifiedName, package, filePath);
       } else {
         throw _UNSUPPORTED_DECLARATION;
       }
diff --git a/lib/transformer.dart b/lib/transformer.dart
index 392d322..8036761 100644
--- a/lib/transformer.dart
+++ b/lib/transformer.dart
@@ -242,9 +242,10 @@
     var initializersBuffer = new StringBuffer();
     var libraryPrefixes = new Map<LibraryElement, String>();
 
-    // Import the static_loader and original entry point.
+    // Import the static_loader, initializer, and original entry point.
     importsBuffer
         .writeln("import 'package:initialize/src/static_loader.dart';");
+    importsBuffer.writeln("import 'package:initialize/initialize.dart';");
     libraryPrefixes[entryLib] = 'i0';
 
     initializersBuffer.writeln('  initializers.addAll([');
@@ -300,7 +301,27 @@
     final metaPrefix = libraryPrefixes[annotationElement.library];
     var elementString;
     if (element is LibraryElement) {
-      elementString = '#${element.name}';
+      var segments = element.source.uri.pathSegments;
+      var package = segments[0];
+      var libraryPath;
+      var packageString;
+      if (_newEntryPoint.package == package &&
+          _newEntryPoint.path.startsWith('${segments[1]}/')) {
+        // reset `package` to null, we will do a relative path in this case.
+        packageString = 'null';
+        libraryPath = path.url.relative(
+            path.url.joinAll(segments.getRange(1, segments.length)),
+            from: path.url.dirname(path.url.join(_newEntryPoint.path)));
+      } else if (segments[1] == 'lib') {
+        packageString = "'$package'";
+        libraryPath = path.url.joinAll(segments.getRange(2, segments.length));
+      } else {
+        _logger.error('Unable to import `${element.source.uri.path}` from '
+            '${_newEntryPoint.path}.');
+      }
+
+      elementString = "const LibraryIdentifier("
+          "#${element.name}, $packageString, '$libraryPath')";
     } else if (element is ClassElement || element is FunctionElement) {
       elementString =
           '${libraryPrefixes[data.element.library]}.${element.name}';
diff --git a/pubspec.yaml b/pubspec.yaml
index 25bc767..018cdef 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: initialize
-version: 0.2.0
+version: 0.3.0-dev
 author: Polymer.dart Authors <web@dartlang.org>
 description: Generic building blocks for doing static initialization.
 homepage: https://github.com/dart-lang/initialize
@@ -9,6 +9,8 @@
   html5lib: '>=0.12.0 <0.13.0'
   path: '>=1.3.0 <2.0.0'
 dev_dependencies:
+  test_package:
+    path: test_package
   unittest: '>=0.10.0 <0.12.0'
 environment:
   sdk: '>=1.4.0 <2.0.0'
diff --git a/test/bar.dart b/test/bar.dart
index 664d4f7..dd83a8f 100644
--- a/test/bar.dart
+++ b/test/bar.dart
@@ -5,7 +5,7 @@
 library initialize.test.bar;
 
 import 'foo.dart';
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 
 // Foo should be initialized first.
 @initializeTracker
diff --git a/test/cycle_a.dart b/test/cycle_a.dart
index b0d55da..6bc6d84 100644
--- a/test/cycle_a.dart
+++ b/test/cycle_a.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 library initialize.test.cycle_a;
 
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 import 'cycle_b.dart';
 
 @initializeTracker
diff --git a/test/cycle_b.dart b/test/cycle_b.dart
index 7dd9b5c..3af725d 100644
--- a/test/cycle_b.dart
+++ b/test/cycle_b.dart
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 library initialize.test.cycle_b;
 
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 import 'cycle_a.dart';
 
 @initializeTracker
diff --git a/test/foo.dart b/test/foo.dart
index 6e5340c..a62e12f 100644
--- a/test/foo.dart
+++ b/test/foo.dart
@@ -4,7 +4,7 @@
 @initializeTracker
 library initialize.test.foo;
 
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 
 @initializeTracker
 class Foo {}
diff --git a/test/initializer_custom_filter_test.dart b/test/initializer_custom_filter_test.dart
index ad54fee..f8f789d 100644
--- a/test/initializer_custom_filter_test.dart
+++ b/test/initializer_custom_filter_test.dart
@@ -7,7 +7,7 @@
 import 'package:initialize/initialize.dart';
 import 'package:unittest/unittest.dart';
 import 'package:unittest/compact_vm_config.dart';
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 
 main() {
   useCompactVMConfiguration();
diff --git a/test/initializer_test.dart b/test/initializer_test.dart
index 9426a9b..098628e 100644
--- a/test/initializer_test.dart
+++ b/test/initializer_test.dart
@@ -6,8 +6,9 @@
 
 import 'foo.dart';
 import 'bar.dart';
-import 'initialize_tracker.dart';
+import 'package:initialize/src/initialize_tracker.dart';
 import 'package:initialize/initialize.dart';
+import 'package:test_package/foo.dart';
 import 'package:unittest/unittest.dart';
 import 'package:unittest/compact_vm_config.dart';
 
@@ -18,14 +19,17 @@
   run().then((_) {
     test('annotations are seen in post-order with superclasses first', () {
       var expectedNames = [
-        #initialize.test.foo,
+        const LibraryIdentifier(#test_package.bar, 'test_package', 'bar.dart'),
+        const LibraryIdentifier(#test_package.foo, 'test_package','foo.dart'),
+        const LibraryIdentifier(#initialize.test.foo, null, 'foo.dart'),
         foo,
         fooBar,
         Foo,
-        #initialize.test.bar,
+        const LibraryIdentifier(#initialize.test.bar, null, 'bar.dart'),
         bar,
         Bar,
-        #initialize.initializer_test,
+        const LibraryIdentifier(
+            #initialize.initializer_test, null, 'initializer_test.dart'),
         zap,
         Zoop, // Zap extends Zoop, so Zoop comes first.
         Zap
diff --git a/test/transformer_test.dart b/test/transformer_test.dart
index 62e2f65..a427e70 100644
--- a/test/transformer_test.dart
+++ b/test/transformer_test.dart
@@ -45,6 +45,7 @@
 
         import 'package:initialize/initialize.dart';
         import 'package:test_initializers/common.dart';
+        import 'baz.dart';
 
         @DynamicInit('Bar')
         @DynamicInit('Bar2')
@@ -54,6 +55,12 @@
         @initMethod
         bar() {}
         ''',
+    'bar|lib/baz.dart': '''
+        @constInit
+        library baz;
+
+        import 'package:test_initializers/common.dart';
+        ''',
     // Mock out the Initialize package plus some initializers.
     'initialize|lib/initialize.dart': mockInitialize,
     'test_initializers|lib/common.dart': commonInitializers,
@@ -65,29 +72,32 @@
         </body></html>'''.replaceAll('        ', ''),
     'a|web/index.bootstrap.dart': '''
         import 'package:initialize/src/static_loader.dart';
+        import 'package:initialize/initialize.dart';
         import 'index.dart' as i0;
-        import 'package:bar/bar.dart' as i1;
+        import 'package:bar/baz.dart' as i1;
         import 'package:test_initializers/common.dart' as i2;
-        import 'package:initialize/initialize.dart' as i3;
-        import 'foo.dart' as i4;
+        import 'package:bar/bar.dart' as i3;
+        import 'package:initialize/initialize.dart' as i4;
+        import 'foo.dart' as i5;
 
         main() {
           initializers.addAll([
-            new InitEntry(const i2.DynamicInit('bar'), #bar),
-            new InitEntry(const i2.DynamicInit('bar2'), #bar),
-            new InitEntry(const i2.DynamicInit('bar()'), i1.bar),
-            new InitEntry(i3.initMethod, i1.bar),
-            new InitEntry(const i2.DynamicInit('Bar'), i1.Bar),
-            new InitEntry(const i2.DynamicInit('Bar2'), i1.Bar),
-            new InitEntry(i2.constInit, #foo),
-            new InitEntry(i3.initMethod, i4.foo),
-            new InitEntry(i2.constInit, i4.Foo),
+            new InitEntry(i2.constInit, const LibraryIdentifier(#baz, 'bar', 'baz.dart')),
+            new InitEntry(const i2.DynamicInit('bar'), const LibraryIdentifier(#bar, 'bar', 'bar.dart')),
+            new InitEntry(const i2.DynamicInit('bar2'), const LibraryIdentifier(#bar, 'bar', 'bar.dart')),
+            new InitEntry(const i2.DynamicInit('bar()'), i3.bar),
+            new InitEntry(i4.initMethod, i3.bar),
+            new InitEntry(const i2.DynamicInit('Bar'), i3.Bar),
+            new InitEntry(const i2.DynamicInit('Bar2'), i3.Bar),
+            new InitEntry(i2.constInit, const LibraryIdentifier(#foo, null, 'foo.dart')),
+            new InitEntry(i4.initMethod, i5.foo),
+            new InitEntry(i2.constInit, i5.Foo),
           ]);
 
           i0.main();
         }
         '''.replaceAll('        ', '')
-  });
+  }, []);
 
   testPhases('constructor arguments', [[transformer]], {
     'a|web/index.dart': '''
@@ -121,26 +131,27 @@
   }, {
     'a|web/index.bootstrap.dart': '''
         import 'package:initialize/src/static_loader.dart';
+        import 'package:initialize/initialize.dart';
         import 'index.dart' as i0;
         import 'package:test_initializers/common.dart' as i1;
         import 'foo.dart' as i2;
 
         main() {
           initializers.addAll([
-            new InitEntry(const i1.DynamicInit(i2.foo), #web_foo),
-            new InitEntry(const i1.DynamicInit(i2.Foo.foo), #web_foo),
-            new InitEntry(const i1.DynamicInit(const [i2.foo, i2.Foo.foo, 'foo']), #web_foo),
-            new InitEntry(const i1.DynamicInit(const {'foo': i2.foo, 'Foo.foo': i2.Foo.foo, 'bar': 'bar'}), #web_foo),
-            new InitEntry(const i1.DynamicInit('foo'), #web_foo),
-            new InitEntry(const i1.DynamicInit(true), #web_foo),
-            new InitEntry(const i1.DynamicInit(null), #web_foo),
-            new InitEntry(const i1.DynamicInit(1), #web_foo),
-            new InitEntry(const i1.DynamicInit(1.1), #web_foo),
-            new InitEntry(const i1.NamedArgInit(1, name: 'Bill'), #web_foo),
+            new InitEntry(const i1.DynamicInit(i2.foo), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(i2.Foo.foo), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(const [i2.foo, i2.Foo.foo, 'foo']), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(const {'foo': i2.foo, 'Foo.foo': i2.Foo.foo, 'bar': 'bar'}), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit('foo'), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(true), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(null), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(1), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.DynamicInit(1.1), const LibraryIdentifier(#web_foo, null, 'index.dart')),
+            new InitEntry(const i1.NamedArgInit(1, name: 'Bill'), const LibraryIdentifier(#web_foo, null, 'index.dart')),
           ]);
 
           i0.main();
         }
         '''.replaceAll('        ', '')
-  });
+  }, []);
 }
diff --git a/test_package/README.md b/test_package/README.md
new file mode 100644
index 0000000..b5c2ddd
--- /dev/null
+++ b/test_package/README.md
@@ -0,0 +1,5 @@
+Initialize tests package
+========================
+
+Package used in the `initialize` packages tests. This is just a helper to make
+sure that it correctly normalizes urls in mirror mode.
diff --git a/test_package/lib/bar.dart b/test_package/lib/bar.dart
new file mode 100644
index 0000000..8cc6129
--- /dev/null
+++ b/test_package/lib/bar.dart
@@ -0,0 +1,7 @@
+// Copyright (c) 2015, 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.
+@initializeTracker
+library test_package.bar;
+
+import 'package:initialize/src/initialize_tracker.dart';
diff --git a/test_package/lib/foo.dart b/test_package/lib/foo.dart
new file mode 100644
index 0000000..c1357f8
--- /dev/null
+++ b/test_package/lib/foo.dart
@@ -0,0 +1,8 @@
+// Copyright (c) 2015, 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.
+@initializeTracker
+library test_package.foo;
+
+import 'bar.dart'; // Keep for the annotation
+import 'package:initialize/src/initialize_tracker.dart';
diff --git a/test_package/pubspec.yaml b/test_package/pubspec.yaml
new file mode 100644
index 0000000..438a6f9
--- /dev/null
+++ b/test_package/pubspec.yaml
@@ -0,0 +1,4 @@
+name: test_package
+dependencies:
+  initialize:
+    path: ../