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