| 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 3ba9ec542cdd60ada05161b50796b654f43bf572..01aa7ac13fb82e04a7b6f3d71c673925d199718a 100644
 | 
| --- a/pkg/compiler/lib/src/js_model/closure.dart
 | 
| +++ b/pkg/compiler/lib/src/js_model/closure.dart
 | 
| @@ -1,4 +1,4 @@
 | 
| -// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 | 
| +// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
 | 
|  // for details. All rights reserved. Use of this source code is governed by a
 | 
|  // BSD-style license that can be found in the LICENSE file.
 | 
|  
 | 
| @@ -9,54 +9,10 @@ import '../common/tasks.dart';
 | 
|  import '../elements/entities.dart';
 | 
|  import '../kernel/element_map.dart';
 | 
|  import '../world.dart';
 | 
| +import 'elements.dart';
 | 
| +import 'closure_visitors.dart';
 | 
|  import 'locals.dart';
 | 
|  
 | 
| -class KernelClosureDataBuilder extends ir.Visitor {
 | 
| -  final KernelToLocalsMap _localsMap;
 | 
| -  final KernelClosureRepresentationInfo info;
 | 
| -
 | 
| -  bool _inTry = false;
 | 
| -
 | 
| -  KernelClosureDataBuilder(this._localsMap, ThisLocal thisLocal)
 | 
| -      : info = new KernelClosureRepresentationInfo(thisLocal);
 | 
| -
 | 
| -  @override
 | 
| -  defaultNode(ir.Node node) {
 | 
| -    node.visitChildren(this);
 | 
| -  }
 | 
| -
 | 
| -  @override
 | 
| -  visitTryCatch(ir.TryCatch node) {
 | 
| -    bool oldInTry = _inTry;
 | 
| -    _inTry = true;
 | 
| -    node.visitChildren(this);
 | 
| -    _inTry = oldInTry;
 | 
| -  }
 | 
| -
 | 
| -  @override
 | 
| -  visitTryFinally(ir.TryFinally node) {
 | 
| -    bool oldInTry = _inTry;
 | 
| -    _inTry = true;
 | 
| -    node.visitChildren(this);
 | 
| -    _inTry = oldInTry;
 | 
| -  }
 | 
| -
 | 
| -  @override
 | 
| -  visitVariableGet(ir.VariableGet node) {
 | 
| -    if (_inTry) {
 | 
| -      info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable));
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
| -  @override
 | 
| -  visitVariableSet(ir.VariableSet node) {
 | 
| -    if (_inTry) {
 | 
| -      info.registerUsedInTryOrSync(_localsMap.getLocal(node.variable));
 | 
| -    }
 | 
| -    node.visitChildren(this);
 | 
| -  }
 | 
| -}
 | 
| -
 | 
|  /// Closure conversion code using our new Entity model. Closure conversion is
 | 
|  /// necessary because the semantics of closures are slightly different in Dart
 | 
|  /// than JavaScript. Closure conversion is separated out into two phases:
 | 
