| 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 '../common/tasks.dart'; | 8 import '../common/tasks.dart'; |
| 9 import '../elements/elements.dart'; | |
| 10 import '../elements/entities.dart'; | 9 import '../elements/entities.dart'; |
| 11 import '../elements/entity_utils.dart' as utils; | |
| 12 import '../elements/names.dart' show Name; | |
| 13 import '../kernel/element_map.dart'; | 10 import '../kernel/element_map.dart'; |
| 14 import '../world.dart'; | 11 import '../world.dart'; |
| 15 import 'elements.dart'; | 12 import 'elements.dart'; |
| 16 import 'closure_visitors.dart'; | 13 import 'closure_visitors.dart'; |
| 17 import 'locals.dart'; | 14 import 'locals.dart'; |
| 18 | 15 |
| 19 /// Closure conversion code using our new Entity model. Closure conversion is | 16 /// Closure conversion code using our new Entity model. Closure conversion is |
| 20 /// necessary because the semantics of closures are slightly different in Dart | 17 /// necessary because the semantics of closures are slightly different in Dart |
| 21 /// than JavaScript. Closure conversion is separated out into two phases: | 18 /// than JavaScript. Closure conversion is separated out into two phases: |
| 22 /// generation of a new (temporary) representation to store where variables need | 19 /// generation of a new (temporary) representation to store where variables need |
| 23 /// to be hoisted/captured up at another level to re-write the closure, and then | 20 /// to be hoisted/captured up at another level to re-write the closure, and then |
| 24 /// the code generation phase where we generate elements and/or instructions to | 21 /// the code generation phase where we generate elements and/or instructions to |
| 25 /// represent this new code path. | 22 /// represent this new code path. |
| 26 /// | 23 /// |
| 27 /// For a general explanation of how closure conversion works at a high level, | 24 /// For a general explanation of how closure conversion works at a high level, |
| 28 /// check out: | 25 /// check out: |
| 29 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or | 26 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or |
| 30 /// http://matt.might.net/articles/closure-conversion/. | 27 /// http://matt.might.net/articles/closure-conversion/. |
| 31 // TODO(efortuna): Change inheritance hierarchy so that the | 28 // TODO(efortuna): Change inheritance hierarchy so that the |
| 32 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a | 29 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a |
| 33 // glorified timer. | 30 // glorified timer. |
| 34 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { | 31 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
| 35 final KernelToElementMapForBuilding _elementMap; | 32 final KernelToElementMapForBuilding _elementMap; |
| 36 final GlobalLocalsMap _globalLocalsMap; | 33 final GlobalLocalsMap _globalLocalsMap; |
| 37 | 34 |
| 38 /// Map of the scoping information that corresponds to a particular entity. | 35 /// Map of the scoping information that corresponds to a particular entity. |
| 39 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; | 36 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; |
| 40 Map<ir.Node, CapturedScope> _capturedScopesMap = <ir.Node, CapturedScope>{}; | 37 Map<ir.Node, CapturedScope> _scopesCapturedInClosureMap = |
| 38 <ir.Node, CapturedScope>{}; |
| 41 | 39 |
| 42 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = | 40 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = |
| 43 <Entity, ClosureRepresentationInfo>{}; | 41 <Entity, ClosureRepresentationInfo>{}; |
| 44 | 42 |
| 45 /// Should only be used at the very beginning to ensure we are looking at the | 43 /// Should only be used at the very beginning to ensure we are looking at the |
| 46 /// right kind of elements. | 44 /// right kind of elements. |
| 47 // TODO(efortuna): Remove this map once we have one kernel backend strategy. | 45 // TODO(efortuna): Remove this map once we have one kernel backend strategy. |
| 48 final JsToFrontendMap _kToJElementMap; | 46 final JsToFrontendMap _kToJElementMap; |
| 49 | 47 |
| 50 KernelClosureConversionTask(Measurer measurer, this._elementMap, | 48 KernelClosureConversionTask(Measurer measurer, this._elementMap, |
| 51 this._kToJElementMap, this._globalLocalsMap) | 49 this._kToJElementMap, this._globalLocalsMap) |
| 52 : super(measurer); | 50 : super(measurer); |
| 53 | 51 |
| 54 /// The combined steps of generating our intermediate representation of | 52 /// The combined steps of generating our intermediate representation of |
| 55 /// closures that need to be rewritten and generating the element model. | 53 /// closures that need to be rewritten and generating the element model. |
| 56 /// Ultimately these two steps will be split apart with the second step | 54 /// Ultimately these two steps will be split apart with the second step |
| 57 /// happening later in compilation just before codegen. These steps are | 55 /// happening later in compilation just before codegen. These steps are |
| 58 /// combined here currently to provide a consistent interface to the rest of | 56 /// combined here currently to provide a consistent interface to the rest of |
| 59 /// the compiler until we are ready to separate these phases. | 57 /// the compiler until we are ready to separate these phases. |
| 60 @override | 58 @override |
| 61 void convertClosures(Iterable<MemberEntity> processedEntities, | 59 void convertClosures(Iterable<MemberEntity> processedEntities, |
| 62 ClosedWorldRefiner closedWorldRefiner) { | 60 ClosedWorldRefiner closedWorldRefiner) { |
| 63 var closuresToGenerate = <ir.TreeNode, ScopeInfo>{}; | 61 var closuresToGenerate = <ir.TreeNode, ScopeInfo>{}; |
| 64 processedEntities.forEach((MemberEntity kEntity) { | 62 processedEntities.forEach((MemberEntity kEntity) { |
| 65 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); | 63 MemberEntity entity = kEntity; |
| 64 if (_kToJElementMap != null) { |
| 65 entity = _kToJElementMap.toBackendMember(kEntity); |
| 66 } |
| 66 if (entity.isAbstract) return; | 67 if (entity.isAbstract) return; |
| 67 if (entity.isField && !entity.isInstanceMember) { | 68 if (entity.isField && !entity.isInstanceMember) { |
| 68 ir.Field field = _elementMap.getMemberNode(entity); | 69 ir.Field field = _elementMap.getMemberNode(entity); |
| 69 // Skip top-level/static fields without an initializer. | 70 // Skip top-level/static fields without an initializer. |
| 70 if (field.initializer == null) return; | 71 if (field.initializer == null) return; |
| 71 } | 72 } |
| 72 _buildClosureModel(entity, closuresToGenerate, closedWorldRefiner); | 73 _buildClosureModel(entity, closuresToGenerate, closedWorldRefiner); |
| 73 }); | 74 }); |
| 74 | 75 |
| 75 for (ir.TreeNode node in closuresToGenerate.keys) { | 76 for (ir.TreeNode node in closuresToGenerate.keys) { |
| 76 _produceSyntheticElements( | 77 _produceSyntheticElements( |
| 77 node, closuresToGenerate[node], closedWorldRefiner); | 78 node, closuresToGenerate[node], closedWorldRefiner); |
| 78 } | 79 } |
| 79 } | 80 } |
| 80 | 81 |
| 81 /// Inspect members and mark if those members capture any state that needs to | 82 /// Inspect members and mark if those members capture any state that needs to |
| 82 /// be marked as free variables. | 83 /// be marked as free variables. |
| 83 void _buildClosureModel( | 84 void _buildClosureModel( |
| 84 MemberEntity entity, | 85 MemberEntity entity, |
| 85 Map<ir.TreeNode, ScopeInfo> closuresToGenerate, | 86 Map<ir.TreeNode, ScopeInfo> closuresToGenerate, |
| 86 ClosedWorldRefiner closedWorldRefiner) { | 87 ClosedWorldRefiner closedWorldRefiner) { |
| 87 if (_scopeMap.keys.contains(entity)) return; | 88 if (_scopeMap.keys.contains(entity)) return; |
| 88 ir.Node node = _elementMap.getMemberNode(entity); | 89 ir.Node node = _elementMap.getMemberNode(entity); |
| 89 if (_capturedScopesMap.keys.contains(node)) return; | 90 if (_scopesCapturedInClosureMap.keys.contains(node)) return; |
| 90 CapturedScopeBuilder translator = new CapturedScopeBuilder( | 91 CapturedScopeBuilder translator = new CapturedScopeBuilder( |
| 91 _capturedScopesMap, | 92 _scopesCapturedInClosureMap, |
| 92 _scopeMap, | 93 _scopeMap, |
| 93 entity, | 94 entity, |
| 94 closuresToGenerate, | 95 closuresToGenerate, |
| 95 _globalLocalsMap.getLocalsMap(entity), | 96 _globalLocalsMap.getLocalsMap(entity), |
| 96 _elementMap); | 97 _elementMap); |
| 97 if (entity.isField) { | 98 if (entity.isField) { |
| 98 if (node is ir.Field && node.initializer != null) { | 99 if (node is ir.Field && node.initializer != null) { |
| 99 translator.translateLazyInitializer(node); | 100 translator.translateLazyInitializer(node); |
| 100 } | 101 } |
| 101 } else { | 102 } else { |
| 102 assert(node is ir.Procedure || node is ir.Constructor); | 103 assert(node is ir.Procedure || node is ir.Constructor); |
| 103 translator.translateConstructorOrProcedure(node); | 104 translator.translateConstructorOrProcedure(node); |
| 104 } | 105 } |
| 105 } | 106 } |
| 106 | 107 |
| 107 /// Given what variables are captured at each point, construct closure classes | 108 /// Given what variables are captured at each point, construct closure classes |
| 108 /// with fields containing the captured variables to replicate the Dart | 109 /// with fields containing the captured variables to replicate the Dart |
| 109 /// closure semantics in JS. If this closure captures any variables (meaning | 110 /// closure semantics in JS. |
| 110 /// the closure accesses a variable that gets accessed at some point), then | |
| 111 /// boxForCapturedVariables stores the local context for those variables. | |
| 112 /// If no variables are captured, this parameter is null. | |
| 113 void _produceSyntheticElements( | 111 void _produceSyntheticElements( |
| 114 ir.TreeNode /* ir.Field | ir.FunctionNode */ node, | 112 ir.TreeNode /* ir.Field | ir.FunctionNode */ node, |
| 115 ScopeInfo info, | 113 ScopeInfo info, |
| 116 ClosedWorldRefiner closedWorldRefiner) { | 114 ClosedWorldRefiner closedWorldRefiner) { |
| 117 Entity entity; | 115 Entity entity; |
| 118 ir.Library library; | 116 KernelClosureClass closureClass = |
| 119 if (node is ir.Member) { | 117 new KernelClosureClass.fromScopeInfo(info); |
| 120 entity = _elementMap.getMember(node); | |
| 121 library = node.enclosingLibrary; | |
| 122 } else { | |
| 123 entity = _elementMap.getLocalFunction(node); | |
| 124 // TODO(efortuna): Consider the less roundabout way of getting this value | |
| 125 // which is just storing the "enclosingLibrary" value of the original call | |
| 126 // to CapturedScopeBuilder. | |
| 127 ir.TreeNode temp = node; | |
| 128 while (temp != null && temp is! ir.Library) { | |
| 129 temp = temp.parent; | |
| 130 } | |
| 131 assert(temp is ir.Library); | |
| 132 library = temp; | |
| 133 } | |
| 134 assert(entity != null); | |
| 135 | |
| 136 String name = _computeClosureName(node); | |
| 137 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo( | |
| 138 name, _elementMap.getLibrary(library), info); | |
| 139 if (node is ir.FunctionNode) { | 118 if (node is ir.FunctionNode) { |
| 140 // We want the original declaration where that function is used to point | 119 // We want the original declaration where that function is used to point |
| 141 // to the correct closure class. | 120 // to the correct closure class. |
| 142 // TODO(efortuna): entity equivalent of element.declaration? | 121 // TODO(efortuna): entity equivalent of element.declaration? |
| 143 node = (node as ir.FunctionNode).parent; | 122 node = (node as ir.FunctionNode).parent; |
| 144 _closureRepresentationMap[closureClass.callMethod] = closureClass; | 123 _closureRepresentationMap[closureClass.callMethod] = closureClass; |
| 145 } | 124 } |
| 146 | 125 |
| 126 if (node is ir.Member) { |
| 127 entity = _elementMap.getMember(node); |
| 128 } else { |
| 129 entity = _elementMap.getLocalFunction(node); |
| 130 } |
| 131 assert(entity != null); |
| 132 |
| 147 _closureRepresentationMap[entity] = closureClass; | 133 _closureRepresentationMap[entity] = closureClass; |
| 148 | 134 |
| 149 // Register that a new class has been created. | 135 // Register that a new class has been created. |
| 150 closedWorldRefiner.registerClosureClass( | 136 closedWorldRefiner.registerClosureClass( |
| 151 closureClass, node is ir.Member && node.isInstanceMember); | 137 closureClass, node is ir.Member && node.isInstanceMember); |
| 152 } | 138 } |
| 153 | 139 |
| 154 // Returns a non-unique name for the given closure element. | |
| 155 String _computeClosureName(ir.TreeNode treeNode) { | |
| 156 var parts = <String>[]; | |
| 157 if (treeNode is ir.Field && treeNode.name.name != "") { | |
| 158 parts.insert(treeNode.name.name); | |
| 159 } else { | |
| 160 parts.insert('closure'); | |
| 161 } | |
| 162 ir.TreeNode node = treeNode.parent; | |
| 163 while (node != null && | |
| 164 (node is ir.Constructor || | |
| 165 node is ir.Class || | |
| 166 node is ir.FunctionNode || | |
| 167 node is ir.Procedure)) { | |
| 168 // TODO(johnniwinther): Simplify computed names. | |
| 169 if (node is ir.Constructor || | |
| 170 node.parent is ir.Constructor || | |
| 171 (node is ir.Procedure && node.kind == ir.ProcedureKind.Factory)) { | |
| 172 FunctionEntity entity; | |
| 173 if (node.parent is ir.Constructor) { | |
| 174 entity = _elementMap.getConstructorBody(node); | |
| 175 } else { | |
| 176 entity = _elementMap.getMember(node); | |
| 177 } | |
| 178 parts.insert(utils.reconstructConstructorName(entity)); | |
| 179 } else { | |
| 180 String surroundingName = ''; | |
| 181 if (node is ir.Class) { | |
| 182 surroundingName = Elements.operatorNameToIdentifier(node.name); | |
| 183 } else if (node is ir.Procedure) { | |
| 184 surroundingName = Elements.operatorNameToIdentifier(node.name.name); | |
| 185 } | |
| 186 parts.insert(surroundingName); | |
| 187 } | |
| 188 // A generative constructors's parent is the class; the class name is | |
| 189 // already part of the generative constructor's name. | |
| 190 if (node is ir.Constructor) break; | |
| 191 node = node.parent; | |
| 192 } | |
| 193 return parts.reverse.join('_'); | |
| 194 } | |
| 195 | |
| 196 @override | 140 @override |
| 197 ScopeInfo getScopeInfo(Entity entity) { | 141 ScopeInfo getScopeInfo(Entity entity) { |
| 198 // TODO(johnniwinther): Remove this check when constructor bodies a created | 142 // TODO(johnniwinther): Remove this check when constructor bodies a created |
| 199 // eagerly with the J-model; a constructor body should have it's own | 143 // eagerly with the J-model; a constructor body should have it's own |
| 200 // [ClosureRepresentationInfo]. | 144 // [ClosureRepresentationInfo]. |
| 201 if (entity is ConstructorBodyEntity) { | 145 if (entity is ConstructorBodyEntity) { |
| 202 ConstructorBodyEntity constructorBody = entity; | 146 ConstructorBodyEntity constructorBody = entity; |
| 203 entity = constructorBody.constructor; | 147 entity = constructorBody.constructor; |
| 204 } | 148 } |
| 205 | 149 |
| 206 return _scopeMap[entity] ?? getClosureRepresentationInfo(entity); | 150 return _scopeMap[entity] ?? getClosureRepresentationInfo(entity); |
| 207 } | 151 } |
| 208 | 152 |
| 209 // TODO(efortuna): Eventually capturedScopesMap[node] should always | 153 // TODO(efortuna): Eventually scopesCapturedInClosureMap[node] should always |
| 210 // be non-null, and we should just test that with an assert. | 154 // be non-null, and we should just test that with an assert. |
| 211 @override | 155 @override |
| 212 CapturedScope getCapturedScope(MemberEntity entity) => | 156 CapturedScope getCapturedScope(MemberEntity entity) => |
| 213 _capturedScopesMap[_elementMap.getMemberNode(entity)] ?? | 157 _scopesCapturedInClosureMap[_elementMap.getMemberNode(entity)] ?? |
| 214 const CapturedScope(); | 158 const CapturedScope(); |
| 215 | 159 |
| 216 @override | 160 @override |
| 217 // TODO(efortuna): Eventually capturedScopesMap[node] should always | 161 // TODO(efortuna): Eventually scopesCapturedInClosureMap[node] should always |
| 218 // be non-null, and we should just test that with an assert. | 162 // be non-null, and we should just test that with an assert. |
| 219 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => | 163 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => |
| 220 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); | 164 _scopesCapturedInClosureMap[loopNode] ?? const CapturedLoopScope(); |
| 221 | 165 |
| 222 @override | 166 @override |
| 223 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be | 167 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be |
| 224 // non-null, and we should just test that with an assert. | 168 // non-null, and we should just test that with an assert. |
| 225 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 169 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
| 226 return _closureRepresentationMap[entity] ?? | 170 return _closureRepresentationMap[entity] ?? |
| 227 const ClosureRepresentationInfo(); | 171 const ClosureRepresentationInfo(); |
| 228 } | 172 } |
| 229 } | 173 } |
| 230 | 174 |
| 231 class KernelScopeInfo extends ScopeInfo { | 175 class KernelScopeInfo extends ScopeInfo { |
| 232 final Set<Local> localsUsedInTryOrSync; | 176 final Set<Local> localsUsedInTryOrSync; |
| 233 final Local thisLocal; | 177 final Local thisLocal; |
| 234 final Set<Local> boxedVariables; | 178 final Set<Local> boxedVariables; |
| 235 | 179 |
| 236 /// The set of variables that were defined in another scope, but are used in | 180 /// The set of variables that were defined in another scope, but are used in |
| 237 /// this scope. | 181 /// this scope. |
| 238 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | 182 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
| 239 | 183 |
| 240 /// Used to map [freeVariables] to their corresponding locals. | 184 KernelScopeInfo(this.thisLocal) |
| 241 final KernelToLocalsMap localsMap; | |
| 242 | |
| 243 KernelScopeInfo(this.thisLocal, this.localsMap) | |
| 244 : localsUsedInTryOrSync = new Set<Local>(), | 185 : localsUsedInTryOrSync = new Set<Local>(), |
| 245 boxedVariables = new Set<Local>(); | 186 boxedVariables = new Set<Local>(); |
| 246 | 187 |
| 247 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) | 188 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) |
| 248 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, | 189 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, |
| 249 boxedVariables = info.boxedVariables, | 190 boxedVariables = info.boxedVariables; |
| 250 localsMap = info.localsMap; | |
| 251 | 191 |
| 252 KernelScopeInfo.withBoxedVariables( | 192 KernelScopeInfo.withBoxedVariables(this.boxedVariables, this.thisLocal) |
| 253 this.boxedVariables, | 193 : localsUsedInTryOrSync = new Set<Local>(); |
| 254 this.localsUsedInTryOrSync, | |
| 255 this.freeVariables, | |
| 256 this.localsMap, | |
| 257 this.thisLocal); | |
| 258 | 194 |
| 259 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | 195 void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
| 260 boxedVariables.forEach((Local l) { | 196 boxedVariables.forEach((Local l) { |
| 261 // TODO(efortuna): add FieldEntities as created. | 197 // TODO(efortuna): add FieldEntities as created. |
| 262 f(l, null); | 198 f(l, null); |
| 263 }); | 199 }); |
| 264 } | 200 } |
| 265 | 201 |
| 266 bool localIsUsedInTryOrSync(Local variable) => | 202 bool localIsUsedInTryOrSync(Local variable) => |
| 267 localsUsedInTryOrSync.contains(variable); | 203 localsUsedInTryOrSync.contains(variable); |
| 268 | 204 |
| 269 String toString() { | 205 String toString() { |
| 270 StringBuffer sb = new StringBuffer(); | 206 StringBuffer sb = new StringBuffer(); |
| 271 sb.write('this=$thisLocal,'); | 207 sb.write('this=$thisLocal,'); |
| 272 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); | 208 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
| 273 return sb.toString(); | 209 return sb.toString(); |
| 274 } | 210 } |
| 275 | 211 |
| 276 bool isBoxed(Local variable) => boxedVariables.contains(variable); | 212 bool isBoxed(Local variable) => boxedVariables.contains(variable); |
| 277 } | 213 } |
| 278 | 214 |
| 279 class KernelCapturedScope extends KernelScopeInfo implements CapturedScope { | 215 class KernelCapturedScope extends KernelScopeInfo implements CapturedScope { |
| 280 final Local context; | 216 final Local context; |
| 281 | 217 |
| 282 KernelCapturedScope( | 218 KernelCapturedScope(Set<Local> boxedVariables, this.context, Local thisLocal) |
| 283 Set<Local> boxedVariables, | 219 : super.withBoxedVariables(boxedVariables, thisLocal); |
| 284 this.context, | |
| 285 Set<Local> localsUsedInTryOrSync, | |
| 286 Set<ir.VariableDeclaration> freeVariables, | |
| 287 KernelToLocalsMap localsMap, | |
| 288 Local thisLocal) | |
| 289 : super.withBoxedVariables(boxedVariables, localsUsedInTryOrSync, | |
| 290 freeVariables, localsMap, thisLocal); | |
| 291 | 220 |
| 292 bool get requiresContextBox => boxedVariables.isNotEmpty; | 221 bool get requiresContextBox => boxedVariables.isNotEmpty; |
| 293 } | 222 } |
| 294 | 223 |
| 295 class KernelCapturedLoopScope extends KernelCapturedScope | 224 class KernelCapturedLoopScope extends KernelCapturedScope |
| 296 implements CapturedLoopScope { | 225 implements CapturedLoopScope { |
| 297 final List<Local> boxedLoopVariables; | 226 final List<Local> boxedLoopVariables; |
| 298 | 227 |
| 299 KernelCapturedLoopScope( | 228 KernelCapturedLoopScope(Set<Local> boxedVariables, this.boxedLoopVariables, |
| 300 Set<Local> boxedVariables, | 229 Local context, Local thisLocal) |
| 301 this.boxedLoopVariables, | 230 : super(boxedVariables, context, thisLocal); |
| 302 Local context, | |
| 303 Set<Local> localsUsedInTryOrSync, | |
| 304 Set<ir.VariableDeclaration> freeVariables, | |
| 305 KernelToLocalsMap localsMap, | |
| 306 Local thisLocal) | |
| 307 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, | |
| 308 localsMap, thisLocal); | |
| 309 | 231 |
| 310 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 232 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 311 } | 233 } |
| 312 | 234 |
| 313 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 235 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 314 class KernelClosureClass extends KernelScopeInfo | 236 class KernelClosureClass extends KernelScopeInfo |
| 315 implements ClosureRepresentationInfo, JClass { | 237 implements ClosureRepresentationInfo, JClass { |
| 316 final String name; | 238 // TODO(efortuna): Generate unique name for each closure class. |
| 317 final JLibrary library; | 239 final String name = 'ClosureClass'; |
| 318 | 240 |
| 319 /// Index into the classData, classList and classEnvironment lists where this | 241 /// Index into the classData, classList and classEnvironment lists where this |
| 320 /// entity is stored in [JsToFrontendMapImpl]. | 242 /// entity is stored in [JsToFrontendMapImpl]. |
| 321 int classIndex; | 243 int classIndex; |
| 322 | 244 |
| 323 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 245 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 324 | 246 |
| 325 KernelClosureClass.fromScopeInfo( | 247 KernelClosureClass.fromScopeInfo(KernelScopeInfo info) |
| 326 this.name, this.library, KernelScopeInfo info) | 248 : super.from(info.thisLocal, info); |
| 327 : super.from(info.thisLocal, info) { | |
| 328 // Make a corresponding field entity in this closure class for every single | |
| 329 // freeVariable in the KernelScopeInfo.freeVariable. | |
| 330 int i = 0; | |
| 331 for (ir.VariableDeclaration variable in info.freeVariables) { | |
| 332 // NOTE: This construction order may be slightly different than the | |
| 333 // old Element version. The old version did all the boxed items and then | |
| 334 // all the others. | |
| 335 Local capturedLocal = info.localsMap.getLocal(variable); | |
| 336 if (info.isBoxed(capturedLocal)) { | |
| 337 // TODO(efortuna): Coming soon. | |
| 338 } else { | |
| 339 localToFieldMap[capturedLocal] = new ClosureField( | |
| 340 _getClosureVariableName(capturedLocal.name, i), | |
| 341 this, | |
| 342 variable.isConst, | |
| 343 variable.isFinal || variable.isConst); | |
| 344 // TODO(efortuna): These probably need to get registered somewhere. | |
| 345 } | |
| 346 i++; | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 /// Generate a unique name for the [id]th closure field, with proposed name | |
| 351 /// [name]. | |
| 352 /// | |
| 353 /// The result is used as the name of [ClosureFieldElement]s, and must | |
| 354 /// therefore be unique to avoid breaking an invariant in the element model | |
| 355 /// (classes cannot declare multiple fields with the same name). | |
| 356 /// | |
| 357 /// Also, the names should be distinct from real field names to prevent | |
| 358 /// clashes with selectors for those fields. | |
| 359 /// | |
| 360 /// These names are not used in generated code, just as element name. | |
| 361 String _getClosureVariableName(String name, int id) { | |
| 362 return "_captured_${name}_$id"; | |
| 363 } | |
| 364 | 249 |
| 365 // TODO(efortuna): Implement. | 250 // TODO(efortuna): Implement. |
| 366 Local get closureEntity => null; | 251 Local get closureEntity => null; |
| 367 | 252 |
| 368 ClassEntity get closureClassEntity => this; | 253 ClassEntity get closureClassEntity => this; |
| 369 | 254 |
| 370 // TODO(efortuna): Implement. | 255 // TODO(efortuna): Implement. |
| 371 FunctionEntity get callMethod => null; | 256 FunctionEntity get callMethod => null; |
| 372 | 257 |
| 373 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 258 // TODO(efortuna): Implement. |
| 259 List<Local> get createdFieldEntities => const <Local>[]; |
| 374 | 260 |
| 375 // TODO(efortuna): Implement. | 261 // TODO(efortuna): Implement. |
| 376 FieldEntity get thisFieldEntity => null; | 262 FieldEntity get thisFieldEntity => null; |
| 377 | 263 |
| 378 void forEachCapturedVariable(f(Local from, JField to)) { | 264 // TODO(efortuna): Implement. |
| 379 localToFieldMap.forEach(f); | 265 void forEachCapturedVariable(f(Local from, FieldEntity to)) {} |
| 380 } | |
| 381 | 266 |
| 382 // TODO(efortuna): Implement. | 267 // TODO(efortuna): Implement. |
| 383 @override | 268 @override |
| 384 void forEachBoxedVariable(f(Local local, JField field)) {} | 269 void forEachBoxedVariable(f(Local local, FieldEntity field)) {} |
| 385 | 270 |
| 386 // TODO(efortuna): Implement. | 271 // TODO(efortuna): Implement. |
| 387 void forEachFreeVariable(f(Local variable, JField field)) {} | 272 void forEachFreeVariable(f(Local variable, FieldEntity field)) {} |
| 388 | 273 |
| 389 // TODO(efortuna): Implement. | 274 // TODO(efortuna): Implement. |
| 390 bool isVariableBoxed(Local variable) => false; | 275 bool isVariableBoxed(Local variable) => false; |
| 391 | 276 |
| 392 bool get isClosure => true; | 277 // TODO(efortuna): Implement. |
| 278 // Why is this closure not actually a closure? Well, to properly call |
| 279 // ourselves a closure, we need to register the new closure class with the |
| 280 // ClosedWorldRefiner, which currently only takes elements. The change to |
| 281 // that (and the subsequent adjustment here) will follow soon. |
| 282 bool get isClosure => false; |
| 393 | 283 |
| 394 bool get isAbstract => false; | 284 bool get isAbstract => false; |
| 395 | 285 |
| 286 // TODO(efortuna): Talk to Johnni. |
| 287 JLibrary get library => null; |
| 288 |
| 396 String toString() => '${jsElementPrefix}class($name)'; | 289 String toString() => '${jsElementPrefix}class($name)'; |
| 397 } | 290 } |
| 398 | |
| 399 class ClosureField extends JField { | |
| 400 ClosureField(String name, KernelClosureClass containingClass, bool isConst, | |
| 401 bool isAssignable) | |
| 402 : super(-1, containingClass.library, containingClass, | |
| 403 new Name(name, containingClass.library), | |
| 404 isAssignable: isAssignable, isConst: isConst); | |
| 405 } | |
| OLD | NEW |