| 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>{};
|
| +}
|
|
|