Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, 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 import 'dart:collection' show HashMap; | |
| 6 import 'package:analyzer/src/generated/ast.dart'; | |
| 7 import 'package:analyzer/src/generated/element.dart'; | |
| 8 import 'package:dev_compiler/src/dependency_graph.dart' show corelibOrder; | |
| 9 | |
| 10 typedef void ModuleItemEmitter(AstNode item); | |
| 11 | |
| 12 /// Helper that tracks order of elements visited by the compiler, detecting | |
| 13 /// if the top level item can be loaded eagerly or not. | |
| 14 class ModuleItemLoadOrder { | |
| 15 | |
| 16 /// The order that elements should be emitted in, with a bit indicating if | |
| 17 /// the element should be generated lazily. The value will be `false` if | |
| 18 /// the item could not be loaded, and needs to be made lazy. | |
| 19 /// | |
| 20 /// The order will match the original source order, unless something needed to | |
| 21 /// be moved sooner to satisfy dependencies. | |
| 22 /// | |
| 23 /// Sometimes it's impossible to ensure an ordering when confronting library | |
| 24 /// cycles. In that case, we mark the definition as lazy. | |
| 25 final _loaded = new Map<Element, bool>.identity(); | |
|
vsm
2015/05/12 18:00:11
Can you add a comment on why identity? Should all
Jennifer Messerly
2015/05/12 18:47:04
ah, I can remove. This was to workaround the temp-
| |
| 26 | |
| 27 final _declarationNodes = new HashMap<Element, AstNode>.identity(); | |
| 28 | |
| 29 /// The stack of currently emitting elements, if generating top-level code | |
| 30 /// for them. This is not used when inside method bodies, because order does | |
| 31 /// not matter for those. | |
| 32 final _topLevelElements = new List<Element>(); | |
| 33 | |
| 34 /// The current element being loaded. | |
| 35 /// We can use this to determine if we're loading top-level code or not: | |
| 36 /// | |
| 37 /// _currentElements.last == _topLevelElements.last | |
| 38 final _currentElements = new List<Element>(); | |
| 39 | |
| 40 /// Memoized results of [_inLibraryCycle]. | |
| 41 final _libraryCycleMemo = new HashMap<LibraryElement, bool>(); | |
| 42 | |
| 43 final ModuleItemEmitter _emitModuleItem; | |
| 44 | |
| 45 LibraryElement _currentLibrary; | |
| 46 | |
| 47 ModuleItemLoadOrder(this._emitModuleItem); | |
| 48 | |
| 49 bool isLoaded(Element e) => _loaded[e] == true; | |
| 50 | |
| 51 /// Collect top-level elements and nodes we need to emit. | |
| 52 void collectElements( | |
| 53 LibraryElement library, Iterable<CompilationUnit> partsThenLibrary) { | |
| 54 assert(_currentLibrary == null); | |
| 55 _currentLibrary = library; | |
| 56 | |
| 57 for (var unit in partsThenLibrary) { | |
| 58 for (var decl in unit.declarations) { | |
| 59 _declarationNodes[decl.element] = decl; | |
| 60 | |
| 61 if (decl is ClassDeclaration) { | |
| 62 for (var member in decl.members) { | |
| 63 if (member is FieldDeclaration && member.isStatic) { | |
| 64 _collectElementsForVariable(member.fields); | |
| 65 } | |
| 66 } | |
| 67 } else if (decl is TopLevelVariableDeclaration) { | |
| 68 _collectElementsForVariable(decl.variables); | |
| 69 } | |
| 70 } | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 void _collectElementsForVariable(VariableDeclarationList fields) { | |
| 75 for (var field in fields.variables) { | |
| 76 _declarationNodes[field.element] = field; | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 /// Start generating top-level code for the element [e]. | |
| 81 /// Subsequent [loadElement] calls will cause those elements to be generated | |
| 82 /// before this one, until [finishElement] is called. | |
| 83 void startTopLevel(Element e) { | |
| 84 assert(isCurrentElement(e)); | |
| 85 // Assume loading will succeed until proven otherwise. | |
| 86 _loaded[e] = true; | |
| 87 _topLevelElements.add(e); | |
| 88 } | |
| 89 | |
| 90 /// Finishes the top-level code for the element [e]. | |
| 91 void finishTopLevel(Element e) { | |
| 92 var last = _topLevelElements.removeLast(); | |
| 93 assert(identical(e, last)); | |
| 94 } | |
| 95 | |
| 96 // Starts generating code for the element [e]. | |
| 97 // | |
| 98 // Normally this is called implicitly by [loadDeclaration] and/or [loadElement ]. | |
|
vsm
2015/05/12 18:00:11
line len?
Jennifer Messerly
2015/05/12 18:47:04
Done.
| |
| 99 // However, for synthetic elements (like temporary variables) it must be | |
| 100 // called explicitly, and paired with a call to [finishElement]. | |
| 101 void startDeclaration(Element e) { | |
| 102 // Assume load will succeed until proven otherwise. | |
| 103 _loaded[e] = true; | |
| 104 _currentElements.add(e); | |
| 105 } | |
| 106 | |
| 107 /// Returns true if all dependencies were loaded successfully. | |
| 108 bool finishDeclaration(Element e) { | |
| 109 var last = _currentElements.removeLast(); | |
| 110 assert(identical(e, last)); | |
| 111 return _loaded[e]; | |
| 112 } | |
| 113 | |
| 114 /// Loads a top-level declaration. This is similar to [loadElement], but it | |
| 115 /// ensures we always visit the node if it has not been emitted yet. In other | |
| 116 /// words, it handles nodes that do not need dependency resolution like | |
| 117 /// top-level functions. | |
| 118 bool loadDeclaration(AstNode node, Element e) { | |
| 119 assert(e.library == _currentLibrary); | |
| 120 | |
| 121 // If we already tried to load it, see if we succeeded or not. | |
| 122 // Otherwise try and load now. | |
| 123 var loaded = _loaded[e]; | |
| 124 if (loaded != null) return loaded; | |
| 125 | |
| 126 // Otherwise, try to load it. | |
| 127 startDeclaration(e); | |
| 128 _emitModuleItem(node); | |
| 129 return finishDeclaration(e); | |
| 130 } | |
| 131 | |
| 132 bool isCurrentElement(Element e) => | |
| 133 _currentElements.isNotEmpty && identical(e, _currentElements.last); | |
| 134 | |
| 135 /// To emit top-level module items, we sometimes need to reorder them. | |
| 136 /// | |
| 137 /// This function takes care of that, and also detects cases where reordering | |
| 138 /// failed, and we need to resort to lazy loading, by marking the element as | |
| 139 /// lazy. All elements need to be aware of this possibility and generate code | |
| 140 /// accordingly. | |
| 141 /// | |
| 142 /// If we are not emitting top-level code, this does nothing, because all | |
| 143 /// declarations are assumed to be available before we start execution. | |
| 144 /// See [startTopLevel]. | |
| 145 void declareBeforeUse(Element e) { | |
| 146 if (e == null || _topLevelElements.isEmpty) return; | |
| 147 if (!isCurrentElement(_topLevelElements.last)) return; | |
| 148 | |
| 149 // If the item is from our library, try to emit it now. | |
| 150 bool loaded; | |
| 151 if (e.library == _currentLibrary) { | |
| 152 // Type parameters are not in scope when generating hoisted fields. | |
| 153 if (e is TypeParameterElement && | |
| 154 _currentElements.last is VariableElement) { | |
| 155 loaded = false; | |
| 156 } else { | |
| 157 var node = _declarationNodes[e]; | |
| 158 loaded = node == null || loadDeclaration(node, e); | |
| 159 } | |
| 160 } else { | |
| 161 // We can't force things from other libraries to be emitted in a different | |
| 162 // order. Instead, we see if the library itself can be loaded before the | |
| 163 // current library. Generally that is possible, unless we have cyclic | |
| 164 // imports. | |
| 165 loaded = libraryIsLoaded(e.library); | |
| 166 } | |
| 167 | |
| 168 if (loaded) return; | |
| 169 | |
| 170 // If we failed to emit it, then we need to make sure all currently emitting | |
| 171 // elements are generated in a lazy way. | |
| 172 for (var current in _topLevelElements) { | |
| 173 // TODO(jmesserly): if we want to log what triggered laziness, this is | |
| 174 // the place to do so. | |
| 175 _loaded[current] = false; | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 bool libraryIsLoaded(LibraryElement library) { | |
| 180 assert(library != _currentLibrary); | |
| 181 | |
| 182 if (library == null) return true; | |
|
vsm
2015/05/12 18:00:11
When would this be null?
Jennifer Messerly
2015/05/12 18:47:04
good catch. This showed up in the code at one poin
| |
| 183 | |
| 184 // The SDK is a special case: we optimize the order to prevent laziness. | |
| 185 if (library.isInSdk) { | |
| 186 // SDK is loaded before non-SDK libraries | |
| 187 if (!_currentLibrary.isInSdk) return true; | |
| 188 | |
| 189 // Compute the order of both SDK libraries. If unknown, assume it's after. | |
| 190 var order = corelibOrder.indexOf(library.name); | |
| 191 if (order == -1) order = corelibOrder.length; | |
| 192 | |
| 193 var currentOrder = corelibOrder.indexOf(_currentLibrary.name); | |
| 194 if (currentOrder == -1) currentOrder = corelibOrder.length; | |
| 195 | |
| 196 // If the dart:* library we are currently compiling is loaded after the | |
| 197 // class's library, then we know the class is available. | |
| 198 if (order != currentOrder) return currentOrder > order; | |
| 199 | |
| 200 // If we don't know the order of the class's library or the current | |
| 201 // library, do the normal cycle check. (Not all SDK libs are cycles.) | |
| 202 } | |
| 203 | |
| 204 return !_inLibraryCycle(library); | |
| 205 } | |
| 206 | |
| 207 /// Returns true if [library] depends on the [currentLibrary] via some | |
| 208 /// transitive import. | |
| 209 bool _inLibraryCycle(LibraryElement library) { | |
| 210 // SDK libs don't depend on things outside the SDK. | |
| 211 // (We can reach this via the recursive call below.) | |
| 212 if (library.isInSdk && !_currentLibrary.isInSdk) return false; | |
| 213 | |
| 214 var result = _libraryCycleMemo[library]; | |
| 215 if (result != null) return result; | |
| 216 | |
| 217 result = library == _currentLibrary; | |
| 218 _libraryCycleMemo[library] = result; | |
| 219 for (var e in library.imports) { | |
| 220 if (result) break; | |
| 221 result = _inLibraryCycle(e.importedLibrary); | |
| 222 } | |
| 223 for (var e in library.exports) { | |
| 224 if (result) break; | |
| 225 result = _inLibraryCycle(e.exportedLibrary); | |
| 226 } | |
| 227 return _libraryCycleMemo[library] = result; | |
| 228 } | |
| 229 } | |
| OLD | NEW |