OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 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 | 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 import 'package:kernel/ast.dart' as ir; | 5 import 'package:kernel/ast.dart' as ir; |
6 | 6 |
7 import '../closure.dart'; | 7 import '../closure.dart'; |
8 import '../elements/entities.dart'; | |
9 import '../kernel/element_map.dart'; | |
10 import 'closure.dart'; | 8 import 'closure.dart'; |
11 | 9 |
12 /// This builder walks the code to determine what variables are captured/free at | 10 /// This builder walks the code to determine what variables are captured/free at |
13 /// various points to build CapturedScope that can respond to queries | 11 /// various points to build CapturedScope that can respond to queries |
14 /// about how a particular variable is being used at any point in the code. | 12 /// about how a particular variable is being used at any point in the code. |
15 class CapturedScopeBuilder extends ir.Visitor { | 13 class CapturedScopeBuilder extends ir.Visitor { |
16 final MemberEntity _currentMember; | 14 ir.TreeNode _currentLocalFunction; |
17 Local _currentLocalFunction; | 15 |
| 16 ClosureModel _model; |
18 | 17 |
19 /// A map of each visited call node with the associated information about what | 18 /// A map of each visited call node with the associated information about what |
20 /// variables are captured/used. Each ir.Node key corresponds to a scope that | 19 /// variables are captured/used. Each ir.Node key corresponds to a scope that |
21 /// was encountered while visiting a closure (initially called through | 20 /// was encountered while visiting a closure (initially called through |
22 /// [translateLazyIntializer] or [translateConstructorOrProcedure]). | 21 /// [translateLazyIntializer] or [translateConstructorOrProcedure]). |
23 final Map<ir.Node, CapturedScope> _scopesCapturedInClosureMap; | 22 Map<ir.Node, KernelCapturedScope> get _scopesCapturedInClosureMap => |
24 | 23 _model.capturedScopesMap; |
25 /// Map entities to their corresponding scope information (such as what | |
26 /// variables are captured/used). | |
27 final Map<Entity, ScopeInfo> _scopeInfoMap; | |
28 | 24 |
29 /// A map of the nodes that we have flagged as necessary to generate closure | 25 /// A map of the nodes that we have flagged as necessary to generate closure |
30 /// classes for in a later stage. We map that node to information ascertained | 26 /// classes for in a later stage. We map that node to information ascertained |
31 /// about variable usage in the surrounding scope. | 27 /// about variable usage in the surrounding scope. |
32 final Map<ir.TreeNode /* ir.Field | ir.FunctionNode */, ScopeInfo> | 28 Map<ir.TreeNode /* ir.Field | ir.FunctionNode */, KernelScopeInfo> |
33 _closuresToGenerate; | 29 get _closuresToGenerate => _model.closuresToGenerate; |
34 | 30 |
35 /// The local variables that have been declared in the current scope. | 31 /// The local variables that have been declared in the current scope. |
36 List<ir.VariableDeclaration> _scopeVariables; | 32 List<ir.VariableDeclaration> _scopeVariables; |
37 | 33 |
38 /// Pointer to the context in which this closure is executed. | 34 /// Pointer to the context in which this closure is executed. |
39 /// For example, in the expression `var foo = () => 3 + i;`, the executable | 35 /// For example, in the expression `var foo = () => 3 + i;`, the executable |
40 /// context as we walk the nodes in that expression is the ir.Field `foo`. | 36 /// context as we walk the nodes in that expression is the ir.Field `foo`. |
41 ir.Node _executableContext; | 37 ir.Node _executableContext; |
42 | 38 |
43 /// A flag to indicate if we are currently inside a closure. | 39 /// A flag to indicate if we are currently inside a closure. |
44 bool _isInsideClosure = false; | 40 bool _isInsideClosure = false; |
45 | 41 |
46 /// Pointer to the original node where this closure builder started. | 42 /// Pointer to the original node where this closure builder started. |
47 ir.Node _outermostNode; | 43 ir.Node _outermostNode; |
48 | 44 |
49 /// Keep track of the mutated local variables so that we don't need to box | 45 /// Keep track of the mutated local variables so that we don't need to box |
50 /// non-mutated variables. | 46 /// non-mutated variables. |
51 Set<ir.VariableDeclaration> _mutatedVariables = | 47 Set<ir.VariableDeclaration> _mutatedVariables = |
52 new Set<ir.VariableDeclaration>(); | 48 new Set<ir.VariableDeclaration>(); |
53 | 49 |
54 /// The set of variables that are accessed in some form, whether they are | 50 /// The set of variables that are accessed in some form, whether they are |
55 /// mutated or not. | 51 /// mutated or not. |
56 Set<ir.VariableDeclaration> _capturedVariables = | 52 Set<ir.VariableDeclaration> _capturedVariables = |
57 new Set<ir.VariableDeclaration>(); | 53 new Set<ir.VariableDeclaration>(); |
58 | 54 |
59 /// If true, the visitor is currently traversing some nodes that are inside a | 55 /// If true, the visitor is currently traversing some nodes that are inside a |
60 /// try block. | 56 /// try block. |
61 bool _inTry = false; | 57 bool _inTry = false; |
62 | 58 |
63 /// Lookup the local entity that corresponds to a kernel variable declaration. | |
64 final KernelToLocalsMap _localsMap; | |
65 | |
66 /// The current scope we are in. | 59 /// The current scope we are in. |
67 KernelScopeInfo _currentScopeInfo; | 60 KernelScopeInfo _currentScopeInfo; |
68 | 61 |
69 final Entity _thisLocal; | 62 final bool _hasThisLocal; |
70 | 63 |
71 CapturedScopeBuilder(this._currentMember, this._scopesCapturedInClosureMap, | 64 CapturedScopeBuilder(this._model, {bool hasThisLocal}) |
72 this._scopeInfoMap, this._closuresToGenerate, this._localsMap) | 65 : this._hasThisLocal = hasThisLocal; |
73 : this._thisLocal = | |
74 _currentMember.isInstanceMember || _currentMember.isConstructor | |
75 ? new ThisLocal(_currentMember) | |
76 : null; | |
77 | 66 |
78 /// Update the [CapturedScope] object corresponding to | 67 /// Update the [CapturedScope] object corresponding to |
79 /// this node if any variables are captured. | 68 /// this node if any variables are captured. |
80 void attachCapturedScopeVariables(ir.Node node) { | 69 void attachCapturedScopeVariables(ir.Node node) { |
81 Set<Local> capturedVariablesForScope = new Set<Local>(); | 70 Set<ir.VariableDeclaration> capturedVariablesForScope = |
| 71 new Set<ir.VariableDeclaration>(); |
82 | 72 |
83 for (ir.VariableDeclaration variable in _scopeVariables) { | 73 for (ir.VariableDeclaration variable in _scopeVariables) { |
84 // No need to box non-assignable elements. | 74 // No need to box non-assignable elements. |
85 if (variable.isFinal || variable.isConst) continue; | 75 if (variable.isFinal || variable.isConst) continue; |
86 if (!_mutatedVariables.contains(variable)) continue; | 76 if (!_mutatedVariables.contains(variable)) continue; |
87 if (_capturedVariables.contains(variable)) { | 77 if (_capturedVariables.contains(variable)) { |
88 capturedVariablesForScope.add(_localsMap.getLocalVariable(variable)); | 78 capturedVariablesForScope.add(variable); |
89 } | 79 } |
90 } | 80 } |
91 if (!capturedVariablesForScope.isEmpty) { | 81 if (!capturedVariablesForScope.isEmpty) { |
92 assert(_scopeInfoMap[_currentMember] != null); | 82 assert(_model.scopeInfo != null); |
93 assert(_currentLocalFunction != null); | 83 assert(_currentLocalFunction != null); |
94 KernelScopeInfo from = _scopeInfoMap[_currentMember]; | 84 KernelScopeInfo from = _model.scopeInfo; |
95 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( | 85 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( |
96 capturedVariablesForScope, | 86 capturedVariablesForScope, |
97 _currentLocalFunction, | 87 _currentLocalFunction, |
98 from.localsUsedInTryOrSync, | 88 from.localsUsedInTryOrSync, |
99 from.freeVariables, | 89 from.freeVariables, |
100 _thisLocal); | 90 _hasThisLocal); |
101 } | 91 } |
102 } | 92 } |
103 | 93 |
104 /// Perform book-keeping with the current set of local variables that have | 94 /// Perform book-keeping with the current set of local variables that have |
105 /// been seen thus far before entering this new scope. | 95 /// been seen thus far before entering this new scope. |
106 void enterNewScope(ir.Node node, void visitNewScope()) { | 96 void enterNewScope(ir.Node node, void visitNewScope()) { |
107 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; | 97 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; |
108 _scopeVariables = <ir.VariableDeclaration>[]; | 98 _scopeVariables = <ir.VariableDeclaration>[]; |
109 visitNewScope(); | 99 visitNewScope(); |
110 attachCapturedScopeVariables(node); | 100 attachCapturedScopeVariables(node); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 if (_isInsideClosure && !_inCurrentContext(variable)) { | 141 if (_isInsideClosure && !_inCurrentContext(variable)) { |
152 // If the element is not declared in the current function and the element | 142 // If the element is not declared in the current function and the element |
153 // is not the closure itself we need to mark the element as free variable. | 143 // is not the closure itself we need to mark the element as free variable. |
154 // Note that the check on [insideClosure] is not just an | 144 // Note that the check on [insideClosure] is not just an |
155 // optimization: factories have type parameters as function | 145 // optimization: factories have type parameters as function |
156 // parameters, and type parameters are declared in the class, not | 146 // parameters, and type parameters are declared in the class, not |
157 // the factory. | 147 // the factory. |
158 _currentScopeInfo.freeVariables.add(variable); | 148 _currentScopeInfo.freeVariables.add(variable); |
159 } | 149 } |
160 if (_inTry) { | 150 if (_inTry) { |
161 _currentScopeInfo.localsUsedInTryOrSync | 151 _currentScopeInfo.localsUsedInTryOrSync.add(variable); |
162 .add(_localsMap.getLocalVariable(variable)); | |
163 } | 152 } |
164 } | 153 } |
165 | 154 |
166 @override | 155 @override |
167 void visitForStatement(ir.ForStatement node) { | 156 void visitForStatement(ir.ForStatement node) { |
168 List<Local> boxedLoopVariables = <Local>[]; | 157 List<ir.VariableDeclaration> boxedLoopVariables = |
| 158 <ir.VariableDeclaration>[]; |
169 enterNewScope(node, () { | 159 enterNewScope(node, () { |
170 // First visit initialized variables and update steps so we can easily | 160 // First visit initialized variables and update steps so we can easily |
171 // check if a loop variable was captured in one of these subexpressions. | 161 // check if a loop variable was captured in one of these subexpressions. |
172 node.variables | 162 node.variables |
173 .forEach((ir.VariableDeclaration variable) => variable.accept(this)); | 163 .forEach((ir.VariableDeclaration variable) => variable.accept(this)); |
174 node.updates | 164 node.updates |
175 .forEach((ir.Expression expression) => expression.accept(this)); | 165 .forEach((ir.Expression expression) => expression.accept(this)); |
176 | 166 |
177 // Loop variables that have not been captured yet can safely be flagged as | 167 // Loop variables that have not been captured yet can safely be flagged as |
178 // non-mutated, because no nested function can observe the mutation. | 168 // non-mutated, because no nested function can observe the mutation. |
179 for (ir.VariableDeclaration variable in node.variables) { | 169 for (ir.VariableDeclaration variable in node.variables) { |
180 if (!_capturedVariables.contains(variable)) { | 170 if (!_capturedVariables.contains(variable)) { |
181 _mutatedVariables.remove(variable); | 171 _mutatedVariables.remove(variable); |
182 } | 172 } |
183 } | 173 } |
184 | 174 |
185 // Visit condition and body. | 175 // Visit condition and body. |
186 // This must happen after the above, so any loop variables mutated in the | 176 // This must happen after the above, so any loop variables mutated in the |
187 // condition or body are indeed flagged as mutated. | 177 // condition or body are indeed flagged as mutated. |
188 if (node.condition != null) node.condition.accept(this); | 178 if (node.condition != null) node.condition.accept(this); |
189 node.body.accept(this); | 179 node.body.accept(this); |
190 | 180 |
191 // See if we have declared loop variables that need to be boxed. | 181 // See if we have declared loop variables that need to be boxed. |
192 for (ir.VariableDeclaration variable in node.variables) { | 182 for (ir.VariableDeclaration variable in node.variables) { |
193 // Non-mutated variables should not be boxed. The _mutatedVariables set | 183 // Non-mutated variables should not be boxed. The _mutatedVariables set |
194 // gets cleared when `enterNewScope` returns, so check it here. | 184 // gets cleared when `enterNewScope` returns, so check it here. |
195 if (_capturedVariables.contains(variable) && | 185 if (_capturedVariables.contains(variable) && |
196 _mutatedVariables.contains(variable)) { | 186 _mutatedVariables.contains(variable)) { |
197 boxedLoopVariables.add(_localsMap.getLocalVariable(variable)); | 187 boxedLoopVariables.add(variable); |
198 } | 188 } |
199 } | 189 } |
200 }); | 190 }); |
201 KernelCapturedScope scope = _scopesCapturedInClosureMap[node]; | 191 KernelCapturedScope scope = _scopesCapturedInClosureMap[node]; |
202 if (scope == null) return; | 192 if (scope == null) return; |
203 _scopesCapturedInClosureMap[node] = new KernelCapturedLoopScope( | 193 _scopesCapturedInClosureMap[node] = new KernelCapturedLoopScope( |
204 scope.boxedVariables, | 194 scope.boxedVariables, |
205 boxedLoopVariables, | 195 boxedLoopVariables, |
206 scope.context, | 196 scope.context, |
207 scope.localsUsedInTryOrSync, | 197 scope.localsUsedInTryOrSync, |
208 scope.freeVariables, | 198 scope.freeVariables, |
209 scope.thisLocal); | 199 scope.hasThisLocal); |
210 } | 200 } |
211 | 201 |
212 void visitInvokable(ir.TreeNode node) { | 202 void visitInvokable(ir.TreeNode node) { |
213 bool oldIsInsideClosure = _isInsideClosure; | 203 bool oldIsInsideClosure = _isInsideClosure; |
214 ir.Node oldExecutableContext = _executableContext; | 204 ir.Node oldExecutableContext = _executableContext; |
215 KernelScopeInfo oldScopeInfo = _currentScopeInfo; | 205 KernelScopeInfo oldScopeInfo = _currentScopeInfo; |
216 Local oldLocalFunction = _currentLocalFunction; | 206 ir.TreeNode oldLocalFunction = _currentLocalFunction; |
217 | 207 |
218 // _outermostNode is only null the first time we enter the body of the | 208 // _outermostNode is only null the first time we enter the body of the |
219 // field, constructor, or method that is being analyzed. | 209 // field, constructor, or method that is being analyzed. |
220 _isInsideClosure = _outermostNode != null; | 210 _isInsideClosure = _outermostNode != null; |
221 _executableContext = node; | 211 _executableContext = node; |
222 | 212 |
223 _currentScopeInfo = new KernelScopeInfo(_thisLocal); | 213 _currentScopeInfo = new KernelScopeInfo(_hasThisLocal); |
224 if (_isInsideClosure) { | 214 if (_isInsideClosure) { |
225 _closuresToGenerate[node] = _currentScopeInfo; | 215 _closuresToGenerate[node] = _currentScopeInfo; |
226 _currentLocalFunction = _localsMap.getLocalFunction(node.parent); | 216 _currentLocalFunction = node.parent; |
227 } else { | 217 } else { |
228 _outermostNode = node; | 218 _outermostNode = node; |
229 _scopeInfoMap[_currentMember] = _currentScopeInfo; | 219 _model.scopeInfo = _currentScopeInfo; |
230 } | 220 } |
231 | 221 |
232 enterNewScope(node, () { | 222 enterNewScope(node, () { |
233 node.visitChildren(this); | 223 node.visitChildren(this); |
234 }); | 224 }); |
235 | 225 |
236 KernelScopeInfo savedScopeInfo = _currentScopeInfo; | 226 KernelScopeInfo savedScopeInfo = _currentScopeInfo; |
237 bool savedIsInsideClosure = _isInsideClosure; | 227 bool savedIsInsideClosure = _isInsideClosure; |
238 | 228 |
239 // Restore old values. | 229 // Restore old values. |
(...skipping 29 matching lines...) Expand all Loading... |
269 } | 259 } |
270 | 260 |
271 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { | 261 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { |
272 constructorOrProcedure.accept(this); | 262 constructorOrProcedure.accept(this); |
273 } | 263 } |
274 | 264 |
275 void visitFunctionNode(ir.FunctionNode functionNode) { | 265 void visitFunctionNode(ir.FunctionNode functionNode) { |
276 visitInvokable(functionNode); | 266 visitInvokable(functionNode); |
277 } | 267 } |
278 } | 268 } |
OLD | NEW |