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