| 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 |