- Fix a few issues around exported libraries
- Add support for html imports without adding an html dependency by running from all libraries in the mirror system in reverse order instead of just the root one.
R=sigmund@google.com
Review URL: https://codereview.chromium.org//973073003
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 568e5af..0375a06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 0.5.1+2
+
+* Fix handling of exported libraries. Specifically, annotations on exported
+libraries will now be reached and imports in exported libraries will now be
+reached.
+* Add support for scripts living in html imports without adding an html
+dependency by crawling all libraries in the mirror system in reverse order,
+instead of just the root one.
+
## 0.5.1+1
* Make sure to always use `path.url` in the transformer.
diff --git a/lib/src/mirror_loader.dart b/lib/src/mirror_loader.dart
index 92a00f8..e73f0cc 100644
--- a/lib/src/mirror_loader.dart
+++ b/lib/src/mirror_loader.dart
@@ -8,6 +8,8 @@
import 'package:path/path.dart' as path;
import 'package:initialize/initialize.dart';
+final _root = currentMirrorSystem().isolate.rootLibrary;
+
Queue<Function> loadInitializers(
{List<Type> typeFilter, InitializerFilter customFilter}) {
return new InitializationCrawler(typeFilter, customFilter).run();
@@ -28,24 +30,60 @@
// function will be processed.
final InitializerFilter customFilter;
- // The root library that we start parsing from.
- LibraryMirror _root;
-
- InitializationCrawler(this.typeFilter, this.customFilter,
- {LibraryMirror root}) {
- _root = root == null ? currentMirrorSystem().isolate.rootLibrary : root;
- }
+ InitializationCrawler(this.typeFilter, this.customFilter);
// The primary function in this class, invoke it to crawl and collect all the
// annotations into a queue of init functions.
- Queue<Function> run() => _readLibraryDeclarations(_root);
+ Queue<Function> run() {
+ var librariesSeen = new Set<LibraryMirror>();
+ var queue = new Queue<Function>();
+
+ var libraries = currentMirrorSystem().libraries;
+ var nonDartOrPackageImports = new List.from(libraries.keys.where(
+ (uri) => uri.scheme != 'package' && uri.scheme != 'dart'));
+
+ for (var import in nonDartOrPackageImports.reversed) {
+ // Always load the package: version of a library if available.
+ var libToRun;
+ if (_isHttpStylePackageUrl(import)) {
+ var packageUri = _packageUriFor(import);
+ libToRun = libraries[packageUri];
+ }
+ if (libToRun == null) libToRun = libraries[import];
+
+ // Dartium creates an extra trampoline lib that loads the main dart script
+ // and breaks our ordering.
+ if (librariesSeen.contains(libToRun) ||
+ libToRun.uri.path.endsWith('\$trampoline')) {
+ continue;
+ }
+ _readLibraryDeclarations(libToRun, librariesSeen, queue);
+ }
+
+ return queue;
+ }
+
+ /// Whether [uri] is an http URI that contains a 'packages' segment, and
+ /// therefore could be converted into a 'package:' URI.
+ bool _isHttpStylePackageUrl(Uri uri) {
+ var uriPath = uri.path;
+ return uri.scheme == _root.uri.scheme &&
+ // Don't process cross-domain uris.
+ uri.authority == _root.uri.authority &&
+ uriPath.endsWith('.dart') &&
+ (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
+ }
+
+ Uri _packageUriFor(Uri httpUri) {
+ var packagePath = httpUri.path.substring(
+ httpUri.path.lastIndexOf('packages/') + 'packages/'.length);
+ return Uri.parse('package:$packagePath');
+ }
// Reads Initializer annotations on this library and all its dependencies in
// post-order.
Queue<Function> _readLibraryDeclarations(LibraryMirror lib,
- [Set<LibraryMirror> librariesSeen, Queue<Function> queue]) {
- if (librariesSeen == null) librariesSeen = new Set<LibraryMirror>();
- if (queue == null) queue = new Queue<Function>();
+ Set<LibraryMirror> librariesSeen, Queue<Function> queue) {
librariesSeen.add(lib);
// First visit all our dependencies.
@@ -148,6 +186,10 @@
var package;
var filePath;
Uri uri = declaration.uri;
+ // Convert to a package style uri if possible.
+ if (_isHttpStylePackageUrl(uri)) {
+ uri = _packageUriFor(uri);
+ }
if (uri.scheme == 'file' || uri.scheme.startsWith('http')) {
filePath = path.url.relative(uri.path,
from: path.url.dirname(_root.uri.path));
diff --git a/lib/transformer.dart b/lib/transformer.dart
index 90faf5d..903abfa 100644
--- a/lib/transformer.dart
+++ b/lib/transformer.dart
@@ -239,11 +239,11 @@
seen.add(library);
// Visit all our dependencies.
- for (var importedLibrary in _sortedLibraryImports(library)) {
+ for (var library in _sortedLibraryDependencies(library)) {
// Don't include anything from the sdk.
- if (importedLibrary.isInSdk) continue;
- if (seen.contains(importedLibrary)) continue;
- _readLibraries(importedLibrary, seen);
+ if (library.isInSdk) continue;
+ if (seen.contains(library)) continue;
+ _readLibraries(library, seen);
}
// Read annotations in this order: library, top level methods, classes.
@@ -415,14 +415,21 @@
}
}
- Iterable<LibraryElement> _sortedLibraryImports(LibraryElement library) =>
- (new List.from(library.imports)
- ..sort((ImportElement a, ImportElement b) {
+ Iterable<LibraryElement> _sortedLibraryDependencies(LibraryElement library) {
+ // TODO(jakemac): Investigate supporting annotations on part-of directives.
+ getLibrary(UriReferencedElement element) {
+ if (element is ImportElement) return element.importedLibrary;
+ if (element is ExportElement) return element.exportedLibrary;
+ }
+
+ 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 a.importedLibrary.name.compareTo(b.importedLibrary.name);
+ return getLibrary(a).name.compareTo(getLibrary(b).name);
}
// package: imports next
@@ -442,7 +449,8 @@
var bUri = path.url.relative(b.source.uri.path,
from: path.url.dirname(library.source.uri.path));
return aUri.compareTo(bUri);
- })).map((import) => import.importedLibrary);
+ })).map(getLibrary);
+ }
}
/// An [Initializer] annotation and the target of that annotation.
diff --git a/pubspec.yaml b/pubspec.yaml
index 8305682..9e1cf9d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: initialize
-version: 0.5.1+1
+version: 0.5.1+2
author: Polymer.dart Authors <web@dartlang.org>
description: Generic building blocks for doing static initialization.
homepage: https://github.com/dart-lang/initialize
diff --git a/test/transformer_test.dart b/test/transformer_test.dart
index e90eefc..dea1545 100644
--- a/test/transformer_test.dart
+++ b/test/transformer_test.dart
@@ -193,6 +193,93 @@
}
''')
}, []);
+
+ testPhases('exported library annotations', phases, {
+ 'a|web/index.dart': '''
+ library web_foo;
+
+ export 'foo.dart';
+ ''',
+ 'a|web/foo.dart': '''
+ @constInit
+ library foo;
+
+ import 'package:test_initializers/common.dart';
+
+ @constInit
+ foo() {};
+
+ @constInit
+ class Foo {}
+ ''',
+ // 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 'foo.dart' as i1;
+ import 'package:test_initializers/common.dart' as i2;
+
+ main() {
+ initializers.addAll([
+ new InitEntry(i2.constInit, const LibraryIdentifier(#foo, null, 'foo.dart')),
+ new InitEntry(i2.constInit, i1.foo),
+ new InitEntry(i2.constInit, i1.Foo),
+ ]);
+
+ i0.main();
+ }
+ ''')
+ }, []);
+
+ testPhases('imports from exported libraries', phases, {
+ 'a|web/index.dart': '''
+ library web_foo;
+
+ export 'foo.dart';
+ ''',
+ 'a|web/foo.dart': '''
+ library foo;
+
+ import 'bar.dart';
+ ''',
+ 'a|web/bar.dart': '''
+ @constInit
+ library bar;
+
+ import 'package:test_initializers/common.dart';
+
+ @constInit
+ bar() {};
+
+ @constInit
+ class Bar {}
+ ''',
+ // 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 'bar.dart' as i1;
+ import 'package:test_initializers/common.dart' as i2;
+
+ main() {
+ initializers.addAll([
+ new InitEntry(i2.constInit, const LibraryIdentifier(#bar, null, 'bar.dart')),
+ new InitEntry(i2.constInit, i1.bar),
+ new InitEntry(i2.constInit, i1.Bar),
+ ]);
+
+ i0.main();
+ }
+ ''')
+ }, []);
}
class SkipConstructorsPlugin extends InitializerPlugin {