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

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

Issue 1133593004: fixes #131, use before define from variables to classes (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: fix static fields Created 5 years, 7 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
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 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698