Chromium Code Reviews| 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 'closure.dart'; | 8 import 'closure.dart'; |
| 9 | 9 |
| 10 /// 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 |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 /// about variable usage in the surrounding scope. | 27 /// about variable usage in the surrounding scope. |
| 28 Map<ir.FunctionNode, KernelScopeInfo> get _closuresToGenerate => | 28 Map<ir.FunctionNode, KernelScopeInfo> get _closuresToGenerate => |
| 29 _model.closuresToGenerate; | 29 _model.closuresToGenerate; |
| 30 | 30 |
| 31 /// The local variables that have been declared in the current scope. | 31 /// The local variables that have been declared in the current scope. |
| 32 List<ir.VariableDeclaration> _scopeVariables; | 32 List<ir.VariableDeclaration> _scopeVariables; |
| 33 | 33 |
| 34 /// Pointer to the context in which this closure is executed. | 34 /// Pointer to the context in which this closure is executed. |
| 35 /// For example, in the expression `var foo = () => 3 + i;`, the executable | 35 /// For example, in the expression `var foo = () => 3 + i;`, the executable |
| 36 /// 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`. |
| 37 ir.Node _executableContext; | 37 ir.TreeNode _executableContext; |
| 38 | 38 |
| 39 /// A flag to indicate if we are currently inside a closure. | 39 /// A flag to indicate if we are currently inside a closure. |
| 40 bool _isInsideClosure = false; | 40 bool _isInsideClosure = false; |
| 41 | 41 |
| 42 /// Pointer to the original node where this closure builder started. | 42 /// Pointer to the original node where this closure builder started. |
| 43 ir.Node _outermostNode; | 43 ir.Node _outermostNode; |
| 44 | 44 |
| 45 /// 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 |
| 46 /// non-mutated variables. | 46 /// non-mutated variables. |
| 47 Set<ir.VariableDeclaration> _mutatedVariables = | 47 Set<ir.VariableDeclaration> _mutatedVariables = |
| 48 new Set<ir.VariableDeclaration>(); | 48 new Set<ir.VariableDeclaration>(); |
| 49 | 49 |
| 50 /// 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 |
| 51 /// mutated or not. | 51 /// mutated or not. |
| 52 Set<ir.VariableDeclaration> _capturedVariables = | 52 Set<ir.VariableDeclaration> _capturedVariables = |
| 53 new Set<ir.VariableDeclaration>(); | 53 new Set<ir.VariableDeclaration>(); |
| 54 | 54 |
| 55 /// 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 |
| 56 /// try block. | 56 /// try block. |
| 57 bool _inTry = false; | 57 bool _inTry = false; |
| 58 | 58 |
| 59 /// The current scope we are in. | 59 /// The current scope we are in. |
| 60 KernelScopeInfo _currentScopeInfo; | 60 KernelScopeInfo _currentScopeInfo; |
| 61 | 61 |
| 62 final bool _hasThisLocal; | 62 final bool _hasThisLocal; |
| 63 | 63 |
| 64 /// Keeps track of the number of boxes that we've created so that they each | |
| 65 /// have unique names. | |
| 66 int _boxCounter = 0; | |
| 67 | |
| 64 CapturedScopeBuilder(this._model, {bool hasThisLocal}) | 68 CapturedScopeBuilder(this._model, {bool hasThisLocal}) |
| 65 : this._hasThisLocal = hasThisLocal; | 69 : this._hasThisLocal = hasThisLocal; |
| 66 | 70 |
| 67 /// Update the [CapturedScope] object corresponding to | 71 /// Update the [CapturedScope] object corresponding to |
| 68 /// this node if any variables are captured. | 72 /// this node if any variables are captured. |
| 69 void attachCapturedScopeVariables(ir.Node node) { | 73 void attachCapturedScopeVariables(ir.Node node) { |
| 70 Set<ir.VariableDeclaration> capturedVariablesForScope = | 74 Set<ir.VariableDeclaration> capturedVariablesForScope = |
| 71 new Set<ir.VariableDeclaration>(); | 75 new Set<ir.VariableDeclaration>(); |
| 72 | 76 |
| 73 for (ir.VariableDeclaration variable in _scopeVariables) { | 77 for (ir.VariableDeclaration variable in _scopeVariables) { |
| 74 // No need to box non-assignable elements. | 78 // No need to box non-assignable elements. |
| 75 if (variable.isFinal || variable.isConst) continue; | 79 if (variable.isFinal || variable.isConst) continue; |
| 76 if (!_mutatedVariables.contains(variable)) continue; | 80 if (!_mutatedVariables.contains(variable)) continue; |
| 77 if (_capturedVariables.contains(variable)) { | 81 if (_capturedVariables.contains(variable)) { |
| 78 capturedVariablesForScope.add(variable); | 82 capturedVariablesForScope.add(variable); |
| 79 } | 83 } |
| 80 } | 84 } |
| 81 if (!capturedVariablesForScope.isEmpty) { | 85 if (!capturedVariablesForScope.isEmpty) { |
| 82 assert(_model.scopeInfo != null); | 86 assert(_model.scopeInfo != null); |
| 83 assert(_currentLocalFunction != null); | 87 assert(_currentLocalFunction != null); |
| 84 KernelScopeInfo from = _model.scopeInfo; | 88 KernelScopeInfo from = _model.scopeInfo; |
| 85 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( | 89 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( |
| 86 capturedVariablesForScope, | 90 capturedVariablesForScope, |
| 91 new NodeBox(getBoxName(), _executableContext, | |
| 92 _getMemberContext(_executableContext)), | |
|
Johnni Winther
2017/08/01 09:09:10
The member context is not needed. See comment in [
Emily Fortuna
2017/08/01 19:30:06
got it.
| |
| 87 _currentLocalFunction, | 93 _currentLocalFunction, |
| 88 from.localsUsedInTryOrSync, | 94 from.localsUsedInTryOrSync, |
| 89 from.freeVariables, | 95 from.freeVariables, |
| 90 _hasThisLocal); | 96 _hasThisLocal); |
| 91 } | 97 } |
| 92 } | 98 } |
| 93 | 99 |
| 100 /// Look up the outermost member that contains this node. | |
| 101 /// | |
| 102 /// For top level, static or instance members, the member context is the | |
| 103 /// node itself. For parameters, local variables and nested closures, the | |
| 104 /// member context is the top level, static or instance member in which it is | |
| 105 /// defined. | |
| 106 ir.Member _getMemberContext(ir.TreeNode startNode) { | |
| 107 ir.TreeNode node = startNode; | |
| 108 while (node is! ir.Member && node != _outermostNode) { | |
| 109 node = node.parent; | |
| 110 } | |
| 111 assert(node is ir.Member); | |
| 112 return node; | |
| 113 } | |
| 114 | |
| 115 /// Generate a unique name for the [_boxCounter]th box field. | |
| 116 /// | |
| 117 /// The result is used as the name of [NodeBox]s and [BoxLocal]s, and must | |
| 118 /// therefore be unique to avoid breaking an invariant in the element model | |
| 119 /// (classes cannot declare multiple fields with the same name). | |
| 120 /// | |
| 121 /// Also, the names should be distinct from real field names to prevent | |
| 122 /// clashes with selectors for those fields. | |
| 123 /// | |
| 124 /// These names are not used in generated code, just as element name. | |
| 125 String getBoxName() { | |
| 126 return "_box_${_boxCounter++}"; | |
| 127 } | |
| 128 | |
| 94 /// Perform book-keeping with the current set of local variables that have | 129 /// Perform book-keeping with the current set of local variables that have |
| 95 /// been seen thus far before entering this new scope. | 130 /// been seen thus far before entering this new scope. |
| 96 void enterNewScope(ir.Node node, void visitNewScope()) { | 131 void enterNewScope(ir.Node node, void visitNewScope()) { |
| 97 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; | 132 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; |
| 98 _scopeVariables = <ir.VariableDeclaration>[]; | 133 _scopeVariables = <ir.VariableDeclaration>[]; |
| 99 visitNewScope(); | 134 visitNewScope(); |
| 100 attachCapturedScopeVariables(node); | 135 attachCapturedScopeVariables(node); |
| 101 _mutatedVariables.removeAll(_scopeVariables); | 136 _mutatedVariables.removeAll(_scopeVariables); |
| 102 _scopeVariables = oldScopeVariables; | 137 _scopeVariables = oldScopeVariables; |
| 103 } | 138 } |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 185 if (_capturedVariables.contains(variable) && | 220 if (_capturedVariables.contains(variable) && |
| 186 _mutatedVariables.contains(variable)) { | 221 _mutatedVariables.contains(variable)) { |
| 187 boxedLoopVariables.add(variable); | 222 boxedLoopVariables.add(variable); |
| 188 } | 223 } |
| 189 } | 224 } |
| 190 }); | 225 }); |
| 191 KernelCapturedScope scope = _scopesCapturedInClosureMap[node]; | 226 KernelCapturedScope scope = _scopesCapturedInClosureMap[node]; |
| 192 if (scope == null) return; | 227 if (scope == null) return; |
| 193 _scopesCapturedInClosureMap[node] = new KernelCapturedLoopScope( | 228 _scopesCapturedInClosureMap[node] = new KernelCapturedLoopScope( |
| 194 scope.boxedVariables, | 229 scope.boxedVariables, |
| 230 scope.capturedVariablesAccessor, | |
| 195 boxedLoopVariables, | 231 boxedLoopVariables, |
| 196 scope.context, | 232 scope.context, |
| 197 scope.localsUsedInTryOrSync, | 233 scope.localsUsedInTryOrSync, |
| 198 scope.freeVariables, | 234 scope.freeVariables, |
| 199 scope.hasThisLocal); | 235 scope.hasThisLocal); |
| 200 } | 236 } |
| 201 | 237 |
| 202 void visitInvokable(ir.TreeNode node) { | 238 void visitInvokable(ir.TreeNode node) { |
| 203 bool oldIsInsideClosure = _isInsideClosure; | 239 bool oldIsInsideClosure = _isInsideClosure; |
| 204 ir.Node oldExecutableContext = _executableContext; | 240 ir.TreeNode oldExecutableContext = _executableContext; |
| 205 KernelScopeInfo oldScopeInfo = _currentScopeInfo; | 241 KernelScopeInfo oldScopeInfo = _currentScopeInfo; |
| 206 ir.TreeNode oldLocalFunction = _currentLocalFunction; | 242 ir.TreeNode oldLocalFunction = _currentLocalFunction; |
| 207 | 243 |
| 208 // _outermostNode is only null the first time we enter the body of the | 244 // _outermostNode is only null the first time we enter the body of the |
| 209 // field, constructor, or method that is being analyzed. | 245 // field, constructor, or method that is being analyzed. |
| 210 _isInsideClosure = _outermostNode != null; | 246 _isInsideClosure = _outermostNode != null; |
| 211 _executableContext = node; | 247 _executableContext = node; |
| 212 | 248 |
| 213 _currentScopeInfo = new KernelScopeInfo(_hasThisLocal); | 249 _currentScopeInfo = new KernelScopeInfo(_hasThisLocal); |
| 214 if (_isInsideClosure) { | 250 if (_isInsideClosure) { |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 258 } | 294 } |
| 259 | 295 |
| 260 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { | 296 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { |
| 261 constructorOrProcedure.accept(this); | 297 constructorOrProcedure.accept(this); |
| 262 } | 298 } |
| 263 | 299 |
| 264 void visitFunctionNode(ir.FunctionNode functionNode) { | 300 void visitFunctionNode(ir.FunctionNode functionNode) { |
| 265 visitInvokable(functionNode); | 301 visitInvokable(functionNode); |
| 266 } | 302 } |
| 267 } | 303 } |
| OLD | NEW |