Chromium Code Reviews

Side by Side Diff: initialize/lib/src/mirror_loader.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « initialize/lib/src/initializer.dart ('k') | initialize/lib/src/static_loader.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 library initialize.mirror_loader;
5
6 import 'dart:collection' show Queue;
7 import 'dart:mirrors';
8 import 'package:path/path.dart' as path;
9 import 'package:initialize/initialize.dart';
10
11 final _root = currentMirrorSystem().isolate.rootLibrary;
12 final _libs = currentMirrorSystem().libraries;
13
14 Queue<Function> loadInitializers(
15 {List<Type> typeFilter, InitializerFilter customFilter, Uri from}) {
16 return new InitializationCrawler(typeFilter, customFilter, from: from).run();
17 }
18
19 // Crawls a library and all its dependencies for `Initializer` annotations using
20 // mirrors
21 class InitializationCrawler {
22 // Set of all visited annotations, keys are the declarations that were
23 // annotated, values are the annotations that have been processed.
24 static final _annotationsFound =
25 new Map<DeclarationMirror, Set<InstanceMirror>>();
26
27 // If non-null, then only these annotations should be processed.
28 final List<Type> typeFilter;
29
30 // If non-null, then only annotations which return true when passed to this
31 // function will be processed.
32 final InitializerFilter customFilter;
33
34 /// The library to start crawling from.
35 final LibraryMirror _rootLibrary;
36
37 /// Note: The [from] argument is only supported in the mirror_loader.dart. It
38 /// is not supported statically.
39 InitializationCrawler(this.typeFilter, this.customFilter, {Uri from})
40 : _rootLibrary = from == null
41 ? _root
42 : _libs[from] {
43 if (_rootLibrary == null) throw 'Unable to find library at $from.';
44 }
45
46 // The primary function in this class, invoke it to crawl and collect all the
47 // annotations into a queue of init functions.
48 Queue<Function> run() {
49 var librariesSeen = new Set<LibraryMirror>();
50 var queue = new Queue<Function>();
51 var libraries = currentMirrorSystem().libraries;
52
53 _readLibraryDeclarations(_rootLibrary, librariesSeen, queue);
54 return queue;
55 }
56
57 /// Returns the canonical [LibraryMirror] for a given [LibraryMirror]. This
58 /// is defined as the one loaded from a `package:` url if available, otherwise
59 /// it is just [lib].
60 LibraryMirror _canonicalLib(LibraryMirror lib) {
61 var uri = lib.uri;
62 if (_isHttpStylePackageUrl(uri)) {
63 var packageUri = _packageUriFor(uri);
64 if (_libs.containsKey(packageUri)) return _libs[packageUri];
65 }
66 return lib;
67 }
68
69 /// Returns the canonical [ClassMirror] for a given [ClassMirror]. This is
70 /// defined as the one that appears in the canonical owner [LibararyMirror].
71 ClassMirror _canonicalClassDeclaration(ClassMirror declaration) =>
72 _canonicalLib(declaration.owner).declarations[declaration.simpleName];
73
74 /// Whether [uri] is an http URI that contains a 'packages' segment, and
75 /// therefore could be converted into a 'package:' URI.
76 bool _isHttpStylePackageUrl(Uri uri) {
77 var uriPath = uri.path;
78 return uri.scheme == _root.uri.scheme &&
79 // Don't process cross-domain uris.
80 uri.authority == _root.uri.authority &&
81 uriPath.endsWith('.dart') &&
82 (uriPath.contains('/packages/') || uriPath.startsWith('packages/'));
83 }
84
85 /// Returns a `package:` version of [uri].
86 Uri _packageUriFor(Uri uri) {
87 var packagePath = uri.path
88 .substring(uri.path.lastIndexOf('packages/') + 'packages/'.length);
89 return Uri.parse('package:$packagePath');
90 }
91
92 // Reads Initializer annotations on this library and all its dependencies in
93 // post-order.
94 Queue<Function> _readLibraryDeclarations(LibraryMirror lib,
95 Set<LibraryMirror> librariesSeen, Queue<Function> queue) {
96 lib = _canonicalLib(lib);
97 if (librariesSeen.contains(lib)) return queue;
98 librariesSeen.add(lib);
99
100 // First visit all our dependencies.
101 for (var dependency in lib.libraryDependencies) {
102 // Skip dart: imports, they never use this package.
103 var targetLibrary = dependency.targetLibrary;
104 if (targetLibrary == null || targetLibrary.uri.scheme == 'dart') continue;
105 _readLibraryDeclarations(dependency.targetLibrary, librariesSeen, queue);
106 }
107
108 // Second parse the library directive annotations.
109 _readAnnotations(lib, queue);
110
111 // Last, parse all class and method annotations.
112 for (var declaration in _sortedDeclarationsWithMetadata(lib)) {
113 _readAnnotations(declaration, queue);
114 // Check classes for static annotations which are not supported
115 if (declaration is ClassMirror) {
116 for (var classDeclaration in declaration.declarations.values) {
117 _readAnnotations(classDeclaration, queue);
118 }
119 }
120 }
121
122 return queue;
123 }
124
125 Iterable<DeclarationMirror> _sortedDeclarationsWithMetadata(
126 LibraryMirror lib) {
127 return new List()
128 ..addAll(_sortDeclarations(lib, lib.declarations.values
129 .where((d) => d is MethodMirror && d.metadata.isNotEmpty)))
130 ..addAll(_sortDeclarations(lib, lib.declarations.values
131 .where((d) => d is ClassMirror && d.metadata.isNotEmpty)));
132 }
133
134 List<DeclarationMirror> _sortDeclarations(
135 LibraryMirror sourceLib, Iterable<DeclarationMirror> declarations) {
136 var declarationList = declarations.toList();
137 declarationList.sort((DeclarationMirror a, DeclarationMirror b) {
138 // If in the same file, compare by line.
139 var aSourceUri = a.location.sourceUri;
140 var bSourceUri = b.location.sourceUri;
141 if (aSourceUri == bSourceUri) {
142 return a.location.line.compareTo(b.location.line);
143 }
144
145 // Run parts first if one is from the original library.
146 if (aSourceUri == sourceLib.uri) return 1;
147 if (bSourceUri == sourceLib.uri) return -1;
148
149 // Sort parts alphabetically.
150 return aSourceUri.path.compareTo(bSourceUri.path);
151 });
152 return declarationList;
153 }
154
155 String _declarationName(DeclarationMirror declaration) =>
156 MirrorSystem.getName(declaration.qualifiedName);
157
158 /// Reads annotations on a [DeclarationMirror] and adds them to [_initQueue]
159 /// if they are [Initializer]s.
160 void _readAnnotations(DeclarationMirror declaration, Queue<Function> queue) {
161 var annotations =
162 declaration.metadata.where((m) => _filterMetadata(declaration, m));
163 for (var meta in annotations) {
164 _annotationsFound.putIfAbsent(
165 declaration, () => new Set<InstanceMirror>());
166 _annotationsFound[declaration].add(meta);
167
168 // Initialize super classes first, if they are in the same library,
169 // otherwise we throw an error. This can only be the case if there are
170 // cycles in the imports.
171 if (declaration is ClassMirror && declaration.superclass != null) {
172 if (declaration.superclass.owner == declaration.owner) {
173 _readAnnotations(declaration.superclass, queue);
174 } else {
175 // Make sure to check the canonical superclass declaration, the one
176 // we get here is not always that. Specifically, this occurs if all of
177 // the following conditions are met:
178 //
179 // 1. The current library is never loaded via a `package:` dart
180 // import anywhere in the program.
181 // 2. The current library loads the superclass via a relative file
182 // import.
183 // 3. The super class is imported via a `package:` import somewhere
184 // else in the program.
185 var canonicalSuperDeclaration =
186 _canonicalClassDeclaration(declaration.superclass);
187 var superMetas = canonicalSuperDeclaration.metadata
188 .where((m) => _filterMetadata(canonicalSuperDeclaration, m))
189 .toList();
190 if (superMetas.isNotEmpty) {
191 throw new UnsupportedError(
192 'We have detected a cycle in your import graph when running '
193 'initializers on ${declaration.qualifiedName}. This means the '
194 'super class ${canonicalSuperDeclaration.qualifiedName} has a '
195 'dependency on this library (possibly transitive).');
196 }
197 }
198 }
199
200 var annotatedValue;
201 if (declaration is ClassMirror) {
202 annotatedValue = declaration.reflectedType;
203 } else if (declaration is MethodMirror) {
204 if (declaration.owner is! LibraryMirror) {
205 // TODO(jakemac): Support static class methods.
206 throw _TOP_LEVEL_FUNCTIONS_ONLY;
207 }
208 annotatedValue = (declaration.owner as ObjectMirror)
209 .getField(declaration.simpleName).reflectee;
210 } else if (declaration is LibraryMirror) {
211 var package;
212 var filePath;
213 Uri uri = declaration.uri;
214 // Convert to a package style uri if possible.
215 if (_isHttpStylePackageUrl(uri)) {
216 uri = _packageUriFor(uri);
217 }
218 if (uri.scheme == 'file' || uri.scheme.startsWith('http')) {
219 filePath = path.url.relative(uri.path,
220 from: _root.uri.path.endsWith('/')
221 ? _root.uri.path
222 : path.url.dirname(_root.uri.path));
223 } else if (uri.scheme == 'package') {
224 var segments = uri.pathSegments;
225 package = segments[0];
226 filePath = path.url.joinAll(segments.getRange(1, segments.length));
227 } else {
228 throw new UnsupportedError('Unsupported uri scheme ${uri.scheme} for '
229 'library ${declaration}.');
230 }
231 annotatedValue =
232 new LibraryIdentifier(declaration.qualifiedName, package, filePath);
233 } else {
234 throw _UNSUPPORTED_DECLARATION;
235 }
236 queue.addLast(() => meta.reflectee.initialize(annotatedValue));
237 }
238 }
239
240 // Filter function that returns true only if `meta` is an `Initializer`,
241 // it passes the `typeFilter` and `customFilter` if they exist, and it has not
242 // yet been seen.
243 bool _filterMetadata(DeclarationMirror declaration, InstanceMirror meta) {
244 if (meta.reflectee is! Initializer) return false;
245 if (typeFilter != null &&
246 !typeFilter.any((t) => meta.reflectee.runtimeType == t)) {
247 return false;
248 }
249 if (customFilter != null && !customFilter(meta.reflectee)) return false;
250 if (!_annotationsFound.containsKey(declaration)) return true;
251 if (_annotationsFound[declaration].contains(meta)) return false;
252 return true;
253 }
254 }
255
256 final _TOP_LEVEL_FUNCTIONS_ONLY = new UnsupportedError(
257 'Only top level methods are supported for initializers');
258
259 final _UNSUPPORTED_DECLARATION = new UnsupportedError(
260 'Initializers are only supported on libraries, classes, and top level '
261 'methods');
OLDNEW
« no previous file with comments | « initialize/lib/src/initializer.dart ('k') | initialize/lib/src/static_loader.dart » ('j') | no next file with comments »

Powered by Google App Engine