Reland [dart2js] Remove GlobalsLocalsMap from JClosedWorld

The removes the GlobalsLocalsMap from JClosedWorld and instead includes
it as a part of GlobalInferenceResults.

Previously landed in https://dart-review.googlesource.com/c/sdk/+/181620
but reverted.

Change-Id: I384b21bf285009c82ca9ec8740443cda0a6fa493
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/183001
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/backend_strategy.dart b/pkg/compiler/lib/src/backend_strategy.dart
index b5307e1..92966bc 100644
--- a/pkg/compiler/lib/src/backend_strategy.dart
+++ b/pkg/compiler/lib/src/backend_strategy.dart
@@ -16,6 +16,7 @@
 import 'js_backend/enqueuer.dart';
 import 'js_backend/inferred_data.dart';
 import 'js_emitter/code_emitter_task.dart';
+import 'js_model/locals.dart';
 import 'serialization/serialization.dart';
 import 'ssa/ssa.dart';
 import 'universe/codegen_world_builder.dart';
@@ -65,8 +66,8 @@
   SourceSpan spanFromSpannable(Spannable spannable, Entity currentElement);
 
   /// Creates the [TypesInferrer] used by this strategy.
-  TypesInferrer createTypesInferrer(
-      JClosedWorld closedWorld, InferredDataBuilder inferredDataBuilder);
+  TypesInferrer createTypesInferrer(JClosedWorld closedWorld,
+      GlobalLocalsMap globalLocalsMap, InferredDataBuilder inferredDataBuilder);
 
   /// Calls [f] for every member that needs to be serialized for modular code
   /// generation and returns an [EntityWriter] for encoding these members in
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart
index 2d0efa6..6f26060 100644
--- a/pkg/compiler/lib/src/closure.dart
+++ b/pkg/compiler/lib/src/closure.dart
@@ -35,6 +35,14 @@
   /// Accessor to the information about scopes that closures capture. Used by
   /// the SSA builder.
   CapturedScope getCapturedScope(MemberEntity entity);
+
+  /// If [entity] is a closure call method or closure signature method, the
+  /// original enclosing member is returned. Otherwise [entity] is returned.
+  ///
+  /// A member and its nested closure share the underlying AST, we need to
+  /// ensure that locals are shared between them. We therefore store the
+  /// locals in the global locals map using the enclosing member as key.
+  MemberEntity getEnclosingMember(MemberEntity entity);
 }
 
 /// Enum used for identifying [ScopeInfo] subclasses in serialization.
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 6faa855..e921146 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -39,6 +39,7 @@
 import 'js_backend/inferred_data.dart';
 import 'js_model/js_strategy.dart';
 import 'js_model/js_world.dart';
+import 'js_model/locals.dart';
 import 'kernel/kernel_strategy.dart';
 import 'kernel/loader.dart' show KernelLoaderTask, KernelResult;
 import 'null_compiler_output.dart' show NullCompilerOutput;
@@ -401,10 +402,12 @@
       JClosedWorld closedWorld) {
     FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction;
     reporter.log('Performing global type inference');
+    GlobalLocalsMap globalLocalsMap =
+        new GlobalLocalsMap(closedWorld.closureDataLookup.getEnclosingMember);
     InferredDataBuilder inferredDataBuilder =
         new InferredDataBuilderImpl(closedWorld.annotationsData);
     return globalInference.runGlobalTypeInference(
-        mainFunction, closedWorld, inferredDataBuilder);
+        mainFunction, closedWorld, globalLocalsMap, inferredDataBuilder);
   }
 
   void runCodegenEnqueuer(CodegenResults codegenResults) {
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 691151c..882895c 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -271,7 +271,7 @@
     List<String> inferredParameterTypes = <String>[];
 
     closedWorld.elementEnvironment.forEachParameterAsLocal(
-        closedWorld.globalLocalsMap, function, (parameter) {
+        _globalInferenceResults.globalLocalsMap, function, (parameter) {
       inferredParameterTypes.add('${_resultOfParameter(parameter)}');
     });
     int parameterIndex = 0;
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 129b30b..39c1a06 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -64,6 +64,7 @@
 
   final TypeSystem types;
   final Map<ir.TreeNode, TypeInformation> concreteTypes = {};
+  final GlobalLocalsMap globalLocalsMap;
   final InferredDataBuilder inferredDataBuilder;
 
   final FunctionEntity mainElement;
@@ -114,9 +115,10 @@
       this._compilerOutput,
       this.closedWorld,
       this.mainElement,
+      this.globalLocalsMap,
       this.inferredDataBuilder)
-      : this.types = new TypeSystem(
-            closedWorld, new KernelTypeSystemStrategy(closedWorld));
+      : this.types = new TypeSystem(closedWorld,
+            new KernelTypeSystemStrategy(closedWorld, globalLocalsMap));
 
   /// Applies [f] to all elements in the universe that match [selector] and
   /// [mask]. If [f] returns false, aborts the iteration.
@@ -634,7 +636,7 @@
         this,
         member,
         body,
-        closedWorld.globalLocalsMap.getLocalsMap(member),
+        globalLocalsMap.getLocalsMap(member),
         closedWorld.elementMap.getStaticTypeProvider(member));
     return visitor.run();
   }