| @@ -69,14 +25,24 @@ class KernelClosureDataBuilder extends ir.Visitor {
 | 
|  /// check out:
 | 
|  /// http://siek.blogspot.com/2012/07/essence-of-closure-conversion.html or
 | 
|  /// http://matt.might.net/articles/closure-conversion/.
 | 
| +// TODO(efortuna): Change inheritance hierarchy so that the
 | 
| +// ClosureConversionTask doesn't inherit from ClosureTask because it's just a
 | 
| +// glorified timer.
 | 
|  class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
 | 
|    final KernelToElementMapForBuilding _elementMap;
 | 
|    final GlobalLocalsMap _globalLocalsMap;
 | 
| -  Map<Entity, ClosureRepresentationInfo> _infoMap =
 | 
| +  Map<ir.Node, ClosureScope> _closureScopeMap = <ir.Node, ClosureScope>{};
 | 
| +
 | 
| +  Map<Entity, ClosureRepresentationInfo> _closureRepresentationMap =
 | 
|        <Entity, ClosureRepresentationInfo>{};
 | 
|  
 | 
| -  KernelClosureConversionTask(
 | 
| -      Measurer measurer, this._elementMap, this._globalLocalsMap)
 | 
| +  /// Should only be used at the very beginning to ensure we are looking at the
 | 
| +  /// right kind of elements.
 | 
| +  // TODO(efortuna): Remove this map once we have one kernel backend strategy.
 | 
| +  final JsToFrontendMap _kToJElementMap;
 | 
| +
 | 
| +  KernelClosureConversionTask(Measurer measurer, this._elementMap,
 | 
| +      this._kToJElementMap, this._globalLocalsMap)
 | 
|        : super(measurer);
 | 
|  
 | 
|    /// The combined steps of generating our intermediate representation of
 | 
| @@ -88,67 +54,195 @@ class KernelClosureConversionTask extends ClosureConversionTask<ir.Node> {
 | 
|    @override
 | 
|    void convertClosures(Iterable<MemberEntity> processedEntities,
 | 
|        ClosedWorldRefiner closedWorldRefiner) {
 | 
| -    // TODO(efortuna): implement.
 | 
| +    var closuresToGenerate = <ir.Node, ScopeInfo>{};
 | 
| +    processedEntities.forEach((MemberEntity kEntity) {
 | 
| +      MemberEntity entity = kEntity;
 | 
| +      if (_kToJElementMap != null) {
 | 
| +        entity = _kToJElementMap.toBackendMember(kEntity);
 | 
| +      }
 | 
| +      if (entity.isAbstract) return;
 | 
| +      if (entity.isField && !entity.isInstanceMember) {
 | 
| +        ir.Field field = _elementMap.getMemberNode(entity);
 | 
| +        // Skip top-level/static fields without an initializer.
 | 
| +        if (field.initializer == null) return;
 | 
| +      }
 | 
| +      _buildClosureModel(entity, closuresToGenerate, closedWorldRefiner);
 | 
| +    });
 | 
| +
 | 
| +    for (ir.Node node in closuresToGenerate.keys) {
 | 
| +      _produceSyntheticElements(
 | 
| +          node, closuresToGenerate[node], closedWorldRefiner);
 | 
| +    }
 | 
|    }
 | 
|  
 | 
| -  /// TODO(johnniwinther,efortuna): Implement this.
 | 
| -  @override
 | 
| -  ClosureScope getClosureScope(ir.Node node) {
 | 
| -    return const ClosureScope();
 | 
| +  /// Inspect members and mark if those members capture any state that needs to
 | 
| +  /// be marked as free variables.
 | 
| +  void _buildClosureModel(
 | 
| +      MemberEntity entity,
 | 
| +      Map<ir.Node, ScopeInfo> closuresToGenerate,
 | 
| +      ClosedWorldRefiner closedWorldRefiner) {
 | 
| +    ir.Node node = _elementMap.getMemberNode(entity);
 | 
| +    if (_closureScopeMap.keys.contains(node)) return;
 | 
| +    ClosureScopeBuilder translator = new ClosureScopeBuilder(_closureScopeMap,
 | 
| +        closuresToGenerate, _globalLocalsMap.getLocalsMap(entity), _elementMap);
 | 
| +    if (entity.isField) {
 | 
| +      if (node is ir.Field && node.initializer != null) {
 | 
| +        translator.translateLazyInitializer(node);
 | 
| +      }
 | 
| +    } else {
 | 
| +      assert(node is ir.Procedure || node is ir.Constructor);
 | 
| +      translator.translateConstructorOrProcedure(node);
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  /// Given what variables are captured at each point, construct closure classes
 | 
| +  /// with fields containing the captured variables to replicate the Dart
 | 
| +  /// closure semantics in JS.
 | 
| +  void _produceSyntheticElements(
 | 
| +      ir.Node node, ScopeInfo info, ClosedWorldRefiner closedWorldRefiner) {
 | 
| +    Entity entity;
 | 
| +    KernelClosureClass closureClass =
 | 
| +        new KernelClosureClass.fromScopeInfo(info);
 | 
| +    if (node is ir.FunctionNode) {
 | 
| +      // We want the original declaration where that function is used to point
 | 
| +      // to the correct closure class.
 | 
| +      // TODO(efortuna): entity equivalent of element.declaration?
 | 
| +      node = (node as ir.FunctionNode).parent;
 | 
| +      _closureRepresentationMap[closureClass.callMethod] = closureClass;
 | 
| +    }
 | 
| +
 | 
| +    if (node is ir.Member) {
 | 
| +      entity = _elementMap.getMember(node);
 | 
| +    } else {
 | 
| +      entity = _elementMap.getLocalFunction(node);
 | 
| +    }
 | 
| +    assert(entity != null);
 | 
| +
 | 
| +    _closureRepresentationMap[entity] = closureClass;
 | 
|    }
 | 
|  
 | 
|    @override
 | 
|    ScopeInfo getScopeInfo(Entity entity) {
 | 
| -    // TODO(efortuna): Specialize this function from the one below.
 | 
|      return getClosureRepresentationInfo(entity);
 | 
|    }
 | 
|  
 | 
| -  /// TODO(johnniwinther,efortuna): Implement this.
 | 
|    @override
 | 
| -  LoopClosureScope getLoopClosureScope(ir.Node loopNode) {
 | 
| -    return const LoopClosureScope();
 | 
| -  }
 | 
| +  // TODO(efortuna): Eventually closureScopeMap[node] should always be non-null,
 | 
| +  // and we should just test that with an assert.
 | 
| +  ClosureScope getClosureScope(ir.Node node) =>
 | 
| +      _closureScopeMap[node] ?? const ClosureScope();
 | 
|  
 | 
|    @override
 | 
| -  ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) {
 | 
| -    return _infoMap.putIfAbsent(entity, () {
 | 
| -      if (entity is MemberEntity) {
 | 
| -        ir.Member node = _elementMap.getMemberNode(entity);
 | 
| -        ThisLocal thisLocal;
 | 
| -        if (entity.isInstanceMember) {
 | 
| -          thisLocal = new ThisLocal(entity);
 | 
| -        }
 | 
| -        KernelClosureDataBuilder builder = new KernelClosureDataBuilder(
 | 
| -            _globalLocalsMap.getLocalsMap(entity), thisLocal);
 | 
| -        node.accept(builder);
 | 
| -        return builder.info;
 | 
| -      }
 | 
| +  // TODO(efortuna): Eventually closureScopeMap[node] should always be non-null,
 | 
| +  // and we should just test that with an assert.
 | 
| +  LoopClosureScope getLoopClosureScope(ir.Node loopNode) =>
 | 
| +      _closureScopeMap[loopNode] ?? const LoopClosureScope();
 | 
|  
 | 
| -      /// TODO(johnniwinther,efortuna): Implement this.
 | 
| -      return const ClosureRepresentationInfo();
 | 
| -    });
 | 
| -  }
 | 
| +  @override
 | 
| +  // TODO(efortuna): Eventually closureRepresentationMap[node] should always be
 | 
| +  // non-null, and we should just test that with an assert.
 | 
| +  ClosureRepresentationInfo getClosureRepresentationInfo(Entity entity) =>
 | 
| +      _closureRepresentationMap[entity] ?? const ClosureRepresentationInfo();
 | 
|  }
 | 
|  
 | 
| -// TODO(johnniwinther): Add unittest for the computed
 | 
| -// [ClosureRepresentationInfo].
 | 
| -class KernelClosureRepresentationInfo extends ClosureRepresentationInfo {
 | 
| -  final ThisLocal thisLocal;
 | 
| -  final Set<Local> _localsUsedInTryOrSync = new Set<Local>();
 | 
| +class KernelScopeInfo 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.
 | 
| +  Set<ir.VariableDeclaration> freeVariables = new Set<ir.VariableDeclaration>();
 | 
|  
 | 
| -  KernelClosureRepresentationInfo(this.thisLocal);
 | 
| +  KernelScopeInfo(this.thisLocal)
 | 
| +      : localsUsedInTryOrSync = new Set<Local>(),
 | 
| +        boxedVariables = new Set<Local>();
 | 
|  
 | 
| -  void registerUsedInTryOrSync(Local local) {
 | 
| -    _localsUsedInTryOrSync.add(local);
 | 
| +  KernelScopeInfo.from(this.thisLocal, KernelScopeInfo info)
 | 
| +      : localsUsedInTryOrSync = info.localsUsedInTryOrSync,
 | 
| +        boxedVariables = info.boxedVariables;
 | 
| +
 | 
| +  KernelScopeInfo.withBoxedVariables(this.boxedVariables, this.thisLocal)
 | 
| +      : localsUsedInTryOrSync = new Set<Local>();
 | 
| +
 | 
| +  void forEachBoxedVariable(f(Local local, FieldEntity field)) {
 | 
| +    boxedVariables.forEach((Local l) {
 | 
| +      // TODO(efortuna): add FieldEntities as created.
 | 
| +      f(l, null);
 | 
| +    });
 | 
|    }
 | 
|  
 | 
| -  bool variableIsUsedInTryOrSync(Local variable) =>
 | 
| -      _localsUsedInTryOrSync.contains(variable);
 | 
| +  bool localIsUsedInTryOrSync(Local variable) =>
 | 
| +      localsUsedInTryOrSync.contains(variable);
 | 
|  
 | 
|    String toString() {
 | 
|      StringBuffer sb = new StringBuffer();
 | 
|      sb.write('this=$thisLocal,');
 | 
| -    sb.write('localsUsedInTryOrSync={${_localsUsedInTryOrSync.join(', ')}}');
 | 
| +    sb.write('localsUsedInTryOrSync={${localsUsedInTryOrSync.join(', ')}}');
 | 
|      return sb.toString();
 | 
|    }
 | 
| +
 | 
| +  bool isBoxed(Local variable) => boxedVariables.contains(variable);
 | 
| +}
 | 
| +
 | 
| +class KernelClosureScope extends KernelScopeInfo implements ClosureScope {
 | 
| +  final Local context;
 | 
| +
 | 
| +  KernelClosureScope(Set<Local> boxedVariables, this.context, Local thisLocal)
 | 
| +      : super.withBoxedVariables(boxedVariables, thisLocal);
 | 
| +
 | 
| +  bool get requiresContextBox => boxedVariables.isNotEmpty;
 | 
| +}
 | 
| +
 | 
| +class KernelLoopClosureScope extends KernelClosureScope
 | 
| +    implements LoopClosureScope {
 | 
| +  final List<Local> boxedLoopVariables;
 | 
| +
 | 
| +  KernelLoopClosureScope(Set<Local> boxedVariables, this.boxedLoopVariables,
 | 
| +      Local context, Local thisLocal)
 | 
| +      : super(boxedVariables, context, thisLocal);
 | 
| +
 | 
| +  bool get hasBoxedLoopVariables => boxedLoopVariables.isNotEmpty;
 | 
| +}
 | 
| +
 | 
| +// TODO(johnniwinther): Add unittest for the computed [ClosureClass].
 | 
| +class KernelClosureClass extends KernelScopeInfo
 | 
| +    implements ClosureRepresentationInfo {
 | 
| +  KernelClosureClass.fromScopeInfo(ScopeInfo info)
 | 
| +      : super.from(info.thisLocal, info);
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  Local get closureEntity => null;
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  ClassEntity get closureClassEntity => null;
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  FunctionEntity get callMethod => null;
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  List<Local> get createdFieldEntities => const <Local>[];
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  FieldEntity get thisFieldEntity => null;
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  void forEachCapturedVariable(f(Local from, FieldEntity to)) {}
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  @override
 | 
| +  void forEachBoxedVariable(f(Local local, FieldEntity field)) {}
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  void forEachFreeVariable(f(Local variable, FieldEntity field)) {}
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  bool isVariableBoxed(Local variable) => false;
 | 
| +
 | 
| +  // TODO(efortuna): Implement.
 | 
| +  // Why is this closure not actually a closure? Well, to properly call
 | 
| +  // ourselves a closure, we need to register the new closure class with the
 | 
| +  // ClosedWorldRefiner, which currently only takes elements. The change to
 | 
| +  // that (and the subsequent adjustment here) will follow soon.
 | 
| +  bool get isClosure => false;
 | 
|  }
 | 
| 
 |