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

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

Powered by Google App Engine
This is Rietveld 408576698