@@ -1224,8 +1226,9 @@
 
 class KernelTypeSystemStrategy implements TypeSystemStrategy {
   final JsClosedWorld _closedWorld;
+  final GlobalLocalsMap _globalLocalsMap;
 
-  KernelTypeSystemStrategy(this._closedWorld);
+  KernelTypeSystemStrategy(this._closedWorld, this._globalLocalsMap);
 
   JElementEnvironment get _elementEnvironment =>
       _closedWorld.elementEnvironment;
@@ -1252,8 +1255,8 @@
   @override
   void forEachParameter(FunctionEntity function, void f(Local parameter)) {
     forEachOrderedParameterAsLocal(
-        _closedWorld.globalLocalsMap, _closedWorld.elementMap, function,
-        (Local parameter, {bool isElided}) {
+        _globalLocalsMap, _closedWorld.elementMap, function, (Local parameter,
+            {bool isElided}) {
       f(parameter);
     });
   }
@@ -1264,8 +1267,7 @@
       covariant JLocal parameter,
       TypeSystem types) {
     MemberEntity context = parameter.memberContext;
-    KernelToLocalsMap localsMap =
-        _closedWorld.globalLocalsMap.getLocalsMap(context);
+    KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(context);
     ir.FunctionNode functionNode =
         localsMap.getFunctionNodeForParameter(parameter);
     DartType type = localsMap.getLocalType(_closedWorld.elementMap, parameter);
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index 7ab25ce..f770d9b 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -13,6 +13,7 @@
 import '../elements/entities.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_model/elements.dart' show JClosureCallMethod;
+import '../js_model/locals.dart';
 import '../world.dart';
 import 'abstract_value_domain.dart';
 import 'inferrer_engine.dart';
@@ -53,11 +54,12 @@
   final JClosedWorld closedWorld;
 
   final Compiler _compiler;
+  final GlobalLocalsMap _globalLocalsMap;
   final InferredDataBuilder _inferredDataBuilder;
   Metrics /*?*/ _metrics;
 
-  TypeGraphInferrer(
-      this._compiler, this.closedWorld, this._inferredDataBuilder);
+  TypeGraphInferrer(this._compiler, this.closedWorld, this._globalLocalsMap,
+      this._inferredDataBuilder);
 
   String get name => 'Graph inferrer';
 
@@ -82,6 +84,7 @@
         _compiler.outputProvider,
         closedWorld,
         main,
+        _globalLocalsMap,
         _inferredDataBuilder);
   }
 
