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.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'; |
11 import '../elements/entities.dart'; | 11 import '../elements/entities.dart'; |
12 import '../elements/entity_utils.dart' as utils; | 12 import '../elements/entity_utils.dart' as utils; |
13 import '../elements/names.dart' show Name; | 13 import '../elements/names.dart' show Name; |
14 import '../kernel/element_map.dart'; | 14 import '../kernel/element_map.dart'; |
15 import '../world.dart'; | 15 import '../world.dart'; |
16 import 'elements.dart'; | 16 import 'elements.dart'; |
17 import 'closure_visitors.dart'; | 17 import 'closure_visitors.dart'; |
18 import 'locals.dart'; | 18 import 'locals.dart'; |
| 19 import 'js_strategy.dart' show JsClosedWorld; |
19 | 20 |
20 /// Closure conversion code using our new Entity model. Closure conversion is | 21 /// Closure conversion code using our new Entity model. Closure conversion is |
21 /// necessary because the semantics of closures are slightly different in Dart | 22 /// necessary because the semantics of closures are slightly different in Dart |
22 /// than JavaScript. Closure conversion is separated out into two phases: | 23 /// than JavaScript. Closure conversion is separated out into two phases: |
23 /// generation of a new (temporary) representation to store where variables need | 24 /// generation of a new (temporary) representation to store where variables need |
24 /// to be hoisted/captured up at another level to re-write the closure, and then | 25 /// to be hoisted/captured up at another level to re-write the closure, and then |
25 /// the code generation phase where we generate elements and/or instructions to | 26 /// the code generation phase where we generate elements and/or instructions to |
26 /// represent this new code path. | 27 /// represent this new code path. |
27 /// | 28 /// |
28 /// For a general explanation of how closure conversion works at a high level, | 29 /// For a general explanation of how closure conversion works at a high level, |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 ir.Field field = definition.node; | 83 ir.Field field = definition.node; |
83 // Skip top-level/static fields without an initializer. | 84 // Skip top-level/static fields without an initializer. |
84 if (field.initializer == null) return; | 85 if (field.initializer == null) return; |
85 } | 86 } |
86 closureModels[entity] = _buildClosureModel(entity); | 87 closureModels[entity] = _buildClosureModel(entity); |
87 }); | 88 }); |
88 return closureModels; | 89 return closureModels; |
89 } | 90 } |
90 | 91 |
91 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, | 92 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, |
92 ClosedWorldRefiner closedWorldRefiner) { | 93 JsClosedWorld closedWorldRefiner) { |
93 closureModels.forEach((MemberEntity member, ClosureModel model) { | 94 closureModels.forEach((MemberEntity member, ClosureModel model) { |
94 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 95 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
95 if (model.scopeInfo != null) { | 96 if (model.scopeInfo != null) { |
96 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | 97 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); |
97 } | 98 } |
98 | 99 |
99 model.capturedScopesMap | 100 model.capturedScopesMap |
100 .forEach((ir.Node node, KernelCapturedScope scope) { | 101 .forEach((ir.Node node, KernelCapturedScope scope) { |
101 if (scope is KernelCapturedLoopScope) { | 102 if (scope is KernelCapturedLoopScope) { |
102 _capturedScopesMap[node] = | 103 _capturedScopesMap[node] = |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
144 /// Given what variables are captured at each point, construct closure classes | 145 /// Given what variables are captured at each point, construct closure classes |
145 /// with fields containing the captured variables to replicate the Dart | 146 /// with fields containing the captured variables to replicate the Dart |
146 /// closure semantics in JS. If this closure captures any variables (meaning | 147 /// closure semantics in JS. If this closure captures any variables (meaning |
147 /// 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 |
148 /// boxForCapturedVariables stores the local context for those variables. | 149 /// boxForCapturedVariables stores the local context for those variables. |
149 /// If no variables are captured, this parameter is null. | 150 /// If no variables are captured, this parameter is null. |
150 void _produceSyntheticElements( | 151 void _produceSyntheticElements( |
151 MemberEntity member, | 152 MemberEntity member, |
152 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, | 153 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, |
153 KernelScopeInfo info, | 154 KernelScopeInfo info, |
154 ClosedWorldRefiner closedWorldRefiner) { | 155 JsClosedWorld closedWorldRefiner) { |
155 String name = _computeClosureName(node); | 156 String name = _computeClosureName(node); |
156 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 157 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
157 KernelClosureClass closureClass = new KernelClosureClass.fromScopeInfo( | 158 KernelClosureClass closureClass = closedWorldRefiner.buildClosureClass( |
158 name, member.library, info, node.location, localsMap); | 159 name, member.library, info, node.location, localsMap); |
159 | 160 |
160 Entity entity; | 161 Entity entity; |
161 if (node is ir.Member) { | 162 if (node is ir.Member) { |
162 entity = member; | 163 entity = member; |
163 } else { | 164 } else { |
164 assert(node is ir.FunctionNode); | 165 assert(node is ir.FunctionNode); |
165 entity = localsMap.getLocalFunction(node.parent); | 166 entity = localsMap.getLocalFunction(node.parent); |
166 // We want the original declaration where that function is used to point | 167 // We want the original declaration where that function is used to point |
167 // to the correct closure class. | 168 // to the correct closure class. |
168 _closureRepresentationMap[closureClass.callMethod] = closureClass; | 169 _closureRepresentationMap[closureClass.callMethod] = closureClass; |
169 } | 170 } |
170 assert(entity != null); | 171 assert(entity != null); |
171 _closureRepresentationMap[entity] = closureClass; | 172 _closureRepresentationMap[entity] = closureClass; |
172 | |
173 // Register that a new class has been created. | |
174 closedWorldRefiner.registerClosureClass(closureClass); | |
175 } | 173 } |
176 | 174 |
177 // Returns a non-unique name for the given closure element. | 175 // Returns a non-unique name for the given closure element. |
178 String _computeClosureName(ir.TreeNode treeNode) { | 176 String _computeClosureName(ir.TreeNode treeNode) { |
179 var parts = <String>[]; | 177 var parts = <String>[]; |
180 if (treeNode is ir.Field && treeNode.name.name != "") { | 178 if (treeNode is ir.Field && treeNode.name.name != "") { |
181 parts.add(treeNode.name.name); | 179 parts.add(treeNode.name.name); |
182 } else { | 180 } else { |
183 parts.add('closure'); | 181 parts.add('closure'); |
184 } | 182 } |
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 385 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
388 class KernelClosureClass extends JsScopeInfo | 386 class KernelClosureClass extends JsScopeInfo |
389 implements ClosureRepresentationInfo, JClass { | 387 implements ClosureRepresentationInfo, JClass { |
390 final ir.Location location; | 388 final ir.Location location; |
391 | 389 |
392 final String name; | 390 final String name; |
393 final JLibrary library; | 391 final JLibrary library; |
394 | 392 |
395 /// Index into the classData, classList and classEnvironment lists where this | 393 /// Index into the classData, classList and classEnvironment lists where this |
396 /// entity is stored in [JsToFrontendMapImpl]. | 394 /// entity is stored in [JsToFrontendMapImpl]. |
397 int classIndex; | 395 final int classIndex; |
398 | 396 |
399 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 397 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
400 | 398 |
401 KernelClosureClass.fromScopeInfo(this.name, this.library, | 399 KernelClosureClass.fromScopeInfo(this.name, this.classIndex, this.library, |
402 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) | 400 KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) |
403 : super.from(info, localsMap) { | 401 : super.from(info, localsMap); |
404 // Make a corresponding field entity in this closure class for every single | |
405 // freeVariable in the KernelScopeInfo.freeVariable. | |
406 int i = 0; | |
407 for (ir.VariableDeclaration variable in info.freeVariables) { | |
408 // NOTE: This construction order may be slightly different than the | |
409 // old Element version. The old version did all the boxed items and then | |
410 // all the others. | |
411 Local capturedLocal = localsMap.getLocalVariable(variable); | |
412 if (isBoxed(capturedLocal)) { | |
413 // TODO(efortuna): Coming soon. | |
414 } else { | |
415 localToFieldMap[capturedLocal] = new ClosureField( | |
416 _getClosureVariableName(capturedLocal.name, i), | |
417 this, | |
418 variable.isConst, | |
419 variable.isFinal || variable.isConst); | |
420 // TODO(efortuna): These probably need to get registered somewhere. | |
421 } | |
422 i++; | |
423 } | |
424 } | |
425 | |
426 /// Generate a unique name for the [id]th closure field, with proposed name | |
427 /// [name]. | |
428 /// | |
429 /// The result is used as the name of [ClosureFieldElement]s, and must | |
430 /// therefore be unique to avoid breaking an invariant in the element model | |
431 /// (classes cannot declare multiple fields with the same name). | |
432 /// | |
433 /// Also, the names should be distinct from real field names to prevent | |
434 /// clashes with selectors for those fields. | |
435 /// | |
436 /// These names are not used in generated code, just as element name. | |
437 String _getClosureVariableName(String name, int id) { | |
438 return "_captured_${name}_$id"; | |
439 } | |
440 | 402 |
441 // TODO(efortuna): Implement. | 403 // TODO(efortuna): Implement. |
442 Local get closureEntity => null; | 404 Local get closureEntity => null; |
443 | 405 |
444 ClassEntity get closureClassEntity => this; | 406 ClassEntity get closureClassEntity => this; |
445 | 407 |
446 // TODO(efortuna): Implement. | 408 // TODO(efortuna): Implement. |
447 FunctionEntity get callMethod => null; | 409 FunctionEntity get callMethod => null; |
448 | 410 |
449 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 411 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
(...skipping 16 matching lines...) Expand all Loading... |
466 bool isVariableBoxed(Local variable) => false; | 428 bool isVariableBoxed(Local variable) => false; |
467 | 429 |
468 bool get isClosure => true; | 430 bool get isClosure => true; |
469 | 431 |
470 bool get isAbstract => false; | 432 bool get isAbstract => false; |
471 | 433 |
472 String toString() => '${jsElementPrefix}class($name)'; | 434 String toString() => '${jsElementPrefix}class($name)'; |
473 } | 435 } |
474 | 436 |
475 class ClosureField extends JField { | 437 class ClosureField extends JField { |
476 ClosureField(String name, KernelClosureClass containingClass, bool isConst, | 438 ClosureField(String name, int memberIndex, KernelClosureClass containingClass, |
477 bool isAssignable) | 439 bool isConst, bool isAssignable) |
478 : super(-1, containingClass.library, containingClass, | 440 : super(memberIndex, containingClass.library, containingClass, |
479 new Name(name, containingClass.library), | 441 new Name(name, containingClass.library), |
480 isAssignable: isAssignable, isConst: isConst); | 442 isAssignable: isAssignable, isConst: isConst); |
481 } | 443 } |
482 | 444 |
483 class ClosureClassDefinition implements ClassDefinition { | 445 class ClosureClassDefinition implements ClassDefinition { |
484 final ClassEntity cls; | 446 final ClassEntity cls; |
485 final ir.Location location; | 447 final ir.Location location; |
486 | 448 |
487 ClosureClassDefinition(this.cls, this.location); | 449 ClosureClassDefinition(this.cls, this.location); |
488 | 450 |
489 ClassKind get kind => ClassKind.closure; | 451 ClassKind get kind => ClassKind.closure; |
490 | 452 |
491 ir.Node get node => | 453 ir.Node get node => |
492 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); | 454 throw new UnsupportedError('ClosureClassDefinition.node for $cls'); |
493 | 455 |
494 String toString() => | 456 String toString() => |
495 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; | 457 'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; |
496 } | 458 } |
497 | 459 |
| 460 class ClosureMemberDefinition implements MemberDefinition { |
| 461 final MemberEntity member; |
| 462 final ir.Location location; |
| 463 final MemberKind kind; |
| 464 final ir.Node node; |
| 465 |
| 466 ClosureMemberDefinition(this.member, this.location, this.kind, this.node); |
| 467 |
| 468 String toString() => |
| 469 'ClosureMemberDefinition(kind:$kind,member:$member,location:$location)'; |
| 470 } |
| 471 |
498 /// Collection of closure data collected for a single member. | 472 /// Collection of closure data collected for a single member. |
499 class ClosureModel { | 473 class ClosureModel { |
500 /// Collection [ScopeInfo] data for the member, if any. | 474 /// Collection [ScopeInfo] data for the member, if any. |
501 KernelScopeInfo scopeInfo; | 475 KernelScopeInfo scopeInfo; |
502 | 476 |
503 /// Collected [CapturedScope] data for nodes. | 477 /// Collected [CapturedScope] data for nodes. |
504 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 478 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
505 <ir.Node, KernelCapturedScope>{}; | 479 <ir.Node, KernelCapturedScope>{}; |
506 | 480 |
507 /// Collected [ScopeInof] data for nodes. | 481 /// Collected [ScopeInof] data for nodes. |
508 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 482 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
509 <ir.TreeNode, KernelScopeInfo>{}; | 483 <ir.TreeNode, KernelScopeInfo>{}; |
510 } | 484 } |
OLD | NEW |