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 | 19 |
| 20 class KernelClosureAnalysis { |
| 21 /// Inspect members and mark if those members capture any state that needs to |
| 22 /// be marked as free variables. |
| 23 static ClosureModel computeClosureModel(MemberEntity entity, ir.Member node) { |
| 24 if (entity.isAbstract) return null; |
| 25 if (entity.isField && !entity.isInstanceMember) { |
| 26 ir.Field field = node; |
| 27 // Skip top-level/static fields without an initializer. |
| 28 if (field.initializer == null) return null; |
| 29 } |
| 30 |
| 31 ClosureModel model = new ClosureModel(); |
| 32 CapturedScopeBuilder translator = new CapturedScopeBuilder(model, |
| 33 hasThisLocal: entity.isInstanceMember || entity.isConstructor); |
| 34 if (entity.isField) { |
| 35 if (node is ir.Field && node.initializer != null) { |
| 36 translator.translateLazyInitializer(node); |
| 37 } |
| 38 } else { |
| 39 assert(node is ir.Procedure || node is ir.Constructor); |
| 40 translator.translateConstructorOrProcedure(node); |
| 41 } |
| 42 return model; |
| 43 } |
| 44 } |
| 45 |
20 /// Closure conversion code using our new Entity model. Closure conversion is | 46 /// Closure conversion code using our new Entity model. Closure conversion is |
21 /// necessary because the semantics of closures are slightly different in Dart | 47 /// necessary because the semantics of closures are slightly different in Dart |
22 /// than JavaScript. Closure conversion is separated out into two phases: | 48 /// than JavaScript. Closure conversion is separated out into two phases: |
23 /// generation of a new (temporary) representation to store where variables need | 49 /// 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 | 50 /// 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 | 51 /// the code generation phase where we generate elements and/or instructions to |
26 /// represent this new code path. | 52 /// represent this new code path. |
27 /// | 53 /// |
28 /// For a general explanation of how closure conversion works at a high level, | 54 /// For a general explanation of how closure conversion works at a high level, |
29 /// check out: | 55 /// check out: |
30 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or | 56 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or |
31 /// http://matt.might.net/articles/closure-conversion/. | 57 /// http://matt.might.net/articles/closure-conversion/. |
32 // TODO(efortuna): Change inheritance hierarchy so that the | 58 // TODO(efortuna): Change inheritance hierarchy so that the |
33 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a | 59 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a |
34 // glorified timer. | 60 // glorified timer. |
35 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { | 61 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
36 final KernelToElementMapForBuilding _elementMap; | 62 final KernelToElementMapForBuilding _elementMap; |
37 final GlobalLocalsMap _globalLocalsMap; | 63 final GlobalLocalsMap _globalLocalsMap; |
| 64 final Map<MemberEntity, ClosureModel> _closureModels; |
38 | 65 |
39 /// Map of the scoping information that corresponds to a particular entity. | 66 /// Map of the scoping information that corresponds to a particular entity. |
40 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; | 67 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; |
41 Map<ir.Node, CapturedScope> _capturedScopesMap = <ir.Node, CapturedScope>{}; | 68 Map<ir.Node, CapturedScope> _capturedScopesMap = <ir.Node, CapturedScope>{}; |
42 | 69 |
43 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = | 70 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = |
44 <Entity, ClosureRepresentationInfo>{}; | 71 <Entity, ClosureRepresentationInfo>{}; |
45 | 72 |
46 /// Should only be used at the very beginning to ensure we are looking at the | |
47 /// right kind of elements. | |
48 // TODO(efortuna): Remove this map once we have one kernel backend strategy. | |
49 final JsToFrontendMap _kToJElementMap; | |
50 | |
51 KernelClosureConversionTask(Measurer measurer, this._elementMap, | 73 KernelClosureConversionTask(Measurer measurer, this._elementMap, |
52 this._kToJElementMap, this._globalLocalsMap) | 74 this._globalLocalsMap, this._closureModels) |
53 : super(measurer); | 75 : super(measurer); |
54 | 76 |
55 /// The combined steps of generating our intermediate representation of | 77 /// The combined steps of generating our intermediate representation of |
56 /// closures that need to be rewritten and generating the element model. | 78 /// closures that need to be rewritten and generating the element model. |
57 /// Ultimately these two steps will be split apart with the second step | 79 /// Ultimately these two steps will be split apart with the second step |
58 /// happening later in compilation just before codegen. These steps are | 80 /// happening later in compilation just before codegen. These steps are |
59 /// combined here currently to provide a consistent interface to the rest of | 81 /// combined here currently to provide a consistent interface to the rest of |
60 /// the compiler until we are ready to separate these phases. | 82 /// the compiler until we are ready to separate these phases. |
61 @override | 83 @override |
62 void convertClosures(Iterable<MemberEntity> processedEntities, | 84 void convertClosures(Iterable<MemberEntity> processedEntities, |
63 ClosedWorldRefiner closedWorldRefiner) { | 85 ClosedWorldRefiner closedWorldRefiner) { |
64 Map<MemberEntity, ClosureModel> closureModels = | 86 _createClosureEntities(_closureModels, closedWorldRefiner); |
65 _computeClosureModels(processedEntities); | |
66 _createClosureEntities(closureModels, closedWorldRefiner); | |
67 } | |
68 | |
69 // TODO(johnniwinther,efortuna): Compute this during resolution. See | |
70 // documentation on [convertClosures]. | |
71 Map<MemberEntity, ClosureModel> _computeClosureModels( | |
72 Iterable<MemberEntity> processedEntities) { | |
73 Map<MemberEntity, ClosureModel> closureModels = | |
74 <MemberEntity, ClosureModel>{}; | |
75 | |
76 processedEntities.forEach((MemberEntity kEntity) { | |
77 MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); | |
78 if (entity.isAbstract) return; | |
79 if (entity.isField && !entity.isInstanceMember) { | |
80 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | |
81 assert(definition.kind == MemberKind.regular, | |
82 failedAt(entity, "Unexpected member definition $definition")); | |
83 ir.Field field = definition.node; | |
84 // Skip top-level/static fields without an initializer. | |
85 if (field.initializer == null) return; | |
86 } | |
87 closureModels[entity] = _buildClosureModel(entity); | |
88 }); | |
89 return closureModels; | |
90 } | 87 } |
91 | 88 |
92 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, | 89 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, |
93 ClosedWorldRefiner closedWorldRefiner) { | 90 ClosedWorldRefiner closedWorldRefiner) { |
94 closureModels.forEach((MemberEntity member, ClosureModel model) { | 91 closureModels.forEach((MemberEntity member, ClosureModel model) { |
95 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 92 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
96 if (model.scopeInfo != null) { | 93 if (model.scopeInfo != null) { |
97 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | 94 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); |
98 } | 95 } |
99 | 96 |
100 model.capturedScopesMap | 97 model.capturedScopesMap |
101 .forEach((ir.Node node, KernelCapturedScope scope) { | 98 .forEach((ir.Node node, KernelCapturedScope scope) { |
102 if (scope is KernelCapturedLoopScope) { | 99 if (scope is KernelCapturedLoopScope) { |
103 _capturedScopesMap[node] = | 100 _capturedScopesMap[node] = |
104 new JsCapturedLoopScope.from(scope, localsMap); | 101 new JsCapturedLoopScope.from(scope, localsMap); |
105 } else { | 102 } else { |
106 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); | 103 _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); |
107 } | 104 } |
108 }); | 105 }); |
109 | 106 |
110 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 107 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
111 model.closuresToGenerate; | 108 model.closuresToGenerate; |
112 for (ir.TreeNode node in closuresToGenerate.keys) { | 109 for (ir.TreeNode node in closuresToGenerate.keys) { |
113 _produceSyntheticElements( | 110 _produceSyntheticElements( |
114 member, node, closuresToGenerate[node], closedWorldRefiner); | 111 member, node, closuresToGenerate[node], closedWorldRefiner); |
115 } | 112 } |
116 }); | 113 }); |
117 } | 114 } |
118 | 115 |
119 /// Inspect members and mark if those members capture any state that needs to | |
120 /// be marked as free variables. | |
121 ClosureModel _buildClosureModel(MemberEntity entity) { | |
122 ClosureModel model = new ClosureModel(); | |
123 MemberDefinition definition = _elementMap.getMemberDefinition(entity); | |
124 switch (definition.kind) { | |
125 case MemberKind.regular: | |
126 case MemberKind.constructor: | |
127 break; | |
128 default: | |
129 failedAt(entity, "Unexpected member definition $definition"); | |
130 } | |
131 ir.Node node = definition.node; | |
132 CapturedScopeBuilder translator = new CapturedScopeBuilder(model, | |
133 hasThisLocal: entity.isInstanceMember || entity.isConstructor); | |
134 if (entity.isField) { | |
135 if (node is ir.Field && node.initializer != null) { | |
136 translator.translateLazyInitializer(node); | |
137 } | |
138 } else { | |
139 assert(node is ir.Procedure || node is ir.Constructor); | |
140 translator.translateConstructorOrProcedure(node); | |
141 } | |
142 return model; | |
143 } | |
144 | |
145 /// Given what variables are captured at each point, construct closure classes | 116 /// Given what variables are captured at each point, construct closure classes |
146 /// with fields containing the captured variables to replicate the Dart | 117 /// with fields containing the captured variables to replicate the Dart |
147 /// closure semantics in JS. If this closure captures any variables (meaning | 118 /// closure semantics in JS. If this closure captures any variables (meaning |
148 /// the closure accesses a variable that gets accessed at some point), then | 119 /// the closure accesses a variable that gets accessed at some point), then |
149 /// boxForCapturedVariables stores the local context for those variables. | 120 /// boxForCapturedVariables stores the local context for those variables. |
150 /// If no variables are captured, this parameter is null. | 121 /// If no variables are captured, this parameter is null. |
151 void _produceSyntheticElements( | 122 void _produceSyntheticElements( |
152 MemberEntity member, | 123 MemberEntity member, |
153 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, | 124 ir.TreeNode /* ir.Member | ir.FunctionNode */ node, |
154 KernelScopeInfo info, | 125 KernelScopeInfo info, |
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
505 KernelScopeInfo scopeInfo; | 476 KernelScopeInfo scopeInfo; |
506 | 477 |
507 /// Collected [CapturedScope] data for nodes. | 478 /// Collected [CapturedScope] data for nodes. |
508 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 479 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
509 <ir.Node, KernelCapturedScope>{}; | 480 <ir.Node, KernelCapturedScope>{}; |
510 | 481 |
511 /// Collected [ScopeInfo] data for nodes. | 482 /// Collected [ScopeInfo] data for nodes. |
512 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = | 483 Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
513 <ir.TreeNode, KernelScopeInfo>{}; | 484 <ir.TreeNode, KernelScopeInfo>{}; |
514 } | 485 } |
OLD | NEW |