Index: pkg/compiler/lib/src/js_model/closure.dart |
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart |
index 829010a6598cae42972183dbde8bf921d027a1b6..6cccab98e8c8d6fb3a291a2c0a30d5522db518e9 100644 |
--- a/pkg/compiler/lib/src/js_model/closure.dart |
+++ b/pkg/compiler/lib/src/js_model/closure.dart |
@@ -61,7 +61,17 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
@override |
void convertClosures(Iterable<MemberEntity> processedEntities, |
ClosedWorldRefiner closedWorldRefiner) { |
- var closuresToGenerate = <MemberEntity, Map<ir.TreeNode, ScopeInfo>>{}; |
+ Map<MemberEntity, ClosureModel> closureModels = |
+ _computeClosureModels(processedEntities); |
+ _createClosureEntities(closureModels, closedWorldRefiner); |
+ } |
+ |
+ // TODO(johnniwinther,efortuna): Compute this during resolution. See |
+ // documentation on [convertClosures]. |
+ Map<MemberEntity, ClosureModel> _computeClosureModels( |
+ Iterable<MemberEntity> processedEntities) { |
+ Map<MemberEntity, ClosureModel> closureModels = |
+ <MemberEntity, ClosureModel>{}; |
processedEntities.forEach((MemberEntity kEntity) { |
MemberEntity entity = _kToJElementMap.toBackendMember(kEntity); |
@@ -74,12 +84,31 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
// Skip top-level/static fields without an initializer. |
if (field.initializer == null) return; |
} |
- closuresToGenerate[entity] = |
- _buildClosureModel(entity, closedWorldRefiner); |
+ closureModels[entity] = _buildClosureModel(entity); |
}); |
+ return closureModels; |
+ } |
+ |
+ void _createClosureEntities(Map<MemberEntity, ClosureModel> closureModels, |
+ ClosedWorldRefiner closedWorldRefiner) { |
+ closureModels.forEach((MemberEntity member, ClosureModel model) { |
+ KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
+ if (model.scopeInfo != null) { |
+ _scopeMap[member] = new JsScopeInfo.from(model.scopeInfo, localsMap); |
+ } |
- closuresToGenerate.forEach( |
- (MemberEntity member, Map<ir.TreeNode, ScopeInfo> closuresToGenerate) { |
+ model.capturedScopesMap |
+ .forEach((ir.Node node, KernelCapturedScope scope) { |
+ if (scope is KernelCapturedLoopScope) { |
+ _capturedScopesMap[node] = |
+ new JsCapturedLoopScope.from(scope, localsMap); |
+ } else { |
+ _capturedScopesMap[node] = new JsCapturedScope.from(scope, localsMap); |
+ } |
+ }); |
+ |
+ Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
+ model.closuresToGenerate; |
for (ir.TreeNode node in closuresToGenerate.keys) { |
_produceSyntheticElements( |
member, node, closuresToGenerate[node], closedWorldRefiner); |
@@ -89,11 +118,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
/// Inspect members and mark if those members capture any state that needs to |
/// be marked as free variables. |
- Map<ir.TreeNode, ScopeInfo> _buildClosureModel( |
- MemberEntity entity, ClosedWorldRefiner closedWorldRefiner) { |
- assert(!_scopeMap.containsKey(entity), |
- failedAt(entity, "ScopeInfo already computed for $entity.")); |
- Map<ir.TreeNode, ScopeInfo> closuresToGenerate = <ir.TreeNode, ScopeInfo>{}; |
+ ClosureModel _buildClosureModel(MemberEntity entity) { |
+ ClosureModel model = new ClosureModel(); |
MemberDefinition definition = _elementMap.getMemberDefinition(entity); |
switch (definition.kind) { |
case MemberKind.regular: |
@@ -103,14 +129,8 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
failedAt(entity, "Unexpected member definition $definition"); |
} |
ir.Node node = definition.node; |
- assert(!_scopeMap.containsKey(entity), |
- failedAt(entity, "CaptureScope already computed for $node.")); |
- CapturedScopeBuilder translator = new CapturedScopeBuilder( |
- entity, |
- _capturedScopesMap, |
- _scopeMap, |
- closuresToGenerate, |
- _globalLocalsMap.getLocalsMap(entity)); |
+ CapturedScopeBuilder translator = new CapturedScopeBuilder(model, |
+ hasThisLocal: entity.isInstanceMember || entity.isConstructor); |
if (entity.isField) { |
if (node is ir.Field && node.initializer != null) { |
translator.translateLazyInitializer(node); |
@@ -119,7 +139,7 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
assert(node is ir.Procedure || node is ir.Constructor); |
translator.translateConstructorOrProcedure(node); |
} |
- return closuresToGenerate; |
+ return model; |
} |
/// Given what variables are captured at each point, construct closure classes |
@@ -131,7 +151,7 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
void _produceSyntheticElements( |
MemberEntity member, |
ir.TreeNode /* ir.Member | ir.FunctionNode */ node, |
- ScopeInfo info, |
+ KernelScopeInfo info, |
ClosedWorldRefiner closedWorldRefiner) { |
String name = _computeClosureName(node); |
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member); |
@@ -240,25 +260,55 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> { |
} |
} |
-class KernelScopeInfo extends ScopeInfo { |
- final Set<Local> localsUsedInTryOrSync; |
- final Local thisLocal; |
- final Set<Local> boxedVariables; |
+class KernelScopeInfo { |
+ final Set<ir.VariableDeclaration> localsUsedInTryOrSync; |
+ final bool hasThisLocal; |
+ final Set<ir.VariableDeclaration> boxedVariables; |
/// The set of variables that were defined in another scope, but are used in |
/// this scope. |
Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>(); |
- KernelScopeInfo(this.thisLocal) |
- : localsUsedInTryOrSync = new Set<Local>(), |
- boxedVariables = new Set<Local>(); |
+ KernelScopeInfo(this.hasThisLocal) |
+ : localsUsedInTryOrSync = new Set<ir.VariableDeclaration>(), |
+ boxedVariables = new Set<ir.VariableDeclaration>(); |
- KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info) |
+ KernelScopeInfo.from(this.hasThisLocal, KernelScopeInfo info) |
: localsUsedInTryOrSync = info.localsUsedInTryOrSync, |
boxedVariables = info.boxedVariables; |
KernelScopeInfo.withBoxedVariables(this.boxedVariables, |
- this.localsUsedInTryOrSync, this.freeVariables, this.thisLocal); |
+ this.localsUsedInTryOrSync, this.freeVariables, this.hasThisLocal); |
+ |
+ String toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('this=$hasThisLocal,'); |
+ sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}'); |
+ return sb.toString(); |
+ } |
+} |
+ |
+class JsScopeInfo extends ScopeInfo { |
+ final Set<Local> localsUsedInTryOrSync; |
+ final Local thisLocal; |
+ final Set<Local> boxedVariables; |
+ |
+ /// The set of variables that were defined in another scope, but are used in |
+ /// this scope. |
+ final Set<Local> freeVariables; |
+ |
+ JsScopeInfo(this.thisLocal, this.localsUsedInTryOrSync, this.boxedVariables, |
+ this.freeVariables); |
+ |
+ JsScopeInfo.from(KernelScopeInfo info, KernelToLocalsMap localsMap) |
+ : this.thisLocal = |
+ info.hasThisLocal ? new ThisLocal(localsMap.currentMember) : null, |
+ this.localsUsedInTryOrSync = |
+ info.localsUsedInTryOrSync.map(localsMap.getLocalVariable).toSet(), |
+ this.boxedVariables = |
+ info.boxedVariables.map(localsMap.getLocalVariable).toSet(), |
+ this.freeVariables = |
+ info.freeVariables.map(localsMap.getLocalVariable).toSet(); |
void forEachBoxedVariable(f(Local local, FieldEntity field)) { |
boxedVariables.forEach((Local l) { |
@@ -280,40 +330,63 @@ class KernelScopeInfo extends ScopeInfo { |
bool isBoxed(Local variable) => boxedVariables.contains(variable); |
} |
-class KernelCapturedScope extends KernelScopeInfo implements CapturedScope { |
- final Local context; |
+class KernelCapturedScope extends KernelScopeInfo { |
+ final ir.TreeNode context; |
KernelCapturedScope( |
- Set<Local> boxedVariables, |
+ Set<ir.VariableDeclaration> boxedVariables, |
this.context, |
- Set<Local> localsUsedInTryOrSync, |
+ Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
Set<ir.VariableDeclaration> freeVariables, |
- Local thisLocal) |
+ bool hasThisLocal) |
: super.withBoxedVariables( |
- boxedVariables, localsUsedInTryOrSync, freeVariables, thisLocal); |
+ boxedVariables, localsUsedInTryOrSync, freeVariables, hasThisLocal); |
bool get requiresContextBox => boxedVariables.isNotEmpty; |
} |
-class KernelCapturedLoopScope extends KernelCapturedScope |
- implements CapturedLoopScope { |
- final List<Local> boxedLoopVariables; |
+class JsCapturedScope extends JsScopeInfo implements CapturedScope { |
+ final Local context; |
+ |
+ JsCapturedScope.from( |
+ KernelCapturedScope capturedScope, KernelToLocalsMap localsMap) |
+ : this.context = localsMap.getLocalVariable(capturedScope.context), |
+ super.from(capturedScope, localsMap); |
+ |
+ bool get requiresContextBox => boxedVariables.isNotEmpty; |
+} |
+ |
+class KernelCapturedLoopScope extends KernelCapturedScope { |
+ final List<ir.VariableDeclaration> boxedLoopVariables; |
KernelCapturedLoopScope( |
- Set<Local> boxedVariables, |
+ Set<ir.VariableDeclaration> boxedVariables, |
this.boxedLoopVariables, |
- Local context, |
- Set<Local> localsUsedInTryOrSync, |
+ ir.TreeNode context, |
+ Set<ir.VariableDeclaration> localsUsedInTryOrSync, |
Set<ir.VariableDeclaration> freeVariables, |
- Local thisLocal) |
+ bool hasThisLocal) |
: super(boxedVariables, context, localsUsedInTryOrSync, freeVariables, |
- thisLocal); |
+ hasThisLocal); |
+ |
+ bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
+} |
+ |
+class JsCapturedLoopScope extends JsCapturedScope implements CapturedLoopScope { |
+ final List<Local> boxedLoopVariables; |
+ |
+ JsCapturedLoopScope.from( |
+ KernelCapturedLoopScope capturedScope, KernelToLocalsMap localsMap) |
+ : this.boxedLoopVariables = capturedScope.boxedLoopVariables |
+ .map(localsMap.getLocalVariable) |
+ .toList(), |
+ super.from(capturedScope, localsMap); |
bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty; |
} |
// TODO(johnniwinther): Add unittest for the computed [ClosureClass]. |
-class KernelClosureClass extends KernelScopeInfo |
+class KernelClosureClass extends JsScopeInfo |
implements ClosureRepresentationInfo, JClass { |
final ir.Location location; |
@@ -328,7 +401,7 @@ class KernelClosureClass extends KernelScopeInfo |
KernelClosureClass.fromScopeInfo(this.name, this.library, |
KernelScopeInfo info, this.location, KernelToLocalsMap localsMap) |
- : super.from(info.thisLocal, info) { |
+ : super.from(info, localsMap) { |
// Make a corresponding field entity in this closure class for every single |
// freeVariable in the KernelScopeInfo.freeVariable. |
int i = 0; |
@@ -337,7 +410,7 @@ class KernelClosureClass extends KernelScopeInfo |
// old Element version. The old version did all the boxed items and then |
// all the others. |
Local capturedLocal = localsMap.getLocalVariable(variable); |
- if (info.isBoxed(capturedLocal)) { |
+ if (isBoxed(capturedLocal)) { |
// TODO(efortuna): Coming soon. |
} else { |
localToFieldMap[capturedLocal] = new ClosureField( |
@@ -422,3 +495,20 @@ class ClosureClassDefinition implements ClassDefinition { |
String toString() => |
'ClosureClassDefinition(kind:$kind,cls:$cls,location:$location)'; |
} |
+ |
+/// Collection of closure data collected for a single member. |
+class ClosureModel { |
+ /// Collection [ScopeInfo] data for the member, if any. |
+ // TODO(johnniwinther): [scopeInfo] seem to be missing only for fields |
+ // without initializers; we shouldn't even create a [ClosureModel] in these |
+ // cases. |
+ KernelScopeInfo scopeInfo; |
+ |
+ /// Collected [CapturedScope] data for nodes. |
+ Map<ir.Node, KernelCapturedScope> capturedScopesMap = |
+ <ir.Node, KernelCapturedScope>{}; |
+ |
+ /// Collected [ScopeInfo] data for nodes. |
+ Map<ir.TreeNode, KernelScopeInfo> closuresToGenerate = |
+ <ir.TreeNode, KernelScopeInfo>{}; |
+} |