| 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) { |
| 34 var c = new SourceCrawler(pathToSdk, packageRoots); | 47 var c = new SourceCrawler(pathToSdk, packageRoots); |
| 35 List<String> imports = <String>[]; | 48 List<String> imports = <String>[]; |
| 36 List<ClassElement> typeFactoryTypes = <ClassElement>[]; | 49 Map<Chunk, List<ClassElement>> typeFactoryTypes = <Chunk, List<ClassElement>>{
}; |
| 37 Map<String, String> typeToImport = new Map<String, String>(); | 50 Map<String, String> typeToImport = new Map<String, String>(); |
| 38 c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source
) { | 51 c.crawl(entryPoint, (CompilationUnitElement compilationUnit, SourceFile source
) { |
| 39 new CompilationUnitVisitor(c.context, source, classAnnotations, imports, | 52 new CompilationUnitVisitor(c.context, source, classAnnotations, imports, |
| 40 typeToImport, typeFactoryTypes).visit(compilationUnit); | 53 typeToImport, typeFactoryTypes).visit(compilationUnit, source); |
| 41 }); | 54 }); |
| 42 var code = printLibraryCode(typeToImport, imports, typeFactoryTypes); | 55 return printLibraryCode(typeToImport, imports, typeFactoryTypes); |
| 43 new File(output).writeAsStringSync(code); | |
| 44 } | 56 } |
| 45 | 57 |
| 46 String printLibraryCode(Map<String, String> typeToImport, List<String> imports, | 58 Map<Chunk, String> printLibraryCode(Map<String, String> typeToImport, |
| 47 List<ClassElement> typeFactoryTypes) { | 59 List<String> imports, Map<Chunk, List<ClassElement>> typeFactoryTypes) { |
| 48 List<String> requiredImports = <String>[]; | 60 Map<Chunk, StringBuffer> factories = <Chunk, StringBuffer>{}; |
| 49 StringBuffer factories = new StringBuffer(); | 61 Map<Chunk, String> result = <Chunk, String>{}; |
| 62 typeFactoryTypes.forEach((Chunk chunk, List<ClassElement> classes) { |
| 63 List<String> requiredImports = <String>[]; |
| 64 String resolveClassIdentifier(InterfaceType type) { |
| 65 if (type.element.library.isDartCore) { |
| 66 return type.name; |
| 67 } |
| 68 String import = typeToImport[getCanonicalName(type)]; |
| 69 if (!requiredImports.contains(import)) { |
| 70 requiredImports.add(import); |
| 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 }); |
| 50 | 112 |
| 51 String resolveClassIdentifier(InterfaceType type) { | 113 return result; |
| 52 if (type.element.library.isDartCore) { | 114 } |
| 53 return type.name; | 115 |
| 54 } | 116 _isParameterized(ParameterElement param) { |
| 55 String import = typeToImport[getCanonicalName(type)]; | 117 String typeName = param.type.toString(); |
| 56 if (!requiredImports.contains(import)) { | 118 |
| 57 requiredImports.add(import); | 119 if (typeName.indexOf('<') > -1) { |
| 58 } | 120 String parameters = |
| 59 return 'import_${imports.indexOf(import)}.${type.name}'; | 121 typeName.substring(typeName.indexOf('<') + 1, typeName.length - 1); |
| 122 return parameters.split(', ').any((p) => p != 'dynamic'); |
| 60 } | 123 } |
| 61 | 124 return false; |
| 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 } | 125 } |
| 92 | 126 |
| 93 class CompilationUnitVisitor { | 127 class CompilationUnitVisitor { |
| 94 List<String> imports; | 128 List<String> imports; |
| 95 Map<String, String> typeToImport; | 129 Map<String, String> typeToImport; |
| 96 List<ClassElement> typeFactoryTypes; | 130 Map<Chunk, List<ClassElement>> typeFactoryTypes; |
| 97 List<String> classAnnotations; | 131 List<String> classAnnotations; |
| 98 SourceFile source; | 132 SourceFile source; |
| 99 AnalysisContext context; | 133 AnalysisContext context; |
| 100 | 134 |
| 101 CompilationUnitVisitor(this.context, this.source, | 135 CompilationUnitVisitor(this.context, this.source, |
| 102 this.classAnnotations, this.imports, this.typeToImport, | 136 this.classAnnotations, this.imports, this.typeToImport, |
| 103 this.typeFactoryTypes); | 137 this.typeFactoryTypes); |
| 104 | 138 |
| 105 visit(CompilationUnitElement compilationUnit) { | 139 visit(CompilationUnitElement compilationUnit, SourceFile source) { |
| 106 visitLibrary(compilationUnit.enclosingElement); | 140 visitLibrary(compilationUnit.enclosingElement, source); |
| 107 | 141 |
| 108 List<ClassElement> types = <ClassElement>[]; | 142 List<ClassElement> types = <ClassElement>[]; |
| 109 types.addAll(compilationUnit.types); | 143 types.addAll(compilationUnit.types); |
| 110 | 144 |
| 111 for (CompilationUnitElement part in compilationUnit.enclosingElement.parts)
{ | 145 for (CompilationUnitElement part in compilationUnit.enclosingElement.parts)
{ |
| 112 types.addAll(part.types); | 146 types.addAll(part.types); |
| 113 } | 147 } |
| 114 | 148 |
| 115 types.forEach(visitClassElement); | 149 types.forEach((clazz) => visitClassElement(clazz, source)); |
| 116 } | 150 } |
| 117 | 151 |
| 118 visitLibrary(LibraryElement libElement) { | 152 visitLibrary(LibraryElement libElement, SourceFile source) { |
| 119 CompilationUnit resolvedUnit = context | 153 CompilationUnit resolvedUnit = context |
| 120 .resolveCompilationUnit(libElement.source, libElement); | 154 .resolveCompilationUnit(libElement.source, libElement); |
| 121 | 155 |
| 122 resolvedUnit.directives.forEach((Directive directive) { | 156 resolvedUnit.directives.forEach((Directive directive) { |
| 123 if (directive is LibraryDirective) { | 157 if (directive is LibraryDirective) { |
| 124 LibraryDirective library = directive; | 158 LibraryDirective library = directive; |
| 125 int annotationIdx = 0; | 159 int annotationIdx = 0; |
| 126 library.metadata.forEach((Annotation ann) { | 160 library.metadata.forEach((Annotation ann) { |
| 127 if (ann.element is ConstructorElement && | 161 if (ann.element is ConstructorElement && |
| 128 getQualifiedName( | 162 getQualifiedName( |
| 129 (ann.element as ConstructorElement).enclosingElement.type) == | 163 (ann.element as ConstructorElement).enclosingElement.type) == |
| 130 'di.annotations.Injectables') { | 164 'di.annotations.Injectables') { |
| 131 var listLiteral = | 165 var listLiteral = |
| 132 library.metadata[annotationIdx].arguments.arguments.first; | 166 library.metadata[annotationIdx].arguments.arguments.first; |
| 133 for (Expression expr in listLiteral.elements) { | 167 for (Expression expr in listLiteral.elements) { |
| 134 Element element = (expr as SimpleIdentifier).bestElement; | 168 Element element = (expr as SimpleIdentifier).bestElement; |
| 135 if (element == null || element is! ClassElement) { | 169 if (element == null || element is! ClassElement) { |
| 136 throw 'Unable to resolve type "$expr" from @Injectables ' | 170 throw 'Unable to resolve type "$expr" from @Injectables ' |
| 137 'in ${library.element.source}'; | 171 'in ${library.element.source}'; |
| 138 } | 172 } |
| 139 typeFactoryTypes.add(element as ClassElement); | 173 if (typeFactoryTypes[source.chunk] == null) { |
| 174 typeFactoryTypes[source.chunk] = <ClassElement>[]; |
| 175 } |
| 176 if (!typeFactoryTypes[source.chunk].contains(element)) { |
| 177 typeFactoryTypes[source.chunk].add(element as ClassElement); |
| 178 } |
| 140 } | 179 } |
| 141 } | 180 } |
| 142 annotationIdx++; | 181 annotationIdx++; |
| 143 }); | 182 }); |
| 144 } | 183 } |
| 145 }); | 184 }); |
| 146 } | 185 } |
| 147 | 186 |
| 148 visitClassElement(ClassElement classElement) { | 187 visitClassElement(ClassElement classElement, SourceFile source) { |
| 149 if (classElement.name.startsWith('_')) { | 188 if (classElement.name.startsWith('_')) { |
| 150 return; // ignore private classes. | 189 return; // ignore private classes. |
| 151 } | 190 } |
| 152 typeToImport[getCanonicalName(classElement.type)] = | 191 typeToImport[getCanonicalName(classElement.type)] = |
| 153 source.entryPointImport; | 192 source.entryPointImport; |
| 154 if (!imports.contains(source.entryPointImport)) { | 193 if (!imports.contains(source.entryPointImport)) { |
| 155 imports.add(source.entryPointImport); | 194 imports.add(source.entryPointImport); |
| 156 } | 195 } |
| 157 for (ElementAnnotation ann in classElement.metadata) { | 196 for (ElementAnnotation ann in classElement.metadata) { |
| 158 if (ann.element is ConstructorElement) { | 197 if (ann.element is ConstructorElement) { |
| 159 ConstructorElement con = ann.element; | 198 ConstructorElement con = ann.element; |
| 160 if (classAnnotations | 199 if (classAnnotations |
| 161 .contains(getQualifiedName(con.enclosingElement.type))) { | 200 .contains(getQualifiedName(con.enclosingElement.type))) { |
| 162 typeFactoryTypes.add(classElement); | 201 if (typeFactoryTypes[source.chunk] == null) { |
| 202 typeFactoryTypes[source.chunk] = <ClassElement>[]; |
| 203 } |
| 204 if (!typeFactoryTypes[source.chunk].contains(classElement)) { |
| 205 typeFactoryTypes[source.chunk].add(classElement); |
| 206 } |
| 163 } | 207 } |
| 164 } | 208 } |
| 165 } | 209 } |
| 166 } | 210 } |
| 167 } | 211 } |
| 168 | 212 |
| 169 String getQualifiedName(InterfaceType type) { | 213 String getQualifiedName(InterfaceType type) { |
| 170 var lib = type.element.library.displayName; | 214 var lib = type.element.library.displayName; |
| 171 var name = type.name; | 215 var name = type.name; |
| 172 return lib == null ? name : '$lib.$name'; | 216 return lib == null ? name : '$lib.$name'; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 ChangeSet changeSet = new ChangeSet(); | 269 ChangeSet changeSet = new ChangeSet(); |
| 226 changeSet.added(source); | 270 changeSet.added(source); |
| 227 context.applyChanges(changeSet); | 271 context.applyChanges(changeSet); |
| 228 LibraryElement rootLib = context.computeLibraryElement(source); | 272 LibraryElement rootLib = context.computeLibraryElement(source); |
| 229 CompilationUnit resolvedUnit = | 273 CompilationUnit resolvedUnit = |
| 230 context.resolveCompilationUnit(source, rootLib); | 274 context.resolveCompilationUnit(source, rootLib); |
| 231 | 275 |
| 232 var sourceFile = new SourceFile( | 276 var sourceFile = new SourceFile( |
| 233 entryPointFile.getAbsolutePath(), | 277 entryPointFile.getAbsolutePath(), |
| 234 entryPointImport, | 278 entryPointImport, |
| 235 resolvedUnit.element); | 279 resolvedUnit, |
| 236 List<SourceFile> visited = <SourceFile>[]; | 280 resolvedUnit.element, |
| 281 new Chunk()); // root chunk |
| 237 List<SourceFile> toVisit = <SourceFile>[sourceFile]; | 282 List<SourceFile> toVisit = <SourceFile>[sourceFile]; |
| 283 List<SourceFile> deferred = <SourceFile>[sourceFile]; |
| 238 | 284 |
| 239 while (toVisit.isNotEmpty) { | 285 while (deferred.isNotEmpty) { |
| 240 SourceFile currentFile = toVisit.removeAt(0); | 286 toVisit.add(deferred.removeAt(0)); |
| 241 visited.add(currentFile); | 287 while (toVisit.isNotEmpty) { |
| 242 _visitor(currentFile.compilationUnit, currentFile); | 288 SourceFile currentFile = toVisit.removeAt(0); |
| 243 var visitor = new CrawlerVisitor(currentFile, context); | 289 currentFile.chunk.addVisited(currentFile); |
| 244 visitor.accept(currentFile.compilationUnit); | 290 _visitor(currentFile.compilationUnitElement, currentFile); |
| 245 visitor.toVisit.forEach((SourceFile todo) { | 291 var visitor = new CrawlerVisitor(currentFile, context); |
| 246 if (!toVisit.contains(todo) && !visited.contains(todo)) { | 292 visitor.accept(currentFile.compilationUnit); |
| 247 toVisit.add(todo); | 293 visitor.toVisit.forEach((SourceFile todo) { |
| 248 } | 294 if (!toVisit.contains(todo) && !currentFile.chunk.alreadyVisited(todo)
) { |
| 249 }); | 295 toVisit.add(todo); |
| 296 } |
| 297 }); |
| 298 visitor.deferred.forEach((SourceFile todo) { |
| 299 if (!deferred.contains(todo) && !currentFile.chunk.alreadyVisited(todo
)) { |
| 300 deferred.add(todo); |
| 301 } |
| 302 }); |
| 303 } |
| 250 } | 304 } |
| 251 } | 305 } |
| 252 } | 306 } |
| 253 | 307 |
| 254 class CrawlerVisitor { | 308 class CrawlerVisitor { |
| 255 List<SourceFile> toVisit = <SourceFile>[]; | 309 List<SourceFile> toVisit = <SourceFile>[]; |
| 310 List<SourceFile> deferred = <SourceFile>[]; |
| 256 SourceFile currentFile; | 311 SourceFile currentFile; |
| 257 AnalysisContext context; | 312 AnalysisContext context; |
| 258 String currentDir; | 313 String currentDir; |
| 259 | 314 |
| 260 CrawlerVisitor(this.currentFile, this.context); | 315 CrawlerVisitor(this.currentFile, this.context); |
| 261 | 316 |
| 262 void accept(CompilationUnitElement cu) { | 317 void accept(CompilationUnit cu) { |
| 263 cu.enclosingElement.imports.forEach((ImportElement import) => | 318 cu.directives.forEach((Directive directive) { |
| 264 visitImportElement(import.uri, import.importedLibrary.source)); | 319 if (directive.element == null) return; // unresolvable, ignore |
| 265 cu.enclosingElement.exports.forEach((ExportElement import) => | 320 if (directive is ImportDirective) { |
| 266 visitImportElement(import.uri, import.exportedLibrary.source)); | 321 var import = directive.element; |
| 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 }); |
| 267 } | 333 } |
| 268 | 334 |
| 269 visitImportElement(String uri, Source source) { | 335 visitImportElement(Library library, Source source) { |
| 336 String uri = library.uri; |
| 270 if (uri == null) return; // dart:core | 337 if (uri == null) return; // dart:core |
| 271 | 338 |
| 272 String systemImport; | 339 String systemImport; |
| 273 bool isSystem = false; | 340 bool isSystem = false; |
| 274 if (uri.startsWith(DART_PACKAGE_PREFIX)) { | 341 if (uri.startsWith(DART_PACKAGE_PREFIX)) { |
| 275 isSystem = true; | 342 isSystem = true; |
| 276 systemImport = uri; | 343 systemImport = uri; |
| 277 } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) { | 344 } else if (currentFile.entryPointImport.startsWith(DART_PACKAGE_PREFIX)) { |
| 278 isSystem = true; | 345 isSystem = true; |
| 279 systemImport = currentFile.entryPointImport; | 346 systemImport = currentFile.entryPointImport; |
| 280 } | 347 } |
| 281 // check if it's some internal hidden library | 348 // check if it's some internal hidden library |
| 282 if (isSystem && | 349 if (isSystem && |
| 283 systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) { | 350 systemImport.substring(DART_PACKAGE_PREFIX.length).startsWith('_')) { |
| 284 return; | 351 return; |
| 285 } | 352 } |
| 286 | 353 |
| 287 var nextCompilationUnit = context | 354 var nextCompilationUnit = context |
| 288 .resolveCompilationUnit(source, context.computeLibraryElement(source)); | 355 .resolveCompilationUnit(source, context.computeLibraryElement(source)); |
| 289 | 356 |
| 357 SourceFile sourceFile; |
| 290 if (uri.startsWith(PACKAGE_PREFIX)) { | 358 if (uri.startsWith(PACKAGE_PREFIX)) { |
| 291 toVisit.add(new SourceFile(source.toString(), uri, nextCompilationUnit.ele
ment)); | 359 sourceFile = new SourceFile(source.toString(), uri, |
| 360 nextCompilationUnit, nextCompilationUnit.element, currentFile.chunk); |
| 292 } else { // relative import. | 361 } else { // relative import. |
| 293 var newImport; | 362 var newImport; |
| 294 if (isSystem) { | 363 if (isSystem) { |
| 295 newImport = systemImport; // original uri | 364 newImport = systemImport; // original uri |
| 296 } else { | 365 } else { |
| 297 // relative import | 366 // relative import |
| 298 String import = currentFile.entryPointImport; | 367 String import = currentFile.entryPointImport; |
| 299 import = import.replaceAll('\\', '/'); // if at all needed, on Windows | 368 import = import.replaceAll('\\', '/'); // if at all needed, on Windows |
| 300 import = import.substring(0, import.lastIndexOf('/')); | 369 import = import.substring(0, import.lastIndexOf('/')); |
| 301 var currentDir = new File(currentFile.canonicalPath).parent.path; | 370 var currentDir = new File(currentFile.canonicalPath).parent.path; |
| 302 currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on W
indows | 371 currentDir = currentDir.replaceAll('\\', '/'); // if at all needed, on W
indows |
| 303 if (uri.startsWith('../')) { | 372 if (uri.startsWith('../')) { |
| 304 while (uri.startsWith('../')) { | 373 while (uri.startsWith('../')) { |
| 305 uri = uri.substring('../'.length); | 374 uri = uri.substring('../'.length); |
| 306 import = import.substring(0, import.lastIndexOf('/')); | 375 import = import.substring(0, import.lastIndexOf('/')); |
| 307 currentDir = currentDir.substring(0, currentDir.lastIndexOf('/')); | 376 currentDir = currentDir.substring(0, currentDir.lastIndexOf('/')); |
| 308 } | 377 } |
| 309 } | 378 } |
| 310 newImport = '$import/$uri'; | 379 newImport = '$import/$uri'; |
| 311 } | 380 } |
| 312 toVisit.add(new SourceFile( | 381 sourceFile = new SourceFile( |
| 313 source.toString(), newImport, nextCompilationUnit.element)); | 382 source.toString(), newImport, |
| 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); |
| 314 } | 391 } |
| 315 } | 392 } |
| 316 } | 393 } |
| 317 | 394 |
| 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 |
| 318 class SourceFile { | 460 class SourceFile { |
| 319 String canonicalPath; | 461 String canonicalPath; |
| 320 String entryPointImport; | 462 String entryPointImport; |
| 321 CompilationUnitElement compilationUnit; | 463 CompilationUnit compilationUnit; |
| 464 CompilationUnitElement compilationUnitElement; |
| 465 Chunk chunk; |
| 322 | 466 |
| 323 SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit); | 467 SourceFile(this.canonicalPath, this.entryPointImport, this.compilationUnit, |
| 468 this.compilationUnitElement, this.chunk); |
| 324 | 469 |
| 325 operator ==(o) { | 470 operator ==(o) { |
| 326 if (o is String) return o == canonicalPath; | 471 if (o is String) return o == canonicalPath; |
| 327 if (o is! SourceFile) return false; | 472 if (o is! SourceFile) return false; |
| 328 return o.canonicalPath == canonicalPath; | 473 return o.canonicalPath == canonicalPath; |
| 329 } | 474 } |
| 330 } | 475 } |
| OLD | NEW |