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

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

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

Powered by Google App Engine
This is Rietveld 408576698