| 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'; | 8 import '../elements/entities.dart'; |
| 9 import '../kernel/element_map.dart'; | 9 import '../kernel/element_map.dart'; |
| 10 import 'closure.dart'; | 10 import 'closure.dart'; |
| 11 | 11 |
| 12 /// This builder walks the code to determine what variables are captured/free at | 12 /// This builder walks the code to determine what variables are captured/free at |
| 13 /// various points to build CapturedScope that can respond to queries | 13 /// 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. | 14 /// about how a particular variable is being used at any point in the code. |
| 15 class CapturedScopeBuilder extends ir.Visitor { | 15 class CapturedScopeBuilder extends ir.Visitor { |
| 16 final MemberEntity _currentMember; |
| 17 Local _currentLocalFunction; |
| 18 |
| 16 /// A map of each visited call node with the associated information about what | 19 /// A map of each visited call node with the associated information about what |
| 17 /// variables are captured/used. Each ir.Node key corresponds to a scope that | 20 /// variables are captured/used. Each ir.Node key corresponds to a scope that |
| 18 /// was encountered while visiting a closure (initially called through | 21 /// was encountered while visiting a closure (initially called through |
| 19 /// [translateLazyIntializer] or [translateConstructorOrProcedure]). | 22 /// [translateLazyIntializer] or [translateConstructorOrProcedure]). |
| 20 final Map<ir.Node, CapturedScope> _scopesCapturedInClosureMap; | 23 final Map<ir.Node, CapturedScope> _scopesCapturedInClosureMap; |
| 21 | 24 |
| 22 /// Map entities to their corresponding scope information (such as what | 25 /// Map entities to their corresponding scope information (such as what |
| 23 /// variables are captured/used). | 26 /// variables are captured/used). |
| 24 final Map<Entity, ScopeInfo> _scopeInfoMap; | 27 final Map<Entity, ScopeInfo> _scopeInfoMap; |
| 25 | 28 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 56 /// If true, the visitor is currently traversing some nodes that are inside a | 59 /// If true, the visitor is currently traversing some nodes that are inside a |
| 57 /// try block. | 60 /// try block. |
| 58 bool _inTry = false; | 61 bool _inTry = false; |
| 59 | 62 |
| 60 /// Lookup the local entity that corresponds to a kernel variable declaration. | 63 /// Lookup the local entity that corresponds to a kernel variable declaration. |
| 61 final KernelToLocalsMap _localsMap; | 64 final KernelToLocalsMap _localsMap; |
| 62 | 65 |
| 63 /// The current scope we are in. | 66 /// The current scope we are in. |
| 64 KernelScopeInfo _currentScopeInfo; | 67 KernelScopeInfo _currentScopeInfo; |
| 65 | 68 |
| 69 // TODO(johnniwinther): Remove the need for this. |
| 66 final KernelToElementMap _kernelToElementMap; | 70 final KernelToElementMap _kernelToElementMap; |
| 67 | 71 |
| 68 /// The original entity from which we start the tree-walk to find closure and | 72 final Entity _thisLocal; |
| 69 /// scope information. | |
| 70 final Entity _originalEntity; | |
| 71 | 73 |
| 72 CapturedScopeBuilder( | 74 CapturedScopeBuilder( |
| 75 this._currentMember, |
| 73 this._scopesCapturedInClosureMap, | 76 this._scopesCapturedInClosureMap, |
| 74 this._scopeInfoMap, | 77 this._scopeInfoMap, |
| 75 this._originalEntity, | |
| 76 this._closuresToGenerate, | 78 this._closuresToGenerate, |
| 77 this._localsMap, | 79 this._localsMap, |
| 78 this._kernelToElementMap); | 80 this._kernelToElementMap) |
| 81 : this._thisLocal = |
| 82 _currentMember.isInstanceMember || _currentMember.isConstructor |
| 83 ? new ThisLocal(_currentMember) |
| 84 : null; |
| 79 | 85 |
| 80 /// Update the [CapturedScope] object corresponding to | 86 /// Update the [CapturedScope] object corresponding to |
| 81 /// this node if any variables are captured. | 87 /// this node if any variables are captured. |
| 82 void attachCapturedScopeVariables(ir.Node node) { | 88 void attachCapturedScopeVariables(ir.Node node) { |
| 83 Set<Local> capturedVariablesForScope = new Set<Local>(); | 89 Set<Local> capturedVariablesForScope = new Set<Local>(); |
| 84 | 90 |
| 85 for (ir.VariableDeclaration variable in _scopeVariables) { | 91 for (ir.VariableDeclaration variable in _scopeVariables) { |
| 86 // No need to box non-assignable elements. | 92 // No need to box non-assignable elements. |
| 87 if (variable.isFinal || variable.isConst) continue; | 93 if (variable.isFinal || variable.isConst) continue; |
| 88 if (!_mutatedVariables.contains(variable)) continue; | 94 if (!_mutatedVariables.contains(variable)) continue; |
| 89 if (_capturedVariables.contains(variable)) { | 95 if (_capturedVariables.contains(variable)) { |
| 90 capturedVariablesForScope.add(_localsMap.getLocal(variable)); | 96 capturedVariablesForScope.add(_localsMap.getLocal(variable)); |
| 91 } | 97 } |
| 92 } | 98 } |
| 93 if (!capturedVariablesForScope.isEmpty) { | 99 if (!capturedVariablesForScope.isEmpty) { |
| 94 ThisLocal thisLocal = null; | 100 assert(_scopeInfoMap[_currentMember] != null); |
| 95 if (node is ir.Member && node.isInstanceMember) { | 101 assert(_currentLocalFunction != null); |
| 96 if (node is ir.Procedure) { | 102 KernelScopeInfo from = _scopeInfoMap[_currentMember]; |
| 97 thisLocal = new ThisLocal(_kernelToElementMap.getMethod(node)); | |
| 98 } else if (node is ir.Field) { | |
| 99 thisLocal = new ThisLocal(_kernelToElementMap.getField(node)); | |
| 100 } | |
| 101 } else if (node is ir.Constructor) { | |
| 102 thisLocal = new ThisLocal(_kernelToElementMap.getConstructor(node)); | |
| 103 } | |
| 104 | |
| 105 assert(_scopeInfoMap[_nodeToEntity(node)] != null); | |
| 106 KernelScopeInfo from = _scopeInfoMap[_nodeToEntity(node)]; | |
| 107 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( | 103 _scopesCapturedInClosureMap[node] = new KernelCapturedScope( |
| 108 capturedVariablesForScope, | 104 capturedVariablesForScope, |
| 109 _nodeToEntity(_executableContext), | 105 _currentLocalFunction, |
| 110 from.localsUsedInTryOrSync, | 106 from.localsUsedInTryOrSync, |
| 111 from.freeVariables, | 107 from.freeVariables, |
| 112 from.localsMap, | 108 from.localsMap, |
| 113 thisLocal); | 109 _thisLocal); |
| 114 } | |
| 115 } | |
| 116 | |
| 117 Entity _nodeToEntity(ir.Node node) { | |
| 118 if (node is ir.Member) { | |
| 119 return _kernelToElementMap.getMember(node); | |
| 120 } else { | |
| 121 return _kernelToElementMap.getLocalFunction(node); | |
| 122 } | 110 } |
| 123 } | 111 } |
| 124 | 112 |
| 125 /// Perform book-keeping with the current set of local variables that have | 113 /// Perform book-keeping with the current set of local variables that have |
| 126 /// been seen thus far before entering this new scope. | 114 /// been seen thus far before entering this new scope. |
| 127 void enterNewScope(ir.Node node, Function visitNewScope) { | 115 void enterNewScope(ir.Node node, void visitNewScope()) { |
| 128 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; | 116 List<ir.VariableDeclaration> oldScopeVariables = _scopeVariables; |
| 129 _scopeVariables = <ir.VariableDeclaration>[]; | 117 _scopeVariables = <ir.VariableDeclaration>[]; |
| 130 visitNewScope(); | 118 visitNewScope(); |
| 131 attachCapturedScopeVariables(node); | 119 attachCapturedScopeVariables(node); |
| 132 _mutatedVariables.removeAll(_scopeVariables); | 120 _mutatedVariables.removeAll(_scopeVariables); |
| 133 _scopeVariables = oldScopeVariables; | 121 _scopeVariables = oldScopeVariables; |
| 134 } | 122 } |
| 135 | 123 |
| 136 @override | 124 @override |
| 137 void defaultNode(ir.Node node) { | 125 void defaultNode(ir.Node node) { |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 228 scope.localsUsedInTryOrSync, | 216 scope.localsUsedInTryOrSync, |
| 229 scope.freeVariables, | 217 scope.freeVariables, |
| 230 _localsMap, | 218 _localsMap, |
| 231 scope.thisLocal); | 219 scope.thisLocal); |
| 232 } | 220 } |
| 233 | 221 |
| 234 void visitInvokable(ir.TreeNode node) { | 222 void visitInvokable(ir.TreeNode node) { |
| 235 bool oldIsInsideClosure = _isInsideClosure; | 223 bool oldIsInsideClosure = _isInsideClosure; |
| 236 ir.Node oldExecutableContext = _executableContext; | 224 ir.Node oldExecutableContext = _executableContext; |
| 237 KernelScopeInfo oldScopeInfo = _currentScopeInfo; | 225 KernelScopeInfo oldScopeInfo = _currentScopeInfo; |
| 226 Local oldLocalFunction = _currentLocalFunction; |
| 238 | 227 |
| 239 // _outermostNode is only null the first time we enter the body of the | 228 // _outermostNode is only null the first time we enter the body of the |
| 240 // field, constructor, or method that is being analyzed. | 229 // field, constructor, or method that is being analyzed. |
| 241 _isInsideClosure = _outermostNode != null; | 230 _isInsideClosure = _outermostNode != null; |
| 242 _executableContext = node; | 231 _executableContext = node; |
| 243 _currentScopeInfo = new KernelScopeInfo(_nodeToThisLocal(node), _localsMap); | 232 |
| 233 _currentScopeInfo = new KernelScopeInfo(_thisLocal, _localsMap); |
| 244 if (_isInsideClosure) { | 234 if (_isInsideClosure) { |
| 245 _closuresToGenerate[node] = _currentScopeInfo; | 235 _closuresToGenerate[node] = _currentScopeInfo; |
| 236 _currentLocalFunction = _kernelToElementMap.getLocalFunction(node.parent); |
| 246 } else { | 237 } else { |
| 247 _outermostNode = node; | 238 _outermostNode = node; |
| 248 _scopeInfoMap[_originalEntity] = _currentScopeInfo; | 239 _scopeInfoMap[_currentMember] = _currentScopeInfo; |
| 249 } | 240 } |
| 250 | 241 |
| 251 enterNewScope(node, () { | 242 enterNewScope(node, () { |
| 252 node.visitChildren(this); | 243 node.visitChildren(this); |
| 253 }); | 244 }); |
| 254 | 245 |
| 255 KernelScopeInfo savedScopeInfo = _currentScopeInfo; | 246 KernelScopeInfo savedScopeInfo = _currentScopeInfo; |
| 256 bool savedIsInsideClosure = _isInsideClosure; | 247 bool savedIsInsideClosure = _isInsideClosure; |
| 257 | 248 |
| 258 // Restore old values. | 249 // Restore old values. |
| 259 _isInsideClosure = oldIsInsideClosure; | 250 _isInsideClosure = oldIsInsideClosure; |
| 260 _currentScopeInfo = oldScopeInfo; | 251 _currentScopeInfo = oldScopeInfo; |
| 261 _executableContext = oldExecutableContext; | 252 _executableContext = oldExecutableContext; |
| 253 _currentLocalFunction = oldLocalFunction; |
| 262 | 254 |
| 263 // Mark all free variables as captured and expect to encounter them in the | 255 // Mark all free variables as captured and expect to encounter them in the |
| 264 // outer function. | 256 // outer function. |
| 265 Iterable<ir.VariableDeclaration> freeVariables = | 257 Iterable<ir.VariableDeclaration> freeVariables = |
| 266 savedScopeInfo.freeVariables; | 258 savedScopeInfo.freeVariables; |
| 267 assert(freeVariables.isEmpty || savedIsInsideClosure); | 259 assert(freeVariables.isEmpty || savedIsInsideClosure); |
| 268 for (ir.VariableDeclaration freeVariable in freeVariables) { | 260 for (ir.VariableDeclaration freeVariable in freeVariables) { |
| 269 assert(!_capturedVariables.contains(freeVariable)); | 261 assert(!_capturedVariables.contains(freeVariable)); |
| 270 _capturedVariables.add(freeVariable); | 262 _capturedVariables.add(freeVariable); |
| 271 _markVariableAsUsed(freeVariable); | 263 _markVariableAsUsed(freeVariable); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 286 visitInvokable(field); | 278 visitInvokable(field); |
| 287 } | 279 } |
| 288 | 280 |
| 289 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { | 281 void translateConstructorOrProcedure(ir.Node constructorOrProcedure) { |
| 290 constructorOrProcedure.accept(this); | 282 constructorOrProcedure.accept(this); |
| 291 } | 283 } |
| 292 | 284 |
| 293 void visitFunctionNode(ir.FunctionNode functionNode) { | 285 void visitFunctionNode(ir.FunctionNode functionNode) { |
| 294 visitInvokable(functionNode); | 286 visitInvokable(functionNode); |
| 295 } | 287 } |
| 296 | |
| 297 /// If [node] is an instance member return the corresponding `this` reference. | |
| 298 /// If not, return null. | |
| 299 Entity _nodeToThisLocal( | |
| 300 ir.TreeNode | |
| 301 /*ir.Field|ir.FunctionNode|ir.Constructor|ir.Procedure*/ node) { | |
| 302 ir.Node nodeToConvert = node; | |
| 303 if (nodeToConvert is ir.Field) { | |
| 304 if (!nodeToConvert.isInstanceMember) return null; | |
| 305 return new ThisLocal(_kernelToElementMap.getField(nodeToConvert)); | |
| 306 } else { | |
| 307 if (nodeToConvert is ir.FunctionNode) { | |
| 308 // Step up one node higher to find the corresponding entity for this | |
| 309 // node. | |
| 310 nodeToConvert = node.parent; | |
| 311 } | |
| 312 if (nodeToConvert is ir.Constructor || | |
| 313 (nodeToConvert is ir.Procedure && | |
| 314 nodeToConvert.kind == ir.ProcedureKind.Factory && | |
| 315 nodeToConvert.isInstanceMember)) { | |
| 316 return new ThisLocal(_kernelToElementMap.getConstructor(nodeToConvert)); | |
| 317 } else if (nodeToConvert is ir.Procedure && | |
| 318 nodeToConvert.isInstanceMember) { | |
| 319 return new ThisLocal(_kernelToElementMap.getMethod(nodeToConvert)); | |
| 320 } | |
| 321 } | |
| 322 return null; | |
| 323 } | |
| 324 } | 288 } |
| OLD | NEW |