Index: initialize/lib/src/mirror_loader.dart |
diff --git a/initialize/lib/src/mirror_loader.dart b/initialize/lib/src/mirror_loader.dart |
deleted file mode 100644 |
index 4ca2004f4aedf172ea0962f84e96df097afbf008..0000000000000000000000000000000000000000 |
--- a/initialize/lib/src/mirror_loader.dart |
+++ /dev/null |
@@ -1,261 +0,0 @@ |
-// 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. |
-library initialize.mirror_loader; |
- |
-import 'dart:collection' show Queue; |
-import 'dart:mirrors'; |
-import 'package:path/path.dart' as path; |
-import 'package:initialize/initialize.dart'; |
- |
-final _root = currentMirrorSystem().isolate.rootLibrary; |
-final _libs = currentMirrorSystem().libraries; |
- |
-Queue<Function> loadInitializers( |
- {List<Type> typeFilter, InitializerFilter customFilter, Uri from}) { |
- return new InitializationCrawler(typeFilter, customFilter, from: from).run(); |
-} |
- |
-// Crawls a library and all its dependencies for `Initializer` annotations using |
-// mirrors |
-class InitializationCrawler { |
- // Set of all visited annotations, keys are the declarations that were |
- // annotated, values are the annotations that have been processed. |
- static final _annotationsFound = |
- new Map<DeclarationMirror, Set<InstanceMirror>>(); |
- |
- // If non-null, then only these annotations should be processed. |
- final List<Type> typeFilter; |
- |
- // If non-null, then only annotations which return true when passed to this |
- // function will be processed. |
- final InitializerFilter customFilter; |
- |
- /// The library to start crawling from. |
- final LibraryMirror _rootLibrary; |
- |
- /// Note: The [from] argument is only supported in the mirror_loader.dart. It |
- /// is not supported statically. |
- InitializationCrawler(this.typeFilter, this.customFilter, {Uri from}) |
- : _rootLibrary = from == null |
- ? _root |
- : _libs[from] { |
- if (_rootLibrary == null) throw 'Unable to find library at $from.'; |
- } |
- |
- // The primary function in this class, invoke it to crawl and collect all the |
- // annotations into a queue of init functions. |
- Queue<Function> run() { |
- var librariesSeen = new Set<LibraryMirror>(); |
- var queue = new Queue<Function>(); |
- var libraries = currentMirrorSystem().libraries; |
- |
- _readLibraryDeclarations(_rootLibrary, librariesSeen, queue); |
- 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) { |
- 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/')); |
- } |
- |
- /// 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'); |
- } |
- |
- // Reads Initializer annotations on this library and all its dependencies in |
- // 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. |
- var targetLibrary = dependency.targetLibrary; |
- if (targetLibrary == null || targetLibrary.uri.scheme == 'dart') continue; |
- _readLibraryDeclarations(dependency.targetLibrary, librariesSeen, queue); |
- } |
- |
- // Second parse the library directive annotations. |
- _readAnnotations(lib, queue); |
- |
- // Last, parse all class and method annotations. |
- for (var declaration in _sortedDeclarationsWithMetadata(lib)) { |
- _readAnnotations(declaration, queue); |
- // Check classes for static annotations which are not supported |
- if (declaration is ClassMirror) { |
- for (var classDeclaration in declaration.declarations.values) { |
- _readAnnotations(classDeclaration, queue); |
- } |
- } |
- } |
- |
- return queue; |
- } |
- |
- 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))); |
- } |
- |
- 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); |
- |
- /// 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, |
- // otherwise we throw an error. This can only be the case if there are |
- // cycles in the imports. |
- if (declaration is ClassMirror && declaration.superclass != null) { |
- if (declaration.superclass.owner == declaration.owner) { |
- _readAnnotations(declaration.superclass, queue); |
- } else { |
- // 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 ${canonicalSuperDeclaration.qualifiedName} has a ' |
- 'dependency on this library (possibly transitive).'); |
- } |
- } |
- } |
- |
- var annotatedValue; |
- if (declaration is ClassMirror) { |
- annotatedValue = declaration.reflectedType; |
- } else if (declaration is MethodMirror) { |
- if (declaration.owner is! LibraryMirror) { |
- // TODO(jakemac): Support static class methods. |
- throw _TOP_LEVEL_FUNCTIONS_ONLY; |
- } |
- annotatedValue = (declaration.owner as ObjectMirror) |
- .getField(declaration.simpleName).reflectee; |
- } else if (declaration is LibraryMirror) { |
- 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: _root.uri.path.endsWith('/') |
- ? _root.uri.path |
- : 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; |
- } |
- queue.addLast(() => meta.reflectee.initialize(annotatedValue)); |
- } |
- } |
- |
- // Filter function that returns true only if `meta` is an `Initializer`, |
- // it passes the `typeFilter` and `customFilter` if they exist, and it has not |
- // yet been seen. |
- bool _filterMetadata(DeclarationMirror declaration, InstanceMirror meta) { |
- if (meta.reflectee is! Initializer) return false; |
- if (typeFilter != null && |
- !typeFilter.any((t) => meta.reflectee.runtimeType == t)) { |
- return false; |
- } |
- if (customFilter != null && !customFilter(meta.reflectee)) return false; |
- if (!_annotationsFound.containsKey(declaration)) return true; |
- if (_annotationsFound[declaration].contains(meta)) return false; |
- return true; |
- } |
-} |
- |
-final _TOP_LEVEL_FUNCTIONS_ONLY = new UnsupportedError( |
- 'Only top level methods are supported for initializers'); |
- |
-final _UNSUPPORTED_DECLARATION = new UnsupportedError( |
- 'Initializers are only supported on libraries, classes, and top level ' |
- 'methods'); |