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

Side by Side Diff: lib/src/codegen/js_module_item_order.dart

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 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
« no previous file with comments | « lib/src/codegen/js_metalet.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
7 import 'package:analyzer/dart/ast/ast.dart';
8 import 'package:analyzer/dart/element/element.dart';
9
10 import '../compiler.dart' show corelibOrder;
11
12 typedef void ModuleItemEmitter(AstNode item);
13
14 /// Helper that tracks order of elements visited by the compiler, detecting
15 /// if the top level item can be loaded eagerly or not.
16 class ModuleItemLoadOrder {
17 /// The order that elements should be emitted in, with a bit indicating if
18 /// the element should be generated lazily. The value will be `false` if
19 /// the item could not be loaded, and needs to be made lazy.
20 ///
21 /// The order will match the original source order, unless something needed to
22 /// be moved sooner to satisfy dependencies.
23 ///
24 /// Sometimes it's impossible to ensure an ordering when confronting library
25 /// cycles. In that case, we mark the definition as lazy.
26 final _loaded = new Map<Element, bool>();
27
28 final _declarationNodes = new HashMap<Element, AstNode>();
29
30 /// The stack of currently emitting elements, if generating top-level code
31 /// for them. This is not used when inside method bodies, because order does
32 /// not matter for those.
33 final _topLevelElements = new List<Element>();
34
35 /// The current element being loaded.
36 /// We can use this to determine if we're loading top-level code or not:
37 ///
38 /// _currentElements.last == _topLevelElements.last
39 final _currentElements = new List<Element>();
40
41 /// Memoized results of [_inLibraryCycle].
42 final _libraryCycleMemo = new HashMap<LibraryElement, bool>();
43
44 bool _checkReferences;
45
46 final ModuleItemEmitter _emitModuleItem;
47
48 LibraryElement _currentLibrary;
49
50 ModuleItemLoadOrder(this._emitModuleItem);
51
52 bool isLoaded(Element e) => (e.library == _currentLibrary)
53 ? _loaded[e] == true
54 : libraryIsLoaded(e.library);
55
56 /// True if the element is currently being loaded.
57 bool _isLoading(Element e) => _currentElements.contains(e);
58
59 /// Collect top-level elements and nodes we need to emit.
60 void collectElements(
61 LibraryElement library, Iterable<CompilationUnit> partsThenLibrary) {
62 assert(_currentLibrary == null);
63 _currentLibrary = library;
64
65 for (var unit in partsThenLibrary) {
66 for (var decl in unit.declarations) {
67 _declarationNodes[decl.element] = decl;
68
69 if (decl is TopLevelVariableDeclaration) {
70 _collectElementsForVariable(decl.variables);
71 }
72 }
73 }
74 }
75
76 void _collectElementsForVariable(VariableDeclarationList fields) {
77 for (var field in fields.variables) {
78 _declarationNodes[field.element] = field;
79 }
80 }
81
82 /// Start generating top-level code for the element [e].
83 /// Subsequent [loadElement] calls will cause those elements to be generated
84 /// before this one, until [finishElement] is called.
85 void startTopLevel(Element e) {
86 assert(isCurrentElement(e));
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 recording calls to [declareBeforeUse], until
97 /// [finishCheckingReferences] is called.
98 void startCheckingReferences() {
99 // This function should not be reentrant, and we should not current be
100 // emitting top-level code.
101 assert(_checkReferences == null);
102 assert(
103 _topLevelElements.isEmpty || !isCurrentElement(_topLevelElements.last));
104 // Assume true until proven otherwise
105 _checkReferences = true;
106 }
107
108 /// Finishes recording references, and returns `true` if all referenced
109 /// items were loaded (or if no items were referenced).
110 bool finishCheckingReferences() {
111 var result = _checkReferences;
112 _checkReferences = null;
113 return result;
114 }
115
116 // Starts generating code for the declaration element [e].
117 //
118 // Normally this is called implicitly by [loadDeclaration] and/or
119 // [loadElement]. However, for synthetic elements (like temporary variables)
120 // it must be called explicitly, and paired with a call to [finishElement].
121 void startDeclaration(Element e) {
122 // Assume load will succeed until proven otherwise.
123 _loaded[e] = true;
124 _currentElements.add(e);
125 }
126
127 /// Returns true if all dependencies were loaded successfully.
128 bool finishDeclaration(Element e) {
129 var last = _currentElements.removeLast();
130 assert(identical(e, last));
131 return _loaded[e];
132 }
133
134 /// Loads a top-level declaration. This is similar to [loadElement], but it
135 /// ensures we always visit the node if it has not been emitted yet. In other
136 /// words, it handles nodes that do not need dependency resolution like
137 /// top-level functions.
138 bool loadDeclaration(AstNode node, Element e) {
139 assert(e.library == _currentLibrary);
140
141 // If we already tried to load it, see if we succeeded or not.
142 // Otherwise try and load now.
143 var loaded = _loaded[e];
144 if (loaded != null) return loaded;
145
146 // Otherwise, try to load it.
147 startDeclaration(e);
148 _emitModuleItem(node);
149 return finishDeclaration(e);
150 }
151
152 bool isCurrentElement(Element e) =>
153 _currentElements.isNotEmpty && identical(e, _currentElements.last);
154
155 /// To emit top-level module items, we sometimes need to reorder them.
156 ///
157 /// This function takes care of that, and also detects cases where reordering
158 /// failed, and we need to resort to lazy loading, by marking the element as
159 /// lazy. All elements need to be aware of this possibility and generate code
160 /// accordingly.
161 ///
162 /// If we are not emitting top-level code, this does nothing, because all
163 /// declarations are assumed to be available before we start execution.
164 /// See [startTopLevel].
165 void declareBeforeUse(Element e) {
166 if (e == null) return;
167
168 if (_checkReferences != null) {
169 _checkReferences = _checkReferences && isLoaded(e) && !_isLoading(e);
170 return;
171 }
172
173 if (_topLevelElements.isEmpty ||
174 !isCurrentElement(_topLevelElements.last)) {
175 return;
176 }
177
178 // If the item is from our library, try to emit it now.
179 bool loaded;
180 if (e.library == _currentLibrary) {
181 // Type parameters are not in scope when generating hoisted fields.
182 if (e is TypeParameterElement &&
183 _currentElements.last is VariableElement) {
184 loaded = false;
185 } else {
186 var node = _declarationNodes[e];
187 loaded = node == null || loadDeclaration(node, e);
188 }
189 } else {
190 // We can't force things from other libraries to be emitted in a different
191 // order. Instead, we see if the library itself can be loaded before the
192 // current library. Generally that is possible, unless we have cyclic
193 // imports.
194 loaded = libraryIsLoaded(e.library);
195 }
196
197 if (loaded) return;
198
199 // If we failed to emit it, then we need to make sure all currently emitting
200 // elements are generated in a lazy way.
201 for (var current in _topLevelElements) {
202 // TODO(jmesserly): if we want to log what triggered laziness, this is
203 // the place to do so.
204 _loaded[current] = false;
205 }
206 }
207
208 bool libraryIsLoaded(LibraryElement library) {
209 assert(library != _currentLibrary);
210
211 // DynamicElementImpl has no library.
212 if (library == null) return true;
213
214 // The SDK is a special case: we optimize the order to prevent laziness.
215 if (library.source.isInSystemLibrary) {
216 // SDK is loaded before non-SDK libraries
217 if (!_currentLibrary.source.isInSystemLibrary) return true;
218
219 // Compute the order of both SDK libraries. If unknown, assume it's after.
220 var order = corelibOrder.indexOf(library.source.uri);
221 if (order == -1) order = corelibOrder.length;
222
223 var currentOrder = corelibOrder.indexOf(_currentLibrary.source.uri);
224 if (currentOrder == -1) currentOrder = corelibOrder.length;
225
226 // If the dart:* library we are currently compiling is loaded after the
227 // class's library, then we know the class is available.
228 if (order != currentOrder) return currentOrder > order;
229
230 // If we don't know the order of the class's library or the current
231 // library, do the normal cycle check. (Not all SDK libs are cycles.)
232 }
233
234 return !_inLibraryCycle(library);
235 }
236
237 /// Returns true if [library] depends on the [currentLibrary] via some
238 /// transitive import.
239 bool _inLibraryCycle(LibraryElement library) {
240 // SDK libs don't depend on things outside the SDK.
241 // (We can reach this via the recursive call below.)
242 if (library.source.isInSystemLibrary &&
243 !_currentLibrary.source.isInSystemLibrary) return false;
244
245 var result = _libraryCycleMemo[library];
246 if (result != null) return result;
247
248 result = library == _currentLibrary;
249 _libraryCycleMemo[library] = result;
250 for (var e in library.imports) {
251 if (result) break;
252 result = _inLibraryCycle(e.importedLibrary);
253 }
254 for (var e in library.exports) {
255 if (result) break;
256 result = _inLibraryCycle(e.exportedLibrary);
257 }
258 return _libraryCycleMemo[library] = result;
259 }
260 }
OLDNEW
« no previous file with comments | « lib/src/codegen/js_metalet.dart ('k') | lib/src/codegen/js_names.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698