Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: third_party/pkg/di/lib/transformer/injector_generator.dart

Issue 257423008: Update all Angular libs (run update_all.sh). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 library di.transformer.injector_generator;
2
3 import 'dart:async';
4 import 'package:analyzer/src/generated/ast.dart';
5 import 'package:analyzer/src/generated/element.dart';
6 import 'package:barback/barback.dart';
7 import 'package:code_transformers/resolver.dart';
8 import 'package:di/transformer/options.dart';
9 import 'package:path/path.dart' as path;
10
11 import 'refactor.dart';
12
13 /**
14 * Pub transformer which generates type factories for all injectable types
15 * in the application.
16 */
17 class InjectorGenerator extends Transformer with ResolverTransformer {
18 final TransformOptions options;
19
20 InjectorGenerator(this.options, Resolvers resolvers) {
21 this.resolvers = resolvers;
22 }
23
24 Future<bool> shouldApplyResolver(Asset asset) => options.isDartEntry(asset);
25
26 applyResolver(Transform transform, Resolver resolver) =>
27 new _Processor(transform, resolver, options).process();
28 }
29
30 /** Class for processing a single apply.*/
31 class _Processor {
32
33 /** Current transform. */
34 final Transform transform;
35
36 final Resolver resolver;
37 final TransformOptions options;
38
39 /** Asset ID for the location of the generated file, for imports. */
40 AssetId _generatedAssetId;
41
42 /** Resolved injectable annotations of the form `@Injectable()`. */
43 final List<TopLevelVariableElement> injectableMetaConsts =
44 <TopLevelVariableElement>[];
45
46 /** Resolved injectable annotations of the form `@injectable`. */
47 final List<ConstructorElement> injectableMetaConstructors =
48 <ConstructorElement>[];
49
50 /** Default list of injectable consts */
51 static const List<String> defaultInjectableMetaConsts = const [
52 'inject.inject'
53 ];
54
55 _Processor(this.transform, this.resolver, this.options);
56
57 TransformLogger get logger => transform.logger;
58
59 process() {
60 _resolveInjectableMetadata();
61
62 var id = transform.primaryInput.id;
63 var outputFilename = '${path.url.basenameWithoutExtension(id.path)}'
64 '_static_injector.dart';
65 var outputPath = path.url.join(path.url.dirname(id.path), outputFilename);
66 _generatedAssetId = new AssetId(id.package, outputPath);
67
68 var constructors = _gatherConstructors();
69
70 var injectLibContents = _generateInjectLibrary(constructors);
71 transform.addOutput(
72 new Asset.fromString(_generatedAssetId, injectLibContents));
73
74 transformIdentifiers(transform, resolver,
75 identifier: 'di.auto_injector.defaultInjector',
76 replacement: 'createStaticInjector',
77 importPrefix: 'generated_static_injector',
78 importUrl: outputFilename);
79 }
80
81 /** Resolves the classes for the injectable annotations in the current AST. */
82 void _resolveInjectableMetadata() {
83 for (var constName in defaultInjectableMetaConsts) {
84 var variable = resolver.getLibraryVariable(constName);
85 if (variable != null) {
86 injectableMetaConsts.add(variable);
87 }
88 }
89
90 // Resolve the user-specified annotations
91 // These may be either type names (constructors) or consts.
92 for (var metaName in options.injectableAnnotations) {
93 var variable = resolver.getLibraryVariable(metaName);
94 if (variable != null) {
95 injectableMetaConsts.add(variable);
96 continue;
97 }
98 var cls = resolver.getType(metaName);
99 if (cls != null && cls.unnamedConstructor != null) {
100 injectableMetaConstructors.add(cls.unnamedConstructor);
101 continue;
102 }
103 if (!DEFAULT_INJECTABLE_ANNOTATIONS.contains(metaName)) {
104 logger.warning('Unable to resolve injectable annotation $metaName');
105 }
106 }
107 }
108
109 /** Finds all annotated constructors or annotated classes in the program. */
110 Iterable<ConstructorElement> _gatherConstructors() {
111 var constructors = resolver.libraries
112 .expand((lib) => lib.units)
113 .expand((compilationUnit) => compilationUnit.types)
114 .map(_findInjectedConstructor)
115 .where((ctor) => ctor != null).toList();
116
117 constructors.addAll(_gatherInjectablesContents());
118 constructors.addAll(_gatherManuallyInjected());
119
120 return constructors.toSet();
121 }
122
123 /**
124 * Get the constructors for all elements in the library @Injectables
125 * statements. These are used to mark types as injectable which would
126 * otherwise not be injected.
127 *
128 * Syntax is:
129 *
130 * @Injectables(const[ElementName])
131 * library my.library;
132 */
133 Iterable<ConstructorElement> _gatherInjectablesContents() {
134 var injectablesClass = resolver.getType('di.annotations.Injectables');
135 if (injectablesClass == null) return const [];
136 var injectablesCtor = injectablesClass.unnamedConstructor;
137
138 var ctors = [];
139
140 for (var lib in resolver.libraries) {
141 var annotationIdx = 0;
142 for (var annotation in lib.metadata) {
143 if (annotation.element == injectablesCtor) {
144 var libDirective = lib.definingCompilationUnit.node.directives
145 .where((d) => d is LibraryDirective).single;
146 var annotationDirective = libDirective.metadata[annotationIdx];
147 var listLiteral = annotationDirective.arguments.arguments.first;
148
149 for (var expr in listLiteral.elements) {
150 var element = (expr as SimpleIdentifier).bestElement;
151 if (element == null || element is! ClassElement) {
152 _warn('Unable to resolve class $expr', element);
153 continue;
154 }
155 var ctor = _findInjectedConstructor(element, true);
156 if (ctor != null) {
157 ctors.add(ctor);
158 }
159 }
160 }
161 }
162 }
163 return ctors;
164 }
165
166 /**
167 * Finds all types which were manually specified as being injected in
168 * the options file.
169 */
170 Iterable<ConstructorElement> _gatherManuallyInjected() {
171 var ctors = [];
172 for (var injectedName in options.injectedTypes) {
173 var injectedClass = resolver.getType(injectedName);
174 if (injectedClass == null) {
175 logger.warning('Unable to resolve injected type name $injectedName');
176 continue;
177 }
178 var ctor = _findInjectedConstructor(injectedClass, true);
179 if (ctor != null) {
180 ctors.add(ctor);
181 }
182 }
183 return ctors;
184 }
185
186 /**
187 * Checks if the element is annotated with one of the known injectablee
188 * annotations.
189 */
190 bool _isElementAnnotated(Element e) {
191 for (var meta in e.metadata) {
192 if (meta.element is PropertyAccessorElement &&
193 injectableMetaConsts.contains(meta.element.variable)) {
194 return true;
195 } else if (meta.element is ConstructorElement &&
196 injectableMetaConstructors.contains(meta.element)) {
197 return true;
198 }
199 }
200 return false;
201 }
202
203 /**
204 * Find an 'injected' constructor for the given class.
205 * If [noAnnotation] is true then this will assume that the class is marked
206 * for injection and will use the default constructor.
207 */
208 ConstructorElement _findInjectedConstructor(ClassElement cls,
209 [bool noAnnotation = false]) {
210 var classInjectedConstructors = [];
211 if (_isElementAnnotated(cls) || noAnnotation) {
212 var defaultConstructor = cls.unnamedConstructor;
213 if (defaultConstructor == null) {
214 _warn('${cls.name} cannot be injected because '
215 'it does not have a default constructor.', cls);
216 } else {
217 classInjectedConstructors.add(defaultConstructor);
218 }
219 }
220
221 classInjectedConstructors.addAll(
222 cls.constructors.where(_isElementAnnotated));
223
224 if (classInjectedConstructors.isEmpty) return null;
225 if (classInjectedConstructors.length > 1) {
226 _warn('${cls.name} has more than one constructor annotated for '
227 'injection.', cls);
228 return null;
229 }
230
231 var ctor = classInjectedConstructors.single;
232 if (!_validateConstructor(ctor)) return null;
233
234 return ctor;
235 }
236
237 /**
238 * Validates that the constructor is injectable and emits warnings for any
239 * errors.
240 */
241 bool _validateConstructor(ConstructorElement ctor) {
242 var cls = ctor.enclosingElement;
243 if (cls.isAbstract && !ctor.isFactory) {
244 _warn('${cls.name} cannot be injected because '
245 'it is an abstract type with no factory constructor.', cls);
246 return false;
247 }
248 if (cls.isPrivate) {
249 _warn('${cls.name} cannot be injected because it is a private type.',
250 cls);
251 return false;
252 }
253 if (resolver.getImportUri(cls.library, from: _generatedAssetId) == null) {
254 _warn('${cls.name} cannot be injected because '
255 'the containing file cannot be imported.', cls);
256 return false;
257 }
258 if (!cls.typeParameters.isEmpty) {
259 _warn('${cls.name} is a parameterized type.', cls);
260 // Only warn.
261 }
262 if (ctor.name != '') {
263 _warn('Named constructors cannot be injected.', ctor);
264 return false;
265 }
266 for (var param in ctor.parameters) {
267 var type = param.type;
268 if (type is InterfaceType &&
269 type.typeArguments.any((t) => !t.isDynamic)) {
270 _warn('${cls.name} cannot be injected because '
271 '${param.type} is a parameterized type.', ctor);
272 return false;
273 }
274 if (type.isDynamic) {
275 _warn('${cls.name} cannot be injected because parameter type '
276 '${param.name} cannot be resolved.', ctor);
277 return false;
278 }
279 }
280 return true;
281 }
282
283 /**
284 * Creates a library file for the specified constructors.
285 */
286 String _generateInjectLibrary(Iterable<ConstructorElement> constructors) {
287 var prefixes = <LibraryElement, String>{};
288
289 var ctorTypes = constructors.map((ctor) => ctor.enclosingElement).toSet();
290 var paramTypes = constructors.expand((ctor) => ctor.parameters)
291 .map((param) => param.type.element).toSet();
292
293 var usedLibs = new Set<LibraryElement>();
294 String resolveClassName(ClassElement type) {
295 var library = type.library;
296 usedLibs.add(library);
297
298 var prefix = prefixes[library];
299 if (prefix == null) {
300 prefix = prefixes[library] =
301 library.isDartCore ? '' : 'import_${prefixes.length}';
302 }
303 if (prefix.isNotEmpty) {
304 prefix = '$prefix.';
305 }
306 return '$prefix${type.name}';
307 }
308
309 var factoriesBuffer = new StringBuffer();
310 for (var ctor in constructors) {
311 var type = ctor.enclosingElement;
312 var typeName = resolveClassName(type);
313 factoriesBuffer.write(' $typeName: (f) => new $typeName(');
314 var params = ctor.parameters.map((param) {
315 var typeName = resolveClassName(param.type.element);
316 var annotations = [];
317 if (param.metadata.isNotEmpty) {
318 annotations = param.metadata.map(
319 (item) => resolveClassName(item.element.returnType.element));
320 }
321 var annotationsSuffix =
322 annotations.isNotEmpty ? ', ${annotations.first}' : '';
323 return 'f($typeName$annotationsSuffix)';
324 });
325 factoriesBuffer.write('${params.join(', ')}),\n');
326 }
327
328 var outputBuffer = new StringBuffer();
329
330 _writeStaticInjectorHeader(transform.primaryInput.id, outputBuffer);
331 usedLibs.forEach((lib) {
332 if (lib.isDartCore) return;
333 var uri = resolver.getImportUri(lib, from: _generatedAssetId);
334 outputBuffer.write('import \'$uri\' as ${prefixes[lib]};\n');
335 });
336 _writePreamble(outputBuffer);
337 outputBuffer.write(factoriesBuffer);
338 _writeFooter(outputBuffer);
339
340 return outputBuffer.toString();
341 }
342
343 void _warn(String msg, Element element) {
344 logger.warning(msg, asset: resolver.getSourceAssetId(element),
345 span: resolver.getSourceSpan(element));
346 }
347 }
348
349 void _writeStaticInjectorHeader(AssetId id, StringSink sink) {
350 var libName = path.withoutExtension(id.path).replaceAll('/', '.');
351 libName = libName.replaceAll('-', '_');
352 sink.write('''
353 library ${id.package}.$libName.generated_static_injector;
354
355 import 'package:di/di.dart';
356 import 'package:di/static_injector.dart';
357
358 ''');
359 }
360
361 void _writePreamble(StringSink sink) {
362 sink.write('''
363 Injector createStaticInjector({List<Module> modules, String name,
364 bool allowImplicitInjection: false}) =>
365 new StaticInjector(modules: modules, name: name,
366 allowImplicitInjection: allowImplicitInjection,
367 typeFactories: factories);
368
369 final Map<Type, TypeFactory> factories = <Type, TypeFactory>{
370 ''');
371 }
372
373 void _writeFooter(StringSink sink) {
374 sink.write('''
375 };
376 ''');
377 }
OLDNEW
« no previous file with comments | « third_party/pkg/di/lib/transformer.dart ('k') | third_party/pkg/di/lib/transformer/options.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698