Index: third_party/pkg/di/lib/generator.dart |
diff --git a/third_party/pkg/di/lib/generator.dart b/third_party/pkg/di/lib/generator.dart |
deleted file mode 100644 |
index 1266b432d76dddc2627728356affca012c2035c6..0000000000000000000000000000000000000000 |
--- a/third_party/pkg/di/lib/generator.dart |
+++ /dev/null |
@@ -1,475 +0,0 @@ |
-library di.generator; |
- |
-import 'package:analyzer/src/generated/java_io.dart'; |
-import 'package:analyzer/src/generated/source_io.dart'; |
-import 'package:analyzer/src/generated/ast.dart'; |
-import 'package:analyzer/src/generated/sdk.dart' show DartSdk; |
-import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; |
-import 'package:analyzer/src/generated/element.dart'; |
-import 'package:analyzer/src/generated/engine.dart'; |
- |
-import 'dart:io'; |
- |
-const String PACKAGE_PREFIX = 'package:'; |
-const String DART_PACKAGE_PREFIX = 'dart:'; |
- |
-main(args) { |
- if (args.length < 4) { |
- print('Usage: generator path_to_sdk file_to_resolve annotations output [package_roots+]'); |
- exit(0); |
- } |
- |
- var pathToSdk = args[0]; |
- var entryPoint = args[1]; |
- var classAnnotations = args[2].split(','); |
- var output = args[3]; |
- var packageRoots = (args.length < 5) ? [Platform.packageRoot] : args.sublist(4); |
- |
- print('pathToSdk: $pathToSdk'); |
- print('entryPoint: $entryPoint'); |
- print('classAnnotations: ${classAnnotations.join(', ')}'); |
- print('output: $output'); |
- print('packageRoots: $packageRoots'); |
- |
- var code = generateCode(entryPoint, classAnnotations, pathToSdk, packageRoots); |
- code.forEach((chunk, code) { |
- String fileName = output; |
- if (chunk.library != null) { |
- var lastDot = fileName.lastIndexOf('.'); |
- fileName = fileName.substring(0, lastDot) + '-' + chunk.library.name + fileName.substring(lastDot); |
- } |
- new File(fileName).writeAsStringSync(code); |
- }); |
-} |
- |
-Map<Chunk, String> generateCode(String entryPoint, List<String> classAnnotations, |
- String pathToSdk, List<String> packageRoots) { |
- var c = new SourceCrawler(pathToSdk, packageRoots); |
- List<String> imports = <String>[]; |
- Map<Chunk, List<ClassElement>> typeFactoryTypes = <Chunk, List<ClassElement>>{}; |
- Map<String, String> typeToImport = new Map<String, String>(); |
- c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source) { |
- new CompilationUnitVisitor(c.context, source, classAnnotations, imports, |
- typeToImport, typeFactoryTypes).visit(compilationUnit, source); |
- }); |
- return printLibraryCode(typeToImport, imports, typeFactoryTypes); |
-} |
- |
-Map<Chunk, String> printLibraryCode(Map<String, String> typeToImport, |
- List<String> imports, Map<Chunk, List<ClassElement>> typeFactoryTypes) { |
- Map<Chunk, StringBuffer> factories = <Chunk, StringBuffer>{}; |
- Map<Chunk, String> result = <Chunk, String>{}; |
- typeFactoryTypes.forEach((Chunk chunk, List<ClassElement> classes) { |
- List<String> requiredImports = <String>[]; |
- String resolveClassIdentifier(InterfaceType type) { |
- if (type.element.library.isDartCore) { |
- return type.name; |
- } |
- String import = typeToImport[getCanonicalName(type)]; |
- if (!requiredImports.contains(import)) { |
- requiredImports.add(import); |
- } |
- return 'import_${imports.indexOf(import)}.${type.name}'; |
- } |
- factories[chunk] = new StringBuffer(); |
- classes.forEach((ClassElement clazz) { |
- StringBuffer factory = new StringBuffer(); |
- bool skip = false; |
- factory.write( |
- '${resolveClassIdentifier(clazz.type)}: (f) => '); |
- factory.write('new ${resolveClassIdentifier(clazz.type)}('); |
- ConstructorElement constr = |
- clazz.constructors.firstWhere((c) => c.name.isEmpty, |
- orElse: () { |
- throw 'Unable to find default constructor for $clazz in ${clazz.source}'; |
- }); |
- factory.write(constr.parameters.map((param) { |
- if (param.type.element is! ClassElement) { |
- throw 'Unable to resolve type for constructor parameter ' |
- '"${param.name}" for type "$clazz" in ${clazz.source}'; |
- } |
- if (_isParameterized(param)) { |
- print('WARNING: parameterized types are not supported: $param in $clazz in ${clazz.source}. Skipping!'); |
- skip = true; |
- } |
- return 'f(${resolveClassIdentifier(param.type)})'; |
- }).join(', ')); |
- factory.write('),\n'); |
- if (!skip) { |
- factories[chunk].write(factory); |
- } |
- }); |
- StringBuffer code = new StringBuffer(); |
- String libSuffix = chunk.library == null ? '' : '.${chunk.library.name}'; |
- code.write('library di.generated.type_factories$libSuffix;\n'); |
- requiredImports.forEach((import) { |
- code.write ('import "$import" as import_${imports.indexOf(import)};\n'); |
- }); |
- code..write('var typeFactories = {\n${factories[chunk]}\n};\n') |
- ..write('main() {}\n'); |
- result[chunk] = code.toString(); |
- }); |
- |
- return result; |
-} |
- |
-_isParameterized(ParameterElement param) { |
- String typeName = param.type.toString(); |
- |
- if (typeName.indexOf('<') > -1) { |
- String parameters = |
- typeName.substring(typeName.indexOf('<') + 1, typeName.length - 1); |
- return parameters.split(', ').any((p) => p != 'dynamic'); |
- } |
- return false; |
-} |
- |
-class CompilationUnitVisitor { |
- List<String> imports; |
- Map<String, String> typeToImport; |
- Map<Chunk, List<ClassElement>> typeFactoryTypes; |
- List<String> classAnnotations; |
- SourceFile source; |
- AnalysisContext context; |
- |
- CompilationUnitVisitor(this.context, this.source, |
- this.classAnnotations, this.imports, this.typeToImport, |
- this.typeFactoryTypes); |
- |
- visit(CompilationUnitElement compilationUnit, SourceFile source) { |
- visitLibrary(compilationUnit.enclosingElement, source); |
- |
- List<ClassElement> types = <ClassElement>[]; |
- types.addAll(compilationUnit.types); |
- |
- for (CompilationUnitElement part in compilationUnit.enclosingElement.parts) { |
- types.addAll(part.types); |
- } |
- |
- types.forEach((clazz) => visitClassElement(clazz, source)); |
- } |
- |
- visitLibrary(LibraryElement libElement, SourceFile source) { |
- CompilationUnit resolvedUnit = context |
- .resolveCompilationUnit(libElement.source, libElement); |
- |
- resolvedUnit.directives.forEach((Directive directive) { |
- if (directive is LibraryDirective) { |
- LibraryDirective library = directive; |
- int annotationIdx = 0; |
- library.metadata.forEach((Annotation ann) { |
- if (ann.element is ConstructorElement && |
- getQualifiedName( |
- (ann.element as ConstructorElement).enclosingElement.type) == |
- 'di.annotations.Injectables') { |
- var listLiteral = |
- library.metadata[annotationIdx].arguments.arguments.first; |
- for (Expression expr in listLiteral.elements) { |
- Element element = (expr as SimpleIdentifier).bestElement; |
- if (element == null || element is! ClassElement) { |
- throw 'Unable to resolve type "$expr" from @Injectables ' |
- 'in ${library.element.source}'; |
- } |
- if (typeFactoryTypes[source.chunk] == null) { |
- typeFactoryTypes[source.chunk] = <ClassElement>[]; |
- } |
- if (!typeFactoryTypes[source.chunk].contains(element)) { |
- typeFactoryTypes[source.chunk].add(element as ClassElement); |
- } |
- } |
- } |
- annotationIdx++; |
- }); |
- } |
- }); |
- } |
- |
- visitClassElement(ClassElement classElement, SourceFile source) { |
- if (classElement.name.startsWith('_')) { |
- return; // ignore private classes. |
- } |
- typeToImport[getCanonicalName(classElement.type)] = |
- source.entryPointImport; |
- if (!imports.contains(source.entryPointImport)) { |
- imports.add(source.entryPointImport); |
- } |
- for (ElementAnnotation ann in classElement.metadata) { |
- if (ann.element is ConstructorElement) { |
- ConstructorElement con = ann.element; |
- if (classAnnotations |
- .contains(getQualifiedName(con.enclosingElement.type))) { |
- if (typeFactoryTypes[source.chunk] == null) { |
- typeFactoryTypes[source.chunk] = <ClassElement>[]; |
- } |
- if (!typeFactoryTypes[source.chunk].contains(classElement)) { |
- typeFactoryTypes[source.chunk].add(classElement); |
- } |
- } |
- } |
- } |
- } |
-} |
- |
-String getQualifiedName(InterfaceType type) { |
- var lib = type.element.library.displayName; |
- var name = type.name; |
- return lib == null ? name : '$lib.$name'; |
-} |
- |
-String getCanonicalName(InterfaceType type) { |
- var source = type.element.source.toString(); |
- var name = type.name; |
- return '$source:$name'; |
-} |
- |
-typedef CompilationUnitCrawler(CompilationUnitElement compilationUnit, |
- SourceFile source); |
- |
-class SourceCrawler { |
- final List<String> packageRoots; |
- final String sdkPath; |
- AnalysisContext context = AnalysisEngine.instance.createAnalysisContext(); |
- |
- SourceCrawler(this.sdkPath, this.packageRoots); |
- |
- void crawl(String entryPoint, CompilationUnitCrawler _visitor) { |
- JavaSystemIO.setProperty("com.google.dart.sdk", sdkPath); |
- DartSdk sdk = DirectoryBasedDartSdk.defaultSdk; |
- |
- AnalysisOptionsImpl contextOptions = new AnalysisOptionsImpl(); |
- contextOptions.cacheSize = 256; |
- contextOptions.preserveComments = false; |
- contextOptions.analyzeFunctionBodies = false; |
- context.analysisOptions = contextOptions; |
- sdk.context.analysisOptions = contextOptions; |
- |
- var packageUriResolver = |
- new PackageUriResolver(packageRoots.map( |
- (pr) => new JavaFile.fromUri(new Uri.file(pr))).toList()); |
- context.sourceFactory = new SourceFactory.con2([ |
- new DartUriResolver(sdk), |
- new FileUriResolver(), |
- packageUriResolver |
- ]); |
- |
- var entryPointFile; |
- var entryPointImport; |
- if (entryPoint.startsWith(PACKAGE_PREFIX)) { |
- entryPointFile = new JavaFile(packageUriResolver |
- .resolveAbsolute(context.sourceFactory.contentCache, |
- Uri.parse(entryPoint)).toString()); |
- entryPointImport = entryPoint; |
- } else { |
- entryPointFile = new JavaFile(entryPoint); |
- entryPointImport = entryPointFile.getAbsolutePath(); |
- } |
- |
- Source source = new FileBasedSource.con1( |
- context.sourceFactory.contentCache, entryPointFile); |
- ChangeSet changeSet = new ChangeSet(); |
- changeSet.added(source); |
- context.applyChanges(changeSet); |
- LibraryElement rootLib = context.computeLibraryElement(source); |
- CompilationUnit resolvedUnit = |
- context.resolveCompilationUnit(source, rootLib); |
- |
- var sourceFile = new SourceFile( |
- entryPointFile.getAbsolutePath(), |
- entryPointImport, |
- resolvedUnit, |
- resolvedUnit.element, |
- new Chunk()); // root chunk |
- List<SourceFile> toVisit = <SourceFile>[sourceFile]; |
- List<SourceFile> deferred = <SourceFile>[sourceFile]; |
- |
- while (deferred.isNotEmpty) { |
- toVisit.add(deferred.removeAt(0)); |
- while (toVisit.isNotEmpty) { |
- SourceFile currentFile = toVisit.removeAt(0); |
- currentFile.chunk.addVisited(currentFile); |
- _visitor(currentFile.compilationUnitElement, currentFile); |
- var visitor = new CrawlerVisitor(currentFile, context); |
- visitor.accept(currentFile.compilationUnit); |
- visitor.toVisit.forEach((SourceFile todo) { |
- if (!toVisit.contains(todo) && !currentFile.chunk.alreadyVisited(todo)) { |
- toVisit.add(todo); |
- } |
- }); |
- visitor.deferred.forEach((SourceFile todo) { |
- if (!deferred.contains(todo) && !currentFile.chunk.alreadyVisited(todo)) { |
- deferred.add(todo); |
- } |
- }); |
- } |
- } |
- } |
-} |
- |
-class CrawlerVisitor { |
- List<SourceFile> toVisit = <SourceFile>[]; |
- List<SourceFile> deferred = <SourceFile>[]; |
- SourceFile currentFile; |
- AnalysisContext context; |
- String currentDir; |
- |
- CrawlerVisitor(this.currentFile, this.context); |
- |
- void accept(CompilationUnit cu) { |
- cu.directives.forEach((Directive directive) { |
- if (directive.element == null) return; // unresolvable, ignore |
- if (directive is ImportDirective) { |
- var import = directive.element; |
- visitImportElement( |
- new Library(import, import.uri, cu, import.importedLibrary.name), |
- import.importedLibrary.source); |
- } |
- if (directive is ExportDirective) { |
- var import = directive.element; |
- visitImportElement( |
- new Library(import, import.uri, cu, import.exportedLibrary.name), |
- import.exportedLibrary.source); |
- } |
- }); |
- } |
- |
- visitImportElement(Library library, Source source) { |
- String uri = library.uri; |
- if (uri == null) return; // dart:core |
- |
- String systemImport; |
- bool isSystem = false; |
- if (uri.startsWith(DART_PACKAGE_PREFIX)) { |
- isSystem = true; |
- systemImport = uri; |
- } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) { |
- isSystem = true; |
- systemImport = currentFile.entryPointImport; |
- } |
- // check if it's some internal hidden library |
- if (isSystem && |
- systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) { |
- return; |
- } |
- |
- var nextCompilationUnit = context |
- .resolveCompilationUnit(source, context.computeLibraryElement(source)); |
- |
- SourceFile sourceFile; |
- if (uri.startsWith(PACKAGE_PREFIX)) { |
- sourceFile = new SourceFile(source.toString(), uri, |
- nextCompilationUnit, nextCompilationUnit.element, currentFile.chunk); |
- } else { // relative import. |
- var newImport; |
- if (isSystem) { |
- newImport = systemImport; // original uri |
- } else { |
- // relative import |
- String import = currentFile.entryPointImport; |
- import = import.replaceAll('\\', '/'); // if at all needed, on Windows |
- import = import.substring(0, import.lastIndexOf('/')); |
- var currentDir = new File(currentFile.canonicalPath).parent.path; |
- currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on Windows |
- if (uri.startsWith('../')) { |
- while (uri.startsWith('../')) { |
- uri = uri.substring('../'.length); |
- import = import.substring(0, import.lastIndexOf('/')); |
- currentDir = currentDir.substring(0, currentDir.lastIndexOf('/')); |
- } |
- } |
- newImport = '$import/$uri'; |
- } |
- sourceFile = new SourceFile( |
- source.toString(), newImport, |
- nextCompilationUnit, nextCompilationUnit.element, currentFile.chunk); |
- } |
- if (isDeferredImport(library)) { |
- var childChunk = currentFile.chunk.createChild(library); |
- deferred.add(new SourceFile(source.toString(), sourceFile.entryPointImport, |
- nextCompilationUnit, nextCompilationUnit.element, childChunk)); |
- } else { |
- toVisit.add(sourceFile); |
- } |
- } |
-} |
- |
-bool isDeferredImport(Library library) { |
- var isDeferred = false; |
- library.element.metadata.forEach((ElementAnnotation annotation) { |
- if (annotation.element is PropertyAccessorElement) { |
- PropertyAccessorElement pa = annotation.element; |
- library.compilationUnit.declarations.forEach((CompilationUnitMember member) { |
- if (member is TopLevelVariableDeclaration && member.variables.isConst) { |
- TopLevelVariableDeclaration topLevel = member; |
- topLevel.variables.variables.forEach((VariableDeclaration varDecl) { |
- if (varDecl.initializer is InstanceCreationExpression && |
- (varDecl.initializer as InstanceCreationExpression).isConst && |
- (varDecl.initializer as InstanceCreationExpression).staticElement is ConstructorElement && |
- varDecl.name.name == pa.name) { |
- ConstructorElement constr = (varDecl.initializer as InstanceCreationExpression).staticElement; |
- if (constr.enclosingElement.library.name == 'dart.async' && |
- constr.enclosingElement.type.name == 'DeferredLibrary') { |
- isDeferred = true; |
- } |
- } |
- }); |
- } |
- }); |
- } |
- }); |
- return isDeferred; |
-} |
- |
-class Library { |
- final Element element; |
- final String uri; |
- final CompilationUnit compilationUnit; |
- final String name; |
- |
- Library(this.element, this.uri, this.compilationUnit, this.name); |
- |
- toString() => 'Library[$name]'; |
-} |
- |
-class Chunk { |
- final Chunk parent; |
- Library library; |
- List<SourceFile> _visited = <SourceFile>[]; |
- |
- addVisited(SourceFile file) { |
- _visited.add(file); |
- } |
- |
- bool alreadyVisited(SourceFile file) { |
- var cursor = this; |
- while (cursor != null) { |
- if (cursor._visited.contains(file)) { |
- return true; |
- } |
- cursor = cursor.parent; |
- } |
- return false; |
- } |
- |
- Chunk([this.parent, this.library]); |
- |
- Chunk createChild(Library library) => new Chunk(this, library); |
- |
- toString() => 'Chunk[$library]'; |
-} |
- |
-class SourceFile { |
- String canonicalPath; |
- String entryPointImport; |
- CompilationUnit compilationUnit; |
- CompilationUnitElement compilationUnitElement; |
- Chunk chunk; |
- |
- SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit, |
- this.compilationUnitElement, this.chunk); |
- |
- operator ==(o) { |
- if (o is String) return o == canonicalPath; |
- if (o is! SourceFile) return false; |
- return o.canonicalPath == canonicalPath; |
- } |
-} |