| 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 '../constants/expressions.dart'; | 10 import '../constants/expressions.dart'; |
| 11 import '../constants/values.dart'; | 11 import '../constants/values.dart'; |
| 12 import '../elements/entities.dart'; | 12 import '../elements/entities.dart'; |
| 13 import '../elements/names.dart' show Name; | 13 import '../elements/names.dart' show Name; |
| 14 import '../elements/types.dart'; | 14 import '../elements/types.dart'; |
| 15 import '../kernel/element_map.dart'; | 15 import '../kernel/element_map.dart'; |
| 16 import '../kernel/env.dart'; | 16 import '../kernel/env.dart'; |
| 17 import '../world.dart'; | 17 import '../world.dart'; |
| 18 import 'elements.dart'; | 18 import 'elements.dart'; |
| 19 import 'closure_visitors.dart'; | 19 import 'closure_visitors.dart'; |
| 20 import 'locals.dart'; | 20 import 'locals.dart'; |
| 21 import 'js_strategy.dart' show JsClosedWorld; | 21 import 'js_strategy.dart' show JsClosedWorld; |
| 22 | 22 |
| 23 class KernelClosureAnalysis { | 23 class KernelClosureAnalysis { |
| 24 /// Inspect members and mark if those members capture any state that needs to | 24 /// Inspect members and mark if those members capture any state that needs to |
| 25 /// be marked as free variables. | 25 /// be marked as free variables. |
| 26 static ClosureModel computeClosureModel(MemberEntity entity, ir.Member node) { | 26 static ScopeModel computeScopeModel(MemberEntity entity, ir.Member node) { |
| 27 if (entity.isAbstract) return null; | 27 if (entity.isAbstract) return null; |
| 28 if (entity.isField && !entity.isInstanceMember) { | 28 if (entity.isField && !entity.isInstanceMember) { |
| 29 ir.Field field = node; | 29 ir.Field field = node; |
| 30 // Skip top-level/static fields without an initializer. | 30 // Skip top-level/static fields without an initializer. |
| 31 if (field.initializer == null) return null; | 31 if (field.initializer == null) return null; |
| 32 } | 32 } |
| 33 | 33 |
| 34 ClosureModel model = new ClosureModel(); | 34 ScopeModel model = new ScopeModel(); |
| 35 CapturedScopeBuilder translator = new CapturedScopeBuilder(model, | 35 CapturedScopeBuilder translator = new CapturedScopeBuilder(model, |
| 36 hasThisLocal: entity.isInstanceMember || entity.isConstructor); | 36 hasThisLocal: entity.isInstanceMember || entity.isConstructor); |
| 37 if (entity.isField) { | 37 if (entity.isField) { |
| 38 if (node is ir.Field && node.initializer != null) { | 38 if (node is ir.Field && node.initializer != null) { |
| 39 translator.translateLazyInitializer(node); | 39 translator.translateLazyInitializer(node); |
| 40 } else { |
| 41 assert(entity.isInstanceMember); |
| 42 model.scopeInfo = new KernelScopeInfo(true); |
| 40 } | 43 } |
| 41 } else { | 44 } else { |
| 42 assert(node is ir.Procedure || node is ir.Constructor); | 45 assert(node is ir.Procedure || node is ir.Constructor); |
| 43 translator.translateConstructorOrProcedure(node); | 46 translator.translateConstructorOrProcedure(node); |
| 44 } | 47 } |
| 45 return model; | 48 return model; |
| 46 } | 49 } |
| 47 } | 50 } |
| 48 | 51 |
| 49 /// Closure conversion code using our new Entity model. Closure conversion is | 52 /// Closure conversion code using our new Entity model. Closure conversion is |
| 50 /// necessary because the semantics of closures are slightly different in Dart | 53 /// necessary because the semantics of closures are slightly different in Dart |
| 51 /// than JavaScript. Closure conversion is separated out into two phases: | 54 /// than JavaScript. Closure conversion is separated out into two phases: |
| 52 /// generation of a new (temporary) representation to store where variables need | 55 /// generation of a new (temporary) representation to store where variables need |
| 53 /// to be hoisted/captured up at another level to re-write the closure, and then | 56 /// to be hoisted/captured up at another level to re-write the closure, and then |
| 54 /// the code generation phase where we generate elements and/or instructions to | 57 /// the code generation phase where we generate elements and/or instructions to |
| 55 /// represent this new code path. | 58 /// represent this new code path. |
| 56 /// | 59 /// |
| 57 /// For a general explanation of how closure conversion works at a high level, | 60 /// For a general explanation of how closure conversion works at a high level, |
| 58 /// check out: | 61 /// check out: |
| 59 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or | 62 /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or |
| 60 /// http://matt.might.net/articles/closure-conversion/. | 63 /// http://matt.might.net/articles/closure-conversion/. |
| 61 // TODO(efortuna): Change inheritance hierarchy so that the | 64 // TODO(efortuna): Change inheritance hierarchy so that the |
| 62 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a | 65 // ClosureConversionTask doesn't inherit from ClosureTask because it's just a |
| 63 // glorified timer. | 66 // glorified timer. |
| 64 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { | 67 class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
| 65 final KernelToElementMapForBuilding _elementMap; | 68 final KernelToElementMapForBuilding _elementMap; |
| 66 final GlobalLocalsMap _globalLocalsMap; | 69 final GlobalLocalsMap _globalLocalsMap; |
| 67 final Map<MemberEntity, ClosureModel> _closureModels; | 70 final Map<MemberEntity, ScopeModel> _closureModels; |
| 68 | 71 |
| 69 /// Map of the scoping information that corresponds to a particular entity. | 72 /// Map of the scoping information that corresponds to a particular entity. |
| 70 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; | 73 Map<Entity, ScopeInfo> _scopeMap = <Entity, ScopeInfo>{}; |
| 71 Map<ir.Node, CapturedScope> _capturedScopesMap = <ir.Node, CapturedScope>{}; | 74 Map<ir.Node, CapturedScope> _capturedScopesMap = <ir.Node, CapturedScope>{}; |
| 72 | 75 |
| 73 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = | 76 Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap = |
| 74 <Entity, ClosureRepresentationInfo>{}; | 77 <Entity, ClosureRepresentationInfo>{}; |
| 75 | 78 |
| 76 KernelClosureConversionTask(Measurer measurer, this._elementMap, | 79 KernelClosureConversionTask(Measurer measurer, this._elementMap, |
| 77 this._globalLocalsMap, this._closureModels) | 80 this._globalLocalsMap, this._closureModels) |
| 78 : super(measurer); | 81 : super(measurer); |
| 79 | 82 |
| 80 /// The combined steps of generating our intermediate representation of | 83 /// The combined steps of generating our intermediate representation of |
| 81 /// closures that need to be rewritten and generating the element model. | 84 /// closures that need to be rewritten and generating the element model. |
| 82 /// Ultimately these two steps will be split apart with the second step | 85 /// Ultimately these two steps will be split apart with the second step |
| 83 /// happening later in compilation just before codegen. These steps are | 86 /// happening later in compilation just before codegen. These steps are |
| 84 /// combined here currently to provide a consistent interface to the rest of | 87 /// combined here currently to provide a consistent interface to the rest of |
| 85 /// the compiler until we are ready to separate these phases. | 88 /// the compiler until we are ready to separate these phases. |
| 86 @override | 89 @override |
| 87 void convertClosures(Iterable<MemberEntity> processedEntities, | 90 void convertClosures(Iterable<MemberEntity> processedEntities, |
| 88 ClosedWorldRefiner closedWorldRefiner) { | 91 ClosedWorldRefiner closedWorldRefiner) { |
| 89 _createClosureEntities(_closureModels, closedWorldRefiner); | 92 _createClosureEntities(_closureModels, closedWorldRefiner); |
| 90 } | 93 } |
| 91 | 94 |
| 92 void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, | 95 void _createClosureEntities(Map<MemberEntity, ScopeModel> closureModels, |
| 93 JsClosedWorld closedWorldRefiner) { | 96 JsClosedWorld closedWorldRefiner) { |
| 94 closureModels.forEach((MemberEntity member, ClosureModel model) { | 97 closureModels.forEach((MemberEntity member, ScopeModel model) { |
| 95 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); | 98 KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
| 96 if (model.scopeInfo != null) { | 99 if (model.scopeInfo != null) { |
| 97 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); | 100 _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); |
| 98 } | 101 } |
| 99 | 102 |
| 100 model.capturedScopesMap | 103 model.capturedScopesMap |
| 101 .forEach((ir.Node node, KernelCapturedScope scope) { | 104 .forEach((ir.Node node, KernelCapturedScope scope) { |
| 102 if (scope is KernelCapturedLoopScope) { | 105 if (scope is KernelCapturedLoopScope) { |
| 103 _capturedScopesMap[node] = | 106 _capturedScopesMap[node] = |
| 104 new JsCapturedLoopScope.from(scope, localsMap); | 107 new JsCapturedLoopScope.from(scope, localsMap); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 } | 173 } |
| 171 } | 174 } |
| 172 | 175 |
| 173 @override | 176 @override |
| 174 // TODO(efortuna): Eventually capturedScopesMap[node] should always | 177 // TODO(efortuna): Eventually capturedScopesMap[node] should always |
| 175 // be non-null, and we should just test that with an assert. | 178 // be non-null, and we should just test that with an assert. |
| 176 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => | 179 CapturedLoopScope getCapturedLoopScope(ir.Node loopNode) => |
| 177 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); | 180 _capturedScopesMap[loopNode] ?? const CapturedLoopScope(); |
| 178 | 181 |
| 179 @override | 182 @override |
| 180 // TODO(efortuna): Eventually closureRepresentationMap[node] should always be | |
| 181 // non-null, and we should just test that with an assert. | |
| 182 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { | 183 ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) { |
| 183 return _closureRepresentationMap[entity] ?? | 184 var closure = _closureRepresentationMap[entity]; |
| 184 const ClosureRepresentationInfo(); | 185 assert(closure != null, |
| 186 "Corresponding closure class not found for $entity. Closures found for $
{_closureRepresentationMap.keys}"); |
| 187 return closure; |
| 185 } | 188 } |
| 186 } | 189 } |
| 187 | 190 |
| 188 class KernelScopeInfo { | 191 class KernelScopeInfo { |
| 189 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; | 192 final Set<ir.VariableDeclaration> localsUsedInTryOrSync; |
| 190 final bool hasThisLocal; | 193 final bool hasThisLocal; |
| 191 final Set<ir.VariableDeclaration> boxedVariables; | 194 final Set<ir.VariableDeclaration> boxedVariables; |
| 192 // If boxedVariables is empty, this will be null, because no variables will | 195 // If boxedVariables is empty, this will be null, because no variables will |
| 193 // need to be boxed. | 196 // need to be boxed. |
| 194 final NodeBox capturedVariablesAccessor; | 197 final NodeBox capturedVariablesAccessor; |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; | 324 bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
| 322 } | 325 } |
| 323 | 326 |
| 324 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. | 327 // TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
| 325 class KernelClosureClass extends JsScopeInfo | 328 class KernelClosureClass extends JsScopeInfo |
| 326 implements ClosureRepresentationInfo, JClass { | 329 implements ClosureRepresentationInfo, JClass { |
| 327 final String name; | 330 final String name; |
| 328 final JLibrary library; | 331 final JLibrary library; |
| 329 JFunction callMethod; | 332 JFunction callMethod; |
| 330 final Local closureEntity; | 333 final Local closureEntity; |
| 334 final Local thisLocal; |
| 331 | 335 |
| 332 /// Index into the classData, classList and classEnvironment lists where this | 336 /// Index into the classData, classList and classEnvironment lists where this |
| 333 /// entity is stored in [JsToFrontendMapImpl]. | 337 /// entity is stored in [JsToFrontendMapImpl]. |
| 334 final int classIndex; | 338 final int classIndex; |
| 335 | 339 |
| 336 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); | 340 final Map<Local, JField> localToFieldMap = new Map<Local, JField>(); |
| 337 | 341 |
| 338 KernelClosureClass.fromScopeInfo( | 342 KernelClosureClass.fromScopeInfo( |
| 339 ir.FunctionNode closureSourceNode, | 343 ir.FunctionNode closureSourceNode, |
| 340 this.name, | 344 this.name, |
| 341 this.classIndex, | 345 this.classIndex, |
| 342 this.library, | 346 this.library, |
| 343 KernelScopeInfo info, | 347 KernelScopeInfo info, |
| 344 KernelToLocalsMap localsMap) | 348 KernelToLocalsMap localsMap) |
| 345 : closureEntity = closureSourceNode.parent is ir.Member | 349 : closureEntity = closureSourceNode.parent is ir.Member |
| 346 ? null | 350 ? null |
| 347 : localsMap.getLocalFunction(closureSourceNode.parent), | 351 : localsMap.getLocalFunction(closureSourceNode.parent), |
| 352 thisLocal = |
| 353 info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
| 348 super.from(info, localsMap); | 354 super.from(info, localsMap); |
| 349 | 355 |
| 350 ClassEntity get closureClassEntity => this; | 356 ClassEntity get closureClassEntity => this; |
| 351 | 357 |
| 352 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); | 358 List<Local> get createdFieldEntities => localToFieldMap.keys.toList(); |
| 353 | 359 |
| 354 // TODO(efortuna): Implement. | 360 FieldEntity get thisFieldEntity => localToFieldMap[thisLocal]; |
| 355 FieldEntity get thisFieldEntity => null; | |
| 356 | 361 |
| 357 void forEachCapturedVariable(f(Local from, JField to)) { | 362 void forEachCapturedVariable(f(Local from, JField to)) { |
| 358 localToFieldMap.forEach(f); | 363 localToFieldMap.forEach(f); |
| 359 } | 364 } |
| 360 | 365 |
| 361 @override | 366 @override |
| 362 void forEachBoxedVariable(f(Local local, JField field)) { | 367 void forEachBoxedVariable(f(Local local, JField field)) { |
| 363 for (Local l in localToFieldMap.keys) { | 368 for (Local l in localToFieldMap.keys) { |
| 364 if (localToFieldMap[l] is JBoxedField) f(l, localToFieldMap[l]); | 369 if (localToFieldMap[l] is JBoxedField) f(l, localToFieldMap[l]); |
| 365 } | 370 } |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 final SourceSpan location; | 502 final SourceSpan location; |
| 498 final MemberKind kind; | 503 final MemberKind kind; |
| 499 final ir.Node node; | 504 final ir.Node node; |
| 500 | 505 |
| 501 ClosureMemberDefinition(this.member, this.location, this.kind, this.node); | 506 ClosureMemberDefinition(this.member, this.location, this.kind, this.node); |
| 502 | 507 |
| 503 String toString() => | 508 String toString() => |
| 504 'ClosureMemberDefinition(kind:$kind,member:$member,location:$location)'; | 509 'ClosureMemberDefinition(kind:$kind,member:$member,location:$location)'; |
| 505 } | 510 } |
| 506 | 511 |
| 507 /// Collection of closure data collected for a single member. | 512 /// Collection of scope data collected for a single member. |
| 508 class ClosureModel { | 513 class ScopeModel { |
| 509 /// Collection [ScopeInfo] data for the member, if any. | 514 /// Collection [ScopeInfo] data for the member. |
| 510 // TODO(johnniwinther): [scopeInfo] seem to be missing only for fields | |
| 511 // without initializers; we shouldn't even create a [ClosureModel] in these | |
| 512 // cases. | |
| 513 KernelScopeInfo scopeInfo; | 515 KernelScopeInfo scopeInfo; |
| 514 | 516 |
| 515 /// Collected [CapturedScope] data for nodes. | 517 /// Collected [CapturedScope] data for nodes. |
| 516 Map<ir.Node, KernelCapturedScope> capturedScopesMap = | 518 Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
| 517 <ir.Node, KernelCapturedScope>{}; | 519 <ir.Node, KernelCapturedScope>{}; |
| 518 | 520 |
| 519 /// Collected [ScopeInfo] data for nodes. | 521 /// Collected [ScopeInfo] data for nodes. |
| 520 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = | 522 Map<ir.FunctionNode, KernelScopeInfo> closuresToGenerate = |
| 521 <ir.FunctionNode, KernelScopeInfo>{}; | 523 <ir.FunctionNode, KernelScopeInfo>{}; |
| 522 } | 524 } |
| OLD | NEW |