Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/library_loader.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698