Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(195)

Side by Side Diff: pkg/compiler/lib/src/js_model/closure.dart

Issue 2961253005: Added for-loop variable tracking and regular closures/initializers captured variable tracking. (Closed)
Patch Set: . Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698