OLD | NEW |
---|---|
1 // Copyright (c) 2016, 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/entities.dart'; | 9 import '../elements/entities.dart'; |
10 import '../kernel/element_map.dart'; | 10 import '../kernel/element_map.dart'; |
11 import '../world.dart'; | 11 import '../world.dart'; |
12 import '../js_model/elements.dart'; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
I might be misreading it, but isn't this also unde
Emily Fortuna
2017/06/30 23:48:09
ah, yup. this was written before Johnni moved more
| |
13 import 'closure_visitors.dart' as visitor; | |
12 import 'locals.dart'; | 14 import 'locals.dart'; |
13 | 15 |
14 class KernelClosureDataBuilder extends ir.Visitor { | |
15 final KernelToLocalsMap _localsMap; | |
16 final KernelClosureRepresentationInfo info; | |
17 | |
18 bool _inTry = false; | |
19 | |
20 KernelClosureDataBuilder(this._localsMap, ThisLocal thisLocal) | |
21 : info = new KernelClosureRepresentationInfo(thisLocal); | |
22 | |
23 @override | |
24 defaultNode(ir.Node node) { | |
25 node.visitChildren(this); | |
26 } | |
27 | |
28 @override | |
29 visitTryCatch(ir.TryCatch node) { | |
30 bool oldInTry = _inTry; | |
31 _inTry = true; | |
32 node.visitChildren(this); | |
33 _inTry = oldInTry; | |
34 } | |
35 | |
36 @override | |
37 visitTryFinally(ir.TryFinally node) { | |
38 bool oldInTry = _inTry; | |
39 _inTry = true; | |
40 node.visitChildren(this); | |
41 _inTry = oldInTry; | |
42 } | |
43 | |
44 @override | |
45 visitVariableGet(ir.VariableGet node) { | |
46 if (_inTry) { | |
47 info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); | |
48 } | |
49 } | |
50 | |
51 @override | |
52 visitVariableSet(ir.VariableSet node) { | |
53 if (_inTry) { | |
54 info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); | |
55 } | |
56 node.visitChildren(this); | |
57 } | |
58 } | |
59 | |
60 /// Closure conversion code using our new Entity model. Closure conversion is | 16 /// Closure conversion code using our new Entity model. Closure conversion is |
61 /// necessary because the semantics of closures are slightly different in Dart | 17 /// necessary because the semantics of closures are slightly different in Dart |
62 /// than JavaScript. Closure conversion is separated out into two phases: | 18 /// than JavaScript. Closure conversion is separated out into two phases: |
63 /// generation of a new (temporary) representation to store where variables need | 19 /// generation of a new (temporary) representation to store where variables need |
64 /// 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 |
65 /// 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 |
66 /// represent this new code path. | 22 /// represent this new code path. |
67 /// | 23 /// |
68 /// 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, |
69 /// check out: | 25 /// check out: |
70 /// 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 |
71 /// http://matt.might.net/articles/closure-conversion/. | 27 /// http://matt.might.net/articles/closure-conversion/. |
28 // TODO(efortuna): Change inheritance hierarchy so that the | |
29 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a | |
30 // glorified timer. | |
72 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { | 31 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
73 final KernelToElementMapForBuilding _elementMap; | 32 final KernelToElementMapForBuilding _elementMap; |
74 final GlobalLocalsMap _globalLocalsMap; | 33 final GlobalLocalsMap _globalLocalsMap; |
75 Map<Entity, ClosureRepresentationInfo> _infoMap = | 34 Map<ir.Node, ClosureScope> _closureScopeMap = <ir.Node, ClosureScope>{}; |
35 | |
36 Map<Entity, ClosureRepresentationInfo> _closureRepresentationCache = | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: Cache => Map (to be consistent)?
Emily Fortuna
2017/06/30 23:48:08
Done.
| |
76 <Entity, ClosureRepresentationInfo>{}; | 37 <Entity, ClosureRepresentationInfo>{}; |
77 | 38 |
78 KernelClosureConversionTask( | 39 /// Should only be used at the very beginning to ensure we are looking at the |
79 Measurer measurer, this._elementMap, this._globalLocalsMap) | 40 /// right kind of elements. |
41 final JsToFrontendMap _kToJElementMap; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
Let's add a TODO to eliminate this mapping.
Johnn
Emily Fortuna
2017/06/30 23:48:09
Done.
Johnni, please see Siggi's comment, too.
| |
42 | |
43 KernelClosureConversionTask(Measurer measurer, this._elementMap, | |
44 this._kToJElementMap, this._globalLocalsMap) | |
80 : super(measurer); | 45 : super(measurer); |
81 | 46 |
82 /// The combined steps of generating our intermediate representation of | 47 /// The combined steps of generating our intermediate representation of |
83 /// closures that need to be rewritten and generating the element model. | 48 /// closures that need to be rewritten and generating the element model. |
84 /// Ultimately these two steps will be split apart with the second step | 49 /// Ultimately these two steps will be split apart with the second step |
85 /// happening later in compilation just before codegen. These steps are | 50 /// happening later in compilation just before codegen. These steps are |
86 /// combined here currently to provide a consistent interface to the rest of | 51 /// combined here currently to provide a consistent interface to the rest of |
87 /// the compiler until we are ready to separate these phases. | 52 /// the compiler until we are ready to separate these phases. |
88 @override | 53 @override |
89 void convertClosures(Iterable<MemberEntity> processedEntities, | 54 void convertClosures(Iterable<MemberEntity> processedEntities, |
90 ClosedWorldRefiner closedWorldRefiner) { | 55 ClosedWorldRefiner closedWorldRefiner) { |
91 // TODO(efortuna): implement. | 56 // The key in flaggedClosures is either a Local or a MemberEntity. |
57 Map<ir.Node, ScopeInfo> flaggedClosures = <ir.Node, ScopeInfo>{}; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: I'd use the type only once (either on the lef
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
Seems we have 3 names for this :) - flaggedClosure
Emily Fortuna
2017/06/30 23:48:08
Done and done!
| |
58 processedEntities.forEach((MemberEntity kEntity) { | |
59 MemberEntity entity = kEntity; | |
60 if (_kToJElementMap != null) { | |
61 entity = _kToJElementMap.toBackendMember(kEntity); | |
62 } | |
63 if (entity.isAbstract) return; | |
64 if (entity.isField && !entity.isInstanceMember) { | |
65 ir.Field field = _elementMap.getMemberNode(entity); | |
66 // Skip top-level/static fields without an initializer. | |
67 if (field.initializer == null) return; | |
68 } | |
69 _buildClosureModel(entity, flaggedClosures, closedWorldRefiner); | |
70 }); | |
71 | |
72 for (ir.Node node in flaggedClosures.keys) { | |
73 _produceSyntheticElements( | |
74 node, flaggedClosures[node], closedWorldRefiner); | |
75 } | |
92 } | 76 } |
93 | 77 |
94 /// TODO(johnniwinther,efortuna): Implement this. | 78 /// Inspect members and mark if those members capture any state that needs to |
79 /// be marked as free variables. | |
80 void _buildClosureModel( | |
81 MemberEntity entity, | |
82 Map<ir.Node, ScopeInfo> closuresToRewrite, | |
83 ClosedWorldRefiner closedWorldRefiner) { | |
84 ir.Node node = _elementMap.getMemberNode(entity); | |
85 if (_closureScopeMap.keys.contains(node)) return; | |
86 visitor.ClosureScopeBuilder translator = new visitor.ClosureScopeBuilder( | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: I'd probably drop the prefix, maybe use a `sh
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
87 _closureScopeMap, | |
88 closuresToRewrite, | |
89 _globalLocalsMap.getLocalsMap(entity), | |
90 _elementMap); | |
91 if (entity.isField) { | |
92 if (node is ir.Field && node.initializer != null) { | |
93 translator.translateLazyInitializer(node); | |
94 } | |
95 } else { | |
96 assert(node is ir.Procedure || node is ir.Constructor); | |
97 translator.translateConstructorOrProcedure(node); | |
98 } | |
99 } | |
100 | |
101 /// Given what variables are captured at each point, construct closure classes | |
102 /// with fields containing the captured variables to replicate the Dart | |
103 /// closure semantics in JS. | |
104 void _produceSyntheticElements( | |
105 ir.Node node, ScopeInfo info, ClosedWorldRefiner closedWorldRefiner) { | |
106 Entity entity; | |
107 KernelClosureClass closureClass = | |
108 new KernelClosureClass.fromScopeInfo(info); | |
109 if (node is ir.FunctionNode) { | |
110 // We want the original declaration where that function is used to point | |
111 // to the correct closure class. | |
112 // TODO(efortuna): entity equivalent of element.declaration? | |
113 node = (node as ir.FunctionNode).parent; | |
114 _closureRepresentationCache[closureClass.callMethod] = closureClass; | |
115 } | |
116 | |
117 if (node is ir.Member) { | |
118 entity = _elementMap.getMember(node); | |
119 } else { | |
120 entity = _elementMap.getLocalFunction(node); | |
121 } | |
122 assert(entity != null); | |
123 | |
124 _closureRepresentationCache[entity] = closureClass; | |
125 } | |
126 | |
127 @override | |
128 ScopeInfo getScopeInfo(Entity entity) { | |
129 return getClosureRepresentationInfo(entity); | |
130 } | |
131 | |
95 @override | 132 @override |
96 ClosureScope getClosureScope(ir.Node node) { | 133 ClosureScope getClosureScope(ir.Node node) { |
134 ClosureScope closureScope = _closureScopeMap[node]; | |
135 if (closureScope != null) { | |
136 // TODO(efortuna): Eventually closureScope should always be non-null, and | |
137 // we should just test that with an assert. | |
138 return closureScope; | |
139 } | |
97 return const ClosureScope(); | 140 return const ClosureScope(); |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: you can also write the entire body as:
Closu
Emily Fortuna
2017/06/30 23:48:09
ah, thanks! This was the product of code evolution
| |
98 } | 141 } |
99 | 142 |
100 @override | 143 @override |
101 ScopeInfo getScopeInfo(Entity entity) { | |
102 // TODO(efortuna): Specialize this function from the one below. | |
103 return getClosureRepresentationInfo(entity); | |
104 } | |
105 | |
106 /// TODO(johnniwinther,efortuna): Implement this. | |
107 @override | |
108 LoopClosureScope getLoopClosureScope(ir.Node loopNode) { | 144 LoopClosureScope getLoopClosureScope(ir.Node loopNode) { |
109 return const LoopClosureScope(); | 145 ClosureScope scope = getClosureScope(loopNode); |
146 if (scope is! KernelLoopClosureScope) { | |
147 // TODO(efortuna): Eventually scope should always be non-null, and | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
is it not null and not KernelLoopClosureScope beca
Emily Fortuna
2017/06/30 23:48:08
To your first question, yes. It was written this w
| |
148 // we should just test that with an assert. | |
149 return const LoopClosureScope(); | |
150 } | |
151 return scope; | |
110 } | 152 } |
111 | 153 |
112 @override | 154 @override |
113 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 155 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
114 return _infoMap.putIfAbsent(entity, () { | 156 var closureImpl = _closureRepresentationCache[entity]; |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
same compact version here
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
115 if (entity is MemberEntity) { | 157 if (closureImpl != null) { |
116 ir.Member node = _elementMap.getMemberNode(entity); | 158 // TODO(efortuna): Eventually closureImpl should always be non-null, and |
117 ThisLocal thisLocal; | 159 // we should just test that with an assert. |
118 if (entity.isInstanceMember) { | 160 return closureImpl; |
119 thisLocal = new ThisLocal(entity); | 161 } |
120 } | |
121 KernelClosureDataBuilder builder = new KernelClosureDataBuilder( | |
122 _globalLocalsMap.getLocalsMap(entity), thisLocal); | |
123 node.accept(builder); | |
124 return builder.info; | |
125 } | |
126 | 162 |
127 /// TODO(johnniwinther,efortuna): Implement this. | 163 return const ClosureRepresentationInfo(); |
128 return const ClosureRepresentationInfo(); | |
129 }); | |
130 } | 164 } |
131 } | 165 } |
132 | 166 |
133 // TODO(johnniwinther): Add unittest for the computed | 167 class KernelScopeInfo extends ScopeInfo { |
134 // [ClosureRepresentationInfo]. | 168 Set<Local> _localsUsedInTryOrSync = new Set<Local>(); |
135 class KernelClosureRepresentationInfo extends ClosureRepresentationInfo { | 169 final Local thisLocal; |
136 final ThisLocal thisLocal; | 170 Set<Local> boxedVariables = new Set<Local>(); |
137 final Set<Local> _localsUsedInTryOrSync = new Set<Local>(); | |
138 | 171 |
139 KernelClosureRepresentationInfo(this.thisLocal); | 172 /// The set of variables that were defined in another scope, but are used in |
173 /// this scope. | |
174 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | |
175 | |
176 KernelScopeInfo(this.thisLocal); | |
177 | |
178 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) | |
179 : this._localsUsedInTryOrSync = info.variablesUsedInTryOrSync, | |
180 this.boxedVariables = info.boxedVariables; | |
181 | |
182 KernelScopeInfo.withBoxedVariables(this.boxedVariables, this.thisLocal); | |
183 | |
184 Set<Local> get variablesUsedInTryOrSync => _localsUsedInTryOrSync; | |
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
since you are not updating the private field, I th
Emily Fortuna
2017/06/30 23:48:09
Yeah... this was a holdover from Johnni's design.
| |
185 | |
186 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | |
187 boxedVariables.forEach((Local l) { | |
188 // TODO(efortuna): add FieldEntities as created. | |
189 f(l, null); | |
190 }); | |
191 } | |
140 | 192 |
141 void registerUsedInTryOrSync(Local local) { | 193 void registerUsedInTryOrSync(Local local) { |
142 _localsUsedInTryOrSync.add(local); | 194 _localsUsedInTryOrSync.add(local); |
143 } | 195 } |
144 | 196 |
145 bool variableIsUsedInTryOrSync(Local variable) => | 197 bool variableIsUsedInTryOrSync(Local variable) => |
146 _localsUsedInTryOrSync.contains(variable); | 198 _localsUsedInTryOrSync.contains(variable); |
147 | 199 |
148 String toString() { | 200 String toString() { |
149 StringBuffer sb = new StringBuffer(); | 201 StringBuffer sb = new StringBuffer(); |
150 sb.write('this=$thisLocal,'); | 202 sb.write('this=$thisLocal,'); |
151 sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); | 203 sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); |
152 return sb.toString(); | 204 return sb.toString(); |
153 } | 205 } |
206 | |
207 bool isBoxed(Local variable) => boxedVariables.contains(variable); | |
154 } | 208 } |
209 | |
210 class KernelClosureScope extends KernelScopeInfo implements ClosureScope { | |
211 final Local context; | |
212 | |
213 KernelClosureScope(Set<Local> boxedVariables, this.context, Local thisLocal) | |
214 : super.withBoxedVariables(boxedVariables, thisLocal); | |
215 | |
216 bool get requiresContextBox => boxedVariables.isNotEmpty; | |
217 } | |
218 | |
219 class KernelLoopClosureScope extends KernelClosureScope | |
220 implements LoopClosureScope { | |
221 final List<Local> boxedLoopVariables; | |
222 | |
223 KernelLoopClosureScope(Set<Local> boxedVariables, this.boxedLoopVariables, | |
224 Local context, Local thisLocal) | |
225 : super(boxedVariables, context, thisLocal); | |
226 | |
227 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | |
228 } | |
229 | |
230 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | |
231 class KernelClosureClass extends KernelScopeInfo | |
232 implements ClosureRepresentationInfo { | |
233 KernelClosureClass.fromScopeInfo(ScopeInfo info) | |
234 : super.from(info.thisLocal, info); | |
235 | |
236 // TODO(efortuna): Implement. | |
237 Local get closureEntity => null; | |
238 | |
239 // TODO(efortuna): Implement. | |
240 ClassEntity get closureClassEntity => null; | |
241 | |
242 // TODO(efortuna): Implement. | |
243 FunctionEntity get callMethod => null; | |
244 | |
245 // TODO(efortuna): Implement. | |
246 List<Local> get createdFieldEntities => const <Local>[]; | |
247 | |
248 // TODO(efortuna): Implement. | |
249 FieldEntity get thisFieldEntity => null; | |
250 | |
251 // TODO(efortuna): Implement. | |
252 void forEachCapturedVariable(f(Local from, FieldEntity to)) {} | |
253 | |
254 // TODO(efortuna): Implement. | |
255 @override | |
256 void forEachBoxedVariable(f(Local local, FieldEntity field)) {} | |
257 | |
258 // TODO(efortuna): Implement. | |
259 void forEachFreeVariable(f(Local variable, FieldEntity field)) {} | |
260 | |
261 // TODO(efortuna): Implement. | |
262 bool isVariableBoxed(Local variable) => false; | |
263 | |
264 // TODO(efortuna): Implement. | |
265 bool get isClosure => false; | |
Emily Fortuna
2017/06/30 17:58:40
what's this? you may ask. Well, to properly call o
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
I'm ok copying this comment into the code if you l
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
266 } | |
OLD | NEW |