@@ -134,8 +137,7 @@
       if (member is JClosureCallMethod) {
         ClosureRepresentationInfo info =
             closedWorld.closureDataLookup.getScopeInfo(member);
-        info.forEachFreeVariable(
-            closedWorld.globalLocalsMap.getLocalsMap(member),
+        info.forEachFreeVariable(_globalLocalsMap.getLocalsMap(member),
             (Local from, FieldEntity to) {
           freeVariables.add(to);
         });
@@ -169,6 +171,7 @@
 
     GlobalTypeInferenceResults results = new GlobalTypeInferenceResultsImpl(
         closedWorld,
+        _globalLocalsMap,
         _inferredDataBuilder.close(closedWorld),
         memberResults,
         parameterResults,
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index 66d551c..2345d53 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -13,6 +13,8 @@
 import '../elements/entities.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_model/element_map.dart';
+import '../js_model/js_world.dart';
+import '../js_model/locals.dart';
 import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
 import '../serialization/serialization.dart';
 import '../universe/selector.dart' show Selector;
@@ -111,13 +113,15 @@
       DataSource source,
       JsToElementMap elementMap,
       JClosedWorld closedWorld,
+      GlobalLocalsMap globalLocalsMap,
       InferredData inferredData) {
     bool isTrivial = source.readBool();
     if (isTrivial) {
-      return new TrivialGlobalTypeInferenceResults(closedWorld);
+      return new TrivialGlobalTypeInferenceResults(
+          closedWorld, globalLocalsMap);
     }
     return new GlobalTypeInferenceResultsImpl.readFromDataSource(
-        source, elementMap, closedWorld, inferredData);
+        source, elementMap, closedWorld, globalLocalsMap, inferredData);
   }
 
   /// Serializes this [GlobalTypeInferenceResults] to [sink].
@@ -125,6 +129,8 @@
 
   JClosedWorld get closedWorld;
 
+  GlobalLocalsMap get globalLocalsMap;
+
   InferredData get inferredData;
 
   GlobalTypeInferenceMemberResult resultOfMember(MemberEntity member);
@@ -171,16 +177,20 @@
   Metrics get metrics => _metrics;
 
   /// Runs the global type-inference algorithm once.
-  GlobalTypeInferenceResults runGlobalTypeInference(FunctionEntity mainElement,
-      JClosedWorld closedWorld, InferredDataBuilder inferredDataBuilder) {
+  GlobalTypeInferenceResults runGlobalTypeInference(
+      FunctionEntity mainElement,
+      JClosedWorld closedWorld,
+      GlobalLocalsMap globalLocalsMap,
+      InferredDataBuilder inferredDataBuilder) {
     return measure(() {
       GlobalTypeInferenceResults results;
       if (compiler.disableTypeInference) {
-        results = new TrivialGlobalTypeInferenceResults(closedWorld);
+        results =
+            new TrivialGlobalTypeInferenceResults(closedWorld, globalLocalsMap);
         _metrics = Metrics.none();
       } else {
-        typesInferrerInternal ??= compiler.backendStrategy
-            .createTypesInferrer(closedWorld, inferredDataBuilder);
+        typesInferrerInternal ??= compiler.backendStrategy.createTypesInferrer(
+            closedWorld, globalLocalsMap, inferredDataBuilder);
         results = typesInferrerInternal.analyzeMain(mainElement);
         _metrics = typesInferrerInternal.metrics;
       }
@@ -201,6 +211,8 @@
   @override
   final JClosedWorld closedWorld;
   @override
+  final GlobalLocalsMap globalLocalsMap;
+  @override
   final InferredData inferredData;
   final GlobalTypeInferenceMemberResult _deadFieldResult;
   final GlobalTypeInferenceMemberResult _deadMethodResult;
@@ -214,6 +226,7 @@
 
   GlobalTypeInferenceResultsImpl(
       this.closedWorld,
+      this.globalLocalsMap,
       this.inferredData,
       this.memberResults,
       this.parameterResults,
@@ -230,7 +243,10 @@
       DataSource source,
       JsToElementMap elementMap,
       JClosedWorld closedWorld,
+      GlobalLocalsMap globalLocalsMap,
       InferredData inferredData) {
+    source.registerLocalLookup(new LocalLookupImpl(globalLocalsMap));
+
     source.begin(tag);
     Map<MemberEntity, GlobalTypeInferenceMemberResult> memberResults =
         source.readMemberMap((MemberEntity member) =>
@@ -250,6 +266,7 @@
     source.end(tag);
     return new GlobalTypeInferenceResultsImpl(
         closedWorld,
+        globalLocalsMap,
         inferredData,
         memberResults,
         parameterResults,
@@ -472,8 +489,10 @@
   final AbstractValue _trivialParameterResult;
   @override
   final InferredData inferredData = new TrivialInferredData();
+  @override
+  final GlobalLocalsMap globalLocalsMap;
 
-  TrivialGlobalTypeInferenceResults(this.closedWorld)
+  TrivialGlobalTypeInferenceResults(this.closedWorld, this.globalLocalsMap)
       : _trivialMemberResult = new TrivialGlobalTypeInferenceMemberResult(
             closedWorld.abstractValueDomain.dynamicType),
         _trivialParameterResult = closedWorld.abstractValueDomain.dynamicType;
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index 8f71fb8..cfdee0b 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -21,7 +21,6 @@
 import '../universe/selector.dart';
 import 'elements.dart';
 import 'js_world_builder.dart' show JsClosedWorldBuilder;
-import 'locals.dart';
 
 class ClosureDataImpl implements ClosureData {
   /// Tag used for identifying serialized [ClosureData] objects in a
@@ -40,8 +39,15 @@
   final Map<ir.LocalFunction, ClosureRepresentationInfo>
       _localClosureRepresentationMap;
 
-  ClosureDataImpl(this._elementMap, this._scopeMap, this._capturedScopesMap,
-      this._capturedScopeForSignatureMap, this._localClosureRepresentationMap);
+  final Map<MemberEntity, MemberEntity> _enclosingMembers;
+
+  ClosureDataImpl(
+      this._elementMap,
+      this._scopeMap,
+      this._capturedScopesMap,
+      this._capturedScopeForSignatureMap,
+      this._localClosureRepresentationMap,
+      this._enclosingMembers);
 
   /// Deserializes a [ClosureData] object from [source].
   factory ClosureDataImpl.readFromDataSource(
@@ -58,9 +64,16 @@
     Map<ir.LocalFunction, ClosureRepresentationInfo>
         localClosureRepresentationMap = source.readTreeNodeMap(
             () => new ClosureRepresentationInfo.readFromDataSource(source));
+    Map<MemberEntity, MemberEntity> enclosingMembers =
+        source.readMemberMap((member) => source.readMember());
     source.end(tag);
-    return new ClosureDataImpl(elementMap, scopeMap, capturedScopesMap,
-        capturedScopeForSignatureMap, localClosureRepresentationMap);
+    return new ClosureDataImpl(
+        elementMap,
+        scopeMap,
+        capturedScopesMap,
+        capturedScopeForSignatureMap,
+        localClosureRepresentationMap,
+        enclosingMembers);
   }
 
   /// Serializes this [ClosureData] to [sink].
@@ -80,6 +93,10 @@
         (ClosureRepresentationInfo info) {
       info.writeToDataSink(sink);
     });
+    sink.writeMemberMap(_enclosingMembers,
+        (MemberEntity member, MemberEntity value) {
+      sink.writeMember(value);
+    });
     sink.end(tag);
   }
 
@@ -132,6 +149,11 @@
         "Closures found for ${_localClosureRepresentationMap.keys}");
     return closure;
   }
+
+  @override
+  MemberEntity getEnclosingMember(MemberEntity member) {
+    return _enclosingMembers[member] ?? member;
+  }
 }
 
 /// Closure conversion code using our new Entity model. Closure conversion is
@@ -149,7 +171,6 @@
 
 class ClosureDataBuilder {
   final JsToElementMap _elementMap;
-  final GlobalLocalsMap _globalLocalsMap;
   final AnnotationsData _annotationsData;
 
   /// Map of the scoping information that corresponds to a particular entity.
@@ -162,8 +183,9 @@
   Map<ir.LocalFunction, ClosureRepresentationInfo>
       _localClosureRepresentationMap = {};
 
-  ClosureDataBuilder(
-      this._elementMap, this._globalLocalsMap, this._annotationsData);
+  Map<MemberEntity, MemberEntity> _enclosingMembers = {};
+
+  ClosureDataBuilder(this._elementMap, this._annotationsData);
 
   void _updateScopeBasedOnRtiNeed(KernelScopeInfo scope, ClosureRtiNeed rtiNeed,
       MemberEntity outermostEntity) {
@@ -360,8 +382,13 @@
         callMethods.add(closureClassInfo.callMethod);
       }
     });
-    return new ClosureDataImpl(_elementMap, _scopeMap, _capturedScopesMap,
-        _capturedScopeForSignatureMap, _localClosureRepresentationMap);
+    return new ClosureDataImpl(
+        _elementMap,
+        _scopeMap,
+        _capturedScopesMap,
+        _capturedScopeForSignatureMap,
+        _localClosureRepresentationMap,
+        _enclosingMembers);
   }
 
   /// Given what variables are captured at each point, construct closure classes
@@ -379,17 +406,15 @@
       ClosureRtiNeed rtiNeed,
       {bool createSignatureMethod}) {
     _updateScopeBasedOnRtiNeed(info, rtiNeed, member);
-    KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(member);
     JsClosureClassInfo closureClassInfo = closedWorldBuilder.buildClosureClass(
         member, node, member.library, boxedVariables, info,
         createSignatureMethod: createSignatureMethod);
 
     // We want the original declaration where that function is used to point
     // to the correct closure class.
-    _globalLocalsMap.setLocalsMap(closureClassInfo.callMethod, localsMap);
+    _enclosingMembers[closureClassInfo.callMethod] = member;
     if (closureClassInfo.signatureMethod != null) {
-      _globalLocalsMap.setLocalsMap(
-          closureClassInfo.signatureMethod, localsMap);
+      _enclosingMembers[closureClassInfo.signatureMethod] = member;
     }
     if (node.parent is ir.Member) {
       assert(_elementMap.getMember(node.parent) == member);
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index bb9ed2b..d432e33 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -169,12 +169,10 @@
         strategy.elementMap,
         closedWorld.liveMemberUsage,
         closedWorld.annotationsData);
-    GlobalLocalsMap _globalLocalsMap = new GlobalLocalsMap();
-    ClosureDataBuilder closureDataBuilder = new ClosureDataBuilder(
-        _elementMap, _globalLocalsMap, closedWorld.annotationsData);
+    ClosureDataBuilder closureDataBuilder =
+        new ClosureDataBuilder(_elementMap, closedWorld.annotationsData);
     JsClosedWorldBuilder closedWorldBuilder = new JsClosedWorldBuilder(
         _elementMap,
-        _globalLocalsMap,
         closureDataBuilder,
         _compiler.options,
         _compiler.abstractValueStrategy);
@@ -383,8 +381,11 @@
 
   @override
   TypesInferrer createTypesInferrer(
-      JClosedWorld closedWorld, InferredDataBuilder inferredDataBuilder) {
-    return new TypeGraphInferrer(_compiler, closedWorld, inferredDataBuilder);
+      JClosedWorld closedWorld,
+      GlobalLocalsMap globalLocalsMap,
+      InferredDataBuilder inferredDataBuilder) {
+    return new TypeGraphInferrer(
+        _compiler, closedWorld, globalLocalsMap, inferredDataBuilder);
   }
 
   @override
diff --git a/pkg/compiler/lib/src/js_model/js_world.dart b/pkg/compiler/lib/src/js_model/js_world.dart
index 2b5263a..868ef6f 100644
--- a/pkg/compiler/lib/src/js_model/js_world.dart
+++ b/pkg/compiler/lib/src/js_model/js_world.dart
@@ -90,8 +90,6 @@
   @override
   final AnnotationsData annotationsData;
   @override
-  final GlobalLocalsMap globalLocalsMap;
-  @override
   final ClosureData closureDataLookup;
   @override
   final OutputUnitData outputUnitData;
@@ -118,7 +116,6 @@
       this.classHierarchy,
       AbstractValueStrategy abstractValueStrategy,
       this.annotationsData,
-      this.globalLocalsMap,
       this.closureDataLookup,
       this.outputUnitData,
       this.memberAccess) {
@@ -138,9 +135,6 @@
     JsKernelToElementMap elementMap =
         new JsKernelToElementMap.readFromDataSource(
             options, reporter, environment, component, source);
-    GlobalLocalsMap globalLocalsMap =
-        new GlobalLocalsMap.readFromDataSource(source);
-    source.registerLocalLookup(new LocalLookupImpl(globalLocalsMap));
     ClassHierarchy classHierarchy = new ClassHierarchy.readFromDataSource(
         source, elementMap.commonElements);
     NativeData nativeData = new NativeData.readFromDataSource(
@@ -203,7 +197,6 @@
         classHierarchy,
         abstractValueStrategy,
         annotationsData,
-        globalLocalsMap,
         closureData,
         outputUnitData,
         memberAccess);
@@ -213,8 +206,6 @@
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
     elementMap.writeToDataSink(sink);
-    globalLocalsMap.writeToDataSink(sink);
-
     classHierarchy.writeToDataSink(sink);
     nativeData.writeToDataSink(sink);
     interceptorData.writeToDataSink(sink);
diff --git a/pkg/compiler/lib/src/js_model/js_world_builder.dart b/pkg/compiler/lib/src/js_model/js_world_builder.dart
index b1a80ce..f2fdbe6 100644
--- a/pkg/compiler/lib/src/js_model/js_world_builder.dart
+++ b/pkg/compiler/lib/src/js_model/js_world_builder.dart
@@ -36,20 +36,18 @@
 import 'elements.dart';
 import 'element_map_impl.dart';
 import 'js_world.dart';
-import 'locals.dart';
 
 class JsClosedWorldBuilder {
   final JsKernelToElementMap _elementMap;
   final Map<ClassEntity, ClassHierarchyNode> _classHierarchyNodes =
       new ClassHierarchyNodesMap();
   final Map<ClassEntity, ClassSet> _classSets = <ClassEntity, ClassSet>{};
-  final GlobalLocalsMap _globalLocalsMap;
   final ClosureDataBuilder _closureDataBuilder;
   final CompilerOptions _options;
   final AbstractValueStrategy _abstractValueStrategy;
 
-  JsClosedWorldBuilder(this._elementMap, this._globalLocalsMap,
-      this._closureDataBuilder, this._options, this._abstractValueStrategy);
+  JsClosedWorldBuilder(this._elementMap, this._closureDataBuilder,
+      this._options, this._abstractValueStrategy);
 
   ElementEnvironment get _elementEnvironment => _elementMap.elementEnvironment;
   CommonElements get _commonElements => _elementMap.commonElements;
@@ -235,7 +233,6 @@
             _elementMap.commonElements, _classHierarchyNodes, _classSets),
         _abstractValueStrategy,
         annotationsData,
-        _globalLocalsMap,
         closureData,
         outputUnitData,
         memberAccess);
diff --git a/pkg/compiler/lib/src/js_model/locals.dart b/pkg/compiler/lib/src/js_model/locals.dart
index 0e458ed..1d5cd00 100644
--- a/pkg/compiler/lib/src/js_model/locals.dart
+++ b/pkg/compiler/lib/src/js_model/locals.dart
@@ -22,14 +22,23 @@
   /// debugging data stream.
   static const String tag = 'global-locals-map';
 
+  /// Lookup up the key used to store a LocalsMap for a member.
+  ///
+  /// While procedures are keyed by their own entity, closures use the
+  /// enclosing member as a key. This ensures that the member and all
+  /// nested closures share the same local map.
+  MemberEntity Function(MemberEntity) _localMapKeyLookup;
+
   final Map<MemberEntity, KernelToLocalsMap> _localsMaps;
 
-  GlobalLocalsMap() : _localsMaps = {};
+  GlobalLocalsMap(this._localMapKeyLookup) : _localsMaps = {};
 
-  GlobalLocalsMap.internal(this._localsMaps);
+  GlobalLocalsMap.internal(this._localMapKeyLookup, this._localsMaps);
 
   /// Deserializes a [GlobalLocalsMap] object from [source].
-  factory GlobalLocalsMap.readFromDataSource(DataSource source) {
+  factory GlobalLocalsMap.readFromDataSource(
+      MemberEntity Function(MemberEntity) localMapKeyLookup,
+      DataSource source) {
     source.begin(tag);
     Map<MemberEntity, KernelToLocalsMap> _localsMaps = {};
     int mapCount = source.readInt();
@@ -42,7 +51,7 @@
       }
     }
     source.end(tag);
-    return new GlobalLocalsMap.internal(_localsMaps);
+    return new GlobalLocalsMap.internal(localMapKeyLookup, _localsMaps);
   }
 
   /// Serializes this [GlobalLocalsMap] to [sink].
