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