| OLD | NEW |
| 1 library di.generator; | 1 library di.generator; |
| 2 | 2 |
| 3 import 'package:analyzer/src/generated/java_io.dart'; | 3 import 'package:analyzer/src/generated/java_io.dart'; |
| 4 import 'package:analyzer/src/generated/source_io.dart'; | 4 import 'package:analyzer/src/generated/source_io.dart'; |
| 5 import 'package:analyzer/src/generated/ast.dart'; | 5 import 'package:analyzer/src/generated/ast.dart'; |
| 6 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; | 6 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; |
| 7 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; | 7 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; |
| 8 import 'package:analyzer/src/generated/element.dart'; | 8 import 'package:analyzer/src/generated/element.dart'; |
| 9 import 'package:analyzer/src/generated/engine.dart'; | 9 import 'package:analyzer/src/generated/engine.dart'; |
| 10 | 10 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 var classAnnotations = args[2].split(','); | 24 var classAnnotations = args[2].split(','); |
| 25 var output = args[3]; | 25 var output = args[3]; |
| 26 var packageRoots = (args.length < 5) ? [Platform.packageRoot] : args.sublist(4
); | 26 var packageRoots = (args.length < 5) ? [Platform.packageRoot] : args.sublist(4
); |
| 27 | 27 |
| 28 print('pathToSdk: $pathToSdk'); | 28 print('pathToSdk: $pathToSdk'); |
| 29 print('entryPoint: $entryPoint'); | 29 print('entryPoint: $entryPoint'); |
| 30 print('classAnnotations: ${classAnnotations.join(', ')}'); | 30 print('classAnnotations: ${classAnnotations.join(', ')}'); |
| 31 print('output: $output'); | 31 print('output: $output'); |
| 32 print('packageRoots: $packageRoots'); | 32 print('packageRoots: $packageRoots'); |
| 33 | 33 |
| 34 var code = generateCode(entryPoint, classAnnotations, pathToSdk, packageRoots)
; | |
| 35 code.forEach((chunk, code) { | |
| 36 String fileName = output; | |
| 37 if (chunk.library != null) { | |
| 38 var lastDot = fileName.lastIndexOf('.'); | |
| 39 fileName = fileName.substring(0, lastDot) + '-' + chunk.library.name + fil
eName.substring(lastDot); | |
| 40 } | |
| 41 new File(fileName).writeAsStringSync(code); | |
| 42 }); | |
| 43 } | |
| 44 | |
| 45 Map<Chunk, String> generateCode(String entryPoint, List<String> classAnnotations
, | |
| 46 String pathToSdk, List<String> packageRoots) { | |
| 47 var c = new SourceCrawler(pathToSdk, packageRoots); | 34 var c = new SourceCrawler(pathToSdk, packageRoots); |
| 48 List<String> imports = <String>[]; | 35 List<String> imports = <String>[]; |
| 49 Map<Chunk, List<ClassElement>> typeFactoryTypes = <Chunk, List<ClassElement>>{
}; | 36 List<ClassElement> typeFactoryTypes = <ClassElement>[]; |
| 50 Map<String, String> typeToImport = new Map<String, String>(); | 37 Map<String, String> typeToImport = new Map<String, String>(); |
| 51 c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source
) { | 38 c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source
) { |
| 52 new CompilationUnitVisitor(c.context, source, classAnnotations, imports, | 39 new CompilationUnitVisitor(c.context, source, classAnnotations, imports, |
| 53 typeToImport, typeFactoryTypes).visit(compilationUnit, source); | 40 typeToImport, typeFactoryTypes).visit(compilationUnit); |
| 54 }); | 41 }); |
| 55 return printLibraryCode(typeToImport, imports, typeFactoryTypes); | 42 var code = printLibraryCode(typeToImport, imports, typeFactoryTypes); |
| 43 new File(output).writeAsStringSync(code); |
| 56 } | 44 } |
| 57 | 45 |
| 58 Map<Chunk, String> printLibraryCode(Map<String, String> typeToImport, | 46 String printLibraryCode(Map<String, String> typeToImport, List<String> imports, |
| 59 List<String> imports, Map<Chunk, List<ClassElement>> typeFactoryTypes) { | 47 List<ClassElement> typeFactoryTypes) { |
| 60 Map<Chunk, StringBuffer> factories = <Chunk, StringBuffer>{}; | 48 List<String> requiredImports = <String>[]; |
| 61 Map<Chunk, String> result = <Chunk, String>{}; | 49 StringBuffer factories = new StringBuffer(); |
| 62 typeFactoryTypes.forEach((Chunk chunk, List<ClassElement> classes) { | 50 |
| 63 List<String> requiredImports = <String>[]; | 51 String resolveClassIdentifier(InterfaceType type) { |
| 64 String resolveClassIdentifier(InterfaceType type) { | 52 if (type.element.library.isDartCore) { |
| 65 if (type.element.library.isDartCore) { | 53 return type.name; |
| 66 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}'; |
| 67 } | 75 } |
| 68 String import = typeToImport[getCanonicalName(type)]; | 76 return 'f(${resolveClassIdentifier(param.type)})'; |
| 69 if (!requiredImports.contains(import)) { | 77 }).join(', ')); |
| 70 requiredImports.add(import); | 78 factories.write(');\n'); |
| 71 } | |
| 72 return 'import_${imports.indexOf(import)}.${type.name}'; | |
| 73 } | |
| 74 factories[chunk] = new StringBuffer(); | |
| 75 classes.forEach((ClassElement clazz) { | |
| 76 StringBuffer factory = new StringBuffer(); | |
| 77 bool skip = false; | |
| 78 factory.write( | |
| 79 '${resolveClassIdentifier(clazz.type)}: (f) => '); | |
| 80 factory.write('new ${resolveClassIdentifier(clazz.type)}('); | |
| 81 ConstructorElement constr = | |
| 82 clazz.constructors.firstWhere((c) => c.name.isEmpty, | |
| 83 orElse: () { | |
| 84 throw 'Unable to find default constructor for $clazz in ${clazz.sour
ce}'; | |
| 85 }); | |
| 86 factory.write(constr.parameters.map((param) { | |
| 87 if (param.type.element is! ClassElement) { | |
| 88 throw 'Unable to resolve type for constructor parameter ' | |
| 89 '"${param.name}" for type "$clazz" in ${clazz.source}'; | |
| 90 } | |
| 91 if (_isParameterized(param)) { | |
| 92 print('WARNING: parameterized types are not supported: $param in $claz
z in ${clazz.source}. Skipping!'); | |
| 93 skip = true; | |
| 94 } | |
| 95 return 'f(${resolveClassIdentifier(param.type)})'; | |
| 96 }).join(', ')); | |
| 97 factory.write('),\n'); | |
| 98 if (!skip) { | |
| 99 factories[chunk].write(factory); | |
| 100 } | |
| 101 }); | |
| 102 StringBuffer code = new StringBuffer(); | |
| 103 String libSuffix = chunk.library == null ? '' : '.${chunk.library.name}'; | |
| 104 code.write('library di.generated.type_factories$libSuffix;\n'); | |
| 105 requiredImports.forEach((import) { | |
| 106 code.write ('import "$import" as import_${imports.indexOf(import)};\n'); | |
| 107 }); | |
| 108 code..write('var typeFactories = {\n${factories[chunk]}\n};\n') | |
| 109 ..write('main() {}\n'); | |
| 110 result[chunk] = code.toString(); | |
| 111 }); | 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'); |
| 112 | 89 |
| 113 return result; | 90 return code.toString(); |
| 114 } | |
| 115 | |
| 116 _isParameterized(ParameterElement param) { | |
| 117 String typeName = param.type.toString(); | |
| 118 | |
| 119 if (typeName.indexOf('<') > -1) { | |
| 120 String parameters = | |
| 121 typeName.substring(typeName.indexOf('<') + 1, typeName.length - 1); | |
| 122 return parameters.split(', ').any((p) => p != 'dynamic'); | |
| 123 } | |
| 124 return false; | |
| 125 } | 91 } |
| 126 | 92 |
| 127 class CompilationUnitVisitor { | 93 class CompilationUnitVisitor { |
| 128 List<String> imports; | 94 List<String> imports; |
| 129 Map<String, String> typeToImport; | 95 Map<String, String> typeToImport; |
| 130 Map<Chunk, List<ClassElement>> typeFactoryTypes; | 96 List<ClassElement> typeFactoryTypes; |
| 131 List<String> classAnnotations; | 97 List<String> classAnnotations; |
| 132 SourceFile source; | 98 SourceFile source; |
| 133 AnalysisContext context; | 99 AnalysisContext context; |
| 134 | 100 |
| 135 CompilationUnitVisitor(this.context, this.source, | 101 CompilationUnitVisitor(this.context, this.source, |
| 136 this.classAnnotations, this.imports, this.typeToImport, | 102 this.classAnnotations, this.imports, this.typeToImport, |
| 137 this.typeFactoryTypes); | 103 this.typeFactoryTypes); |
| 138 | 104 |
| 139 visit(CompilationUnitElement compilationUnit, SourceFile source) { | 105 visit(CompilationUnitElement compilationUnit) { |
| 140 visitLibrary(compilationUnit.enclosingElement, source); | 106 visitLibrary(compilationUnit.enclosingElement); |
| 141 | 107 |
| 142 List<ClassElement> types = <ClassElement>[]; | 108 List<ClassElement> types = <ClassElement>[]; |
| 143 types.addAll(compilationUnit.types); | 109 types.addAll(compilationUnit.types); |
| 144 | 110 |
| 145 for (CompilationUnitElement part in compilationUnit.enclosingElement.parts)
{ | 111 for (CompilationUnitElement part in compilationUnit.enclosingElement.parts)
{ |
| 146 types.addAll(part.types); | 112 types.addAll(part.types); |
| 147 } | 113 } |
| 148 | 114 |
| 149 types.forEach((clazz) => visitClassElement(clazz, source)); | 115 types.forEach(visitClassElement); |
| 150 } | 116 } |
| 151 | 117 |
| 152 visitLibrary(LibraryElement libElement, SourceFile source) { | 118 visitLibrary(LibraryElement libElement) { |
| 153 CompilationUnit resolvedUnit = context | 119 CompilationUnit resolvedUnit = context |
| 154 .resolveCompilationUnit(libElement.source, libElement); | 120 .resolveCompilationUnit(libElement.source, libElement); |
| 155 | 121 |
| 156 resolvedUnit.directives.forEach((Directive directive) { | 122 resolvedUnit.directives.forEach((Directive directive) { |
| 157 if (directive is LibraryDirective) { | 123 if (directive is LibraryDirective) { |
| 158 LibraryDirective library = directive; | 124 LibraryDirective library = directive; |
| 159 int annotationIdx = 0; | 125 int annotationIdx = 0; |
| 160 library.metadata.forEach((Annotation ann) { | 126 library.metadata.forEach((Annotation ann) { |
| 161 if (ann.element is ConstructorElement && | 127 if (ann.element is ConstructorElement && |
| 162 getQualifiedName( | 128 getQualifiedName( |
| 163 (ann.element as ConstructorElement).enclosingElement.type) == | 129 (ann.element as ConstructorElement).enclosingElement.type) == |
| 164 'di.annotations.Injectables') { | 130 'di.annotations.Injectables') { |
| 165 var listLiteral = | 131 var listLiteral = |
| 166 library.metadata[annotationIdx].arguments.arguments.first; | 132 library.metadata[annotationIdx].arguments.arguments.first; |
| 167 for (Expression expr in listLiteral.elements) { | 133 for (Expression expr in listLiteral.elements) { |
| 168 Element element = (expr as SimpleIdentifier).bestElement; | 134 Element element = (expr as SimpleIdentifier).bestElement; |
| 169 if (element == null || element is! ClassElement) { | 135 if (element == null || element is! ClassElement) { |
| 170 throw 'Unable to resolve type "$expr" from @Injectables ' | 136 throw 'Unable to resolve type "$expr" from @Injectables ' |
| 171 'in ${library.element.source}'; | 137 'in ${library.element.source}'; |
| 172 } | 138 } |
| 173 if (typeFactoryTypes[source.chunk] == null) { | 139 typeFactoryTypes.add(element as ClassElement); |
| 174 typeFactoryTypes[source.chunk] = <ClassElement>[]; | |
| 175 } | |
| 176 if (!typeFactoryTypes[source.chunk].contains(element)) { | |
| 177 typeFactoryTypes[source.chunk].add(element as ClassElement); | |
| 178 } | |
| 179 } | 140 } |
| 180 } | 141 } |
| 181 annotationIdx++; | 142 annotationIdx++; |
| 182 }); | 143 }); |
| 183 } | 144 } |
| 184 }); | 145 }); |
| 185 } | 146 } |
| 186 | 147 |
| 187 visitClassElement(ClassElement classElement, SourceFile source) { | 148 visitClassElement(ClassElement classElement) { |
| 188 if (classElement.name.startsWith('_')) { | 149 if (classElement.name.startsWith('_')) { |
| 189 return; // ignore private classes. | 150 return; // ignore private classes. |
| 190 } | 151 } |
| 191 typeToImport[getCanonicalName(classElement.type)] = | 152 typeToImport[getCanonicalName(classElement.type)] = |
| 192 source.entryPointImport; | 153 source.entryPointImport; |
| 193 if (!imports.contains(source.entryPointImport)) { | 154 if (!imports.contains(source.entryPointImport)) { |
| 194 imports.add(source.entryPointImport); | 155 imports.add(source.entryPointImport); |
| 195 } | 156 } |
| 196 for (ElementAnnotation ann in classElement.metadata) { | 157 for (ElementAnnotation ann in classElement.metadata) { |
| 197 if (ann.element is ConstructorElement) { | 158 if (ann.element is ConstructorElement) { |
| 198 ConstructorElement con = ann.element; | 159 ConstructorElement con = ann.element; |
| 199 if (classAnnotations | 160 if (classAnnotations |
| 200 .contains(getQualifiedName(con.enclosingElement.type))) { | 161 .contains(getQualifiedName(con.enclosingElement.type))) { |
| 201 if (typeFactoryTypes[source.chunk] == null) { | 162 typeFactoryTypes.add(classElement); |
| 202 typeFactoryTypes[source.chunk] = <ClassElement>[]; | |
| 203 } | |
| 204 if (!typeFactoryTypes[source.chunk].contains(classElement)) { | |
| 205 typeFactoryTypes[source.chunk].add(classElement); | |
| 206 } | |
| 207 } | 163 } |
| 208 } | 164 } |
| 209 } | 165 } |
| 210 } | 166 } |
| 211 } | 167 } |
| 212 | 168 |
| 213 String getQualifiedName(InterfaceType type) { | 169 String getQualifiedName(InterfaceType type) { |
| 214 var lib = type.element.library.displayName; | 170 var lib = type.element.library.displayName; |
| 215 var name = type.name; | 171 var name = type.name; |
| 216 return lib == null ? name : '$lib.$name'; | 172 return lib == null ? name : '$lib.$name'; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 ChangeSet changeSet = new ChangeSet(); | 225 ChangeSet changeSet = new ChangeSet(); |
| 270 changeSet.added(source); | 226 changeSet.added(source); |
| 271 context.applyChanges(changeSet); | 227 context.applyChanges(changeSet); |
| 272 LibraryElement rootLib = context.computeLibraryElement(source); | 228 LibraryElement rootLib = context.computeLibraryElement(source); |
| 273 CompilationUnit resolvedUnit = | 229 CompilationUnit resolvedUnit = |
| 274 context.resolveCompilationUnit(source, rootLib); | 230 context.resolveCompilationUnit(source, rootLib); |
| 275 | 231 |
| 276 var sourceFile = new SourceFile( | 232 var sourceFile = new SourceFile( |
| 277 entryPointFile.getAbsolutePath(), | 233 entryPointFile.getAbsolutePath(), |
| 278 entryPointImport, | 234 entryPointImport, |
| 279 resolvedUnit, | 235 resolvedUnit.element); |
| 280 resolvedUnit.element, | 236 List<SourceFile> visited = <SourceFile>[]; |
| 281 new Chunk()); // root chunk | |
| 282 List<SourceFile> toVisit = <SourceFile>[sourceFile]; | 237 List<SourceFile> toVisit = <SourceFile>[sourceFile]; |
| 283 List<SourceFile> deferred = <SourceFile>[sourceFile]; | |
| 284 | 238 |
| 285 while (deferred.isNotEmpty) { | 239 while (toVisit.isNotEmpty) { |
| 286 toVisit.add(deferred.removeAt(0)); | 240 SourceFile currentFile = toVisit.removeAt(0); |
| 287 while (toVisit.isNotEmpty) { | 241 visited.add(currentFile); |
| 288 SourceFile currentFile = toVisit.removeAt(0); | 242 _visitor(currentFile.compilationUnit, currentFile); |
| 289 currentFile.chunk.addVisited(currentFile); | 243 var visitor = new CrawlerVisitor(currentFile, context); |
| 290 _visitor(currentFile.compilationUnitElement, currentFile); | 244 visitor.accept(currentFile.compilationUnit); |
| 291 var visitor = new CrawlerVisitor(currentFile, context); | 245 visitor.toVisit.forEach((SourceFile todo) { |
| 292 visitor.accept(currentFile.compilationUnit); | 246 if (!toVisit.contains(todo) && !visited.contains(todo)) { |
| 293 visitor.toVisit.forEach((SourceFile todo) { | 247 toVisit.add(todo); |
| 294 if (!toVisit.contains(todo) && !currentFile.chunk.alreadyVisited(todo)
) { | 248 } |
| 295 toVisit.add(todo); | 249 }); |
| 296 } | |
| 297 }); | |
| 298 visitor.deferred.forEach((SourceFile todo) { | |
| 299 if (!deferred.contains(todo) && !currentFile.chunk.alreadyVisited(todo
)) { | |
| 300 deferred.add(todo); | |
| 301 } | |
| 302 }); | |
| 303 } | |
| 304 } | 250 } |
| 305 } | 251 } |
| 306 } | 252 } |
| 307 | 253 |
| 308 class CrawlerVisitor { | 254 class CrawlerVisitor { |
| 309 List<SourceFile> toVisit = <SourceFile>[]; | 255 List<SourceFile> toVisit = <SourceFile>[]; |
| 310 List<SourceFile> deferred = <SourceFile>[]; | |
| 311 SourceFile currentFile; | 256 SourceFile currentFile; |
| 312 AnalysisContext context; | 257 AnalysisContext context; |
| 313 String currentDir; | 258 String currentDir; |
| 314 | 259 |
| 315 CrawlerVisitor(this.currentFile, this.context); | 260 CrawlerVisitor(this.currentFile, this.context); |
| 316 | 261 |
| 317 void accept(CompilationUnit cu) { | 262 void accept(CompilationUnitElement cu) { |
| 318 cu.directives.forEach((Directive directive) { | 263 cu.enclosingElement.imports.forEach((ImportElement import) => |
| 319 if (directive.element == null) return; // unresolvable, ignore | 264 visitImportElement(import.uri, import.importedLibrary.source)); |
| 320 if (directive is ImportDirective) { | 265 cu.enclosingElement.exports.forEach((ExportElement import) => |
| 321 var import = directive.element; | 266 visitImportElement(import.uri, import.exportedLibrary.source)); |
| 322 visitImportElement( | |
| 323 new Library(import, import.uri, cu, import.importedLibrary.name), | |
| 324 import.importedLibrary.source); | |
| 325 } | |
| 326 if (directive is ExportDirective) { | |
| 327 var import = directive.element; | |
| 328 visitImportElement( | |
| 329 new Library(import, import.uri, cu, import.exportedLibrary.name), | |
| 330 import.exportedLibrary.source); | |
| 331 } | |
| 332 }); | |
| 333 } | 267 } |
| 334 | 268 |
| 335 visitImportElement(Library library, Source source) { | 269 visitImportElement(String uri, Source source) { |
| 336 String uri = library.uri; | |
| 337 if (uri == null) return; // dart:core | 270 if (uri == null) return; // dart:core |
| 338 | 271 |
| 339 String systemImport; | 272 String systemImport; |
| 340 bool isSystem = false; | 273 bool isSystem = false; |
| 341 if (uri.startsWith(DART_PACKAGE_PREFIX)) { | 274 if (uri.startsWith(DART_PACKAGE_PREFIX)) { |
| 342 isSystem = true; | 275 isSystem = true; |
| 343 systemImport = uri; | 276 systemImport = uri; |
| 344 } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) { | 277 } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) { |
| 345 isSystem = true; | 278 isSystem = true; |
| 346 systemImport = currentFile.entryPointImport; | 279 systemImport = currentFile.entryPointImport; |
| 347 } | 280 } |
| 348 // check if it's some internal hidden library | 281 // check if it's some internal hidden library |
| 349 if (isSystem && | 282 if (isSystem && |
| 350 systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) { | 283 systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) { |
| 351 return; | 284 return; |
| 352 } | 285 } |
| 353 | 286 |
| 354 var nextCompilationUnit = context | 287 var nextCompilationUnit = context |
| 355 .resolveCompilationUnit(source, context.computeLibraryElement(source)); | 288 .resolveCompilationUnit(source, context.computeLibraryElement(source)); |
| 356 | 289 |
| 357 SourceFile sourceFile; | |
| 358 if (uri.startsWith(PACKAGE_PREFIX)) { | 290 if (uri.startsWith(PACKAGE_PREFIX)) { |
| 359 sourceFile = new SourceFile(source.toString(), uri, | 291 toVisit.add(new SourceFile(source.toString(), uri, nextCompilationUnit.ele
ment)); |
| 360 nextCompilationUnit, nextCompilationUnit.element, currentFile.chunk); | |
| 361 } else { // relative import. | 292 } else { // relative import. |
| 362 var newImport; | 293 var newImport; |
| 363 if (isSystem) { | 294 if (isSystem) { |
| 364 newImport = systemImport; // original uri | 295 newImport = systemImport; // original uri |
| 365 } else { | 296 } else { |
| 366 // relative import | 297 // relative import |
| 367 String import = currentFile.entryPointImport; | 298 String import = currentFile.entryPointImport; |
| 368 import = import.replaceAll('\\', '/'); // if at all needed, on Windows | 299 import = import.replaceAll('\\', '/'); // if at all needed, on Windows |
| 369 import = import.substring(0, import.lastIndexOf('/')); | 300 import = import.substring(0, import.lastIndexOf('/')); |
| 370 var currentDir = new File(currentFile.canonicalPath).parent.path; | 301 var currentDir = new File(currentFile.canonicalPath).parent.path; |
| 371 currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on W
indows | 302 currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on W
indows |
| 372 if (uri.startsWith('../')) { | 303 if (uri.startsWith('../')) { |
| 373 while (uri.startsWith('../')) { | 304 while (uri.startsWith('../')) { |
| 374 uri = uri.substring('../'.length); | 305 uri = uri.substring('../'.length); |
| 375 import = import.substring(0, import.lastIndexOf('/')); | 306 import = import.substring(0, import.lastIndexOf('/')); |
| 376 currentDir = currentDir.substring(0, currentDir.lastIndexOf('/')); | 307 currentDir = currentDir.substring(0, currentDir.lastIndexOf('/')); |
| 377 } | 308 } |
| 378 } | 309 } |
| 379 newImport = '$import/$uri'; | 310 newImport = '$import/$uri'; |
| 380 } | 311 } |
| 381 sourceFile = new SourceFile( | 312 toVisit.add(new SourceFile( |
| 382 source.toString(), newImport, | 313 source.toString(), newImport, nextCompilationUnit.element)); |
| 383 nextCompilationUnit, nextCompilationUnit.element, currentFile.chunk); | |
| 384 } | |
| 385 if (isDeferredImport(library)) { | |
| 386 var childChunk = currentFile.chunk.createChild(library); | |
| 387 deferred.add(new SourceFile(source.toString(), sourceFile.entryPointImport
, | |
| 388 nextCompilationUnit, nextCompilationUnit.element, childChunk)); | |
| 389 } else { | |
| 390 toVisit.add(sourceFile); | |
| 391 } | 314 } |
| 392 } | 315 } |
| 393 } | 316 } |
| 394 | 317 |
| 395 bool isDeferredImport(Library library) { | |
| 396 var isDeferred = false; | |
| 397 library.element.metadata.forEach((ElementAnnotation annotation) { | |
| 398 if (annotation.element is PropertyAccessorElement) { | |
| 399 PropertyAccessorElement pa = annotation.element; | |
| 400 library.compilationUnit.declarations.forEach((CompilationUnitMember member
) { | |
| 401 if (member is TopLevelVariableDeclaration && member.variables.isConst) { | |
| 402 TopLevelVariableDeclaration topLevel = member; | |
| 403 topLevel.variables.variables.forEach((VariableDeclaration varDecl) { | |
| 404 if (varDecl.initializer is InstanceCreationExpression && | |
| 405 (varDecl.initializer as InstanceCreationExpression).isConst && | |
| 406 (varDecl.initializer as InstanceCreationExpression).staticElemen
t is ConstructorElement && | |
| 407 varDecl.name.name == pa.name) { | |
| 408 ConstructorElement constr = (varDecl.initializer as InstanceCreati
onExpression).staticElement; | |
| 409 if (constr.enclosingElement.library.name == 'dart.async' && | |
| 410 constr.enclosingElement.type.name == 'DeferredLibrary') { | |
| 411 isDeferred = true; | |
| 412 } | |
| 413 } | |
| 414 }); | |
| 415 } | |
| 416 }); | |
| 417 } | |
| 418 }); | |
| 419 return isDeferred; | |
| 420 } | |
| 421 | |
| 422 class Library { | |
| 423 final Element element; | |
| 424 final String uri; | |
| 425 final CompilationUnit compilationUnit; | |
| 426 final String name; | |
| 427 | |
| 428 Library(this.element, this.uri, this.compilationUnit, this.name); | |
| 429 | |
| 430 toString() => 'Library[$name]'; | |
| 431 } | |
| 432 | |
| 433 class Chunk { | |
| 434 final Chunk parent; | |
| 435 Library library; | |
| 436 List<SourceFile> _visited = <SourceFile>[]; | |
| 437 | |
| 438 addVisited(SourceFile file) { | |
| 439 _visited.add(file); | |
| 440 } | |
| 441 | |
| 442 bool alreadyVisited(SourceFile file) { | |
| 443 var cursor = this; | |
| 444 while (cursor != null) { | |
| 445 if (cursor._visited.contains(file)) { | |
| 446 return true; | |
| 447 } | |
| 448 cursor = cursor.parent; | |
| 449 } | |
| 450 return false; | |
| 451 } | |
| 452 | |
| 453 Chunk([this.parent, this.library]); | |
| 454 | |
| 455 Chunk createChild(Library library) => new Chunk(this, library); | |
| 456 | |
| 457 toString() => 'Chunk[$library]'; | |
| 458 } | |
| 459 | |
| 460 class SourceFile { | 318 class SourceFile { |
| 461 String canonicalPath; | 319 String canonicalPath; |
| 462 String entryPointImport; | 320 String entryPointImport; |
| 463 CompilationUnit compilationUnit; | 321 CompilationUnitElement compilationUnit; |
| 464 CompilationUnitElement compilationUnitElement; | |
| 465 Chunk chunk; | |
| 466 | 322 |
| 467 SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit, | 323 SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit); |
| 468 this.compilationUnitElement, this.chunk); | |
| 469 | 324 |
| 470 operator ==(o) { | 325 operator ==(o) { |
| 471 if (o is String) return o == canonicalPath; | 326 if (o is String) return o == canonicalPath; |
| 472 if (o is! SourceFile) return false; | 327 if (o is! SourceFile) return false; |
| 473 return o.canonicalPath == canonicalPath; | 328 return o.canonicalPath == canonicalPath; |
| 474 } | 329 } |
| 475 } | 330 } |
| OLD | NEW |