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

Side by Side Diff: third_party/pkg/di/lib/generator.dart

Issue 107003007: Adding Angular's DI package & Dart unittests which test it. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 12 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
« no previous file with comments | « third_party/pkg/di/lib/errors.dart ('k') | third_party/pkg/di/lib/injector.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 library di.generator;
2
3 import 'package:analyzer/src/generated/java_io.dart';
4 import 'package:analyzer/src/generated/source_io.dart';
5 import 'package:analyzer/src/generated/ast.dart';
6 import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
7 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
8 import 'package:analyzer/src/generated/element.dart';
9 import 'package:analyzer/src/generated/engine.dart';
10
11 import 'dart:io';
12
13 const String PACKAGE_PREFIX = 'package:';
14 const String DART_PACKAGE_PREFIX = 'dart:';
15
16 main(args) {
17 if (args.length < 4) {
18 print('Usage: generator path_to_sdk file_to_resolve annotations output [pack age_roots+]');
19 exit(0);
20 }
21
22 var pathToSdk = args[0];
23 var entryPoint = args[1];
24 var classAnnotations = args[2].split(',');
25 var output = args[3];
26 var packageRoots = (args.length < 5) ? [Platform.packageRoot] : args.sublist(4 );
27
28 print('pathToSdk: $pathToSdk');
29 print('entryPoint: $entryPoint');
30 print('classAnnotations: ${classAnnotations.join(', ')}');
31 print('output: $output');
32 print('packageRoots: $packageRoots');
33
34 var c = new SourceCrawler(pathToSdk, packageRoots);
35 List<String> imports = <String>[];
36 List<ClassElement> typeFactoryTypes = <ClassElement>[];
37 Map<String, String> typeToImport = new Map<String, String>();
38 c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source ) {
39 new CompilationUnitVisitor(c.context, source, classAnnotations, imports,
40 typeToImport, typeFactoryTypes).visit(compilationUnit);
41 });
42 var code = printLibraryCode(typeToImport, imports, typeFactoryTypes);
43 new File(output).writeAsStringSync(code);
44 }
45
46 String printLibraryCode(Map<String, String> typeToImport, List<String> imports,
47 List<ClassElement> typeFactoryTypes) {
48 List<String> requiredImports = <String>[];
49 StringBuffer factories = new StringBuffer();
50
51 String resolveClassIdentifier(InterfaceType type) {
52 if (type.element.library.isDartCore) {
53 return type.name;
54 }
55 String import = typeToImport[getCanonicalName(type)];
56 if (!requiredImports.contains(import)) {
57 requiredImports.add(import);
58 }
59 return 'import_${imports.indexOf(import)}.${type.name}';
60 }
61
62 typeFactoryTypes.forEach((ClassElement clazz) {
63 factories.write(
64 'typeFactories[${resolveClassIdentifier(clazz.type)}] = (f) => ');
65 factories.write('new ${resolveClassIdentifier(clazz.type)}(');
66 ConstructorElement constr =
67 clazz.constructors.firstWhere((c) => c.name.isEmpty,
68 orElse: () {
69 throw 'Unable to find default constructor for $clazz in ${clazz.source }';
70 });
71 factories.write(constr.parameters.map((param) {
72 if (param.type.element is! ClassElement) {
73 throw 'Unable to resolve type for constructor parameter '
74 '"${param.name}" for type "$clazz" in ${clazz.source}';
75 }
76 return 'f(${resolveClassIdentifier(param.type)})';
77 }).join(', '));
78 factories.write(');\n');
79 });
80 StringBuffer code = new StringBuffer();
81 code.write('library di.generated.type_factories;\n');
82 requiredImports.forEach((import) {
83 code.write ('import "$import" as import_${imports.indexOf(import)};\n');
84 });
85 code..write('var typeFactories = new Map();\n')
86 ..write('main() {\n')
87 ..write(factories)
88 ..write('}\n');
89
90 return code.toString();
91 }
92
93 class CompilationUnitVisitor {
94 List<String> imports;
95 Map<String, String> typeToImport;
96 List<ClassElement> typeFactoryTypes;
97 List<String> classAnnotations;
98 SourceFile source;
99 AnalysisContext context;
100
101 CompilationUnitVisitor(this.context, this.source,
102 this.classAnnotations, this.imports, this.typeToImport,
103 this.typeFactoryTypes);
104
105 visit(CompilationUnitElement compilationUnit) {
106 visitLibrary(compilationUnit.enclosingElement);
107
108 List<ClassElement> types = <ClassElement>[];
109 types.addAll(compilationUnit.types);
110
111 for (CompilationUnitElement part in compilationUnit.enclosingElement.parts) {
112 types.addAll(part.types);
113 }
114
115 types.forEach(visitClassElement);
116 }
117
118 visitLibrary(LibraryElement libElement) {
119 CompilationUnit resolvedUnit = context
120 .resolveCompilationUnit(libElement.source, libElement);
121
122 resolvedUnit.directives.forEach((Directive directive) {
123 if (directive is LibraryDirective) {
124 LibraryDirective library = directive;
125 int annotationIdx = 0;
126 library.metadata.forEach((Annotation ann) {
127 if (ann.element is ConstructorElement &&
128 getQualifiedName(
129 (ann.element as ConstructorElement).enclosingElement.type) ==
130 'di.annotations.Injectables') {
131 var listLiteral =
132 library.metadata[annotationIdx].arguments.arguments.first;
133 for (Expression expr in listLiteral.elements) {
134 Element element = (expr as SimpleIdentifier).bestElement;
135 if (element == null || element is! ClassElement) {
136 throw 'Unable to resolve type "$expr" from @Injectables '
137 'in ${library.element.source}';
138 }
139 typeFactoryTypes.add(element as ClassElement);
140 }
141 }
142 annotationIdx++;
143 });
144 }
145 });
146 }
147
148 visitClassElement(ClassElement classElement) {
149 if (classElement.name.startsWith('_')) {
150 return; // ignore private classes.
151 }
152 typeToImport[getCanonicalName(classElement.type)] =
153 source.entryPointImport;
154 if (!imports.contains(source.entryPointImport)) {
155 imports.add(source.entryPointImport);
156 }
157 for (ElementAnnotation ann in classElement.metadata) {
158 if (ann.element is ConstructorElement) {
159 ConstructorElement con = ann.element;
160 if (classAnnotations
161 .contains(getQualifiedName(con.enclosingElement.type))) {
162 typeFactoryTypes.add(classElement);
163 }
164 }
165 }
166 }
167 }
168
169 String getQualifiedName(InterfaceType type) {
170 var lib = type.element.library.displayName;
171 var name = type.name;
172 return lib == null ? name : '$lib.$name';
173 }
174
175 String getCanonicalName(InterfaceType type) {
176 var source = type.element.source.toString();
177 var name = type.name;
178 return '$source:$name';
179 }
180
181 typedef CompilationUnitCrawler(CompilationUnitElement compilationUnit,
182 SourceFile source);
183
184 class SourceCrawler {
185 final List<String> packageRoots;
186 final String sdkPath;
187 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
188
189 SourceCrawler(this.sdkPath, this.packageRoots);
190
191 void crawl(String entryPoint, CompilationUnitCrawler _visitor) {
192 JavaSystemIO.setProperty("com.google.dart.sdk", sdkPath);
193 DartSdk sdk = DirectoryBasedDartSdk.defaultSdk;
194
195 AnalysisOptionsImpl contextOptions = new AnalysisOptionsImpl();
196 contextOptions.cacheSize = 256;
197 contextOptions.preserveComments = false;
198 contextOptions.analyzeFunctionBodies = false;
199 context.analysisOptions = contextOptions;
200 sdk.context.analysisOptions = contextOptions;
201
202 var packageUriResolver =
203 new PackageUriResolver(packageRoots.map(
204 (pr) => new JavaFile.fromUri(new Uri.file(pr))).toList());
205 context.sourceFactory = new SourceFactory.con2([
206 new DartUriResolver(sdk),
207 new FileUriResolver(),
208 packageUriResolver
209 ]);
210
211 var entryPointFile;
212 var entryPointImport;
213 if (entryPoint.startsWith(PACKAGE_PREFIX)) {
214 entryPointFile = new JavaFile(packageUriResolver
215 .resolveAbsolute(context.sourceFactory.contentCache,
216 Uri.parse(entryPoint)).toString());
217 entryPointImport = entryPoint;
218 } else {
219 entryPointFile = new JavaFile(entryPoint);
220 entryPointImport = entryPointFile.getAbsolutePath();
221 }
222
223 Source source = new FileBasedSource.con1(
224 context.sourceFactory.contentCache, entryPointFile);
225 ChangeSet changeSet = new ChangeSet();
226 changeSet.added(source);
227 context.applyChanges(changeSet);
228 LibraryElement rootLib = context.computeLibraryElement(source);
229 CompilationUnit resolvedUnit =
230 context.resolveCompilationUnit(source, rootLib);
231
232 var sourceFile = new SourceFile(
233 entryPointFile.getAbsolutePath(),
234 entryPointImport,
235 resolvedUnit.element);
236 List<SourceFile> visited = <SourceFile>[];
237 List<SourceFile> toVisit = <SourceFile>[sourceFile];
238
239 while (toVisit.isNotEmpty) {
240 SourceFile currentFile = toVisit.removeAt(0);
241 visited.add(currentFile);
242 _visitor(currentFile.compilationUnit, currentFile);
243 var visitor = new CrawlerVisitor(currentFile, context);
244 visitor.accept(currentFile.compilationUnit);
245 visitor.toVisit.forEach((SourceFile todo) {
246 if (!toVisit.contains(todo) && !visited.contains(todo)) {
247 toVisit.add(todo);
248 }
249 });
250 }
251 }
252 }
253
254 class CrawlerVisitor {
255 List<SourceFile> toVisit = <SourceFile>[];
256 SourceFile currentFile;
257 AnalysisContext context;
258 String currentDir;
259
260 CrawlerVisitor(this.currentFile, this.context);
261
262 void accept(CompilationUnitElement cu) {
263 cu.enclosingElement.imports.forEach((ImportElement import) =>
264 visitImportElement(import.uri, import.importedLibrary.source));
265 cu.enclosingElement.exports.forEach((ExportElement import) =>
266 visitImportElement(import.uri, import.exportedLibrary.source));
267 }
268
269 visitImportElement(String uri, Source source) {
270 if (uri == null) return; // dart:core
271
272 String systemImport;
273 bool isSystem = false;
274 if (uri.startsWith(DART_PACKAGE_PREFIX)) {
275 isSystem = true;
276 systemImport = uri;
277 } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) {
278 isSystem = true;
279 systemImport = currentFile.entryPointImport;
280 }
281 // check if it's some internal hidden library
282 if (isSystem &&
283 systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) {
284 return;
285 }
286
287 var nextCompilationUnit = context
288 .resolveCompilationUnit(source, context.computeLibraryElement(source));
289
290 if (uri.startsWith(PACKAGE_PREFIX)) {
291 toVisit.add(new SourceFile(source.toString(), uri, nextCompilationUnit.ele ment));
292 } else { // relative import.
293 var newImport;
294 if (isSystem) {
295 newImport = systemImport; // original uri
296 } else {
297 // relative import
298 String import = currentFile.entryPointImport;
299 import = import.replaceAll('\\', '/'); // if at all needed, on Windows
300 import = import.substring(0, import.lastIndexOf('/'));
301 var currentDir = new File(currentFile.canonicalPath).parent.path;
302 currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on W indows
303 if (uri.startsWith('../')) {
304 while (uri.startsWith('../')) {
305 uri = uri.substring('../'.length);
306 import = import.substring(0, import.lastIndexOf('/'));
307 currentDir = currentDir.substring(0, currentDir.lastIndexOf('/'));
308 }
309 }
310 newImport = '$import/$uri';
311 }
312 toVisit.add(new SourceFile(
313 source.toString(), newImport, nextCompilationUnit.element));
314 }
315 }
316 }
317
318 class SourceFile {
319 String canonicalPath;
320 String entryPointImport;
321 CompilationUnitElement compilationUnit;
322
323 SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit);
324
325 operator ==(o) {
326 if (o is String) return o == canonicalPath;
327 if (o is! SourceFile) return false;
328 return o.canonicalPath == canonicalPath;
329 }
330 }
OLDNEW
« no previous file with comments | « third_party/pkg/di/lib/errors.dart ('k') | third_party/pkg/di/lib/injector.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698