| 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 library dart2js.library_loader; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart2jslib.dart' | |
| 9 show Compiler, | |
| 10 CompilerTask, | |
| 11 MessageKind, | |
| 12 Script, | |
| 13 invariant; | |
| 14 import 'elements/elements.dart' | |
| 15 show CompilationUnitElement, | |
| 16 Element, | |
| 17 LibraryElement, | |
| 18 PrefixElement; | |
| 19 import 'elements/modelx.dart' | |
| 20 show CompilationUnitElementX, | |
| 21 DeferredLoaderGetterElementX, | |
| 22 ErroneousElementX, | |
| 23 LibraryElementX, | |
| 24 PrefixElementX; | |
| 25 import 'helpers/helpers.dart'; // Included for debug helpers. | |
| 26 import 'native/native.dart' as native; | |
| 27 import 'tree/tree.dart'; | |
| 28 import 'util/util.dart' show Link, LinkBuilder; | |
| 29 | |
| 30 /** | |
| 31 * [CompilerTask] for loading libraries and setting up the import/export scopes. | |
| 32 * | |
| 33 * The library loader uses four different kinds of URIs in different parts of | |
| 34 * the loading process. | |
| 35 * | |
| 36 * ## User URI ## | |
| 37 * | |
| 38 * A 'user URI' is a URI provided by the user in code and as the main entry URI | |
| 39 * at the command line. These generally come in 3 versions: | |
| 40 * | |
| 41 * * A relative URI such as 'foo.dart', '../bar.dart', and 'baz/boz.dart'. | |
| 42 * | |
| 43 * * A dart URI such as 'dart:core' and 'dart:_js_helper'. | |
| 44 * | |
| 45 * * A package URI such as 'package:foo.dart' and 'package:bar/baz.dart'. | |
| 46 * | |
| 47 * A user URI can also be absolute, like 'file:///foo.dart' or | |
| 48 * 'http://example.com/bar.dart', but such URIs cannot necessarily be used for | |
| 49 * locating source files, since the scheme must be supported by the input | |
| 50 * provider. The standard input provider for dart2js only supports the 'file' | |
| 51 * and 'http' scheme. | |
| 52 * | |
| 53 * ## Resolved URI ## | |
| 54 * | |
| 55 * A 'resolved URI' is a (user) URI that has been resolved to an absolute URI | |
| 56 * based on the readable URI (see below) from which it was loaded. A URI with an | |
| 57 * explicit scheme (such as 'dart:', 'package:' or 'file:') is already resolved. | |
| 58 * A relative URI like for instance '../foo/bar.dart' is translated into an | |
| 59 * resolved URI in one of three ways: | |
| 60 * | |
| 61 * * If provided as the main entry URI at the command line, the URI is resolved | |
| 62 * relative to the current working directory, say | |
| 63 * 'file:///current/working/dir/', and the resolved URI is therefore | |
| 64 * 'file:///current/working/foo/bar.dart'. | |
| 65 * | |
| 66 * * If the relative URI is provided in an import, export or part tag, and the | |
| 67 * readable URI of the enclosing compilation unit is a file URI, | |
| 68 * 'file://some/path/baz.dart', then the resolved URI is | |
| 69 * 'file://some/foo/bar.dart'. | |
| 70 * | |
| 71 * * If the relative URI is provided in an import, export or part tag, and the | |
| 72 * readable URI of the enclosing compilation unit is a package URI, | |
| 73 * 'package:some/path/baz.dart', then the resolved URI is | |
| 74 * 'package:some/foo/bar.dart'. | |
| 75 * | |
| 76 * The resolved URI thus preserves the scheme through resolution: A readable | |
| 77 * file URI results in an resolved file URI and a readable package URI results | |
| 78 * in an resolved package URI. Note that since a dart URI is not a readable URI, | |
| 79 * import, export or part tags within platform libraries are not interpreted as | |
| 80 * dart URIs but instead relative to the library source file location. | |
| 81 * | |
| 82 * The resolved URI of a library is also used as the canonical URI | |
| 83 * ([LibraryElement.canonicalUri]) by which we identify which libraries are | |
| 84 * identical. This means that libraries loaded through the 'package' scheme will | |
| 85 * resolve to the same library when loaded from within using relative URIs (see | |
| 86 * for instance the test 'standalone/package/package1_test.dart'). But loading a | |
| 87 * platform library using a relative URI will _not_ result in the same library | |
| 88 * as when loaded through the dart URI. | |
| 89 * | |
| 90 * ## Readable URI ## | |
| 91 * | |
| 92 * A 'readable URI' is an absolute URI whose scheme is either 'package' or | |
| 93 * something supported by the input provider, normally 'file'. Dart URIs such as | |
| 94 * 'dart:core' and 'dart:_js_helper' are not readable themselves but are instead | |
| 95 * resolved into a readable URI using the library root URI provided from the | |
| 96 * command line and the list of platform libraries found in | |
| 97 * 'sdk/lib/_internal/libraries.dart'. This is done through the | |
| 98 * [Compiler.translateResolvedUri] method which checks whether a library by that | |
| 99 * name exists and in case of internal libraries whether access is granted. | |
| 100 * | |
| 101 * ## Resource URI ## | |
| 102 * | |
| 103 * A 'resource URI' is an absolute URI with a scheme supported by the input | |
| 104 * provider. For the standard implementation this means a URI with the 'file' | |
| 105 * scheme. Readable URIs are converted into resource URIs as part of the | |
| 106 * [Compiler.readScript] method. In the standard implementation the package URIs | |
| 107 * are converted to file URIs using the package root URI provided on the | |
| 108 * command line as base. If the package root URI is | |
| 109 * 'file:///current/working/dir/' then the package URI 'package:foo/bar.dart' | |
| 110 * will be resolved to the resource URI | |
| 111 * 'file:///current/working/dir/foo/bar.dart'. | |
| 112 * | |
| 113 * The distinction between readable URI and resource URI is necessary to ensure | |
| 114 * that these imports | |
| 115 * | |
| 116 * import 'package:foo.dart' as a; | |
| 117 * import 'packages/foo.dart' as b; | |
| 118 * | |
| 119 * do _not_ resolve to the same library when the package root URI happens to | |
| 120 * point to the 'packages' folder. | |
| 121 * | |
| 122 */ | |
| 123 abstract class LibraryLoaderTask implements CompilerTask { | |
| 124 factory LibraryLoaderTask(Compiler compiler) = _LibraryLoaderTask; | |
| 125 | |
| 126 /// Returns all libraries that have been loaded. | |
| 127 Iterable<LibraryElement> get libraries; | |
| 128 | |
| 129 /// Looks up the library with the [canonicalUri]. | |
| 130 LibraryElement lookupLibrary(Uri canonicalUri); | |
| 131 | |
| 132 /// Loads the library specified by the [resolvedUri] and returns its | |
| 133 /// [LibraryElement]. | |
| 134 /// | |
| 135 /// If the library is not already loaded, the method creates the | |
| 136 /// [LibraryElement] for the library and computes the import/export scope, | |
| 137 /// loading and computing the import/export scopes of all required libraries | |
| 138 /// in the process. The method handles cyclic dependency between libraries. | |
| 139 Future<LibraryElement> loadLibrary(Uri resolvedUri); | |
| 140 | |
| 141 /// Reset the library loader task to prepare for compilation. If provided, | |
| 142 /// libraries matching [reuseLibrary] are reused. | |
| 143 /// | |
| 144 /// This method is used for incremental compilation. | |
| 145 void reset({bool reuseLibrary(LibraryElement library)}); | |
| 146 | |
| 147 /// Asynchronous version of [reset]. | |
| 148 Future resetAsync(Future<bool> reuseLibrary(LibraryElement library)); | |
| 149 } | |
| 150 | |
| 151 /// Handle for creating synthesized/patch libraries during library loading. | |
| 152 abstract class LibraryLoader { | |
| 153 /// This method must be called when a new synthesized/patch library has been | |
| 154 /// created to ensure that [library] will part of library dependency graph | |
| 155 /// used for computing import/export scopes. | |
| 156 void registerNewLibrary(LibraryElement library); | |
| 157 | |
| 158 /// This method must be called when a new synthesized/patch library has been | |
| 159 /// scanned in order to process the library tags in [library] and thus handle | |
| 160 /// imports/exports/parts in the synthesized/patch library. | |
| 161 Future processLibraryTags(LibraryElement library); | |
| 162 } | |
| 163 | |
| 164 /** | |
| 165 * [CombinatorFilter] is a succinct representation of a list of combinators from | |
| 166 * a library dependency tag. | |
| 167 */ | |
| 168 class CombinatorFilter { | |
| 169 const CombinatorFilter(); | |
| 170 | |
| 171 /** | |
| 172 * Returns [:true:] if [element] is excluded by this filter. | |
| 173 */ | |
| 174 bool exclude(Element element) => false; | |
| 175 | |
| 176 /** | |
| 177 * Creates a filter based on the combinators of [tag]. | |
| 178 */ | |
| 179 factory CombinatorFilter.fromTag(LibraryDependency tag) { | |
| 180 if (tag == null || tag.combinators == null) { | |
| 181 return const CombinatorFilter(); | |
| 182 } | |
| 183 | |
| 184 // If the list of combinators contain at least one [:show:] we can create | |
| 185 // a positive list of elements to include, otherwise we create a negative | |
| 186 // list of elements to exclude. | |
| 187 bool show = false; | |
| 188 Set<String> nameSet; | |
| 189 for (Combinator combinator in tag.combinators) { | |
| 190 if (combinator.isShow) { | |
| 191 show = true; | |
| 192 var set = new Set<String>(); | |
| 193 for (Identifier identifier in combinator.identifiers) { | |
| 194 set.add(identifier.source); | |
| 195 } | |
| 196 if (nameSet == null) { | |
| 197 nameSet = set; | |
| 198 } else { | |
| 199 nameSet = nameSet.intersection(set); | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 if (nameSet == null) { | |
| 204 nameSet = new Set<String>(); | |
| 205 } | |
| 206 for (Combinator combinator in tag.combinators) { | |
| 207 if (combinator.isHide) { | |
| 208 for (Identifier identifier in combinator.identifiers) { | |
| 209 if (show) { | |
| 210 // We have a positive list => Remove hidden elements. | |
| 211 nameSet.remove(identifier.source); | |
| 212 } else { | |
| 213 // We have no positive list => Accumulate hidden elements. | |
| 214 nameSet.add(identifier.source); | |
| 215 } | |
| 216 } | |
| 217 } | |
| 218 } | |
| 219 return show ? new ShowFilter(nameSet) : new HideFilter(nameSet); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * A list of combinators represented as a list of element names to include. | |
| 225 */ | |
| 226 class ShowFilter extends CombinatorFilter { | |
| 227 final Set<String> includedNames; | |
| 228 | |
| 229 ShowFilter(this.includedNames); | |
| 230 | |
| 231 bool exclude(Element element) => !includedNames.contains(element.name); | |
| 232 } | |
| 233 | |
| 234 /** | |
| 235 * A list of combinators represented as a list of element names to exclude. | |
| 236 */ | |
| 237 class HideFilter extends CombinatorFilter { | |
| 238 final Set<String> excludedNames; | |
| 239 | |
| 240 HideFilter(this.excludedNames); | |
| 241 | |
| 242 bool exclude(Element element) => excludedNames.contains(element.name); | |
| 243 } | |
| 244 | |
| 245 /** | |
| 246 * Implementation class for [LibraryLoader]. The distinction between | |
| 247 * [LibraryLoader] and [LibraryLoaderTask] is made to hide internal members from | |
| 248 * the [LibraryLoader] interface. | |
| 249 */ | |
| 250 class _LibraryLoaderTask extends CompilerTask implements LibraryLoaderTask { | |
| 251 _LibraryLoaderTask(Compiler compiler) : super(compiler); | |
| 252 String get name => 'LibraryLoader'; | |
| 253 | |
| 254 final Map<Uri, LibraryElement> libraryCanonicalUriMap = | |
| 255 new Map<Uri, LibraryElement>(); | |
| 256 final Map<Uri, LibraryElement> libraryResourceUriMap = | |
| 257 new Map<Uri, LibraryElement>(); | |
| 258 final Map<String, LibraryElement> libraryNames = | |
| 259 new Map<String, LibraryElement>(); | |
| 260 | |
| 261 LibraryDependencyHandler currentHandler; | |
| 262 | |
| 263 Iterable<LibraryElement> get libraries => libraryCanonicalUriMap.values; | |
| 264 | |
| 265 LibraryElement lookupLibrary(Uri canonicalUri) { | |
| 266 return libraryCanonicalUriMap[canonicalUri]; | |
| 267 } | |
| 268 | |
| 269 void reset({bool reuseLibrary(LibraryElement library)}) { | |
| 270 measure(() { | |
| 271 assert(currentHandler == null); | |
| 272 | |
| 273 Iterable<LibraryElement> reusedLibraries = null; | |
| 274 if (reuseLibrary != null) { | |
| 275 reusedLibraries = compiler.reuseLibraryTask.measure(() { | |
| 276 // Call [toList] to force eager calls to [reuseLibrary]. | |
| 277 return libraryCanonicalUriMap.values.where(reuseLibrary).toList(); | |
| 278 }); | |
| 279 } | |
| 280 | |
| 281 resetImplementation(reusedLibraries); | |
| 282 }); | |
| 283 } | |
| 284 | |
| 285 void resetImplementation(Iterable<LibraryElement> reusedLibraries) { | |
| 286 measure(() { | |
| 287 libraryCanonicalUriMap.clear(); | |
| 288 libraryResourceUriMap.clear(); | |
| 289 libraryNames.clear(); | |
| 290 | |
| 291 if (reusedLibraries != null) { | |
| 292 reusedLibraries.forEach(mapLibrary); | |
| 293 } | |
| 294 }); | |
| 295 } | |
| 296 | |
| 297 Future resetAsync(Future<bool> reuseLibrary(LibraryElement library)) { | |
| 298 return measure(() { | |
| 299 assert(currentHandler == null); | |
| 300 | |
| 301 Future<LibraryElement> wrapper(LibraryElement library) { | |
| 302 try { | |
| 303 return reuseLibrary(library).then( | |
| 304 (bool reuse) => reuse ? library : null); | |
| 305 } catch (exception, trace) { | |
| 306 compiler.diagnoseCrashInUserCode( | |
| 307 'Uncaught exception in reuseLibrary', exception, trace); | |
| 308 rethrow; | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 List<Future<LibraryElement>> reusedLibrariesFuture = | |
| 313 compiler.reuseLibraryTask.measure( | |
| 314 () => libraryCanonicalUriMap.values.map(wrapper).toList()); | |
| 315 | |
| 316 return Future.wait(reusedLibrariesFuture).then( | |
| 317 (List<LibraryElement> reusedLibraries) { | |
| 318 resetImplementation(reusedLibraries.where((e) => e != null)); | |
| 319 }); | |
| 320 }); | |
| 321 } | |
| 322 | |
| 323 /// Insert [library] in the internal maps. Used for compiler reuse. | |
| 324 void mapLibrary(LibraryElement library) { | |
| 325 libraryCanonicalUriMap[library.canonicalUri] = library; | |
| 326 | |
| 327 Uri resourceUri = library.entryCompilationUnit.script.resourceUri; | |
| 328 libraryResourceUriMap[resourceUri] = library; | |
| 329 | |
| 330 String name = library.getLibraryOrScriptName(); | |
| 331 libraryNames[name] = library; | |
| 332 } | |
| 333 | |
| 334 Future<LibraryElement> loadLibrary(Uri resolvedUri) { | |
| 335 return measure(() { | |
| 336 assert(currentHandler == null); | |
| 337 // TODO(johnniwinther): Ensure that currentHandler correctly encloses the | |
| 338 // loading of a library cluster. | |
| 339 currentHandler = new LibraryDependencyHandler(this); | |
| 340 return createLibrary(currentHandler, null, resolvedUri) | |
| 341 .then((LibraryElement library) { | |
| 342 return compiler.withCurrentElement(library, () { | |
| 343 return measure(() { | |
| 344 currentHandler.computeExports(); | |
| 345 Map<Uri, LibraryElement> loadedLibraries = <Uri, LibraryElement>{}; | |
| 346 currentHandler.loadedLibraries.forEach( | |
| 347 (LibraryElement loadedLibrary) { | |
| 348 loadedLibraries[loadedLibrary.canonicalUri] = loadedLibrary; | |
| 349 }); | |
| 350 currentHandler = null; | |
| 351 return compiler.onLibrariesLoaded(loadedLibraries) | |
| 352 .then((_) => library); | |
| 353 }); | |
| 354 }); | |
| 355 }); | |
| 356 }); | |
| 357 } | |
| 358 | |
| 359 /** | |
| 360 * Processes the library tags in [library]. | |
| 361 * | |
| 362 * The imported/exported libraries are loaded and processed recursively but | |
| 363 * the import/export scopes are not set up. | |
| 364 */ | |
| 365 Future processLibraryTags(LibraryDependencyHandler handler, | |
| 366 LibraryElement library) { | |
| 367 int tagState = TagState.NO_TAG_SEEN; | |
| 368 | |
| 369 /** | |
| 370 * If [value] is less than [tagState] complain and return | |
| 371 * [tagState]. Otherwise return the new value for [tagState] | |
| 372 * (transition function for state machine). | |
| 373 */ | |
| 374 int checkTag(int value, LibraryTag tag) { | |
| 375 if (tagState > value) { | |
| 376 compiler.reportFatalError( | |
| 377 tag, | |
| 378 MessageKind.GENERIC, {'text': 'Error: Out of order.'}); | |
| 379 return tagState; | |
| 380 } | |
| 381 return TagState.NEXT[value]; | |
| 382 } | |
| 383 | |
| 384 bool importsDartCore = false; | |
| 385 var libraryDependencies = new LinkBuilder<LibraryDependency>(); | |
| 386 Uri base = library.entryCompilationUnit.script.readableUri; | |
| 387 | |
| 388 return Future.forEach(library.tags, (LibraryTag tag) { | |
| 389 return compiler.withCurrentElement(library, () { | |
| 390 if (tag.isImport) { | |
| 391 Import import = tag; | |
| 392 tagState = checkTag(TagState.IMPORT_OR_EXPORT, import); | |
| 393 if (import.uri.dartString.slowToString() == 'dart:core') { | |
| 394 importsDartCore = true; | |
| 395 } | |
| 396 libraryDependencies.addLast(import); | |
| 397 } else if (tag.isExport) { | |
| 398 tagState = checkTag(TagState.IMPORT_OR_EXPORT, tag); | |
| 399 libraryDependencies.addLast(tag); | |
| 400 } else if (tag.isLibraryName) { | |
| 401 tagState = checkTag(TagState.LIBRARY, tag); | |
| 402 if (library.libraryTag != null) { | |
| 403 compiler.internalError(tag, "Duplicated library declaration."); | |
| 404 } else { | |
| 405 library.libraryTag = tag; | |
| 406 } | |
| 407 } else if (tag.isPart) { | |
| 408 Part part = tag; | |
| 409 StringNode uri = part.uri; | |
| 410 Uri resolvedUri = base.resolve(uri.dartString.slowToString()); | |
| 411 tagState = checkTag(TagState.SOURCE, part); | |
| 412 return scanPart(part, resolvedUri, library); | |
| 413 } else { | |
| 414 compiler.internalError(tag, "Unhandled library tag."); | |
| 415 } | |
| 416 }); | |
| 417 }).then((_) { | |
| 418 return compiler.onLibraryScanned(library, handler); | |
| 419 }).then((_) { | |
| 420 return compiler.withCurrentElement(library, () { | |
| 421 checkDuplicatedLibraryName(library); | |
| 422 | |
| 423 // Import dart:core if not already imported. | |
| 424 if (!importsDartCore && library.canonicalUri != Compiler.DART_CORE) { | |
| 425 return createLibrary(handler, null, Compiler.DART_CORE) | |
| 426 .then((LibraryElement coreLibrary) { | |
| 427 handler.registerDependency(library, null, coreLibrary); | |
| 428 }); | |
| 429 } | |
| 430 }); | |
| 431 }).then((_) { | |
| 432 return Future.forEach(libraryDependencies.toList(), (tag) { | |
| 433 return compiler.withCurrentElement(library, () { | |
| 434 return registerLibraryFromTag(handler, library, tag); | |
| 435 }); | |
| 436 }); | |
| 437 }); | |
| 438 } | |
| 439 | |
| 440 /// True if the uris are pointing to a library that is shared between dart2js | |
| 441 /// and the core libraries. By construction they must be imported into the | |
| 442 /// runtime, and, at the same time, into dart2js. This can lead to | |
| 443 /// duplicated imports, like in the docgen. | |
| 444 // TODO(johnniwinther): is this necessary, or should we change docgen not | |
| 445 // to include both libraries (compiler and lib) at the same time? | |
| 446 bool _isSharedDart2jsLibrary(Uri uri1, Uri uri2) { | |
| 447 bool inJsLibShared(Uri uri) { | |
| 448 List<String> segments = uri.pathSegments; | |
| 449 if (segments.length < 3) return false; | |
| 450 if (segments[segments.length - 2] != 'shared') return false; | |
| 451 return (segments[segments.length - 3] == 'js_lib'); | |
| 452 } | |
| 453 return inJsLibShared(uri1) && inJsLibShared(uri2); | |
| 454 } | |
| 455 | |
| 456 void checkDuplicatedLibraryName(LibraryElement library) { | |
| 457 Uri resourceUri = library.entryCompilationUnit.script.resourceUri; | |
| 458 LibraryName tag = library.libraryTag; | |
| 459 LibraryElement existing = | |
| 460 libraryResourceUriMap.putIfAbsent(resourceUri, () => library); | |
| 461 if (!identical(existing, library)) { | |
| 462 if (tag != null) { | |
| 463 compiler.withCurrentElement(library, () { | |
| 464 compiler.reportWarning(tag.name, | |
| 465 MessageKind.DUPLICATED_LIBRARY_RESOURCE, | |
| 466 {'libraryName': tag.name, | |
| 467 'resourceUri': resourceUri, | |
| 468 'canonicalUri1': library.canonicalUri, | |
| 469 'canonicalUri2': existing.canonicalUri}); | |
| 470 }); | |
| 471 } else { | |
| 472 compiler.reportHint(library, | |
| 473 MessageKind.DUPLICATED_RESOURCE, | |
| 474 {'resourceUri': resourceUri, | |
| 475 'canonicalUri1': library.canonicalUri, | |
| 476 'canonicalUri2': existing.canonicalUri}); | |
| 477 } | |
| 478 } else if (tag != null) { | |
| 479 String name = library.getLibraryOrScriptName(); | |
| 480 existing = libraryNames.putIfAbsent(name, () => library); | |
| 481 if (!identical(existing, library) && | |
| 482 !_isSharedDart2jsLibrary(resourceUri, existing.canonicalUri)) { | |
| 483 compiler.withCurrentElement(library, () { | |
| 484 compiler.reportWarning(tag.name, | |
| 485 MessageKind.DUPLICATED_LIBRARY_NAME, | |
| 486 {'libraryName': name}); | |
| 487 }); | |
| 488 compiler.withCurrentElement(existing, () { | |
| 489 compiler.reportWarning(existing.libraryTag.name, | |
| 490 MessageKind.DUPLICATED_LIBRARY_NAME, | |
| 491 {'libraryName': name}); | |
| 492 }); | |
| 493 } | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 /** | |
| 498 * Handle a part tag in the scope of [library]. The [resolvedUri] given is | |
| 499 * used as is, any URI resolution should be done beforehand. | |
| 500 */ | |
| 501 Future scanPart(Part part, Uri resolvedUri, LibraryElement library) { | |
| 502 if (!resolvedUri.isAbsolute) throw new ArgumentError(resolvedUri); | |
| 503 Uri readableUri = compiler.translateResolvedUri(library, resolvedUri, part); | |
| 504 if (readableUri == null) return new Future.value(); | |
| 505 return compiler.withCurrentElement(library, () { | |
| 506 return compiler.readScript(part, readableUri). | |
| 507 then((Script sourceScript) { | |
| 508 if (sourceScript == null) return; | |
| 509 | |
| 510 CompilationUnitElement unit = | |
| 511 new CompilationUnitElementX(sourceScript, library); | |
| 512 compiler.withCurrentElement(unit, () { | |
| 513 compiler.scanner.scan(unit); | |
| 514 if (unit.partTag == null) { | |
| 515 compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG); | |
| 516 } | |
| 517 }); | |
| 518 }); | |
| 519 }); | |
| 520 } | |
| 521 | |
| 522 /** | |
| 523 * Handle an import/export tag by loading the referenced library and | |
| 524 * registering its dependency in [handler] for the computation of the import/ | |
| 525 * export scope. | |
| 526 */ | |
| 527 Future registerLibraryFromTag(LibraryDependencyHandler handler, | |
| 528 LibraryElement library, | |
| 529 LibraryDependency tag) { | |
| 530 Uri base = library.entryCompilationUnit.script.readableUri; | |
| 531 Uri resolvedUri = base.resolve(tag.uri.dartString.slowToString()); | |
| 532 return createLibrary(handler, library, resolvedUri, tag.uri) | |
| 533 .then((LibraryElement loadedLibrary) { | |
| 534 if (loadedLibrary == null) return; | |
| 535 compiler.withCurrentElement(library, () { | |
| 536 handler.registerDependency(library, tag, loadedLibrary); | |
| 537 }); | |
| 538 }); | |
| 539 } | |
| 540 | |
| 541 /** | |
| 542 * Create (or reuse) a library element for the library specified by the | |
| 543 * [resolvedUri]. | |
| 544 * | |
| 545 * If a new library is created, the [handler] is notified. | |
| 546 */ | |
| 547 Future<LibraryElement> createLibrary(LibraryDependencyHandler handler, | |
| 548 LibraryElement importingLibrary, | |
| 549 Uri resolvedUri, | |
| 550 [Node node]) { | |
| 551 // TODO(johnniwinther): Create erroneous library elements for missing | |
| 552 // libraries. | |
| 553 Uri readableUri = | |
| 554 compiler.translateResolvedUri(importingLibrary, resolvedUri, node); | |
| 555 if (readableUri == null) return new Future.value(); | |
| 556 LibraryElement library = libraryCanonicalUriMap[resolvedUri]; | |
| 557 if (library != null) { | |
| 558 return new Future.value(library); | |
| 559 } | |
| 560 return compiler.withCurrentElement(importingLibrary, () { | |
| 561 return compiler.readScript(node, readableUri).then((Script script) { | |
| 562 if (script == null) return null; | |
| 563 LibraryElement element = | |
| 564 createLibrarySync(handler, script, resolvedUri); | |
| 565 return processLibraryTags(handler, element).then((_) { | |
| 566 compiler.withCurrentElement(element, () { | |
| 567 handler.registerLibraryExports(element); | |
| 568 }); | |
| 569 return element; | |
| 570 }); | |
| 571 }); | |
| 572 }); | |
| 573 } | |
| 574 | |
| 575 LibraryElement createLibrarySync( | |
| 576 LibraryDependencyHandler handler, | |
| 577 Script script, | |
| 578 Uri resolvedUri) { | |
| 579 LibraryElement element = new LibraryElementX(script, resolvedUri); | |
| 580 return compiler.withCurrentElement(element, () { | |
| 581 if (handler != null) { | |
| 582 handler.registerNewLibrary(element); | |
| 583 libraryCanonicalUriMap[resolvedUri] = element; | |
| 584 } | |
| 585 native.maybeEnableNative(compiler, element); | |
| 586 compiler.scanner.scanLibrary(element); | |
| 587 return element; | |
| 588 }); | |
| 589 } | |
| 590 } | |
| 591 | |
| 592 | |
| 593 /** | |
| 594 * The fields of this class models a state machine for checking script | |
| 595 * tags come in the correct order. | |
| 596 */ | |
| 597 class TagState { | |
| 598 static const int NO_TAG_SEEN = 0; | |
| 599 static const int LIBRARY = 1; | |
| 600 static const int IMPORT_OR_EXPORT = 2; | |
| 601 static const int SOURCE = 3; | |
| 602 static const int RESOURCE = 4; | |
| 603 | |
| 604 /** Next state. */ | |
| 605 static const List<int> NEXT = | |
| 606 const <int>[NO_TAG_SEEN, | |
| 607 IMPORT_OR_EXPORT, // Only one library tag is allowed. | |
| 608 IMPORT_OR_EXPORT, | |
| 609 SOURCE, | |
| 610 RESOURCE]; | |
| 611 } | |
| 612 | |
| 613 /** | |
| 614 * An [import] tag and the [importedLibrary] imported through [import]. | |
| 615 */ | |
| 616 class ImportLink { | |
| 617 final Import import; | |
| 618 final LibraryElement importedLibrary; | |
| 619 | |
| 620 ImportLink(this.import, this.importedLibrary); | |
| 621 | |
| 622 /** | |
| 623 * Imports the library into the [importingLibrary]. | |
| 624 */ | |
| 625 void importLibrary(Compiler compiler, LibraryElement importingLibrary) { | |
| 626 assert(invariant(importingLibrary, | |
| 627 importedLibrary.exportsHandled, | |
| 628 message: 'Exports not handled on $importedLibrary')); | |
| 629 var combinatorFilter = new CombinatorFilter.fromTag(import); | |
| 630 if (import != null && import.prefix != null) { | |
| 631 String prefix = import.prefix.source; | |
| 632 Element existingElement = importingLibrary.find(prefix); | |
| 633 PrefixElement prefixElement; | |
| 634 if (existingElement == null || !existingElement.isPrefix) { | |
| 635 prefixElement = new PrefixElementX(prefix, | |
| 636 importingLibrary.entryCompilationUnit, import.getBeginToken()); | |
| 637 } else { | |
| 638 prefixElement = existingElement; | |
| 639 } | |
| 640 importingLibrary.addToScope(prefixElement, compiler); | |
| 641 importedLibrary.forEachExport((Element element) { | |
| 642 if (combinatorFilter.exclude(element)) return; | |
| 643 prefixElement.addImport(element, import, compiler); | |
| 644 }); | |
| 645 if (import.isDeferred) { | |
| 646 prefixElement.addImport( | |
| 647 new DeferredLoaderGetterElementX(prefixElement), | |
| 648 import, compiler); | |
| 649 // TODO(sigurdm): When we remove support for the annotation based | |
| 650 // syntax the [PrefixElement] constructor should receive this | |
| 651 // information. | |
| 652 prefixElement.markAsDeferred(import); | |
| 653 } | |
| 654 } else { | |
| 655 importedLibrary.forEachExport((Element element) { | |
| 656 compiler.withCurrentElement(importingLibrary, () { | |
| 657 if (combinatorFilter.exclude(element)) return; | |
| 658 importingLibrary.addImport(element, import, compiler); | |
| 659 }); | |
| 660 }); | |
| 661 } | |
| 662 } | |
| 663 } | |
| 664 | |
| 665 /** | |
| 666 * The combinator filter computed from an export tag and the library dependency | |
| 667 * node for the library that declared the export tag. This represents an edge in | |
| 668 * the library dependency graph. | |
| 669 */ | |
| 670 class ExportLink { | |
| 671 final Export export; | |
| 672 final CombinatorFilter combinatorFilter; | |
| 673 final LibraryDependencyNode exportNode; | |
| 674 | |
| 675 ExportLink(Export export, LibraryDependencyNode this.exportNode) | |
| 676 : this.export = export, | |
| 677 this.combinatorFilter = new CombinatorFilter.fromTag(export); | |
| 678 | |
| 679 /** | |
| 680 * Exports [element] to the dependent library unless [element] is filtered by | |
| 681 * the export combinators. Returns [:true:] if the set pending exports of the | |
| 682 * dependent library was modified. | |
| 683 */ | |
| 684 bool exportElement(Element element) { | |
| 685 if (combinatorFilter.exclude(element)) return false; | |
| 686 return exportNode.addElementToPendingExports(element, export); | |
| 687 } | |
| 688 } | |
| 689 | |
| 690 /** | |
| 691 * A node in the library dependency graph. | |
| 692 * | |
| 693 * This class is used to collect the library dependencies expressed through | |
| 694 * import and export tags, and as the work-list entry in computations of library | |
| 695 * exports performed in [LibraryDependencyHandler.computeExports]. | |
| 696 */ | |
| 697 class LibraryDependencyNode { | |
| 698 final LibraryElement library; | |
| 699 | |
| 700 // TODO(ahe): Remove [hashCodeCounter] and [hashCode] when | |
| 701 // VM implementation of Object.hashCode is not slow. | |
| 702 final int hashCode = ++hashCodeCounter; | |
| 703 static int hashCodeCounter = 0; | |
| 704 | |
| 705 | |
| 706 /** | |
| 707 * A linked list of the import tags that import [library] mapped to the | |
| 708 * corresponding libraries. This is used to propagate exports into imports | |
| 709 * after the export scopes have been computed. | |
| 710 */ | |
| 711 Link<ImportLink> imports = const Link<ImportLink>(); | |
| 712 | |
| 713 /** | |
| 714 * A linked list of the export tags the dependent upon this node library. | |
| 715 * This is used to propagate exports during the computation of export scopes. | |
| 716 */ | |
| 717 Link<ExportLink> dependencies = const Link<ExportLink>(); | |
| 718 | |
| 719 /** | |
| 720 * The export scope for [library] which is gradually computed by the work-list | |
| 721 * computation in [LibraryDependencyHandler.computeExports]. | |
| 722 */ | |
| 723 Map<String, Element> exportScope = | |
| 724 new Map<String, Element>(); | |
| 725 | |
| 726 /// Map from exported elements to the export directives that exported them. | |
| 727 Map<Element, Link<Export>> exporters = new Map<Element, Link<Export>>(); | |
| 728 | |
| 729 /** | |
| 730 * The set of exported elements that need to be propageted to dependent | |
| 731 * libraries as part of the work-list computation performed in | |
| 732 * [LibraryDependencyHandler.computeExports]. Each export element is mapped | |
| 733 * to a list of exports directives that export it. | |
| 734 */ | |
| 735 Map<Element, Link<Export>> pendingExportMap = | |
| 736 new Map<Element, Link<Export>>(); | |
| 737 | |
| 738 LibraryDependencyNode(LibraryElement this.library); | |
| 739 | |
| 740 /** | |
| 741 * Registers that the library of this node imports [importLibrary] through the | |
| 742 * [import] tag. | |
| 743 */ | |
| 744 void registerImportDependency(Import import, | |
| 745 LibraryElement importedLibrary) { | |
| 746 imports = imports.prepend(new ImportLink(import, importedLibrary)); | |
| 747 } | |
| 748 | |
| 749 /** | |
| 750 * Registers that the library of this node is exported by | |
| 751 * [exportingLibraryNode] through the [export] tag. | |
| 752 */ | |
| 753 void registerExportDependency(Export export, | |
| 754 LibraryDependencyNode exportingLibraryNode) { | |
| 755 dependencies = | |
| 756 dependencies.prepend(new ExportLink(export, exportingLibraryNode)); | |
| 757 } | |
| 758 | |
| 759 /** | |
| 760 * Registers all non-private locally declared members of the library of this | |
| 761 * node to be exported. This forms the basis for the work-list computation of | |
| 762 * the export scopes performed in [LibraryDependencyHandler.computeExports]. | |
| 763 */ | |
| 764 void registerInitialExports() { | |
| 765 for (Element element in library.getNonPrivateElementsInScope()) { | |
| 766 pendingExportMap[element] = const Link<Export>(); | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 void registerHandledExports(LibraryElement exportedLibraryElement, | |
| 771 Export export, | |
| 772 CombinatorFilter filter) { | |
| 773 assert(invariant(library, exportedLibraryElement.exportsHandled)); | |
| 774 for (Element exportedElement in exportedLibraryElement.exports) { | |
| 775 if (!filter.exclude(exportedElement)) { | |
| 776 Link<Export> exports = | |
| 777 pendingExportMap.putIfAbsent(exportedElement, | |
| 778 () => const Link<Export>()); | |
| 779 pendingExportMap[exportedElement] = exports.prepend(export); | |
| 780 } | |
| 781 } | |
| 782 } | |
| 783 | |
| 784 /** | |
| 785 * Registers the compute export scope with the node library. | |
| 786 */ | |
| 787 void registerExports() { | |
| 788 library.setExports(exportScope.values.toList()); | |
| 789 } | |
| 790 | |
| 791 /** | |
| 792 * Registers the imports of the node library. | |
| 793 */ | |
| 794 void registerImports(Compiler compiler) { | |
| 795 for (ImportLink link in imports) { | |
| 796 link.importLibrary(compiler, library); | |
| 797 } | |
| 798 } | |
| 799 | |
| 800 /** | |
| 801 * Copies and clears pending export set for this node. | |
| 802 */ | |
| 803 Map<Element, Link<Export>> pullPendingExports() { | |
| 804 Map<Element, Link<Export>> pendingExports = | |
| 805 new Map<Element, Link<Export>>.from(pendingExportMap); | |
| 806 pendingExportMap.clear(); | |
| 807 return pendingExports; | |
| 808 } | |
| 809 | |
| 810 /** | |
| 811 * Adds [element] to the export scope for this node. If the [element] name | |
| 812 * is a duplicate, an error element is inserted into the export scope. | |
| 813 */ | |
| 814 Element addElementToExportScope(Compiler compiler, Element element, | |
| 815 Link<Export> exports) { | |
| 816 String name = element.name; | |
| 817 | |
| 818 void reportDuplicateExport(Element duplicate, | |
| 819 Link<Export> duplicateExports, | |
| 820 {bool reportError: true}) { | |
| 821 assert(invariant(library, !duplicateExports.isEmpty, | |
| 822 message: "No export for $duplicate from ${duplicate.library} " | |
| 823 "in $library.")); | |
| 824 compiler.withCurrentElement(library, () { | |
| 825 for (Export export in duplicateExports) { | |
| 826 if (reportError) { | |
| 827 compiler.reportError(export, | |
| 828 MessageKind.DUPLICATE_EXPORT, {'name': name}); | |
| 829 reportError = false; | |
| 830 } else { | |
| 831 compiler.reportInfo(export, | |
| 832 MessageKind.DUPLICATE_EXPORT_CONT, {'name': name}); | |
| 833 } | |
| 834 } | |
| 835 }); | |
| 836 } | |
| 837 | |
| 838 void reportDuplicateExportDecl(Element duplicate, | |
| 839 Link<Export> duplicateExports) { | |
| 840 assert(invariant(library, !duplicateExports.isEmpty, | |
| 841 message: "No export for $duplicate from ${duplicate.library} " | |
| 842 "in $library.")); | |
| 843 compiler.reportInfo(duplicate, MessageKind.DUPLICATE_EXPORT_DECL, | |
| 844 {'name': name, 'uriString': duplicateExports.head.uri}); | |
| 845 } | |
| 846 | |
| 847 Element existingElement = exportScope[name]; | |
| 848 if (existingElement != null && existingElement != element) { | |
| 849 if (existingElement.isErroneous) { | |
| 850 reportDuplicateExport(element, exports); | |
| 851 reportDuplicateExportDecl(element, exports); | |
| 852 element = existingElement; | |
| 853 } else if (existingElement.library == library) { | |
| 854 // Do nothing. [existingElement] hides [element]. | |
| 855 } else if (element.library == library) { | |
| 856 // [element] hides [existingElement]. | |
| 857 exportScope[name] = element; | |
| 858 exporters[element] = exports; | |
| 859 } else { | |
| 860 // Declared elements hide exported elements. | |
| 861 Link<Export> existingExports = exporters[existingElement]; | |
| 862 reportDuplicateExport(existingElement, existingExports); | |
| 863 reportDuplicateExport(element, exports, reportError: false); | |
| 864 reportDuplicateExportDecl(existingElement, existingExports); | |
| 865 reportDuplicateExportDecl(element, exports); | |
| 866 element = exportScope[name] = new ErroneousElementX( | |
| 867 MessageKind.DUPLICATE_EXPORT, {'name': name}, name, library); | |
| 868 } | |
| 869 } else { | |
| 870 exportScope[name] = element; | |
| 871 exporters[element] = exports; | |
| 872 } | |
| 873 return element; | |
| 874 } | |
| 875 | |
| 876 /** | |
| 877 * Propagates the exported [element] to all library nodes that depend upon | |
| 878 * this node. If the propagation updated any pending exports, [:true:] is | |
| 879 * returned. | |
| 880 */ | |
| 881 bool propagateElement(Element element) { | |
| 882 bool change = false; | |
| 883 for (ExportLink link in dependencies) { | |
| 884 if (link.exportElement(element)) { | |
| 885 change = true; | |
| 886 } | |
| 887 } | |
| 888 return change; | |
| 889 } | |
| 890 | |
| 891 /** | |
| 892 * Adds [element] to the pending exports of this node and returns [:true:] if | |
| 893 * the pending export set was modified. The combinators of [export] are used | |
| 894 * to filter the element. | |
| 895 */ | |
| 896 bool addElementToPendingExports(Element element, Export export) { | |
| 897 bool changed = false; | |
| 898 if (!identical(exportScope[element.name], element)) { | |
| 899 Link<Export> exports = pendingExportMap.putIfAbsent(element, () { | |
| 900 changed = true; | |
| 901 return const Link<Export>(); | |
| 902 }); | |
| 903 pendingExportMap[element] = exports.prepend(export); | |
| 904 } | |
| 905 return changed; | |
| 906 } | |
| 907 } | |
| 908 | |
| 909 /** | |
| 910 * Helper class used for computing the possibly cyclic import/export scopes of | |
| 911 * a set of libraries. | |
| 912 * | |
| 913 * This class is used by [ScannerTask.scanLibrary] to collect all newly loaded | |
| 914 * libraries and to compute their import/export scopes through a fixed-point | |
| 915 * algorithm. | |
| 916 */ | |
| 917 class LibraryDependencyHandler implements LibraryLoader { | |
| 918 final _LibraryLoaderTask task; | |
| 919 | |
| 920 /** | |
| 921 * Newly loaded libraries and their corresponding node in the library | |
| 922 * dependency graph. Libraries that have already been fully loaded are not | |
| 923 * part of the dependency graph of this handler since their export scopes have | |
| 924 * already been computed. | |
| 925 */ | |
| 926 Map<LibraryElement, LibraryDependencyNode> nodeMap = | |
| 927 new Map<LibraryElement, LibraryDependencyNode>(); | |
| 928 | |
| 929 LibraryDependencyHandler(this.task); | |
| 930 | |
| 931 Compiler get compiler => task.compiler; | |
| 932 | |
| 933 /// The libraries loaded with this handler. | |
| 934 Iterable<LibraryElement> get loadedLibraries => nodeMap.keys; | |
| 935 | |
| 936 /** | |
| 937 * Performs a fixed-point computation on the export scopes of all registered | |
| 938 * libraries and creates the import/export of the libraries based on the | |
| 939 * fixed-point. | |
| 940 */ | |
| 941 void computeExports() { | |
| 942 bool changed = true; | |
| 943 while (changed) { | |
| 944 changed = false; | |
| 945 Map<LibraryDependencyNode, Map<Element, Link<Export>>> tasks = | |
| 946 new Map<LibraryDependencyNode, Map<Element, Link<Export>>>(); | |
| 947 | |
| 948 // Locally defined elements take precedence over exported | |
| 949 // elements. So we must propagate local elements first. We | |
| 950 // ensure this by pulling the pending exports before | |
| 951 // propagating. This enforces that we handle exports | |
| 952 // breadth-first, with locally defined elements being level 0. | |
| 953 nodeMap.forEach((_, LibraryDependencyNode node) { | |
| 954 Map<Element, Link<Export>> pendingExports = node.pullPendingExports(); | |
| 955 tasks[node] = pendingExports; | |
| 956 }); | |
| 957 tasks.forEach((LibraryDependencyNode node, | |
| 958 Map<Element, Link<Export>> pendingExports) { | |
| 959 pendingExports.forEach((Element element, Link<Export> exports) { | |
| 960 element = node.addElementToExportScope(compiler, element, exports); | |
| 961 if (node.propagateElement(element)) { | |
| 962 changed = true; | |
| 963 } | |
| 964 }); | |
| 965 }); | |
| 966 } | |
| 967 | |
| 968 // Setup export scopes. These have to be set before computing the import | |
| 969 // scopes to avoid accessing uncomputed export scopes during handling of | |
| 970 // imports. | |
| 971 nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { | |
| 972 node.registerExports(); | |
| 973 }); | |
| 974 | |
| 975 // Setup import scopes. | |
| 976 nodeMap.forEach((LibraryElement library, LibraryDependencyNode node) { | |
| 977 node.registerImports(compiler); | |
| 978 }); | |
| 979 } | |
| 980 | |
| 981 /** | |
| 982 * Registers that [library] depends on [loadedLibrary] through [tag]. | |
| 983 */ | |
| 984 void registerDependency(LibraryElement library, | |
| 985 LibraryDependency tag, | |
| 986 LibraryElement loadedLibrary) { | |
| 987 if (tag != null) { | |
| 988 library.recordResolvedTag(tag, loadedLibrary); | |
| 989 } | |
| 990 if (tag is Export) { | |
| 991 // [loadedLibrary] is exported by [library]. | |
| 992 LibraryDependencyNode exportingNode = nodeMap[library]; | |
| 993 if (loadedLibrary.exportsHandled) { | |
| 994 // Export scope already computed on [loadedLibrary]. | |
| 995 var combinatorFilter = new CombinatorFilter.fromTag(tag); | |
| 996 exportingNode.registerHandledExports( | |
| 997 loadedLibrary, tag, combinatorFilter); | |
| 998 return; | |
| 999 } | |
| 1000 LibraryDependencyNode exportedNode = nodeMap[loadedLibrary]; | |
| 1001 assert(invariant(loadedLibrary, exportedNode != null, | |
| 1002 message: "$loadedLibrary has not been registered")); | |
| 1003 assert(invariant(library, exportingNode != null, | |
| 1004 message: "$library has not been registered")); | |
| 1005 exportedNode.registerExportDependency(tag, exportingNode); | |
| 1006 } else if (tag == null || tag is Import) { | |
| 1007 // [loadedLibrary] is imported by [library]. | |
| 1008 LibraryDependencyNode importingNode = nodeMap[library]; | |
| 1009 assert(invariant(library, importingNode != null, | |
| 1010 message: "$library has not been registered")); | |
| 1011 importingNode.registerImportDependency(tag, loadedLibrary); | |
| 1012 } | |
| 1013 } | |
| 1014 | |
| 1015 /** | |
| 1016 * Registers [library] for the processing of its import/export scope. | |
| 1017 */ | |
| 1018 void registerNewLibrary(LibraryElement library) { | |
| 1019 nodeMap[library] = new LibraryDependencyNode(library); | |
| 1020 compiler.onLibraryCreated(library); | |
| 1021 } | |
| 1022 | |
| 1023 /** | |
| 1024 * Registers all top-level entities of [library] as starting point for the | |
| 1025 * fixed-point computation of the import/export scopes. | |
| 1026 */ | |
| 1027 void registerLibraryExports(LibraryElement library) { | |
| 1028 nodeMap[library].registerInitialExports(); | |
| 1029 } | |
| 1030 | |
| 1031 Future processLibraryTags(LibraryElement library) { | |
| 1032 return task.processLibraryTags(this, library); | |
| 1033 } | |
| 1034 } | |
| OLD | NEW |