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

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

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