OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017, 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 'package:kernel/ast.dart' as ir; | |
6 | |
7 import '../closure.dart'; | |
8 import '../elements/entities.dart'; | |
9 import 'closure.dart'; | |
10 import '../kernel/element_map.dart'; | |
11 | |
12 /// This builder walks the code to determine what variables are captured/free at | |
13 /// various points to build ClosureScope that can respond to queries | |
14 /// about how a particular variable is being used at any point in the code. | |
15 class ClosureScopeBuilder extends ir.Visitor { | |
16 /// A map of each visited call node with the associated information about what | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
I am not sure what the keys of the map are, maybe
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
17 /// variables are captured/used. | |
18 Map<ir.Node, ClosureScope> _closureInfoMap = <ir.Node, ClosureScope>{}; | |
19 | |
20 /// A map of the elements corresponding to nodes that we have flagged as | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
now that you are using ir.Node as keys, maybe remo
Emily Fortuna
2017/06/30 23:48:10
Done.
| |
21 /// necessary to generate closure classes for in a later stage. We map that | |
22 /// node to information ascertained about variable usage in the surrounding | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
I had to look it up in the dictionary :)
| |
23 /// scope. | |
24 Map<ir.Node, ScopeInfo> _closuresToGenerate = <ir.Node, ScopeInfo>{}; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
to better document what kind of nodes, sometimes w
Emily Fortuna
2017/06/30 23:48:10
Done.
| |
25 | |
26 /// The local variables that have been declared in the current scope. | |
27 List<ir.VariableDeclaration> _scopeVariables; | |
28 | |
29 /// Pointer to the context in which this closure is executed. | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
maybe include an example of what that context can
Emily Fortuna
2017/06/30 23:48:10
Done.
| |
30 ir.Node _executableContext; | |
31 | |
32 /// A flag to indicate if we are currently inside a closure. | |
33 bool _isInsideClosure = false; | |
34 | |
35 /// Pointer to the original node where this closure builder started. | |
36 ir.Node _outermostNode; | |
37 | |
38 /// Keep track of the mutated local variables so that we don't need to box | |
39 /// non-mutated variables. | |
40 Set<ir.VariableDeclaration> _mutatedVariables = | |
41 new Set<ir.VariableDeclaration>(); | |
42 | |
43 /// The set of variables that are accessed in some form, whether they are | |
44 /// mutated or not. | |
45 Set<ir.VariableDeclaration> _capturedVariables = | |
46 new Set<ir.VariableDeclaration>(); | |
47 | |
48 /// If true, the visitor is currently traversing some nodes that are inside a | |
49 /// try block. | |
50 bool _inTry = false; | |
51 | |
52 /// Lookup the local entity that corresponds to a particular kernel node. | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
nit: "particular kernel node" => "kernel variable
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
53 final KernelToLocalsMap _localsMap; | |
54 | |
55 /// The current scope we are in. | |
56 KernelScopeInfo _currentScopeInfo; | |
57 | |
58 final KernelToElementMap _kernelToElementMap; | |
59 | |
60 ClosureScopeBuilder(this._closureInfoMap, this._closuresToGenerate, | |
61 this._localsMap, this._kernelToElementMap); | |
62 | |
63 /// Update the [ClosureScope] object corresponding to | |
64 /// this node if any variables are captured. | |
65 void attachCapturedScopeVariables(ir.Node node) { | |
66 Set<Local> capturedVariablesForScope = new Set<Local>(); | |
67 | |
68 for (ir.VariableDeclaration variable in _scopeVariables) { | |
69 // No need to box non-assignable elements. | |
70 // TODO(efortuna): Need to figure out how to test isAssignable with Local | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
you could directly inspect this on the ir.Variable
Emily Fortuna
2017/06/30 23:48:10
oh DUH! This comment was from when I was looking a
| |
71 //if (!variable.isAssignable) continue; | |
72 if (!_mutatedVariables.contains(variable)) continue; | |
73 if (_capturedVariables.contains(variable)) { | |
74 capturedVariablesForScope.add(_localsMap.getLocal(variable)); | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
<no action here, just a general comment> - here it
Emily Fortuna
2017/06/30 23:48:10
agreed. I was seeing that as I was making the ir.N
| |
75 } | |
76 } | |
77 if (!capturedVariablesForScope.isEmpty) { | |
78 ThisLocal thisLocal = null; | |
79 if (node is ir.Member && node.isInstanceMember) { | |
80 if (node is ir.Procedure) { | |
81 thisLocal = new ThisLocal(_kernelToElementMap.getMethod(node)); | |
82 } else if (node is ir.Field) { | |
83 thisLocal = new ThisLocal(_kernelToElementMap.getField(node)); | |
84 } | |
85 } else if (node is ir.Constructor) { | |
86 thisLocal = new ThisLocal(_kernelToElementMap.getConstructor(node)); | |
87 } | |
88 | |
89 Entity context; | |
90 if (_executableContext is ir.Member) { | |
91 context = _kernelToElementMap.getMember(_executableContext); | |
92 } else { | |
93 context = _kernelToElementMap.getLocalFunction(_executableContext); | |
94 } | |
95 _closureInfoMap[node] = | |
96 new KernelClosureScope(capturedVariablesForScope, context, thisLocal); | |
97 } | |
98 } | |
99 | |
100 /// Perform book-keeping with the current set of local variables that have | |
101 /// been seen thus far before entering this new scope. | |
102 void enterNewScope(ir.Node node, Function visitNewScope) { | |
103 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; | |
104 _scopeVariables = <ir.VariableDeclaration>[]; | |
105 visitNewScope(); | |
106 attachCapturedScopeVariables(node); | |
107 _mutatedVariables.removeAll(_scopeVariables); | |
108 _scopeVariables = oldScopeVariables; | |
109 } | |
110 | |
111 @override | |
112 void defaultNode(ir.Node node) { | |
113 node.visitChildren(this); | |
114 } | |
115 | |
116 @override | |
117 visitTryCatch(ir.TryCatch node) { | |
118 bool oldInTry = _inTry; | |
119 _inTry = true; | |
120 node.visitChildren(this); | |
121 _inTry = oldInTry; | |
122 } | |
123 | |
124 @override | |
125 visitTryFinally(ir.TryFinally node) { | |
126 bool oldInTry = _inTry; | |
127 _inTry = true; | |
128 node.visitChildren(this); | |
129 _inTry = oldInTry; | |
130 } | |
131 | |
132 @override | |
133 visitVariableGet(ir.VariableGet node) { | |
134 _markVariableAsUsed(node.variable); | |
135 } | |
136 | |
137 @override | |
138 visitVariableSet(ir.VariableSet node) { | |
139 _markVariableAsUsed(node.variable); | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
don't we need to also add node.variable as a mutat
Emily Fortuna
2017/06/30 23:48:09
thank you. you're finding all the missing pieces f
| |
140 node.visitChildren(this); | |
141 } | |
142 | |
143 /// Add this variable to the set of free variables if appropriate and add to | |
144 /// the tally of variables used in try or sync blocks. | |
145 void _markVariableAsUsed(ir.VariableDeclaration variable) { | |
146 if (_isInsideClosure && !_inCurrentContext(variable)) { | |
147 _currentScopeInfo.freeVariables.add(variable); | |
148 } | |
149 if (_inTry) { | |
150 _currentScopeInfo.registerUsedInTryOrSync(_localsMap.getLocal(variable)); | |
151 } | |
152 } | |
153 | |
154 @override | |
155 void visitForStatement(ir.ForStatement node) { | |
156 List<Local> boxedLoopVariables = <Local>[]; | |
157 enterNewScope(node, () { | |
158 // First visit initialized variables and update steps so we can easily | |
159 // check if a loop variable was captured in one of these subexpressions. | |
160 node.variables | |
161 .forEach((ir.VariableDeclaration variable) => variable.accept(this)); | |
162 node.updates | |
163 .forEach((ir.Expression expression) => expression.accept(this)); | |
164 | |
165 // Loop variables that have not been captured yet can safely be flagged as | |
166 // non-mutated, because no nested function can observe the mutation. | |
167 for (ir.VariableDeclaration variable in node.variables) { | |
168 Local local = _localsMap.getLocal(variable); | |
169 if (!_capturedVariables.contains(variable)) { | |
170 _mutatedVariables.remove(local); | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
local => variable?
In that case, it appears you c
Emily Fortuna
2017/06/30 23:48:09
oh goodness, yes, that was a holdover from the Loc
| |
171 } | |
172 } | |
173 | |
174 // Visit condition and body. | |
175 // This must happen after the above, so any loop variables mutated in the | |
176 // condition or body are indeed flagged as mutated. | |
177 if (node.condition != null) node.condition.accept(this); | |
178 node.body.accept(this); | |
179 | |
180 // See if we have declared loop variables that need to be boxed. | |
181 if (node.variables.isEmpty) return; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
minor nit: delete this line? (since the next loop
Emily Fortuna
2017/06/30 23:48:10
Done.
| |
182 for (ir.VariableDeclaration variable in node.variables) { | |
183 Local local = _localsMap.getLocal(variable); | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
move getLocal inside the branch below?
Emily Fortuna
2017/06/30 23:48:09
Done. Yeah, that was a holdover from when the enti
| |
184 // Non-mutated variables should not be boxed. The _mutatedVariables set | |
185 // gets cleared when `enterNewScope` returns, so check it here. | |
186 if (_capturedVariables.contains(variable) && | |
187 _mutatedVariables.contains(variable)) { | |
188 boxedLoopVariables.add(local); | |
189 } | |
190 } | |
191 }); | |
192 KernelClosureScope scope = _closureInfoMap[node]; | |
193 if (scope == null) return; | |
194 _closureInfoMap[node] = new KernelLoopClosureScope(scope.boxedVariables, | |
195 boxedLoopVariables, scope.context, scope.thisLocal); | |
196 } | |
197 | |
198 void visitInvokable(ir.Node node) { | |
199 bool oldIsInsideClosure = _isInsideClosure; | |
200 ir.Node oldExecutableContext = _executableContext; | |
201 KernelScopeInfo oldScopeInfo = _currentScopeInfo; | |
202 | |
203 _isInsideClosure = _outermostNode != null; | |
204 _executableContext = node; | |
205 if (!_isInsideClosure) { | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
consider adding a comment saying that outermostNod
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
206 _outermostNode = node; | |
207 } | |
208 _closuresToGenerate[node] = _currentScopeInfo; | |
209 | |
210 enterNewScope(node, () { | |
211 node.visitChildren(this); | |
212 }); | |
213 | |
214 KernelScopeInfo savedScopeInfo = _currentScopeInfo; | |
215 bool savedIsInsideClosure = _isInsideClosure; | |
216 | |
217 // Restore old values. | |
218 _isInsideClosure = oldIsInsideClosure; | |
219 _currentScopeInfo = oldScopeInfo; | |
220 _executableContext = oldExecutableContext; | |
221 | |
222 // Mark all free variables as captured and expect to encounter them in the | |
223 // outer function. | |
224 Iterable<ir.VariableDeclaration> freeVariables = | |
225 savedScopeInfo.freeVariables; | |
226 assert(freeVariables.isEmpty || savedIsInsideClosure); | |
227 for (ir.VariableDeclaration freeVariable in freeVariables) { | |
228 assert(!_capturedVariables.contains(freeVariable)); | |
229 _capturedVariables.add(freeVariable); | |
230 // If the element is not declared in the current function and the element | |
231 // is not the closure itself we need to mark the element as free variable. | |
232 // Note that the check on [insideClosure] is not just an | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
seems like this comment should move next to where
Emily Fortuna
2017/06/30 23:48:10
Done.
| |
233 // optimization: factories have type parameters as function | |
234 // parameters, and type parameters are declared in the class, not | |
235 // the factory. | |
236 _markVariableAsUsed(freeVariable); | |
237 } | |
238 } | |
239 | |
240 /// Return true if [variable]'s context is the same as the current executable | |
241 /// context. | |
242 bool _inCurrentContext(ir.VariableDeclaration variable) { | |
243 ir.TreeNode node = variable; | |
244 while (node.parent != null) { | |
245 if (variable == node) return true; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
Did you mean to check here against _exectuableCont
Emily Fortuna
2017/06/30 23:48:10
oops, good catch!
| |
246 node = node.parent; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
since ir nodes are connected all the way to the li
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
247 } | |
248 return variable == node; | |
249 } | |
250 | |
251 void translateLazyInitializer(ir.Field field) { | |
252 _currentScopeInfo = | |
253 new KernelScopeInfo(new ThisLocal(_kernelToElementMap.getField(field))); | |
254 visitInvokable(field); | |
255 } | |
256 | |
257 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { | |
258 Entity element; | |
259 if (constructorOrProcedure is ir.Constructor || | |
260 (constructorOrProcedure is ir.Procedure && | |
261 constructorOrProcedure.kind == ir.ProcedureKind.Factory)) { | |
262 element = _kernelToElementMap.getConstructor(constructorOrProcedure); | |
263 } else { | |
264 assert(constructorOrProcedure is ir.Procedure); | |
265 element = _kernelToElementMap.getMethod(constructorOrProcedure); | |
266 } | |
267 _currentScopeInfo = new KernelScopeInfo(new ThisLocal(element)); | |
268 constructorOrProcedure.accept(this); | |
269 } | |
270 | |
271 void visitFunctionNode(ir.FunctionNode functionNode) { | |
272 visitInvokable(functionNode); | |
273 } | |
274 } | |
OLD | NEW |