change to source-order crawling of directives instead of alphabetical

R=sigmund@google.com

Review URL: https://codereview.chromium.org//1018643002
diff --git a/.status b/.status
index 1aea97f..5fc034b 100644
--- a/.status
+++ b/.status
@@ -21,6 +21,7 @@
 build/test/initializer_custom_filter_test: Skip
 build/test/initializer_cycle_error_test: Skip
 build/test/initializer_test: Skip
+build/test/initializer_parts_test: Skip
 build/test/initializer_type_filter_test: Skip
 build/test/init_method_test: Skip
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 583e378..0282c7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.5.1+7
+
+* Change to source order-crawling of directives instead of alphabetical. The one
+exception is for `part` directives, those are still crawled in alphabetical
+order since we can't currently get the original source order from the mirror
+system.
+
 ## 0.5.1+6
 
 * Fix some analyzer warnings.
diff --git a/lib/src/mirror_loader.dart b/lib/src/mirror_loader.dart
index ba49566..1caf144 100644
--- a/lib/src/mirror_loader.dart
+++ b/lib/src/mirror_loader.dart
@@ -98,7 +98,7 @@
     librariesSeen.add(lib);
 
     // First visit all our dependencies.
-    for (var dependency in _sortedLibraryDependencies(lib)) {
+    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;
@@ -110,7 +110,7 @@
     _readAnnotations(lib, queue);
 
     // Last, parse all class and method annotations.
-    for (var declaration in _sortedLibraryDeclarations(lib)) {
+    for (var declaration in _sortedDeclarationsWithMetadata(lib)) {
       _readAnnotations(declaration, queue);
       // Check classes for static annotations which are not supported
       if (declaration is ClassMirror) {
@@ -123,34 +123,37 @@
     return queue;
   }
 
-  Iterable<LibraryDependencyMirror> _sortedLibraryDependencies(
-      LibraryMirror lib) => new List.from(lib.libraryDependencies)
-    ..sort((a, b) {
-      var aScheme = a.targetLibrary.uri.scheme;
-      var bScheme = b.targetLibrary.uri.scheme;
-      if (aScheme != 'file' && bScheme == 'file') return -1;
-      if (bScheme != 'file' && aScheme == 'file') return 1;
-      return _relativeLibraryUri(a).compareTo(_relativeLibraryUri(b));
-    });
-
-  String _relativeLibraryUri(LibraryDependencyMirror lib) {
-    if (lib.targetLibrary.uri.scheme == 'file' &&
-        lib.sourceLibrary.uri.scheme == 'file') {
-      return path.relative(lib.targetLibrary.uri.path,
-          from: path.dirname(lib.sourceLibrary.uri.path));
-    }
-    return lib.targetLibrary.uri.toString();
+  Iterable<DeclarationMirror> _sortedDeclarationsWithMetadata(
+      LibraryMirror lib) {
+    return new List()
+      ..addAll(_sortDeclarations(
+          lib, lib.declarations.values.where(
+                  (d) => d is MethodMirror && d.metadata.isNotEmpty)))
+      ..addAll(_sortDeclarations(
+          lib, lib.declarations.values.where(
+                  (d) => d is ClassMirror && d.metadata.isNotEmpty)));
   }
 
-  Iterable<DeclarationMirror> _sortedLibraryDeclarations(LibraryMirror lib) =>
-      lib.declarations.values
-          .where((d) => d is ClassMirror || d is MethodMirror)
-          .toList()
-    ..sort((a, b) {
-      if (a is MethodMirror && b is ClassMirror) return -1;
-      if (a is ClassMirror && b is MethodMirror) return 1;
-      return _declarationName(a).compareTo(_declarationName(b));
+  List<DeclarationMirror> _sortDeclarations(
+      LibraryMirror sourceLib, Iterable<DeclarationMirror> declarations) {
+    var declarationList = declarations.toList();
+    declarationList.sort((DeclarationMirror a, DeclarationMirror b) {
+      // If in the same file, compare by line.
+      var aSourceUri = a.location.sourceUri;
+      var bSourceUri = b.location.sourceUri;
+      if (aSourceUri == bSourceUri) {
+        return a.location.line.compareTo(b.location.line);
+      }
+
+      // Run parts first if one is from the original library.
+      if (aSourceUri == sourceLib.uri) return 1;
+      if (bSourceUri == sourceLib.uri) return -1;
+
+      // Sort parts alphabetically.
+      return aSourceUri.path.compareTo(bSourceUri.path);
     });
+    return declarationList;
+  }
 
   String _declarationName(DeclarationMirror declaration) =>
       MirrorSystem.getName(declaration.qualifiedName);
diff --git a/lib/transformer.dart b/lib/transformer.dart
index 3d3a18c..8fec4b7 100644
--- a/lib/transformer.dart
+++ b/lib/transformer.dart
@@ -351,46 +351,53 @@
   /// [lib]. This includes exported methods from other libraries too.
   List<FunctionElement> _topLevelMethodsOfLibrary(
       LibraryElement library, Set<LibraryElement> seen) {
-    var result = [];
-    result.addAll(library.units.expand((u) => u.functions));
-    for (var export in library.exports) {
+    var methods = [];
+
+    var orderedExports = new List.from(library.exports)
+      ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
+    for (var export in orderedExports) {
       if (seen.contains(export.exportedLibrary)) continue;
-      var exported = _topLevelMethodsOfLibrary(export.exportedLibrary, seen);
-      _filter(exported, export.combinators);
-      result.addAll(exported);
+      methods.addAll(_topLevelMethodsOfLibrary(export.exportedLibrary, seen));
     }
-    result.sort((a, b) => a.name.compareTo(b.name));
-    return result;
+
+    for (CompilationUnitElement unit in _orderedUnits(library)) {
+      methods.addAll(new List.from(unit.functions)
+        ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
+    }
+
+    return methods;
   }
 
   /// Retrieves all classes that are visible if you were to import [lib]. This
   /// includes exported classes from other libraries.
   List<ClassElement> _classesOfLibrary(
       LibraryElement library, Set<LibraryElement> seen) {
-    var result = [];
-    result.addAll(library.units.expand((u) => u.types));
-    for (var export in library.exports) {
+    var classes = [];
+
+    var orderedExports = new List.from(library.exports)
+      ..sort((a, b) => a.uriOffset.compareTo(b.uriOffset));
+    for (var export in orderedExports) {
       if (seen.contains(export.exportedLibrary)) continue;
-      var exported = _classesOfLibrary(export.exportedLibrary, seen);
-      _filter(exported, export.combinators);
-      result.addAll(exported);
+      classes.addAll(_classesOfLibrary(export.exportedLibrary, seen));
     }
-    result.sort((a, b) => a.name.compareTo(b.name));
-    return result;
+
+    for (var unit in _orderedUnits(library)) {
+      classes.addAll(new List.from(unit.types)
+        ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset)));
+    }
+
+    return classes;
   }
 
-  /// Filters [elements] that come from an export, according to its show/hide
-  /// combinators. This modifies [elements] in place.
-  void _filter(List<Element> elements, List<NamespaceCombinator> combinators) {
-    for (var c in combinators) {
-      if (c is ShowElementCombinator) {
-        var show = c.shownNames.toSet();
-        elements.retainWhere((e) => show.contains(e.displayName));
-      } else if (c is HideElementCombinator) {
-        var hide = c.hiddenNames.toSet();
-        elements.removeWhere((e) => hide.contains(e.displayName));
-      }
-    }
+  List<CompilationUnitElement> _orderedUnits(LibraryElement library) {
+    var definingUnit = library.definingCompilationUnit;
+    // The first item is the source library, remove it for now.
+    return new List.from(library.units)
+      ..sort((a, b) {
+        if (a == definingUnit) return 1;
+        if (b == definingUnit) return -1;
+        return a.uri.compareTo(b.uri);
+      });
   }
 
   Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) {
@@ -402,32 +409,7 @@
 
     return (new List.from(library.imports)
       ..addAll(library.exports)
-      ..sort((a, b) {
-        // dart: imports don't have a uri
-        if (a.uri == null && b.uri != null) return -1;
-        if (b.uri == null && a.uri != null) return 1;
-        if (a.uri == null && b.uri == null) {
-          return getLibrary(a).name.compareTo(getLibrary(b).name);
-        }
-
-        // package: imports next
-        var aIsPackage = a.uri.startsWith('package:');
-        var bIsPackage = b.uri.startsWith('package:');
-        if (aIsPackage && !bIsPackage) {
-          return -1;
-        } else if (bIsPackage && !aIsPackage) {
-          return 1;
-        } else if (bIsPackage && aIsPackage) {
-          return a.uri.compareTo(b.uri);
-        }
-
-        // And finally compare based on the relative uri if both are file paths.
-        var aUri = path.url.relative(a.source.uri.path,
-            from: path.url.dirname(library.source.uri.path));
-        var bUri = path.url.relative(b.source.uri.path,
-            from: path.url.dirname(library.source.uri.path));
-        return aUri.compareTo(bUri);
-      })).map(getLibrary);
+      ..sort((a, b) => a.nameOffset.compareTo(b.nameOffset))).map(getLibrary);
   }
 }
 
diff --git a/pubspec.yaml b/pubspec.yaml
index b418049..c2dc506 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: initialize
-version: 0.5.1+6
+version: 0.5.1+7
 author: Polymer.dart Authors <web@dartlang.org>
 description: Generic building blocks for doing static initialization.
 homepage: https://github.com/dart-lang/initialize
@@ -25,6 +25,7 @@
     entry_points:
       - test/deferred_library_test.dart
       - test/initializer_test.dart
+      - test/initializer_parts_test.dart
       - test/initializer_cycle_error_test.dart
       - test/initializer_custom_filter_test.dart
       - test/initializer_type_filter_test.dart
diff --git a/test/foo/bar.dart b/test/foo/bar.dart
index 3889e35..ae9d27b 100644
--- a/test/foo/bar.dart
+++ b/test/foo/bar.dart
@@ -4,6 +4,7 @@
 @initializeTracker
 library initialize.test.foo.bar;
 
+export '../foo.dart';
 import '../foo.dart';
 import 'package:initialize/src/initialize_tracker.dart';
 
diff --git a/test/initializer_parts_test.dart b/test/initializer_parts_test.dart
new file mode 100644
index 0000000..bfba5e8
--- /dev/null
+++ b/test/initializer_parts_test.dart
@@ -0,0 +1,41 @@
+// 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 initialize.initializer_parts_test;
+
+import 'package:initialize/src/initialize_tracker.dart';
+import 'package:initialize/initialize.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/compact_vm_config.dart';
+
+part 'parts/foo.dart';
+part 'parts/bar.dart';
+
+main() {
+  useCompactVMConfiguration();
+
+  // Run all initializers.
+  run().then((_) {
+    test('parts', () {
+      var expectedNames = [
+        const LibraryIdentifier(#initialize.initializer_parts_test, null, 'initializer_parts_test.dart'),
+        bar2,
+        bar,
+        foo,
+        baz,
+        Bar2,
+        Bar,
+        Foo,
+        Baz,
+      ];
+      expect(InitializeTracker.seen, expectedNames);
+    });
+  });
+}
+
+@initializeTracker
+class Baz {}
+
+@initializeTracker
+baz() {}
diff --git a/test/initializer_test.dart b/test/initializer_test.dart
index 9b87a87..1488b1d 100644
--- a/test/initializer_test.dart
+++ b/test/initializer_test.dart
@@ -4,7 +4,6 @@
 @initializeTracker
 library initialize.initializer_test;
 
-import 'foo.dart';
 import 'foo/bar.dart';
 import 'package:initialize/src/initialize_tracker.dart';
 import 'package:initialize/initialize.dart';
@@ -19,20 +18,20 @@
   run().then((_) {
     test('annotations are seen in post-order with superclasses first', () {
       var expectedNames = [
-        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,
         Foo,
         const LibraryIdentifier(#initialize.test.foo.bar, null, 'foo/bar.dart'),
         bar,
         Bar,
+        const LibraryIdentifier(#test_package.bar, 'test_package', 'bar.dart'),
+        const LibraryIdentifier(#test_package.foo, 'test_package', 'foo.dart'),
         const LibraryIdentifier(
             #initialize.initializer_test, null, 'initializer_test.dart'),
         zap,
         Zoop, // Zap extends Zoop, so Zoop comes first.
-        Zap
+        Zap,
       ];
       expect(InitializeTracker.seen, expectedNames);
     });
diff --git a/test/parts/bar.dart b/test/parts/bar.dart
new file mode 100644
index 0000000..4aa5f5c
--- /dev/null
+++ b/test/parts/bar.dart
@@ -0,0 +1,16 @@
+// 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.
+part of initialize.initializer_parts_test;
+
+@initializeTracker
+class Bar2 {}
+
+@initializeTracker
+class Bar {}
+
+@initializeTracker
+bar2() {}
+
+@initializeTracker
+bar() {}
diff --git a/test/parts/foo.dart b/test/parts/foo.dart
new file mode 100644
index 0000000..2c0528f
--- /dev/null
+++ b/test/parts/foo.dart
@@ -0,0 +1,10 @@
+// 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.
+part of initialize.initializer_parts_test;
+
+@initializeTracker
+class Foo {}
+
+@initializeTracker
+foo() {}
diff --git a/test/transformer_test.dart b/test/transformer_test.dart
index e6dda99..8b43d45 100644
--- a/test/transformer_test.dart
+++ b/test/transformer_test.dart
@@ -280,6 +280,79 @@
         }
         ''')
   }, []);
+
+  testPhases('library parts and exports', phases, {
+    'a|web/index.dart': '''
+        @constInit
+        library index;
+
+        import 'package:test_initializers/common.dart';
+        export 'export.dart';
+
+        part 'foo.dart';
+        part 'bar.dart';
+
+        @constInit
+        index() {};
+
+        @constInit
+        class Index {};
+        ''',
+    'a|web/foo.dart': '''
+        part of index;
+
+        @constInit
+        foo() {};
+
+        @constInit
+        class Foo {};
+        ''',
+    'a|web/bar.dart': '''
+        part of index;
+
+        @constInit
+        bar() {};
+
+        @constInit
+        class Bar {};
+        ''',
+    'a|web/export.dart': '''
+        @constInit
+        library export;
+
+        import 'package:test_initializers/common.dart';
+
+        @constInit
+        class Export {};
+        ''',
+    // Mock out the Initialize package plus some initializers.
+    'initialize|lib/initialize.dart': mockInitialize,
+    'test_initializers|lib/common.dart': commonInitializers,
+  }, {
+    'a|web/index.initialize.dart': formatter.format('''
+        import 'package:initialize/src/static_loader.dart';
+        import 'package:initialize/initialize.dart';
+        import 'index.dart' as i0;
+        import 'export.dart' as i1;
+        import 'package:test_initializers/common.dart' as i2;
+
+        main() {
+          initializers.addAll([
+            new InitEntry(i2.constInit, const LibraryIdentifier(#export, null, 'export.dart')),
+            new InitEntry(i2.constInit, i1.Export),
+            new InitEntry(i2.constInit, const LibraryIdentifier(#index, null, 'index.dart')),
+            new InitEntry(i2.constInit, i0.bar),
+            new InitEntry(i2.constInit, i0.foo),
+            new InitEntry(i2.constInit, i0.index),
+            new InitEntry(i2.constInit, i0.Bar),
+            new InitEntry(i2.constInit, i0.Foo),
+            new InitEntry(i2.constInit, i0.Index),
+          ]);
+
+          i0.main();
+        }
+        ''')
+  }, []);
 }
 
 class SkipConstructorsPlugin extends InitializerPlugin {
diff --git a/tool/all_tests.sh b/tool/all_tests.sh
index 4c0bb39..13ed73c 100755
--- a/tool/all_tests.sh
+++ b/tool/all_tests.sh
@@ -7,12 +7,24 @@
 # Fast fail the script on failures.
 set -e
 
-# Run the command-line tests.
+# Run the un-transformed command-line tests.
 # TODO(jakemac): Add back once http://dartbug.com/22592 is fixed.
 # dart test/deferred_library_test.dart
 dart test/init_method_test.dart
 dart test/initializer_custom_filter_test.dart
 dart test/initializer_cycle_error_test.dart
 dart test/initializer_test.dart
+dart test/initializer_parts_test.dart
 dart test/initializer_type_filter_test.dart
 dart test/transformer_test.dart
+
+pub build test --mode=debug
+
+# Run the transformed command-line tests.
+# TODO(jakemac): Add back once initialize supports deferred libraries.
+# dart test/deferred_library_test.dart
+dart build/test/init_method_test.initialize.dart
+dart build/test/initializer_custom_filter_test.initialize.dart
+dart build/test/initializer_test.initialize.dart
+dart build/test/initializer_parts_test.initialize.dart
+dart build/test/initializer_type_filter_test.initialize.dart