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