Merge pull request #36 from dart-lang/canonical-super

Canonical super
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0abd507..6c50378 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.6.0+3
+
+* Make sure to always use the canonical libraries and super declarations in
+development mode. This eliminates an uncommon issue where a single initializer
+could be ran more than once.
+
 ## 0.6.0+2
 
 * Private identifiers will now be evaluated and inlined into the bootstrap file
diff --git a/lib/src/mirror_loader.dart b/lib/src/mirror_loader.dart
index 0c482d3..3865e72 100644
--- a/lib/src/mirror_loader.dart
+++ b/lib/src/mirror_loader.dart
@@ -9,6 +9,7 @@
 import 'package:initialize/initialize.dart';
 
 final _root = currentMirrorSystem().isolate.rootLibrary;
+final _libs = currentMirrorSystem().libraries;
 
 Queue<Function> loadInitializers(
     {List<Type> typeFilter, InitializerFilter customFilter, Uri from}) {
@@ -38,7 +39,7 @@
   InitializationCrawler(this.typeFilter, this.customFilter, {Uri from})
       : _rootLibrary = from == null
           ? _root
-          : currentMirrorSystem().libraries[from] {
+          : _libs[from] {
     if (_rootLibrary == null) throw 'Unable to find library at $from.';
   }
 
@@ -53,6 +54,23 @@
     return queue;
   }
 
+  /// Returns the canonical [LibraryMirror] for a given [LibraryMirror]. This
+  /// is defined as the one loaded from a `package:` url if available, otherwise
+  /// it is just [lib].
+  LibraryMirror _canonicalLib(LibraryMirror lib) {
+    var uri = lib.uri;
+    if (_isHttpStylePackageUrl(uri)) {
+      var packageUri = _packageUriFor(uri);
+      if (_libs.containsKey(packageUri)) return _libs[packageUri];
+    }
+    return lib;
+  }
+
+  /// Returns the canonical [ClassMirror] for a given [ClassMirror]. This is
+  /// defined as the one that appears in the canonical owner [LibararyMirror].
+  ClassMirror _canonicalClassDeclaration(ClassMirror declaration) =>
+      _canonicalLib(declaration.owner).declarations[declaration.simpleName];
+
   /// Whether [uri] is an http URI that contains a 'packages' segment, and
   /// therefore could be converted into a 'package:' URI.
   bool _isHttpStylePackageUrl(Uri uri) {
@@ -64,9 +82,10 @@
         (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
   }
 
-  Uri _packageUriFor(Uri httpUri) {
-    var packagePath = httpUri.path
-        .substring(httpUri.path.lastIndexOf('packages/') + 'packages/'.length);
+  /// Returns a `package:` version of [uri].
+  Uri _packageUriFor(Uri uri) {
+    var packagePath = uri.path
+        .substring(uri.path.lastIndexOf('packages/') + 'packages/'.length);
     return Uri.parse('package:$packagePath');
   }
 
@@ -74,14 +93,14 @@
   // post-order.
   Queue<Function> _readLibraryDeclarations(LibraryMirror lib,
       Set<LibraryMirror> librariesSeen, Queue<Function> queue) {
+    lib = _canonicalLib(lib);
+    if (librariesSeen.contains(lib)) return queue;
     librariesSeen.add(lib);
 
     // First visit all our dependencies.
     for (var dependency in lib.libraryDependencies) {
       // Skip dart: imports, they never use this package.
       if (dependency.targetLibrary.uri.toString().startsWith('dart:')) continue;
-      if (librariesSeen.contains(dependency.targetLibrary)) continue;
-
       _readLibraryDeclarations(dependency.targetLibrary, librariesSeen, queue);
     }
 
@@ -135,12 +154,14 @@
   String _declarationName(DeclarationMirror declaration) =>
       MirrorSystem.getName(declaration.qualifiedName);
 
-  // Reads annotations on declarations and adds them to `_initQueue` if they are
-  // initializers.
+  /// Reads annotations on a [DeclarationMirror] and adds them to [_initQueue]
+  /// if they are [Initializer]s.
   void _readAnnotations(DeclarationMirror declaration, Queue<Function> queue) {
     var annotations =
         declaration.metadata.where((m) => _filterMetadata(declaration, m));
     for (var meta in annotations) {
+      _annotationsFound.putIfAbsent(
+          declaration, () => new Set<InstanceMirror>());
       _annotationsFound[declaration].add(meta);
 
       // Initialize super classes first, if they are in the same library,
@@ -150,14 +171,26 @@
         if (declaration.superclass.owner == declaration.owner) {
           _readAnnotations(declaration.superclass, queue);
         } else {
-          var superMetas = declaration.superclass.metadata
-              .where((m) => _filterMetadata(declaration.superclass, m))
+          // Make sure to check the canonical superclass declaration, the one
+          // we get here is not always that. Specifically, this occurs if all of
+          // the following conditions are met:
+          //
+          //   1. The current library is never loaded via a `package:` dart
+          //      import anywhere in the program.
+          //   2. The current library loads the superclass via a relative file
+          //      import.
+          //   3. The super class is imported via a `package:` import somewhere
+          //      else in the program.
+          var canonicalSuperDeclaration =
+              _canonicalClassDeclaration(declaration.superclass);
+          var superMetas = canonicalSuperDeclaration.metadata
+              .where((m) => _filterMetadata(canonicalSuperDeclaration, m))
               .toList();
           if (superMetas.isNotEmpty) {
             throw new UnsupportedError(
                 'We have detected a cycle in your import graph when running '
                 'initializers on ${declaration.qualifiedName}. This means the '
-                'super class ${declaration.superclass.qualifiedName} has a '
+                'super class ${canonicalSuperDeclaration.qualifiedName} has a '
                 'dependency on this library (possibly transitive).');
           }
         }
@@ -213,9 +246,7 @@
       return false;
     }
     if (customFilter != null && !customFilter(meta.reflectee)) return false;
-    if (!_annotationsFound.containsKey(declaration)) {
-      _annotationsFound[declaration] = new Set<InstanceMirror>();
-    }
+    if (!_annotationsFound.containsKey(declaration)) return true;
     if (_annotationsFound[declaration].contains(meta)) return false;
     return true;
   }
diff --git a/pubspec.yaml b/pubspec.yaml
index 2d0b048..a05326e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: initialize
-version: 0.6.0+2
+version: 0.6.0+3
 author: Polymer.dart Authors <web@dartlang.org>
 description: Generic building blocks for doing static initialization.
 homepage: https://github.com/dart-lang/initialize