@@ -68,26 +77,19 @@
 
   /// Returns the [KernelToLocalsMap] for [member].
   KernelToLocalsMap getLocalsMap(MemberEntity member) {
-    // If element is a ConstructorBodyEntity, its localsMap is the same as for
+    // If [member] is a closure call method or closure signature method, its
+    // localsMap is the same as for the enclosing member since the locals are
+    // derived from the same kernel AST.
+    MemberEntity key = _localMapKeyLookup(member);
+    // If [member] is a ConstructorBodyEntity, its localsMap is the same as for
     // ConstructorEntity, because both of these entities came from the same
     // constructor node. The entities are two separate parts because JS does not
     // have the concept of an initializer list, so the constructor (initializer
     // list) and the constructor body are implemented as two separate
     // constructor steps.
-    MemberEntity entity = member;
-    if (entity is ConstructorBodyEntity) member = entity.constructor;
-    return _localsMaps.putIfAbsent(
-        member, () => new KernelToLocalsMapImpl(member));
-  }
-
-  /// Associates [localsMap] with [member].
-  ///
-  /// Use this for sharing maps between members that share IR nodes.
-  void setLocalsMap(MemberEntity member, KernelToLocalsMap localsMap) {
-    assert(member != null, "No member provided.");
-    assert(!_localsMaps.containsKey(member),
-        "Locals map already created for $member.");
-    _localsMaps[member] = localsMap;
+    MemberEntity entity = key;
+    if (entity is ConstructorBodyEntity) key = entity.constructor;
+    return _localsMaps.putIfAbsent(key, () => new KernelToLocalsMapImpl(key));
   }
 }
 
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 707def0..3ac7e86 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -19,6 +19,7 @@
 import '../js_backend/backend.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_model/js_world.dart';
