Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * [CompilerTask] for loading libraries and setting up the import/export scopes. | |
| 7 */ | |
| 8 abstract class LibraryLoader implements CompilerTask { | |
| 9 /** | |
| 10 * Loads the library located at [uri] and returns its [LibraryElement]. | |
| 11 * | |
| 12 * If the library is not already loaded, the method creates the | |
| 13 * [LibraryElement] for the library and computes the import/export scope, | |
| 14 * loading and computing the import/export scopes of all required libraries in | |
| 15 * the process. The method handles cyclic dependency between libraries. | |
| 16 * | |
| 17 * This is the main entry point for [LibraryLoader]. | |
| 18 */ | |
| 19 abstract LibraryElement loadLibrary(Uri uri, Node node, Uri canonicalUri); | |
| 20 | |
| 21 // TODO(johnniwinther): Remove this when patches don't need special parsing. | |
| 22 abstract void loadLibraryFromTag(LibraryDependencyHandler handler, | |
| 23 LibraryElement library, | |
| 24 LibraryDependency tag); | |
|
ahe
2012/10/10 12:06:27
Add newline.
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 25 /** | |
| 26 * Adds the elements in the export scope if [importedLibrary] to the import | |
|
ahe
2012/10/10 12:06:27
if -> of
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 27 * scope of [importingLibrary]. | |
| 28 */ | |
| 29 // TODO(johnniwinther): Move handling of 'js_helper' to the library loader | |
| 30 // to remove this method from the [LibraryLoader] interface. | |
| 31 abstract void importLibrary(Compiler compiler, | |
| 32 LibraryElement importingLibrary, | |
| 33 LibraryElement importedLibrary, | |
| 34 Import tag); | |
| 35 } | |
| 36 | |
| 37 /** | |
| 38 * Implementation class for [LibraryLoader]. The distinction between | |
| 39 * [LibraryLoader] and [LibraryLoaderTask] is made to hide internal members from | |
| 40 * the [LibraryLoader] interface. | |
| 41 */ | |
| 42 class LibraryLoaderTask extends CompilerTask implements LibraryLoader { | |
|
ahe
2012/10/10 12:06:27
I feel it would be simpler to extend LibraryLoader
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 43 LibraryLoaderTask(Compiler compiler) : super(compiler); | |
| 44 String get name => 'LibraryLoader'; | |
| 45 | |
| 46 final Map<String, LibraryElement> libraryNames = | |
| 47 new Map<String, LibraryElement>(); | |
| 48 | |
| 49 LibraryDependencyHandler currentHandler; | |
| 50 | |
| 51 /** | |
| 52 * Loads the library located at [uri] and returns its [LibraryElement]. | |
| 53 * | |
| 54 * If the library is not already loaded, the method creates the | |
| 55 * [LibraryElement] for the library and computes the import/export scope, | |
| 56 * loading and computing the import/export scopes of all required libraries in | |
| 57 * the process. The method handles cyclic dependency between libraries. | |
| 58 * | |
| 59 * This is the main entry point for [LibraryLoaderTask]. | |
| 60 */ | |
| 61 LibraryElement loadLibrary(Uri uri, Node node, Uri canonicalUri) { | |
|
ahe
2012/10/10 12:06:27
Do you have to repeat documentation like this?
Johnni Winther
2012/10/10 14:56:00
No. Docs removed.
| |
| 62 return measure(() { | |
| 63 assert(currentHandler == null); | |
| 64 currentHandler = new LibraryDependencyHandler(compiler); | |
| 65 LibraryElement library = | |
| 66 loadLibraryInternal(currentHandler, uri, node, canonicalUri); | |
| 67 currentHandler.computeExports(); | |
| 68 currentHandler = null; | |
| 69 return library; | |
| 70 }); | |
| 71 } | |
| 72 | |
| 73 /** | |
| 74 * Processes the library tags in [library]. | |
| 75 * | |
| 76 * The imported/exported libraries are loaded and processed recursively but | |
| 77 * the import/export scopes are not set up. | |
| 78 */ | |
| 79 void processLibraryTags(LibraryDependencyHandler handler, | |
| 80 LibraryElement library) { | |
| 81 int tagState = TagState.NO_TAG_SEEN; | |
| 82 | |
| 83 /** | |
| 84 * If [value] is less than [tagState] complain and return | |
| 85 * [tagState]. Otherwise return the new value for [tagState] | |
| 86 * (transition function for state machine). | |
| 87 */ | |
| 88 int checkTag(int value, LibraryTag tag) { | |
| 89 if (tagState > value) { | |
| 90 compiler.reportError(tag, 'out of order'); | |
| 91 return tagState; | |
| 92 } | |
| 93 return TagState.NEXT[value]; | |
| 94 } | |
| 95 | |
| 96 bool importsDartCore = false; | |
| 97 var libraryDependencies = new LinkBuilder<LibraryDependency>(); | |
| 98 Uri base = library.entryCompilationUnit.script.uri; | |
| 99 for (LibraryTag tag in library.tags.reverse()) { | |
| 100 if (tag.isImport) { | |
| 101 tagState = checkTag(TagState.IMPORT_OR_EXPORT, tag); | |
| 102 if (tag.combinators != null) { | |
| 103 compiler.unimplemented('combinators', node: tag.combinators); | |
| 104 } | |
| 105 // It is not safe to import other libraries at this point as | |
|
ahe
2012/10/10 12:06:27
Isn't this comment outdated by now?
Johnni Winther
2012/10/10 14:56:00
Yes. Removed.
| |
| 106 // another library could then observe the current library | |
| 107 // before it fully declares all the members that are sourced | |
| 108 // in. | |
| 109 if (tag.uri.dartString.slowToString() == 'dart:core') { | |
|
ahe
2012/10/10 12:06:27
We should convert tag.uri to an instance of Uri ea
| |
| 110 importsDartCore = true; | |
| 111 } | |
| 112 libraryDependencies.addLast(tag); | |
| 113 } else if (tag.isExport) { | |
| 114 tagState = checkTag(TagState.IMPORT_OR_EXPORT, tag); | |
| 115 libraryDependencies.addLast(tag); | |
| 116 } else if (tag.isLibraryName) { | |
| 117 tagState = checkTag(TagState.LIBRARY, tag); | |
| 118 if (library.libraryTag !== null) { | |
| 119 compiler.cancel("duplicated library declaration", node: tag); | |
| 120 } else { | |
| 121 library.libraryTag = tag; | |
| 122 } | |
| 123 checkDuplicatedLibraryName(library); | |
| 124 } else if (tag.isPart) { | |
| 125 StringNode uri = tag.uri; | |
| 126 Uri resolved = base.resolve(uri.dartString.slowToString()); | |
| 127 tagState = checkTag(TagState.SOURCE, tag); | |
| 128 loadPart(tag, resolved, library); | |
| 129 } else { | |
| 130 compiler.internalError("Unhandled library tag.", node: tag); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 // Apply patch, if any. | |
| 135 if (library.uri.scheme == 'dart') { | |
| 136 patchDartLibrary(handler, library, library.uri.path); | |
| 137 } | |
| 138 | |
| 139 // Now that we have processed all the source tags, it is safe to | |
|
ahe
2012/10/10 12:06:27
Is this still relevant?
Johnni Winther
2012/10/10 14:56:00
No. Removed.
| |
| 140 // start loading other libraries. | |
| 141 | |
| 142 // Import dart:core if not already imported. | |
| 143 if (!importsDartCore && !isDartCore(library.uri)) { | |
| 144 handler.registerDependency(library, null, loadCoreLibrary(handler)); | |
| 145 } | |
| 146 | |
| 147 for (LibraryDependency tag in libraryDependencies.toLink()) { | |
| 148 loadLibraryFromTag(handler, library, tag); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 void checkDuplicatedLibraryName(LibraryElement library) { | |
| 153 LibraryTag tag = library.libraryTag; | |
| 154 if (tag != null) { | |
| 155 String name = library.getLibraryOrScriptName(); | |
| 156 LibraryElement existing = | |
| 157 libraryNames.putIfAbsent(name, () => library); | |
| 158 if (existing !== library) { | |
| 159 Uri uri = library.entryCompilationUnit.script.uri; | |
| 160 compiler.reportMessage( | |
| 161 compiler.spanFromNode(tag.name, uri), | |
| 162 MessageKind.DUPLICATED_LIBRARY_NAME.error([name]), | |
| 163 api.Diagnostic.WARNING); | |
| 164 Uri existingUri = existing.entryCompilationUnit.script.uri; | |
| 165 compiler.reportMessage( | |
| 166 compiler.spanFromNode(existing.libraryTag.name, existingUri), | |
| 167 MessageKind.DUPLICATED_LIBRARY_NAME.error([name]), | |
| 168 api.Diagnostic.WARNING); | |
| 169 } | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 bool isDartCore(Uri uri) => uri.scheme == "dart" && uri.path == "core"; | |
| 174 | |
| 175 /** | |
| 176 * Lazily loads and returns the [LibraryElement] for the dart:core library. | |
| 177 */ | |
| 178 LibraryElement loadCoreLibrary(LibraryDependencyHandler handler) { | |
| 179 if (compiler.coreLibrary === null) { | |
| 180 Uri coreUri = new Uri.fromComponents(scheme: 'dart', path: 'core'); | |
| 181 compiler.coreLibrary = | |
| 182 loadLibraryInternal(handler, coreUri, null, coreUri); | |
| 183 } | |
| 184 return compiler.coreLibrary; | |
| 185 } | |
| 186 | |
| 187 void patchDartLibrary(LibraryDependencyHandler handler, | |
| 188 LibraryElement library, String dartLibraryPath) { | |
| 189 if (library.isPatched) return; | |
| 190 Uri patchUri = compiler.resolvePatchUri(dartLibraryPath); | |
| 191 if (patchUri !== null) { | |
| 192 compiler.patchParser.patchLibrary(handler, patchUri, library); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 /** | |
| 197 * Handle a part tag in the scope of [library]. The [path] given is used as | |
| 198 * is, any resolution should be done beforehand. | |
|
ahe
2012/10/10 12:06:27
resolution -> URI resolution.
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 199 */ | |
| 200 void loadPart(Part part, Uri path, LibraryElement library) { | |
|
ahe
2012/10/10 12:06:27
if (!path.isAbsolute()) throw ArgumenError(path);
ahe
2012/10/10 12:06:27
Rename method to "scanPart" or "scanCompilationUni
Johnni Winther
2012/10/10 14:56:00
Done.
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 201 Script sourceScript = compiler.readScript(path, part); | |
| 202 CompilationUnitElement unit = | |
| 203 new CompilationUnitElement(sourceScript, library); | |
| 204 compiler.withCurrentElement(unit, () => compiler.scanner.scan(unit)); | |
| 205 } | |
| 206 | |
| 207 /** | |
| 208 * Handle an import/export tag by loading the referenced library and | |
| 209 * registering its dependency in [handler] for the computation of the import/ | |
| 210 * export scope. | |
| 211 */ | |
| 212 void loadLibraryFromTag(LibraryDependencyHandler handler, | |
| 213 LibraryElement library, | |
| 214 LibraryDependency tag) { | |
| 215 Uri base = library.entryCompilationUnit.script.uri; | |
| 216 Uri resolved = base.resolve(tag.uri.dartString.slowToString()); | |
| 217 LibraryElement loadedLibrary = | |
| 218 loadLibraryInternal(handler, resolved, tag.uri, resolved); | |
| 219 handler.registerDependency(library, tag, loadedLibrary); | |
| 220 | |
| 221 if (!loadedLibrary.hasLibraryName()) { | |
| 222 compiler.withCurrentElement(library, () { | |
| 223 compiler.reportError(tag === null ? null : tag.uri, | |
| 224 'no library tag found in ${loadedLibrary.uri}'); | |
| 225 }); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 LibraryElement loadLibraryInternal(LibraryDependencyHandler handler, | |
| 230 Uri uri, Node node, Uri canonicalUri) { | |
| 231 bool newLibrary = false; | |
| 232 LibraryElement createLibrary() { | |
| 233 newLibrary = true; | |
| 234 Script script = compiler.readScript(uri, node); | |
| 235 LibraryElement element = new LibraryElement(script, canonicalUri); | |
| 236 handler.registerNewLibrary(element); | |
| 237 native.maybeEnableNative(compiler, element, uri); | |
| 238 return element; | |
| 239 } | |
| 240 LibraryElement library; | |
| 241 if (canonicalUri === null) { | |
| 242 library = createLibrary(); | |
| 243 } else { | |
| 244 library = compiler.libraries.putIfAbsent(canonicalUri.toString(), | |
| 245 createLibrary); | |
| 246 } | |
| 247 if (newLibrary) { | |
| 248 compiler.withCurrentElement(library, () { | |
| 249 compiler.scanner.scanLibrary(library); | |
| 250 processLibraryTags(handler, library); | |
| 251 handler.registerLibraryExports(library); | |
| 252 compiler.onLibraryLoaded(library, uri); | |
|
ahe
2012/10/10 12:06:27
So this is called before the library is fully load
Johnni Winther
2012/10/10 14:56:00
Yes. Comment added on [onLibraryLoaded].
| |
| 253 }); | |
| 254 } | |
| 255 return library; | |
| 256 } | |
| 257 | |
| 258 void importLibrary(Compiler compiler, | |
| 259 LibraryElement importingLibrary, | |
| 260 LibraryElement importedLibrary, | |
| 261 Import tag) { | |
| 262 assert(invariant(importingLibrary, | |
| 263 importedLibrary.exportsHandled, | |
| 264 message: 'Exports not handled on $importedLibrary')); | |
| 265 if (!importedLibrary.hasLibraryName()) { | |
| 266 compiler.withCurrentElement(importingLibrary, () { | |
| 267 compiler.reportError(tag === null ? null : tag.uri, | |
| 268 'no #library tag found in ${importedLibrary.uri}'); | |
|
ahe
2012/10/10 12:06:27
Why is this error reported twice?
Johnni Winther
2012/10/10 14:56:00
Bad rebase. Removed from here.
| |
| 269 }); | |
| 270 } | |
| 271 if (tag !== null && tag.prefix !== null) { | |
| 272 SourceString prefix = tag.prefix.source; | |
| 273 Element e = importingLibrary.find(prefix); | |
| 274 if (e === null) { | |
| 275 e = new PrefixElement(prefix, importingLibrary.entryCompilationUnit, | |
| 276 tag.getBeginToken()); | |
| 277 importingLibrary.addToScope(e, compiler); | |
| 278 } | |
| 279 if (e.kind !== ElementKind.PREFIX) { | |
| 280 compiler.withCurrentElement(e, () { | |
| 281 compiler.reportWarning(new Identifier(e.position()), | |
| 282 'duplicated definition'); | |
| 283 }); | |
| 284 compiler.reportError(tag.prefix, 'duplicate definition'); | |
| 285 } | |
| 286 PrefixElement prefixElement = e; | |
| 287 importedLibrary.forEachExport((Element element) { | |
| 288 // TODO(johnniwinther): Handle show and hide combinators. | |
| 289 Element existing = | |
| 290 prefixElement.imported.putIfAbsent(element.name, () => element); | |
| 291 if (existing !== element) { | |
| 292 compiler.withCurrentElement(existing, () { | |
| 293 compiler.reportWarning(new Identifier(existing.position()), | |
|
ahe
2012/10/10 12:06:27
This can be cleaned up. See checkDuplicatedLibrary
Johnni Winther
2012/10/10 14:56:00
Added a TODO.
| |
| 294 'duplicated import'); | |
| 295 }); | |
| 296 compiler.withCurrentElement(element, () { | |
| 297 compiler.reportError(new Identifier(element.position()), | |
| 298 'duplicated import'); | |
| 299 }); | |
| 300 } | |
| 301 }); | |
| 302 } else { | |
| 303 importedLibrary.forEachExport((Element element) { | |
| 304 compiler.withCurrentElement(element, () { | |
| 305 // TODO(johnniwinther): Handle show and hide combinators. | |
| 306 importingLibrary.addImport(element, compiler); | |
| 307 }); | |
| 308 }); | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 | |
| 314 /** | |
| 315 * The fields of this class models a state machine for checking script | |
| 316 * tags come in the correct order. | |
| 317 */ | |
| 318 class TagState { | |
| 319 static const int NO_TAG_SEEN = 0; | |
| 320 static const int LIBRARY = 1; | |
| 321 static const int IMPORT_OR_EXPORT = 2; | |
| 322 static const int SOURCE = 3; | |
| 323 static const int RESOURCE = 4; | |
| 324 | |
| 325 /** Next state. */ | |
| 326 static const List<int> NEXT = | |
| 327 const <int>[NO_TAG_SEEN, | |
| 328 IMPORT_OR_EXPORT, // Only one library tag is allowed. | |
| 329 IMPORT_OR_EXPORT, | |
| 330 SOURCE, | |
| 331 RESOURCE]; | |
| 332 } | |
| 333 | |
| 334 /** | |
| 335 * An [import] tag and the [importedLibrary] imported through [import]. | |
| 336 */ | |
| 337 class ImportLink { | |
| 338 final Import import; | |
| 339 final LibraryElement importedLibrary; | |
| 340 | |
| 341 ImportLink(this.import, this.importedLibrary); | |
| 342 | |
| 343 /** | |
| 344 * Imports the library into the [importingLibrary]. | |
| 345 */ | |
| 346 void importLibrary(Compiler compiler, LibraryElement importingLibrary) { | |
| 347 compiler.libraryLoader.importLibrary(compiler, importingLibrary, | |
| 348 importedLibrary, import); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 /** | |
| 353 * A node in the library dependency graph. | |
| 354 * | |
| 355 * This class is used to collect the library dependencies expressed through | |
| 356 * import and export tags, and as the work-list entry in computations of library | |
| 357 * exports performed in [LibraryDependencyHandler.computeExports]. | |
| 358 */ | |
| 359 class LibraryDependencyNode { | |
| 360 final LibraryElement library; | |
| 361 | |
| 362 /** | |
| 363 * A linked list of the import tags that import [library] mapped to the | |
| 364 * corresponding libraries. This is used to propagate exports into imports | |
| 365 * after the export scopes have been computed. | |
| 366 */ | |
| 367 Link<ImportLink> imports = const EmptyLink<ImportLink>(); | |
| 368 | |
| 369 /** | |
| 370 * The export tags that export [library] mapped to the nodes for the libraries | |
| 371 * that declared each export tag. This is used to propagete exports during the | |
| 372 * computation of export scopes. | |
| 373 */ | |
| 374 Map<Export, LibraryDependencyNode> dependencyMap = | |
| 375 new Map<Export, LibraryDependencyNode>(); | |
| 376 | |
| 377 /** | |
| 378 * The export scope for [library] which is gradually computed by the work-list | |
| 379 * computation in [LibraryDependencyHandler.computeExports]. | |
| 380 */ | |
| 381 Map<SourceString, Element> exportScope = new Map<SourceString, Element>(); | |
|
ahe
2012/10/10 12:06:27
Nit: I'm glad this isn't a [Scope] because scope d
| |
| 382 | |
| 383 /** | |
| 384 * The set of exported elements that need to be propageted to dependent | |
| 385 * libraries as part of the work-list computation performed in | |
| 386 * [LibraryDependencyHandler.computeExports]. | |
| 387 */ | |
| 388 Set<Element> pendingExportSet = new Set<Element>(); | |
| 389 | |
| 390 LibraryDependencyNode(LibraryElement this.library); | |
| 391 | |
| 392 /** | |
| 393 * Registers that the library of this node imports [importLibrary] through the | |
| 394 * [import] tag. | |
| 395 */ | |
| 396 void registerImportDependency(Import import, | |
| 397 LibraryElement importedLibrary) { | |
| 398 imports = imports.prepend(new ImportLink(import, importedLibrary)); | |
| 399 } | |
| 400 | |
| 401 /** | |
| 402 * Registers that the library of this node is exported by | |
| 403 * [exportingLibraryNode] through the [export] tag. | |
| 404 */ | |
| 405 void registerExportDependency(Export export, | |
| 406 LibraryDependencyNode exportingLibraryNode) { | |
| 407 dependencyMap[export] = exportingLibraryNode; | |
| 408 } | |
| 409 | |
| 410 /** | |
| 411 * Registers all non-private locally declared members of the library of this | |
| 412 * node to be exported. This forms the basis for the work-list computation of | |
| 413 * the export scopes performed in [LibraryDependencyHandler.computeExports]. | |
| 414 */ | |
| 415 void registerInitialExports() { | |
| 416 pendingExportSet.addAll( | |
| 417 library.localScope.getValues().filter((Element element) { | |
| 418 // At this point [localScope] only contains members so we don't need | |
| 419 // to check for foreign or prefix elements. | |
| 420 return !element.name.isPrivate(); | |
| 421 })); | |
| 422 } | |
| 423 | |
| 424 /** | |
| 425 * Registers the compute export scope with the node library. | |
| 426 */ | |
| 427 void registerExports() { | |
| 428 library.setExports(exportScope.getValues()); | |
| 429 } | |
| 430 | |
| 431 /** | |
| 432 * Registers the imports of the node library. | |
| 433 */ | |
| 434 void registerImports(Compiler compiler) { | |
| 435 for (ImportLink link in imports) { | |
| 436 link.importLibrary(compiler, library); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 /** | |
| 441 * Copies and clears pending export set for this node. | |
| 442 */ | |
| 443 List<Element> pullPendingExports() { | |
| 444 List<Element> pendingExports = new List.from(pendingExportSet); | |
| 445 pendingExportSet.clear(); | |
| 446 return pendingExports; | |
| 447 } | |
| 448 | |
| 449 /** | |
| 450 * Adds [element] to the export scope for this node. If the [element] name | |
| 451 * is a duplicate, an error element is inserted into the exscope. | |
| 452 */ | |
| 453 Element addElementToExportScope(Compiler compiler, Element element) { | |
| 454 SourceString name = element.name; | |
| 455 Element existingElement = exportScope[name]; | |
| 456 exportScope.putIfAbsent(name, () => element); | |
|
ahe
2012/10/10 12:06:27
Another weird use of putIfAbsent.
Johnni Winther
2012/10/10 14:56:00
Done.
| |
| 457 if (existingElement !== null) { | |
| 458 if (existingElement.getLibrary() != library) { | |
| 459 // Declared elements hide exported elements. | |
| 460 element = exportScope[name] = new ErroneousElement( | |
| 461 MessageKind.DUPLICATE_EXPORT, [name], name, library); | |
| 462 } | |
| 463 } | |
| 464 return element; | |
| 465 } | |
| 466 | |
| 467 /** | |
| 468 * Propagates the exported [element] to all library nodes that depend upon | |
| 469 * this node. If the propagation updated any pending exports, [:true:] is | |
| 470 * returned. | |
| 471 */ | |
| 472 bool propagateElement(Element element) { | |
| 473 bool change = false; | |
| 474 dependencyMap.forEach((Export export, LibraryDependencyNode exportNode) { | |
| 475 if (exportNode.addElementToPendingExports(export, element)) { | |
| 476 change = true; | |
| 477 } | |
| 478 }); | |
| 479 return change; | |
| 480 } | |
| 481 | |
| 482 /** | |
| 483 * Adds [element] to the pending exports of this node and returns [:true:] if | |
| 484 * the pending export set was modified. The combinators of [export] are used | |
| 485 * to filter the element. | |
| 486 */ | |
| 487 bool addElementToPendingExports(Export export, Element element) { | |
| 488 // TODO(johnniwinther): Use [export] to handle show and hide combinators. | |
| 489 if (exportScope[element.name] !== element) { | |
| 490 if (!pendingExportSet.contains(element)) { | |
| 491 pendingExportSet.add(element); | |
| 492 return true; | |
| 493 } | |
| 494 } | |
| 495 return false; | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 /** | |
| 500 * Helper class used for computing the possibly cyclic import/export scopes of | |
| 501 * a set of libraries. | |
| 502 * | |
| 503 * This class is used by [ScannerTask.loadLibrary] to collect all newly loaded | |
| 504 * libraries and to compute their import/export scopes through a fixed-point | |
| 505 * algorithm. | |
| 506 */ | |
| 507 class LibraryDependencyHandler { | |
| 508 final Compiler compiler; | |
| 509 | |
| 510 /** | |
| 511 * Newly loaded libraries and their corresponding node in the library | |
| 512 * dependency graph. Libraries that have already been fully loaded are not | |
| 513 * part of the dependency graph of this handler since their export scopes have | |
| 514 * already been computed. | |
| 515 */ | |
| 516 Map<LibraryElement,LibraryDependencyNode> nodeMap = | |
| 517 new Map<LibraryElement,LibraryDependencyNode>(); | |
| 518 | |
| 519 LibraryDependencyHandler(Compiler this.compiler); | |
| 520 | |
| 521 /** | |
| 522 * Performs a fixed-point computation on the export scopes of all registered | |
| 523 * libraries and creates the import/export of the libraries based on the | |
| 524 * fixed-point. | |
| 525 */ | |
| 526 void computeExports() { | |
| 527 bool changed = true; | |
| 528 while (changed) { | |
| 529 changed = false; | |
| 530 nodeMap.forEach((_, LibraryDependencyNode node) { | |
| 531 var pendingExports = node.pullPendingExports(); | |
| 532 pendingExports.forEach((Element element) { | |
| 533 element = node.addElementToExportScope(compiler, element); | |
| 534 if (node.propagateElement(element)) { | |
| 535 changed = true; | |
| 536 } | |
| 537 }); | |
| 538 }); | |
| 539 } | |
| 540 | |
| 541 // Setup export scopes. These have to be set before computing the import | |
| 542 // scopes to avoid accessing uncomputed export scopes during handling of | |
| 543 // imports. | |
| 544 nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { | |
| 545 node.registerExports(); | |
| 546 }); | |
| 547 | |
| 548 // Setup import scopes. | |
| 549 nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { | |
| 550 node.registerImports(compiler); | |
| 551 }); | |
| 552 } | |
| 553 | |
| 554 /** | |
| 555 * Registers that [library] depends on [loadedLibrary] through [tag]. | |
| 556 */ | |
| 557 void registerDependency(LibraryElement library, | |
| 558 LibraryDependency tag, | |
| 559 LibraryElement loadedLibrary) { | |
| 560 if (tag is Export) { | |
| 561 // [loadedLibrary] is exported by [library]. | |
| 562 if (loadedLibrary.exportsHandled) { | |
| 563 // Export scope already computed on [loadedLibrary]. | |
| 564 return; | |
| 565 } | |
| 566 LibraryDependencyNode exportedNode = nodeMap[loadedLibrary]; | |
| 567 LibraryDependencyNode exportingNode = nodeMap[library]; | |
| 568 assert(invariant(loadedLibrary, exportedNode != null, | |
| 569 message: "$loadedLibrary has not been registered")); | |
| 570 assert(invariant(library, exportingNode != null, | |
| 571 message: "$library has not been registered")); | |
| 572 exportedNode.registerExportDependency(tag, exportingNode); | |
| 573 } else if (tag == null || tag is Import) { | |
| 574 // [loadedLibrary] is imported by [library]. | |
| 575 LibraryDependencyNode importingNode = nodeMap[library]; | |
| 576 assert(invariant(library, importingNode != null, | |
| 577 message: "$library has not been registered")); | |
| 578 importingNode.registerImportDependency(tag, loadedLibrary); | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 /** | |
| 583 * Registers [library] for the processing of its import/export scope. | |
| 584 */ | |
| 585 void registerNewLibrary(LibraryElement library) { | |
| 586 nodeMap[library] = new LibraryDependencyNode(library); | |
| 587 } | |
| 588 | |
| 589 /** | |
| 590 * Registers all top-level entities of [library] as starting point for the | |
| 591 * fixed-point computation of the import/export scopes. | |
| 592 */ | |
| 593 void registerLibraryExports(LibraryElement library) { | |
| 594 nodeMap[library].registerInitialExports(); | |
| 595 } | |
| 596 } | |
| OLD | NEW |