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

Side by Side Diff: pkg/compiler/lib/src/js_model/closure_visitors.dart

Issue 2961253005: Added for-loop variable tracking and regular closures/initializers captured variable tracking. (Closed)
Patch Set: . Created 3 years, 5 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) 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698