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

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

Issue 2988553003: Compute closure model purely from IR nodes (Closed)
Patch Set: Updated cf. comments Created 3 years, 4 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
« no previous file with comments | « no previous file | pkg/compiler/lib/src/js_model/closure_visitors.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.dart'; 8 import '../common.dart';
9 import '../common/tasks.dart'; 9 import '../common/tasks.dart';
10 import '../elements/elements.dart'; 10 import '../elements/elements.dart';
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 54
55 /// The combined steps of generating our intermediate representation of 55 /// The combined steps of generating our intermediate representation of
56 /// closures that need to be rewritten and generating the element model. 56 /// closures that need to be rewritten and generating the element model.
57 /// Ultimately these two steps will be split apart with the second step 57 /// Ultimately these two steps will be split apart with the second step
58 /// happening later in compilation just before codegen. These steps are 58 /// happening later in compilation just before codegen. These steps are
59 /// combined here currently to provide a consistent interface to the rest of 59 /// combined here currently to provide a consistent interface to the rest of
60 /// the compiler until we are ready to separate these phases. 60 /// the compiler until we are ready to separate these phases.
61 @override 61 @override
62 void convertClosures(Iterable<MemberEntity> processedEntities, 62 void convertClosures(Iterable<MemberEntity> processedEntities,
63 ClosedWorldRefiner closedWorldRefiner) { 63 ClosedWorldRefiner closedWorldRefiner) {
64 var closuresToGenerate = <MemberEntity, Map<ir.TreeNode, ScopeInfo>>{}; 64 Map<MemberEntity, ClosureModel> closureModels =
65 _computeClosureModels(processedEntities);
66 _createClosureEntities(closureModels, closedWorldRefiner);
67 }
68
69 // TODO(johnniwinther,efortuna): Compute this during resolution. See
70 // documentation on [convertClosures].
71 Map<MemberEntity, ClosureModel> _computeClosureModels(
72 Iterable<MemberEntity> processedEntities) {
73 Map<MemberEntity, ClosureModel> closureModels =
74 <MemberEntity, ClosureModel>{};
65 75
66 processedEntities.forEach((MemberEntity kEntity) { 76 processedEntities.forEach((MemberEntity kEntity) {
67 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); 77 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity);
68 if (entity.isAbstract) return; 78 if (entity.isAbstract) return;
69 if (entity.isField && !entity.isInstanceMember) { 79 if (entity.isField && !entity.isInstanceMember) {
70 MemberDefinition definition = _elementMap.getMemberDefinition(entity); 80 MemberDefinition definition = _elementMap.getMemberDefinition(entity);
71 assert(definition.kind == MemberKind.regular, 81 assert(definition.kind == MemberKind.regular,
72 failedAt(entity, "Unexpected member definition $definition")); 82 failedAt(entity, "Unexpected member definition $definition"));
73 ir.Field field = definition.node; 83 ir.Field field = definition.node;
74 // Skip top-level/static fields without an initializer. 84 // Skip top-level/static fields without an initializer.
75 if (field.initializer == null) return; 85 if (field.initializer == null) return;
76 } 86 }
77 closuresToGenerate[entity] = 87 closureModels[entity] = _buildClosureModel(entity);
78 _buildClosureModel(entity, closedWorldRefiner);
79 }); 88 });
89 return closureModels;
90 }
80 91
81 closuresToGenerate.forEach( 92 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels,
82 (MemberEntity member, Map<ir.TreeNode, ScopeInfo> closuresToGenerate) { 93 ClosedWorldRefiner closedWorldRefiner) {
94 closureModels.forEach((MemberEntity member, ClosureModel model) {
95 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
96 if (model.scopeInfo != null) {
97 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap);
98 }
99
100 model.capturedScopesMap
101 .forEach((ir.Node node, KernelCapturedScope scope) {
102 if (scope is KernelCapturedLoopScope) {
103 _capturedScopesMap[node] =
104 new JsCapturedLoopScope.from(scope, localsMap);
105 } else {
106 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap);
107 }
108 });
109
110 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate =
111 model.closuresToGenerate;
83 for (ir.TreeNode node in closuresToGenerate.keys) { 112 for (ir.TreeNode node in closuresToGenerate.keys) {
84 _produceSyntheticElements( 113 _produceSyntheticElements(
85 member, node, closuresToGenerate[node], closedWorldRefiner); 114 member, node, closuresToGenerate[node], closedWorldRefiner);
86 } 115 }
87 }); 116 });
88 } 117 }
89 118
90 /// Inspect members and mark if those members capture any state that needs to 119 /// Inspect members and mark if those members capture any state that needs to
91 /// be marked as free variables. 120 /// be marked as free variables.
92 Map<ir.TreeNode, ScopeInfo> _buildClosureModel( 121 ClosureModel _buildClosureModel(MemberEntity entity) {
93 MemberEntity entity, ClosedWorldRefiner closedWorldRefiner) { 122 ClosureModel model = new ClosureModel();
94 assert(!_scopeMap.containsKey(entity),
95 failedAt(entity, "ScopeInfo already computed for $entity."));
96 Map<ir.TreeNode, ScopeInfo> closuresToGenerate = <ir.TreeNode, ScopeInfo>{};
97 MemberDefinition definition = _elementMap.getMemberDefinition(entity); 123 MemberDefinition definition = _elementMap.getMemberDefinition(entity);
98 switch (definition.kind) { 124 switch (definition.kind) {
99 case MemberKind.regular: 125 case MemberKind.regular:
100 case MemberKind.constructor: 126 case MemberKind.constructor:
101 break; 127 break;
102 default: 128 default:
103 failedAt(entity, "Unexpected member definition $definition"); 129 failedAt(entity, "Unexpected member definition $definition");
104 } 130 }
105 ir.Node node = definition.node; 131 ir.Node node = definition.node;
106 assert(!_scopeMap.containsKey(entity), 132 CapturedScopeBuilder translator = new CapturedScopeBuilder(model,
107 failedAt(entity, "CaptureScope already computed for $node.")); 133 hasThisLocal: entity.isInstanceMember || entity.isConstructor);
108 CapturedScopeBuilder translator = new CapturedScopeBuilder(
109 entity,
110 _capturedScopesMap,
111 _scopeMap,
112 closuresToGenerate,
113 _globalLocalsMap.getLocalsMap(entity));
114 if (entity.isField) { 134 if (entity.isField) {
115 if (node is ir.Field && node.initializer != null) { 135 if (node is ir.Field && node.initializer != null) {
116 translator.translateLazyInitializer(node); 136 translator.translateLazyInitializer(node);
117 } 137 }
118 } else { 138 } else {
119 assert(node is ir.Procedure || node is ir.Constructor); 139 assert(node is ir.Procedure || node is ir.Constructor);
120 translator.translateConstructorOrProcedure(node); 140 translator.translateConstructorOrProcedure(node);
121 } 141 }
122 return closuresToGenerate; 142 return model;
123 } 143 }
124 144
125 /// Given what variables are captured at each point, construct closure classes 145 /// Given what variables are captured at each point, construct closure classes
126 /// with fields containing the captured variables to replicate the Dart 146 /// with fields containing the captured variables to replicate the Dart
127 /// closure semantics in JS. If this closure captures any variables (meaning 147 /// closure semantics in JS. If this closure captures any variables (meaning
128 /// the closure accesses a variable that gets accessed at some point), then 148 /// the closure accesses a variable that gets accessed at some point), then
129 /// boxForCapturedVariables stores the local context for those variables. 149 /// boxForCapturedVariables stores the local context for those variables.
130 /// If no variables are captured, this parameter is null. 150 /// If no variables are captured, this parameter is null.
131 void _produceSyntheticElements( 151 void _produceSyntheticElements(
132 MemberEntity member, 152 MemberEntity member,
133 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, 153 ir.TreeNode /* ir.Member | ir.FunctionNode */ node,
134 ScopeInfo info, 154 KernelScopeInfo info,
135 ClosedWorldRefiner closedWorldRefiner) { 155 ClosedWorldRefiner closedWorldRefiner) {
136 String name = _computeClosureName(node); 156 String name = _computeClosureName(node);
137 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); 157 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
138 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo( 158 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo(
139 name, member.library, info, node.location, localsMap); 159 name, member.library, info, node.location, localsMap);
140 160
141 Entity entity; 161 Entity entity;
142 if (node is ir.Member) { 162 if (node is ir.Member) {
143 entity = member; 163 entity = member;
144 } else { 164 } else {
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 253
234 @override 254 @override
235 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be 255 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be
236 // non-null, and we should just test that with an assert. 256 // non-null, and we should just test that with an assert.
237 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { 257 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) {
238 return _closureRepresentationMap[entity] ?? 258 return _closureRepresentationMap[entity] ??
239 const ClosureRepresentationInfo(); 259 const ClosureRepresentationInfo();
240 } 260 }
241 } 261 }
242 262
243 class KernelScopeInfo extends ScopeInfo { 263 class KernelScopeInfo {
264 final Set<ir.VariableDeclaration> localsUsedInTryOrSync;
265 final bool hasThisLocal;
266 final Set<ir.VariableDeclaration> boxedVariables;
267
268 /// The set of variables that were defined in another scope, but are used in
269 /// this scope.
270 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>();
271
272 KernelScopeInfo(this.hasThisLocal)
273 : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(),
274 boxedVariables = new Set<ir.VariableDeclaration>();
275
276 KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info)
277 : localsUsedInTryOrSync = info.localsUsedInTryOrSync,
278 boxedVariables = info.boxedVariables;
279
280 KernelScopeInfo.withBoxedVariables(this.boxedVariables,
281 this.localsUsedInTryOrSync, this.freeVariables, this.hasThisLocal);
282
283 String toString() {
284 StringBuffer sb = new StringBuffer();
285 sb.write('this=$hasThisLocal,');
286 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}');
287 return sb.toString();
288 }
289 }
290
291 class JsScopeInfo extends ScopeInfo {
244 final Set<Local> localsUsedInTryOrSync; 292 final Set<Local> localsUsedInTryOrSync;
245 final Local thisLocal; 293 final Local thisLocal;
246 final Set<Local> boxedVariables; 294 final Set<Local> boxedVariables;
247 295
248 /// The set of variables that were defined in another scope, but are used in 296 /// The set of variables that were defined in another scope, but are used in
249 /// this scope. 297 /// this scope.
250 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); 298 final Set<Local> freeVariables;
251 299
252 KernelScopeInfo(this.thisLocal) 300 JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables,
253 : localsUsedInTryOrSync = new Set<Local>(), 301 this.freeVariables);
254 boxedVariables = new Set<Local>();
255 302
256 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) 303 JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap)
257 : localsUsedInTryOrSync = info.localsUsedInTryOrSync, 304 : this.thisLocal =
258 boxedVariables = info.boxedVariables; 305 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null,
259 306 this.localsUsedInTryOrSync =
260 KernelScopeInfo.withBoxedVariables(this.boxedVariables, 307 info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(),
261 this.localsUsedInTryOrSync, this.freeVariables, this.thisLocal); 308 this.boxedVariables =
309 info.boxedVariables.map(localsMap.getLocalVariable).toSet(),
310 this.freeVariables =
311 info.freeVariables.map(localsMap.getLocalVariable).toSet();
262 312
263 void forEachBoxedVariable(f(Local local, FieldEntity field)) { 313 void forEachBoxedVariable(f(Local local, FieldEntity field)) {
264 boxedVariables.forEach((Local l) { 314 boxedVariables.forEach((Local l) {
265 // TODO(efortuna): add FieldEntities as created. 315 // TODO(efortuna): add FieldEntities as created.
266 f(l, null); 316 f(l, null);
267 }); 317 });
268 } 318 }
269 319
270 bool localIsUsedInTryOrSync(Local variable) => 320 bool localIsUsedInTryOrSync(Local variable) =>
271 localsUsedInTryOrSync.contains(variable); 321 localsUsedInTryOrSync.contains(variable);
272 322
273 String toString() { 323 String toString() {
274 StringBuffer sb = new StringBuffer(); 324 StringBuffer sb = new StringBuffer();
275 sb.write('this=$thisLocal,'); 325 sb.write('this=$thisLocal,');
276 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); 326 sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}');
277 return sb.toString(); 327 return sb.toString();
278 } 328 }
279 329
280 bool isBoxed(Local variable) => boxedVariables.contains(variable); 330 bool isBoxed(Local variable) => boxedVariables.contains(variable);
281 } 331 }
282 332
283 class KernelCapturedScope extends KernelScopeInfo implements CapturedScope { 333 class KernelCapturedScope extends KernelScopeInfo {
284 final Local context; 334 final ir.TreeNode context;
285 335
286 KernelCapturedScope( 336 KernelCapturedScope(
287 Set<Local> boxedVariables, 337 Set<ir.VariableDeclaration> boxedVariables,
288 this.context, 338 this.context,
289 Set<Local> localsUsedInTryOrSync, 339 Set<ir.VariableDeclaration> localsUsedInTryOrSync,
290 Set<ir.VariableDeclaration> freeVariables, 340 Set<ir.VariableDeclaration> freeVariables,
291 Local thisLocal) 341 bool hasThisLocal)
292 : super.withBoxedVariables( 342 : super.withBoxedVariables(
293 boxedVariables, localsUsedInTryOrSync, freeVariables, thisLocal); 343 boxedVariables, localsUsedInTryOrSync, freeVariables, hasThisLocal);
294 344
295 bool get requiresContextBox => boxedVariables.isNotEmpty; 345 bool get requiresContextBox => boxedVariables.isNotEmpty;
296 } 346 }
297 347
298 class KernelCapturedLoopScope extends KernelCapturedScope 348 class JsCapturedScope extends JsScopeInfo implements CapturedScope {
299 implements CapturedLoopScope { 349 final Local context;
350
351 JsCapturedScope.from(
352 KernelCapturedScope capturedScope, KernelToLocalsMap localsMap)
353 : this.context = localsMap.getLocalVariable(capturedScope.context),
354 super.from(capturedScope, localsMap);
355
356 bool get requiresContextBox => boxedVariables.isNotEmpty;
357 }
358
359 class KernelCapturedLoopScope extends KernelCapturedScope {
360 final List<ir.VariableDeclaration> boxedLoopVariables;
361
362 KernelCapturedLoopScope(
363 Set<ir.VariableDeclaration> boxedVariables,
364 this.boxedLoopVariables,
365 ir.TreeNode context,
366 Set<ir.VariableDeclaration> localsUsedInTryOrSync,
367 Set<ir.VariableDeclaration> freeVariables,
368 bool hasThisLocal)
369 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables,
370 hasThisLocal);
371
372 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty;
373 }
374
375 class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope {
300 final List<Local> boxedLoopVariables; 376 final List<Local> boxedLoopVariables;
301 377
302 KernelCapturedLoopScope( 378 JsCapturedLoopScope.from(
303 Set<Local> boxedVariables, 379 KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap)
304 this.boxedLoopVariables, 380 : this.boxedLoopVariables = capturedScope.boxedLoopVariables
305 Local context, 381 .map(localsMap.getLocalVariable)
306 Set<Local> localsUsedInTryOrSync, 382 .toList(),
307 Set<ir.VariableDeclaration> freeVariables, 383 super.from(capturedScope, localsMap);
308 Local thisLocal)
309 : super(boxedVariables, context, localsUsedInTryOrSync, freeVariables,
310 thisLocal);
311 384
312 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; 385 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty;
313 } 386 }
314 387
315 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. 388 // TODO(johnniwinther): Add unittest for the computed [ClosureClass].
316 class KernelClosureClass extends KernelScopeInfo 389 class KernelClosureClass extends JsScopeInfo
317 implements ClosureRepresentationInfo, JClass { 390 implements ClosureRepresentationInfo, JClass {
318 final ir.Location location; 391 final ir.Location location;
319 392
320 final String name; 393 final String name;
321 final JLibrary library; 394 final JLibrary library;
322 395
323 /// Index into the classData, classList and classEnvironment lists where this 396 /// Index into the classData, classList and classEnvironment lists where this
324 /// entity is stored in [JsToFrontendMapImpl]. 397 /// entity is stored in [JsToFrontendMapImpl].
325 int classIndex; 398 int classIndex;
326 399
327 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); 400 final Map<Local, JField> localToFieldMap = new Map<Local, JField>();
328 401
329 KernelClosureClass.fromScopeInfo(this.name, this.library, 402 KernelClosureClass.fromScopeInfo(this.name, this.library,
330 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) 403 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap)
331 : super.from(info.thisLocal, info) { 404 : super.from(info, localsMap) {
332 // Make a corresponding field entity in this closure class for every single 405 // Make a corresponding field entity in this closure class for every single
333 // freeVariable in the KernelScopeInfo.freeVariable. 406 // freeVariable in the KernelScopeInfo.freeVariable.
334 int i = 0; 407 int i = 0;
335 for (ir.VariableDeclaration variable in info.freeVariables) { 408 for (ir.VariableDeclaration variable in info.freeVariables) {
336 // NOTE: This construction order may be slightly different than the 409 // NOTE: This construction order may be slightly different than the
337 // old Element version. The old version did all the boxed items and then 410 // old Element version. The old version did all the boxed items and then
338 // all the others. 411 // all the others.
339 Local capturedLocal = localsMap.getLocalVariable(variable); 412 Local capturedLocal = localsMap.getLocalVariable(variable);
340 if (info.isBoxed(capturedLocal)) { 413 if (isBoxed(capturedLocal)) {
341 // TODO(efortuna): Coming soon. 414 // TODO(efortuna): Coming soon.
342 } else { 415 } else {
343 localToFieldMap[capturedLocal] = new ClosureField( 416 localToFieldMap[capturedLocal] = new ClosureField(
344 _getClosureVariableName(capturedLocal.name, i), 417 _getClosureVariableName(capturedLocal.name, i),
345 this, 418 this,
346 variable.isConst, 419 variable.isConst,
347 variable.isFinal || variable.isConst); 420 variable.isFinal || variable.isConst);
348 // TODO(efortuna): These probably need to get registered somewhere. 421 // TODO(efortuna): These probably need to get registered somewhere.
349 } 422 }
350 i++; 423 i++;
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 ClosureClassDefinition(this.cls, this.location); 488 ClosureClassDefinition(this.cls, this.location);
416 489
417 ClassKind get kind => ClassKind.closure; 490 ClassKind get kind => ClassKind.closure;
418 491
419 ir.Node get node => 492 ir.Node get node =>
420 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); 493 throw new UnsupportedError('ClosureClassDefinition.node for $cls');
421 494
422 String toString() => 495 String toString() =>
423 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; 496 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)';
424 } 497 }
498
499 /// Collection of closure data collected for a single member.
500 class ClosureModel {
501 /// Collection [ScopeInfo] data for the member, if any.
502 // TODO(johnniwinther): [scopeInfo] seem to be missing only for fields
503 // without initializers; we shouldn't even create a [ClosureModel] in these
504 // cases.
505 KernelScopeInfo scopeInfo;
506
507 /// Collected [CapturedScope] data for nodes.
508 Map<ir.Node, KernelCapturedScope> capturedScopesMap =
509 <ir.Node, KernelCapturedScope>{};
510
511 /// Collected [ScopeInfo] data for nodes.
512 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate =
513 <ir.TreeNode, KernelScopeInfo>{};
514 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/js_model/closure_visitors.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698