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