Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2016, 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/tasks.dart'; | 8 import '../common/tasks.dart'; |
| 9 import '../elements/entities.dart'; | 9 import '../elements/entities.dart'; |
| 10 import '../kernel/element_map.dart'; | 10 import '../kernel/element_map.dart'; |
| 11 import '../world.dart'; | 11 import '../world.dart'; |
| 12 import '../js_model/elements.dart'; | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
I might be misreading it, but isn't this also unde
Emily Fortuna
2017/06/30 23:48:09
ah, yup. this was written before Johnni moved more
| |
| 13 import 'closure_visitors.dart' as visitor; | |
| 12 import 'locals.dart'; | 14 import 'locals.dart'; |
| 13 | 15 |
| 14 class KernelClosureDataBuilder extends ir.Visitor { | |
| 15 final KernelToLocalsMap _localsMap; | |
| 16 final KernelClosureRepresentationInfo info; | |
| 17 | |
| 18 bool _inTry = false; | |
| 19 | |
| 20 KernelClosureDataBuilder(this._localsMap, ThisLocal thisLocal) | |
| 21 : info = new KernelClosureRepresentationInfo(thisLocal); | |
| 22 | |
| 23 @override | |
| 24 defaultNode(ir.Node node) { | |
| 25 node.visitChildren(this); | |
| 26 } | |
| 27 | |
| 28 @override | |
| 29 visitTryCatch(ir.TryCatch node) { | |
| 30 bool oldInTry = _inTry; | |
| 31 _inTry = true; | |
| 32 node.visitChildren(this); | |
| 33 _inTry = oldInTry; | |
| 34 } | |
| 35 | |
| 36 @override | |
| 37 visitTryFinally(ir.TryFinally node) { | |
| 38 bool oldInTry = _inTry; | |
| 39 _inTry = true; | |
| 40 node.visitChildren(this); | |
| 41 _inTry = oldInTry; | |
| 42 } | |
| 43 | |
| 44 @override | |
| 45 visitVariableGet(ir.VariableGet node) { | |
| 46 if (_inTry) { | |
| 47 info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 @override | |
| 52 visitVariableSet(ir.VariableSet node) { | |
| 53 if (_inTry) { | |
| 54 info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable)); | |
| 55 } | |
| 56 node.visitChildren(this); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 /// Closure conversion code using our new Entity model. Closure conversion is | 16 /// Closure conversion code using our new Entity model. Closure conversion is |
| 61 /// necessary because the semantics of closures are slightly different in Dart | 17 /// necessary because the semantics of closures are slightly different in Dart |
| 62 /// than JavaScript. Closure conversion is separated out into two phases: | 18 /// than JavaScript. Closure conversion is separated out into two phases: |
| 63 /// generation of a new (temporary) representation to store where variables need | 19 /// generation of a new (temporary) representation to store where variables need |
| 64 /// to be hoisted/captured up at another level to re-write the closure, and then | 20 /// to be hoisted/captured up at another level to re-write the closure, and then |
| 65 /// the code generation phase where we generate elements and/or instructions to | 21 /// the code generation phase where we generate elements and/or instructions to |
| 66 /// represent this new code path. | 22 /// represent this new code path. |
| 67 /// | 23 /// |
| 68 /// For a general explanation of how closure conversion works at a high level, | 24 /// For a general explanation of how closure conversion works at a high level, |
| 69 /// check out: | 25 /// check out: |
| 70 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or | 26 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or |
| 71 /// http://matt.might.net/articles/closure-conversion/. | 27 /// http://matt.might.net/articles/closure-conversion/. |
| 28 // TODO(efortuna): Change inheritance hierarchy so that the | |
| 29 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a | |
| 30 // glorified timer. | |
| 72 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { | 31 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
| 73 final KernelToElementMapForBuilding _elementMap; | 32 final KernelToElementMapForBuilding _elementMap; |
| 74 final GlobalLocalsMap _globalLocalsMap; | 33 final GlobalLocalsMap _globalLocalsMap; |
| 75 Map<Entity, ClosureRepresentationInfo> _infoMap = | 34 Map<ir.Node, ClosureScope> _closureScopeMap = <ir.Node, ClosureScope>{}; |
| 35 | |
| 36 Map<Entity, ClosureRepresentationInfo> _closureRepresentationCache = | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: Cache => Map (to be consistent)?
Emily Fortuna
2017/06/30 23:48:08
Done.
| |
| 76 <Entity, ClosureRepresentationInfo>{}; | 37 <Entity, ClosureRepresentationInfo>{}; |
| 77 | 38 |
| 78 KernelClosureConversionTask( | 39 /// Should only be used at the very beginning to ensure we are looking at the |
| 79 Measurer measurer, this._elementMap, this._globalLocalsMap) | 40 /// right kind of elements. |
| 41 final JsToFrontendMap _kToJElementMap; | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
Let's add a TODO to eliminate this mapping.
Johnn
Emily Fortuna
2017/06/30 23:48:09
Done.
Johnni, please see Siggi's comment, too.
| |
| 42 | |
| 43 KernelClosureConversionTask(Measurer measurer, this._elementMap, | |
| 44 this._kToJElementMap, this._globalLocalsMap) | |
| 80 : super(measurer); | 45 : super(measurer); |
| 81 | 46 |
| 82 /// The combined steps of generating our intermediate representation of | 47 /// The combined steps of generating our intermediate representation of |
| 83 /// closures that need to be rewritten and generating the element model. | 48 /// closures that need to be rewritten and generating the element model. |
| 84 /// Ultimately these two steps will be split apart with the second step | 49 /// Ultimately these two steps will be split apart with the second step |
| 85 /// happening later in compilation just before codegen. These steps are | 50 /// happening later in compilation just before codegen. These steps are |
| 86 /// combined here currently to provide a consistent interface to the rest of | 51 /// combined here currently to provide a consistent interface to the rest of |
| 87 /// the compiler until we are ready to separate these phases. | 52 /// the compiler until we are ready to separate these phases. |
| 88 @override | 53 @override |
| 89 void convertClosures(Iterable<MemberEntity> processedEntities, | 54 void convertClosures(Iterable<MemberEntity> processedEntities, |
| 90 ClosedWorldRefiner closedWorldRefiner) { | 55 ClosedWorldRefiner closedWorldRefiner) { |
| 91 // TODO(efortuna): implement. | 56 // The key in flaggedClosures is either a Local or a MemberEntity. |
| 57 Map<ir.Node, ScopeInfo> flaggedClosures = <ir.Node, ScopeInfo>{}; | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: I'd use the type only once (either on the lef
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
Seems we have 3 names for this :) - flaggedClosure
Emily Fortuna
2017/06/30 23:48:08
Done and done!
| |
| 58 processedEntities.forEach((MemberEntity kEntity) { | |
| 59 MemberEntity entity = kEntity; | |
| 60 if (_kToJElementMap != null) { | |
| 61 entity = _kToJElementMap.toBackendMember(kEntity); | |
| 62 } | |
| 63 if (entity.isAbstract) return; | |
| 64 if (entity.isField && !entity.isInstanceMember) { | |
| 65 ir.Field field = _elementMap.getMemberNode(entity); | |
| 66 // Skip top-level/static fields without an initializer. | |
| 67 if (field.initializer == null) return; | |
| 68 } | |
| 69 _buildClosureModel(entity, flaggedClosures, closedWorldRefiner); | |
| 70 }); | |
| 71 | |
| 72 for (ir.Node node in flaggedClosures.keys) { | |
| 73 _produceSyntheticElements( | |
| 74 node, flaggedClosures[node], closedWorldRefiner); | |
| 75 } | |
| 92 } | 76 } |
| 93 | 77 |
| 94 /// TODO(johnniwinther,efortuna): Implement this. | 78 /// Inspect members and mark if those members capture any state that needs to |
| 79 /// be marked as free variables. | |
| 80 void _buildClosureModel( | |
| 81 MemberEntity entity, | |
| 82 Map<ir.Node, ScopeInfo> closuresToRewrite, | |
| 83 ClosedWorldRefiner closedWorldRefiner) { | |
| 84 ir.Node node = _elementMap.getMemberNode(entity); | |
| 85 if (_closureScopeMap.keys.contains(node)) return; | |
| 86 visitor.ClosureScopeBuilder translator = new visitor.ClosureScopeBuilder( | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: I'd probably drop the prefix, maybe use a `sh
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
| 87 _closureScopeMap, | |
| 88 closuresToRewrite, | |
| 89 _globalLocalsMap.getLocalsMap(entity), | |
| 90 _elementMap); | |
| 91 if (entity.isField) { | |
| 92 if (node is ir.Field && node.initializer != null) { | |
| 93 translator.translateLazyInitializer(node); | |
| 94 } | |
| 95 } else { | |
| 96 assert(node is ir.Procedure || node is ir.Constructor); | |
| 97 translator.translateConstructorOrProcedure(node); | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 /// Given what variables are captured at each point, construct closure classes | |
| 102 /// with fields containing the captured variables to replicate the Dart | |
| 103 /// closure semantics in JS. | |
| 104 void _produceSyntheticElements( | |
| 105 ir.Node node, ScopeInfo info, ClosedWorldRefiner closedWorldRefiner) { | |
| 106 Entity entity; | |
| 107 KernelClosureClass closureClass = | |
| 108 new KernelClosureClass.fromScopeInfo(info); | |
| 109 if (node is ir.FunctionNode) { | |
| 110 // We want the original declaration where that function is used to point | |
| 111 // to the correct closure class. | |
| 112 // TODO(efortuna): entity equivalent of element.declaration? | |
| 113 node = (node as ir.FunctionNode).parent; | |
| 114 _closureRepresentationCache[closureClass.callMethod] = closureClass; | |
| 115 } | |
| 116 | |
| 117 if (node is ir.Member) { | |
| 118 entity = _elementMap.getMember(node); | |
| 119 } else { | |
| 120 entity = _elementMap.getLocalFunction(node); | |
| 121 } | |
| 122 assert(entity != null); | |
| 123 | |
| 124 _closureRepresentationCache[entity] = closureClass; | |
| 125 } | |
| 126 | |
| 127 @override | |
| 128 ScopeInfo getScopeInfo(Entity entity) { | |
| 129 return getClosureRepresentationInfo(entity); | |
| 130 } | |
| 131 | |
| 95 @override | 132 @override |
| 96 ClosureScope getClosureScope(ir.Node node) { | 133 ClosureScope getClosureScope(ir.Node node) { |
| 134 ClosureScope closureScope = _closureScopeMap[node]; | |
| 135 if (closureScope != null) { | |
| 136 // TODO(efortuna): Eventually closureScope should always be non-null, and | |
| 137 // we should just test that with an assert. | |
| 138 return closureScope; | |
| 139 } | |
| 97 return const ClosureScope(); | 140 return const ClosureScope(); |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
nit: you can also write the entire body as:
Closu
Emily Fortuna
2017/06/30 23:48:09
ah, thanks! This was the product of code evolution
| |
| 98 } | 141 } |
| 99 | 142 |
| 100 @override | 143 @override |
| 101 ScopeInfo getScopeInfo(Entity entity) { | |
| 102 // TODO(efortuna): Specialize this function from the one below. | |
| 103 return getClosureRepresentationInfo(entity); | |
| 104 } | |
| 105 | |
| 106 /// TODO(johnniwinther,efortuna): Implement this. | |
| 107 @override | |
| 108 LoopClosureScope getLoopClosureScope(ir.Node loopNode) { | 144 LoopClosureScope getLoopClosureScope(ir.Node loopNode) { |
| 109 return const LoopClosureScope(); | 145 ClosureScope scope = getClosureScope(loopNode); |
| 146 if (scope is! KernelLoopClosureScope) { | |
| 147 // TODO(efortuna): Eventually scope should always be non-null, and | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:09
is it not null and not KernelLoopClosureScope beca
Emily Fortuna
2017/06/30 23:48:08
To your first question, yes. It was written this w
| |
| 148 // we should just test that with an assert. | |
| 149 return const LoopClosureScope(); | |
| 150 } | |
| 151 return scope; | |
| 110 } | 152 } |
| 111 | 153 |
| 112 @override | 154 @override |
| 113 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 155 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
| 114 return _infoMap.putIfAbsent(entity, () { | 156 var closureImpl = _closureRepresentationCache[entity]; |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
same compact version here
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
| 115 if (entity is MemberEntity) { | 157 if (closureImpl != null) { |
| 116 ir.Member node = _elementMap.getMemberNode(entity); | 158 // TODO(efortuna): Eventually closureImpl should always be non-null, and |
| 117 ThisLocal thisLocal; | 159 // we should just test that with an assert. |
| 118 if (entity.isInstanceMember) { | 160 return closureImpl; |
| 119 thisLocal = new ThisLocal(entity); | 161 } |
| 120 } | |
| 121 KernelClosureDataBuilder builder = new KernelClosureDataBuilder( | |
| 122 _globalLocalsMap.getLocalsMap(entity), thisLocal); | |
| 123 node.accept(builder); | |
| 124 return builder.info; | |
| 125 } | |
| 126 | 162 |
| 127 /// TODO(johnniwinther,efortuna): Implement this. | 163 return const ClosureRepresentationInfo(); |
| 128 return const ClosureRepresentationInfo(); | |
| 129 }); | |
| 130 } | 164 } |
| 131 } | 165 } |
| 132 | 166 |
| 133 // TODO(johnniwinther): Add unittest for the computed | 167 class KernelScopeInfo extends ScopeInfo { |
| 134 // [ClosureRepresentationInfo]. | 168 Set<Local> _localsUsedInTryOrSync = new Set<Local>(); |
| 135 class KernelClosureRepresentationInfo extends ClosureRepresentationInfo { | 169 final Local thisLocal; |
| 136 final ThisLocal thisLocal; | 170 Set<Local> boxedVariables = new Set<Local>(); |
| 137 final Set<Local> _localsUsedInTryOrSync = new Set<Local>(); | |
| 138 | 171 |
| 139 KernelClosureRepresentationInfo(this.thisLocal); | 172 /// The set of variables that were defined in another scope, but are used in |
| 173 /// this scope. | |
| 174 Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); | |
| 175 | |
| 176 KernelScopeInfo(this.thisLocal); | |
| 177 | |
| 178 KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) | |
| 179 : this._localsUsedInTryOrSync = info.variablesUsedInTryOrSync, | |
| 180 this.boxedVariables = info.boxedVariables; | |
| 181 | |
| 182 KernelScopeInfo.withBoxedVariables(this.boxedVariables, this.thisLocal); | |
| 183 | |
| 184 Set<Local> get variablesUsedInTryOrSync => _localsUsedInTryOrSync; | |
|
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
since you are not updating the private field, I th
Emily Fortuna
2017/06/30 23:48:09
Yeah... this was a holdover from Johnni's design.
| |
| 185 | |
| 186 void forEachBoxedVariable(f(Local local, FieldEntity field)) { | |
| 187 boxedVariables.forEach((Local l) { | |
| 188 // TODO(efortuna): add FieldEntities as created. | |
| 189 f(l, null); | |
| 190 }); | |
| 191 } | |
| 140 | 192 |
| 141 void registerUsedInTryOrSync(Local local) { | 193 void registerUsedInTryOrSync(Local local) { |
| 142 _localsUsedInTryOrSync.add(local); | 194 _localsUsedInTryOrSync.add(local); |
| 143 } | 195 } |
| 144 | 196 |
| 145 bool variableIsUsedInTryOrSync(Local variable) => | 197 bool variableIsUsedInTryOrSync(Local variable) => |
| 146 _localsUsedInTryOrSync.contains(variable); | 198 _localsUsedInTryOrSync.contains(variable); |
| 147 | 199 |
| 148 String toString() { | 200 String toString() { |
| 149 StringBuffer sb = new StringBuffer(); | 201 StringBuffer sb = new StringBuffer(); |
| 150 sb.write('this=$thisLocal,'); | 202 sb.write('this=$thisLocal,'); |
| 151 sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); | 203 sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}'); |
| 152 return sb.toString(); | 204 return sb.toString(); |
| 153 } | 205 } |
| 206 | |
| 207 bool isBoxed(Local variable) => boxedVariables.contains(variable); | |
| 154 } | 208 } |
| 209 | |
| 210 class KernelClosureScope extends KernelScopeInfo implements ClosureScope { | |
| 211 final Local context; | |
| 212 | |
| 213 KernelClosureScope(Set<Local> boxedVariables, this.context, Local thisLocal) | |
| 214 : super.withBoxedVariables(boxedVariables, thisLocal); | |
| 215 | |
| 216 bool get requiresContextBox => boxedVariables.isNotEmpty; | |
| 217 } | |
| 218 | |
| 219 class KernelLoopClosureScope extends KernelClosureScope | |
| 220 implements LoopClosureScope { | |
| 221 final List<Local> boxedLoopVariables; | |
| 222 | |
| 223 KernelLoopClosureScope(Set<Local> boxedVariables, this.boxedLoopVariables, | |
| 224 Local context, Local thisLocal) | |
| 225 : super(boxedVariables, context, thisLocal); | |
| 226 | |
| 227 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | |
| 228 } | |
| 229 | |
| 230 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | |
| 231 class KernelClosureClass extends KernelScopeInfo | |
| 232 implements ClosureRepresentationInfo { | |
| 233 KernelClosureClass.fromScopeInfo(ScopeInfo info) | |
| 234 : super.from(info.thisLocal, info); | |
| 235 | |
| 236 // TODO(efortuna): Implement. | |
| 237 Local get closureEntity => null; | |
| 238 | |
| 239 // TODO(efortuna): Implement. | |
| 240 ClassEntity get closureClassEntity => null; | |
| 241 | |
| 242 // TODO(efortuna): Implement. | |
| 243 FunctionEntity get callMethod => null; | |
| 244 | |
| 245 // TODO(efortuna): Implement. | |
| 246 List<Local> get createdFieldEntities => const <Local>[]; | |
| 247 | |
| 248 // TODO(efortuna): Implement. | |
| 249 FieldEntity get thisFieldEntity => null; | |
| 250 | |
| 251 // TODO(efortuna): Implement. | |
| 252 void forEachCapturedVariable(f(Local from, FieldEntity to)) {} | |
| 253 | |
| 254 // TODO(efortuna): Implement. | |
| 255 @override | |
| 256 void forEachBoxedVariable(f(Local local, FieldEntity field)) {} | |
| 257 | |
| 258 // TODO(efortuna): Implement. | |
| 259 void forEachFreeVariable(f(Local variable, FieldEntity field)) {} | |
| 260 | |
| 261 // TODO(efortuna): Implement. | |
| 262 bool isVariableBoxed(Local variable) => false; | |
| 263 | |
| 264 // TODO(efortuna): Implement. | |
| 265 bool get isClosure => false; | |
|
Emily Fortuna
2017/06/30 17:58:40
what's this? you may ask. Well, to properly call o
Siggi Cherem (dart-lang)
2017/06/30 22:02:08
I'm ok copying this comment into the code if you l
Emily Fortuna
2017/06/30 23:48:09
Done.
| |
| 266 } | |
| OLD | NEW |