+import '../js_model/locals.dart';
 import '../options.dart';
 import '../util/sink_adapter.dart';
 import '../world.dart';
@@ -27,8 +28,10 @@
 void serializeGlobalTypeInferenceResultsToSink(
     GlobalTypeInferenceResults results, DataSink sink) {
   JsClosedWorld closedWorld = results.closedWorld;
+  GlobalLocalsMap globalLocalsMap = results.globalLocalsMap;
   InferredData inferredData = results.inferredData;
   closedWorld.writeToDataSink(sink);
+  globalLocalsMap.writeToDataSink(sink);
   inferredData.writeToDataSink(sink);
   results.writeToDataSink(sink, closedWorld.elementMap);
   sink.close();
@@ -42,10 +45,16 @@
     ir.Component component,
     JsClosedWorld newClosedWorld,
     DataSource source) {
+  GlobalLocalsMap newGlobalLocalsMap = GlobalLocalsMap.readFromDataSource(
+      newClosedWorld.closureDataLookup.getEnclosingMember, source);
   InferredData newInferredData =
       InferredData.readFromDataSource(source, newClosedWorld);
   return GlobalTypeInferenceResults.readFromDataSource(
-      source, newClosedWorld.elementMap, newClosedWorld, newInferredData);
+      source,
+      newClosedWorld.elementMap,
+      newClosedWorld,
+      newGlobalLocalsMap,
+      newInferredData);
 }
 
 GlobalTypeInferenceResults deserializeGlobalTypeInferenceResultsFromSource(
@@ -57,10 +66,16 @@
     DataSource source) {
   JsClosedWorld newClosedWorld = new JsClosedWorld.readFromDataSource(
       options, reporter, environment, abstractValueStrategy, component, source);
+  GlobalLocalsMap newGlobalLocalsMap = GlobalLocalsMap.readFromDataSource(
+      newClosedWorld.closureDataLookup.getEnclosingMember, source);
   InferredData newInferredData =
       new InferredData.readFromDataSource(source, newClosedWorld);
   return new GlobalTypeInferenceResults.readFromDataSource(
-      source, newClosedWorld.elementMap, newClosedWorld, newInferredData);
+      source,
+      newClosedWorld.elementMap,
+      newClosedWorld,
+      newGlobalLocalsMap,
+      newInferredData);
 }
 
 void serializeClosedWorldToSink(JsClosedWorld closedWorld, DataSink sink) {
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index f3b714a..4bd37c8 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -34,7 +34,7 @@
 import '../js_backend/native_data.dart';
 import '../js_backend/runtime_types_resolution.dart';
 import '../js_emitter/code_emitter_task.dart' show ModularEmitter;
-import '../js_model/locals.dart' show JumpVisitor;
+import '../js_model/locals.dart' show GlobalLocalsMap, JumpVisitor;
 import '../js_model/elements.dart' show JGeneratorBody;
 import '../js_model/element_map.dart';
 import '../js_model/js_strategy.dart';
@@ -212,6 +212,9 @@
 
   RuntimeTypesNeed get _rtiNeed => closedWorld.rtiNeed;
 
+  GlobalLocalsMap get _globalLocalsMap =>
+      globalInferenceResults.globalLocalsMap;
+
   InferredData get _inferredData => globalInferenceResults.inferredData;
 
   DartTypes get dartTypes => closedWorld.dartTypes;
@@ -408,7 +411,7 @@
         _currentFrame,
         member,
         asyncMarker,
-        closedWorld.globalLocalsMap.getLocalsMap(member),
+        _globalLocalsMap.getLocalsMap(member),
         {},
         new KernelToTypeInferenceMapImpl(member, globalInferenceResults),
         _currentFrame != null
@@ -6030,8 +6033,7 @@
 
     ir.Member memberContextNode = _elementMap.getMemberContextNode(function);
     bool hasBox = false;
-    KernelToLocalsMap localsMap =
-        closedWorld.globalLocalsMap.getLocalsMap(function);
+    KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(function);
     forEachOrderedParameter(_elementMap, function,
         (ir.VariableDeclaration variable, {bool isElided}) {
       Local local = localsMap.getLocalVariable(variable);
@@ -6198,8 +6200,7 @@
       _checkTypeVariableBounds(function);
     }
 
-    KernelToLocalsMap localsMap =
-        closedWorld.globalLocalsMap.getLocalsMap(function);
+    KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(function);
     forEachOrderedParameter(_elementMap, function,
         (ir.VariableDeclaration variable, {bool isElided}) {
       Local parameter = localsMap.getLocalVariable(variable);
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index e1edc64..ea01915 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -14,7 +14,7 @@
 import '../js_backend/native_data.dart';
 import '../js_backend/interceptor_data.dart';
 import '../js_model/closure.dart' show JRecordField, JClosureField;
-import '../js_model/locals.dart' show JLocal;
+import '../js_model/locals.dart' show GlobalLocalsMap, JLocal;
 import '../world.dart' show JClosedWorld;
 
 import 'builder_kernel.dart';
@@ -79,6 +79,9 @@
   GlobalTypeInferenceResults get _globalInferenceResults =>
       builder.globalInferenceResults;
 
+  GlobalLocalsMap get _globalLocalsMap =>
+      _globalInferenceResults.globalLocalsMap;
+
   /// Substituted type variables occurring in [type] into the context of
   /// [contextClass].
   DartType substInContext(DartType type) {
@@ -123,7 +126,7 @@
     _scopeInfoMember = member;
     if (member != null) {
       _scopeInfo = _closedWorld.closureDataLookup.getScopeInfo(member);
-      _localsMap = _closedWorld.globalLocalsMap.getLocalsMap(member);
+      _localsMap = _globalLocalsMap.getLocalsMap(member);
     } else {
       _scopeInfo = null;
       _localsMap = null;
diff --git a/pkg/compiler/lib/src/world.dart b/pkg/compiler/lib/src/world.dart
index fc7f564..68effb3 100644
--- a/pkg/compiler/lib/src/world.dart
+++ b/pkg/compiler/lib/src/world.dart
@@ -26,7 +26,6 @@
 import 'js_backend/native_data.dart' show NativeData;
 import 'js_backend/no_such_method_registry.dart' show NoSuchMethodData;
 import 'js_backend/runtime_types_resolution.dart' show RuntimeTypesNeed;
-import 'js_model/locals.dart';
 import 'js_emitter/sorter.dart';
 import 'universe/class_hierarchy.dart';
 import 'universe/member_usage.dart';
@@ -78,7 +77,6 @@
 
   AnnotationsData get annotationsData;
 
-  GlobalLocalsMap get globalLocalsMap;
   ClosureData get closureDataLookup;
 
   OutputUnitData get outputUnitData;
diff --git a/pkg/compiler/test/closure/closure_test.dart b/pkg/compiler/test/closure/closure_test.dart
index f1ed2bd..231025d 100644
--- a/pkg/compiler/test/closure/closure_test.dart
+++ b/pkg/compiler/test/closure/closure_test.dart
@@ -38,7 +38,8 @@
       {bool verbose: false}) {
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     JsToElementMap elementMap = closedWorld.elementMap;
-    GlobalLocalsMap localsMap = closedWorld.globalLocalsMap;
+    GlobalLocalsMap localsMap =
+        compiler.globalInference.resultsForTesting.globalLocalsMap;
     ClosureData closureDataLookup = closedWorld.closureDataLookup;
     MemberDefinition definition = elementMap.getMemberDefinition(member);
     assert(
diff --git a/pkg/compiler/test/codegen/expect_annotations_test.dart b/pkg/compiler/test/codegen/expect_annotations_test.dart
index 9ed3517..a3df294 100644
--- a/pkg/compiler/test/codegen/expect_annotations_test.dart
+++ b/pkg/compiler/test/codegen/expect_annotations_test.dart
@@ -55,7 +55,7 @@
       AbstractValue expectedReturnType,
       GlobalTypeInferenceResults results) {
     closedWorld.elementEnvironment.forEachParameterAsLocal(
-        closedWorld.globalLocalsMap, function, (Local parameter) {
+        results.globalLocalsMap, function, (Local parameter) {
       AbstractValue type = results.resultOfParameter(parameter);
       Expect.equals(
           expectedParameterType, simplify(type, commonMasks), "$parameter");
diff --git a/pkg/compiler/test/codegen/type_inference8_test.dart b/pkg/compiler/test/codegen/type_inference8_test.dart
index 382ef35..252f2e4 100644
--- a/pkg/compiler/test/codegen/type_inference8_test.dart
+++ b/pkg/compiler/test/codegen/type_inference8_test.dart
@@ -62,7 +62,7 @@
   // the argument to 'bar' is always false
   MemberEntity bar = elementEnvironment.lookupLibraryMember(
       elementEnvironment.mainLibrary, 'bar');
-  elementEnvironment.forEachParameterAsLocal(closedWorld.globalLocalsMap, bar,
+  elementEnvironment.forEachParameterAsLocal(results.globalLocalsMap, bar,
       (barArg) {
     AbstractValue barArgMask = results.resultOfParameter(barArg);
     Expect.equals(falseType, barArgMask);
@@ -110,7 +110,7 @@
   Expect.identical(commonMasks.boolType, mask);
   MemberEntity bar = elementEnvironment.lookupLibraryMember(
       elementEnvironment.mainLibrary, 'bar');
-  elementEnvironment.forEachParameterAsLocal(closedWorld.globalLocalsMap, bar,
+  elementEnvironment.forEachParameterAsLocal(results.globalLocalsMap, bar,
       (barArg) {
     AbstractValue barArgMask = results.resultOfParameter(barArg);
     // The argument to bar should have the same type as the return type of foo
diff --git a/pkg/compiler/test/inference/inference_test_helper.dart b/pkg/compiler/test/inference/inference_test_helper.dart
index fa5feb0..d049559 100644
--- a/pkg/compiler/test/inference/inference_test_helper.dart
+++ b/pkg/compiler/test/inference/inference_test_helper.dart
@@ -52,7 +52,9 @@
       {bool verbose: false}) {
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     JsToElementMap elementMap = closedWorld.elementMap;
-    GlobalLocalsMap localsMap = closedWorld.globalLocalsMap;
+    GlobalTypeInferenceResults results =
+        compiler.globalInference.resultsForTesting;
+    GlobalLocalsMap localsMap = results.globalLocalsMap;
     MemberDefinition definition = elementMap.getMemberDefinition(member);
     new TypeMaskIrComputer(
             compiler.reporter,
@@ -60,7 +62,7 @@
             elementMap,
             member,
             localsMap.getLocalsMap(member),
-            compiler.globalInference.resultsForTesting,
+            results,
             closedWorld.closureDataLookup)
         .run(definition.node);
   }
diff --git a/pkg/compiler/test/inference/load_deferred_library_test.dart b/pkg/compiler/test/inference/load_deferred_library_test.dart
index aad3724..8c6e15e 100644
--- a/pkg/compiler/test/inference/load_deferred_library_test.dart
+++ b/pkg/compiler/test/inference/load_deferred_library_test.dart
@@ -51,8 +51,9 @@
       helperLibrary, 'loadDeferredLibrary');
   TypeMask typeMask;
 
-  KernelToLocalsMap localsMap =
-      closedWorld.globalLocalsMap.getLocalsMap(loadDeferredLibrary);
+  KernelToLocalsMap localsMap = compiler
+      .globalInference.resultsForTesting.globalLocalsMap
+      .getLocalsMap(loadDeferredLibrary);
   MemberDefinition definition =
       closedWorld.elementMap.getMemberDefinition(loadDeferredLibrary);
   ir.Procedure procedure = definition.node;
diff --git a/pkg/compiler/test/jumps/jump_test.dart b/pkg/compiler/test/jumps/jump_test.dart
index 35b67d1..b848bd7 100644
--- a/pkg/compiler/test/jumps/jump_test.dart
+++ b/pkg/compiler/test/jumps/jump_test.dart
@@ -39,7 +39,8 @@
       {bool verbose: false}) {
     JsClosedWorld closedWorld = compiler.backendClosedWorldForTesting;
     JsToElementMap elementMap = closedWorld.elementMap;
-    GlobalLocalsMap localsMap = closedWorld.globalLocalsMap;
+    GlobalLocalsMap localsMap =
+        compiler.globalInference.resultsForTesting.globalLocalsMap;
     MemberDefinition definition = elementMap.getMemberDefinition(member);
     new JumpsIrChecker(
             compiler.reporter, actualMap, localsMap.getLocalsMap(member))