[dart2js] Support serialization of tree nodes in a known member context

* allows for a more compact encoding since every serialized
  ir.TreeNode doesn't have to be prefixed by a references to its
  surrounding member
* faster encoding since the surround member doesn't have to be found
  for each serialized tree node
* better assertion message when trying to serialize an orphaned tree node

Change-Id: I1a5411d134a8c36c00b6f4164617bc0dce631009
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106088
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/deferred_load.dart b/pkg/compiler/lib/src/deferred_load.dart
index 118d4ed..437ff2a 100644
--- a/pkg/compiler/lib/src/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load.dart
@@ -1336,7 +1336,8 @@
     Map<ClassEntity, OutputUnit> classToUnit = source.readClassMap(() {
       return outputUnits[source.readInt()];
     });
-    Map<MemberEntity, OutputUnit> memberToUnit = source.readMemberMap(() {
+    Map<MemberEntity, OutputUnit> memberToUnit =
+        source.readMemberMap((MemberEntity member) {
       return outputUnits[source.readInt()];
     });
     Map<ConstantValue, OutputUnit> constantToUnit = source.readConstantMap(() {
@@ -1387,7 +1388,8 @@
     sink.writeClassMap(_classToUnit, (OutputUnit outputUnit) {
       sink.writeInt(outputUnitIndices[outputUnit]);
     });
-    sink.writeMemberMap(_memberToUnit, (OutputUnit outputUnit) {
+    sink.writeMemberMap(_memberToUnit,
+        (MemberEntity member, OutputUnit outputUnit) {
       sink.writeInt(outputUnitIndices[outputUnit]);
     });
     sink.writeConstantMap(_constantToUnit, (OutputUnit outputUnit) {
diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
index 6266882..bf214c1 100644
--- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
+++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart
@@ -1433,49 +1433,59 @@
 
   /// Deserializes a [GlobalTypeInferenceElementData] object from [source].
   factory KernelGlobalTypeInferenceElementData.readFromDataSource(
-      DataSource source, AbstractValueDomain abstractValueDomain) {
-    source.begin(tag);
-    Map<ir.TreeNode, AbstractValue> sendMap = source.readTreeNodeMap(
-        () => abstractValueDomain.readAbstractValueFromDataSource(source),
-        emptyAsNull: true);
-    Map<ir.ForInStatement, AbstractValue> iteratorMap = source.readTreeNodeMap(
-        () => abstractValueDomain.readAbstractValueFromDataSource(source),
-        emptyAsNull: true);
-    Map<ir.ForInStatement, AbstractValue> currentMap = source.readTreeNodeMap(
-        () => abstractValueDomain.readAbstractValueFromDataSource(source),
-        emptyAsNull: true);
-    Map<ir.ForInStatement, AbstractValue> moveNextMap = source.readTreeNodeMap(
-        () => abstractValueDomain.readAbstractValueFromDataSource(source),
-        emptyAsNull: true);
-    source.end(tag);
-    return new KernelGlobalTypeInferenceElementData.internal(
-        sendMap, iteratorMap, currentMap, moveNextMap);
+      DataSource source,
+      ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
+    return source.inMemberContext(context, () {
+      source.begin(tag);
+      Map<ir.TreeNode, AbstractValue> sendMap = source.readTreeNodeMapInContext(
+          () => abstractValueDomain.readAbstractValueFromDataSource(source),
+          emptyAsNull: true);
+      Map<ir.ForInStatement, AbstractValue> iteratorMap =
+          source.readTreeNodeMapInContext(
+              () => abstractValueDomain.readAbstractValueFromDataSource(source),
+              emptyAsNull: true);
+      Map<ir.ForInStatement, AbstractValue> currentMap =
+          source.readTreeNodeMapInContext(
+              () => abstractValueDomain.readAbstractValueFromDataSource(source),
+              emptyAsNull: true);
+      Map<ir.ForInStatement, AbstractValue> moveNextMap =
+          source.readTreeNodeMapInContext(
+              () => abstractValueDomain.readAbstractValueFromDataSource(source),
+              emptyAsNull: true);
+      source.end(tag);
+      return new KernelGlobalTypeInferenceElementData.internal(
+          sendMap, iteratorMap, currentMap, moveNextMap);
+    });
   }
 
   @override
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) {
-    sink.begin(tag);
-    sink.writeTreeNodeMap(
-        _sendMap,
-        (AbstractValue value) =>
-            abstractValueDomain.writeAbstractValueToDataSink(sink, value),
-        allowNull: true);
-    sink.writeTreeNodeMap(
-        _iteratorMap,
-        (AbstractValue value) =>
-            abstractValueDomain.writeAbstractValueToDataSink(sink, value),
-        allowNull: true);
-    sink.writeTreeNodeMap(
-        _currentMap,
-        (AbstractValue value) =>
-            abstractValueDomain.writeAbstractValueToDataSink(sink, value),
-        allowNull: true);
-    sink.writeTreeNodeMap(
-        _moveNextMap,
-        (AbstractValue value) =>
-            abstractValueDomain.writeAbstractValueToDataSink(sink, value),
-        allowNull: true);
-    sink.end(tag);
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
+    sink.inMemberContext(context, () {
+      sink.begin(tag);
+      sink.writeTreeNodeMapInContext(
+          _sendMap,
+          (AbstractValue value) =>
+              abstractValueDomain.writeAbstractValueToDataSink(sink, value),
+          allowNull: true);
+      sink.writeTreeNodeMapInContext(
+          _iteratorMap,
+          (AbstractValue value) =>
+              abstractValueDomain.writeAbstractValueToDataSink(sink, value),
+          allowNull: true);
+      sink.writeTreeNodeMapInContext(
+          _currentMap,
+          (AbstractValue value) =>
+              abstractValueDomain.writeAbstractValueToDataSink(sink, value),
+          allowNull: true);
+      sink.writeTreeNodeMapInContext(
+          _moveNextMap,
+          (AbstractValue value) =>
+              abstractValueDomain.writeAbstractValueToDataSink(sink, value),
+          allowNull: true);
+      sink.end(tag);
+    });
   }
 
   @override
diff --git a/pkg/compiler/lib/src/inferrer/types.dart b/pkg/compiler/lib/src/inferrer/types.dart
index f68b519..374f01a 100644
--- a/pkg/compiler/lib/src/inferrer/types.dart
+++ b/pkg/compiler/lib/src/inferrer/types.dart
@@ -11,6 +11,7 @@
 import '../compiler.dart' show Compiler;
 import '../elements/entities.dart';
 import '../js_backend/inferred_data.dart';
+import '../js_model/element_map.dart';
 import '../inferrer/type_graph_inferrer.dart' show TypeGraphInferrer;
 import '../serialization/serialization.dart';
 import '../universe/selector.dart' show Selector;
@@ -30,12 +31,13 @@
 /// based queries (the runtime value could be anything).
 abstract class GlobalTypeInferenceMemberResult {
   /// Deserializes a [GlobalTypeInferenceMemberResult] object from [source].
-  factory GlobalTypeInferenceMemberResult.readFromDataSource(
-          DataSource source, AbstractValueDomain abstractValueDomain) =
+  factory GlobalTypeInferenceMemberResult.readFromDataSource(DataSource source,
+          ir.Member context, AbstractValueDomain abstractValueDomain) =
       GlobalTypeInferenceMemberResultImpl.readFromDataSource;
 
   /// Serializes this [GlobalTypeInferenceMemberResult] to [sink].
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain);
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain);
 
   /// The inferred type when this result belongs to a field, null otherwise.
   AbstractValue get type;
@@ -72,12 +74,13 @@
 /// a single element.
 abstract class GlobalTypeInferenceElementData {
   /// Deserializes a [GlobalTypeInferenceElementData] object from [source].
-  factory GlobalTypeInferenceElementData.readFromDataSource(
-          DataSource source, AbstractValueDomain abstractValueDomain) =
+  factory GlobalTypeInferenceElementData.readFromDataSource(DataSource source,
+          ir.Member context, AbstractValueDomain abstractValueDomain) =
       KernelGlobalTypeInferenceElementData.readFromDataSource;
 
   /// Serializes this [GlobalTypeInferenceElementData] to [sink].
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain);
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain);
 
   /// Compresses the inner representation by removing [AbstractValue] mappings
   /// to `null`. Returns the data object itself or `null` if the data object
@@ -109,17 +112,20 @@
 abstract class GlobalTypeInferenceResults {
   /// Deserializes a [GlobalTypeInferenceResults] object from [source].
   factory GlobalTypeInferenceResults.readFromDataSource(
-      DataSource source, JClosedWorld closedWorld, InferredData inferredData) {
+      DataSource source,
+      JsToElementMap elementMap,
+      JClosedWorld closedWorld,
+      InferredData inferredData) {
     bool isTrivial = source.readBool();
     if (isTrivial) {
       return new TrivialGlobalTypeInferenceResults(closedWorld);
     }
     return new GlobalTypeInferenceResultsImpl.readFromDataSource(
-        source, closedWorld, inferredData);
+        source, elementMap, closedWorld, inferredData);
   }
 
   /// Serializes this [GlobalTypeInferenceResults] to [sink].
-  void writeToDataSink(DataSink sink);
+  void writeToDataSink(DataSink sink, JsToElementMap elementMap);
 
   JClosedWorld get closedWorld;
 
@@ -217,12 +223,17 @@
         _trivialParameterResult = closedWorld.abstractValueDomain.dynamicType;
 
   factory GlobalTypeInferenceResultsImpl.readFromDataSource(
-      DataSource source, JClosedWorld closedWorld, InferredData inferredData) {
+      DataSource source,
+      JsToElementMap elementMap,
+      JClosedWorld closedWorld,
+      InferredData inferredData) {
     source.begin(tag);
     Map<MemberEntity, GlobalTypeInferenceMemberResult> memberResults =
-        source.readMemberMap(() =>
+        source.readMemberMap((MemberEntity member) =>
             new GlobalTypeInferenceMemberResult.readFromDataSource(
-                source, closedWorld.abstractValueDomain));
+                source,
+                elementMap.getMemberContextNode(member),
+                closedWorld.abstractValueDomain));
     Map<Local, AbstractValue> parameterResults = source.readLocalMap(() =>
         closedWorld.abstractValueDomain
             .readAbstractValueFromDataSource(source));
@@ -244,13 +255,16 @@
   }
 
   @override
-  void writeToDataSink(DataSink sink) {
+  void writeToDataSink(DataSink sink, JsToElementMap elementMap) {
     sink.writeBool(false); // Is _not_ trivial.
     sink.begin(tag);
     sink.writeMemberMap(
         memberResults,
-        (GlobalTypeInferenceMemberResult result) =>
-            result.writeToDataSink(sink, closedWorld.abstractValueDomain));
+        (MemberEntity member, GlobalTypeInferenceMemberResult result) =>
+            result.writeToDataSink(
+                sink,
+                elementMap.getMemberContextNode(member),
+                closedWorld.abstractValueDomain));
     sink.writeLocalMap(
         parameterResults,
         (AbstractValue value) => closedWorld.abstractValueDomain
@@ -395,11 +409,13 @@
       {this.throwsAlways, this.isCalledOnce});
 
   factory GlobalTypeInferenceMemberResultImpl.readFromDataSource(
-      DataSource source, AbstractValueDomain abstractValueDomain) {
+      DataSource source,
+      ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
     source.begin(tag);
     GlobalTypeInferenceElementData data = source.readValueOrNull(() {
       return new GlobalTypeInferenceElementData.readFromDataSource(
-          source, abstractValueDomain);
+          source, context, abstractValueDomain);
     });
     AbstractValue returnType =
         abstractValueDomain.readAbstractValueFromDataSource(source);
@@ -413,10 +429,11 @@
   }
 
   @override
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) {
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
     sink.begin(tag);
     sink.writeValueOrNull(_data, (GlobalTypeInferenceElementData data) {
-      data.writeToDataSink(sink, abstractValueDomain);
+      data.writeToDataSink(sink, context, abstractValueDomain);
     });
     abstractValueDomain.writeAbstractValueToDataSink(sink, returnType);
     abstractValueDomain.writeAbstractValueToDataSink(sink, type);
@@ -453,7 +470,7 @@
         _trivialParameterResult = closedWorld.abstractValueDomain.dynamicType;
 
   @override
-  void writeToDataSink(DataSink sink) {
+  void writeToDataSink(DataSink sink, JsToElementMap elementMap) {
     sink.writeBool(true); // Is trivial.
   }
 
@@ -516,7 +533,8 @@
   bool get isCalledOnce => false;
 
   @override
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) {
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
     throw new UnsupportedError(
         "TrivialGlobalTypeInferenceMemberResult.writeToDataSink");
   }
@@ -559,7 +577,8 @@
   bool get isCalledOnce => false;
 
   @override
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) {
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
     throw new UnsupportedError(
         "DeadFieldGlobalTypeInferenceResult.writeToDataSink");
   }
@@ -602,7 +621,8 @@
   bool get isCalledOnce => false;
 
   @override
-  void writeToDataSink(DataSink sink, AbstractValueDomain abstractValueDomain) {
+  void writeToDataSink(DataSink sink, ir.Member context,
+      AbstractValueDomain abstractValueDomain) {
     throw new UnsupportedError(
         "DeadFieldGlobalTypeInferenceResult.writeToDataSink");
   }
diff --git a/pkg/compiler/lib/src/ir/impact_data.dart b/pkg/compiler/lib/src/ir/impact_data.dart
index 79a7960..7276ae4 100644
--- a/pkg/compiler/lib/src/ir/impact_data.dart
+++ b/pkg/compiler/lib/src/ir/impact_data.dart
@@ -574,7 +574,7 @@
         emptyAsNull: true);
     _fieldInitializers = source.readMemberNodes(emptyAsNull: true);
     _fieldConstantInitializers =
-        source.readMemberMap(() => source.readTreeNodes(), emptyAsNull: true);
+        source.readMemberNodeMap(source.readTreeNodes, emptyAsNull: true);
     _typeLiterals = source.readList(
         () => new _TypeLiteral.fromDataSource(source),
         emptyAsNull: true);
diff --git a/pkg/compiler/lib/src/ir/static_type_cache.dart b/pkg/compiler/lib/src/ir/static_type_cache.dart
index 64cf6b8..2e25d65 100644
--- a/pkg/compiler/lib/src/ir/static_type_cache.dart
+++ b/pkg/compiler/lib/src/ir/static_type_cache.dart
@@ -19,22 +19,28 @@
   const StaticTypeCache(
       [this._expressionTypes = const {}, this._forInIteratorTypes]);
 
-  factory StaticTypeCache.readFromDataSource(DataSource source) {
-    source.begin(tag);
-    Map<ir.Expression, ir.DartType> expressionTypes =
-        source.readTreeNodeMap(source.readDartTypeNode);
-    Map<ir.ForInStatement, ir.DartType> forInIteratorTypes =
-        source.readTreeNodeMap(source.readDartTypeNode, emptyAsNull: true);
-    source.end(tag);
-    return new StaticTypeCache(expressionTypes, forInIteratorTypes);
+  factory StaticTypeCache.readFromDataSource(
+      DataSource source, ir.Member context) {
+    return source.inMemberContext(context, () {
+      source.begin(tag);
+      Map<ir.Expression, ir.DartType> expressionTypes =
+          source.readTreeNodeMapInContext(source.readDartTypeNode);
+      Map<ir.ForInStatement, ir.DartType> forInIteratorTypes = source
+          .readTreeNodeMapInContext(source.readDartTypeNode, emptyAsNull: true);
+      source.end(tag);
+      return new StaticTypeCache(expressionTypes, forInIteratorTypes);
+    });
   }
 
-  void writeToDataSink(DataSink sink) {
-    sink.begin(tag);
-    sink.writeTreeNodeMap(_expressionTypes, sink.writeDartTypeNode);
-    sink.writeTreeNodeMap(_forInIteratorTypes, sink.writeDartTypeNode,
-        allowNull: true);
-    sink.end(tag);
+  void writeToDataSink(DataSink sink, ir.Member context) {
+    sink.inMemberContext(context, () {
+      sink.begin(tag);
+      sink.writeTreeNodeMapInContext(_expressionTypes, sink.writeDartTypeNode);
+      sink.writeTreeNodeMapInContext(
+          _forInIteratorTypes, sink.writeDartTypeNode,
+          allowNull: true);
+      sink.end(tag);
+    });
   }
 
   ir.DartType operator [](ir.Expression node) => _expressionTypes[node];
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index 1464c0b..97678ca 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -268,7 +268,8 @@
   factory AnnotationsDataImpl.readFromDataSource(DataSource source) {
     source.begin(tag);
     Map<MemberEntity, EnumSet<PragmaAnnotation>> pragmaAnnotations =
-        source.readMemberMap(() => new EnumSet.fromValue(source.readInt()));
+        source.readMemberMap(
+            (MemberEntity member) => new EnumSet.fromValue(source.readInt()));
     source.end(tag);
     return new AnnotationsDataImpl(pragmaAnnotations);
   }
@@ -276,7 +277,8 @@
   @override
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
-    sink.writeMemberMap(pragmaAnnotations, (EnumSet<PragmaAnnotation> set) {
+    sink.writeMemberMap(pragmaAnnotations,
+        (MemberEntity member, EnumSet<PragmaAnnotation> set) {
       sink.writeInt(set.value);
     });
     sink.end(tag);
diff --git a/pkg/compiler/lib/src/js_backend/field_analysis.dart b/pkg/compiler/lib/src/js_backend/field_analysis.dart
index 77aa43e..aa2c328 100644
--- a/pkg/compiler/lib/src/js_backend/field_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/field_analysis.dart
@@ -235,8 +235,8 @@
   factory JFieldAnalysis.readFromDataSource(
       DataSource source, CompilerOptions options) {
     source.begin(tag);
-    Map<FieldEntity, FieldAnalysisData> fieldData = source
-        .readMemberMap(() => new FieldAnalysisData.fromDataSource(source));
+    Map<FieldEntity, FieldAnalysisData> fieldData = source.readMemberMap(
+        (MemberEntity member) => new FieldAnalysisData.fromDataSource(source));
     source.end(tag);
     return new JFieldAnalysis._(fieldData);
   }
@@ -245,7 +245,9 @@
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
     sink.writeMemberMap(
-        _fieldData, (FieldAnalysisData data) => data.writeToDataSink(sink));
+        _fieldData,
+        (MemberEntity member, FieldAnalysisData data) =>
+            data.writeToDataSink(sink));
     sink.end(tag);
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/inferred_data.dart b/pkg/compiler/lib/src/js_backend/inferred_data.dart
index be9b3dc..b84035c 100644
--- a/pkg/compiler/lib/src/js_backend/inferred_data.dart
+++ b/pkg/compiler/lib/src/js_backend/inferred_data.dart
@@ -103,8 +103,8 @@
       DataSource source, JClosedWorld closedWorld) {
     source.begin(tag);
     Set<MemberEntity> functionsCalledInLoop = source.readMembers().toSet();
-    Map<FunctionEntity, SideEffects> sideEffects =
-        source.readMemberMap(() => new SideEffects.readFromDataSource(source));
+    Map<FunctionEntity, SideEffects> sideEffects = source.readMemberMap(
+        (MemberEntity member) => new SideEffects.readFromDataSource(source));
     Set<FunctionEntity> sideEffectsFreeElements =
         source.readMembers<FunctionEntity>().toSet();
     Set<FunctionEntity> elementsThatCannotThrow =
@@ -126,8 +126,10 @@
     sink.writeBool(false); // Is _not_ trivial.
     sink.begin(tag);
     sink.writeMembers(_functionsCalledInLoop);
-    sink.writeMemberMap(_sideEffects,
-        (SideEffects sideEffects) => sideEffects.writeToDataSink(sink));
+    sink.writeMemberMap(
+        _sideEffects,
+        (MemberEntity member, SideEffects sideEffects) =>
+            sideEffects.writeToDataSink(sink));
     sink.writeMembers(_sideEffectsFreeElements);
     sink.writeMembers(_elementsThatCannotThrow);
     sink.writeMembers(_functionsThatMightBePassedToApply);
diff --git a/pkg/compiler/lib/src/js_backend/native_data.dart b/pkg/compiler/lib/src/js_backend/native_data.dart
index 3342b54..cc517d2 100644
--- a/pkg/compiler/lib/src/js_backend/native_data.dart
+++ b/pkg/compiler/lib/src/js_backend/native_data.dart
@@ -348,7 +348,7 @@
         source.readClassMap(source.readString);
     Set<ClassEntity> anonymousJsInteropClasses = source.readClasses().toSet();
     Map<MemberEntity, String> jsInteropMembers =
-        source.readMemberMap(source.readString);
+        source.readMemberMap((MemberEntity member) => source.readString());
     source.end(tag);
     return new NativeBasicDataImpl(
         elementEnvironment,
@@ -369,7 +369,8 @@
     sink.writeLibraryMap(jsInteropLibraries, sink.writeString);
     sink.writeClassMap(jsInteropClasses, sink.writeString);
     sink.writeClasses(anonymousJsInteropClasses);
-    sink.writeMemberMap(jsInteropMembers, sink.writeString);
+    sink.writeMemberMap(jsInteropMembers,
+        (MemberEntity member, String name) => sink.writeString(name));
     sink.end(tag);
   }
 
@@ -576,13 +577,16 @@
     NativeBasicData nativeBasicData =
         new NativeBasicData.readFromDataSource(source, elementEnvironment);
     Map<MemberEntity, String> nativeMemberName =
-        source.readMemberMap(source.readString);
-    Map<FunctionEntity, NativeBehavior> nativeMethodBehavior = source
-        .readMemberMap(() => new NativeBehavior.readFromDataSource(source));
-    Map<MemberEntity, NativeBehavior> nativeFieldLoadBehavior = source
-        .readMemberMap(() => new NativeBehavior.readFromDataSource(source));
-    Map<MemberEntity, NativeBehavior> nativeFieldStoreBehavior = source
-        .readMemberMap(() => new NativeBehavior.readFromDataSource(source));
+        source.readMemberMap((MemberEntity member) => source.readString());
+    Map<FunctionEntity, NativeBehavior> nativeMethodBehavior =
+        source.readMemberMap((MemberEntity member) =>
+            new NativeBehavior.readFromDataSource(source));
+    Map<MemberEntity, NativeBehavior> nativeFieldLoadBehavior =
+        source.readMemberMap((MemberEntity member) =>
+            new NativeBehavior.readFromDataSource(source));
+    Map<MemberEntity, NativeBehavior> nativeFieldStoreBehavior =
+        source.readMemberMap((MemberEntity member) =>
+            new NativeBehavior.readFromDataSource(source));
     source.end(tag);
     return new NativeDataImpl(
         nativeBasicData,
@@ -597,16 +601,20 @@
     sink.begin(tag);
     _nativeBasicData.writeToDataSink(sink);
 
-    sink.writeMemberMap(nativeMemberName, sink.writeString);
+    sink.writeMemberMap(nativeMemberName,
+        (MemberEntity member, String name) => sink.writeString(name));
 
-    sink.writeMemberMap(nativeMethodBehavior, (NativeBehavior behavior) {
+    sink.writeMemberMap(nativeMethodBehavior,
+        (MemberEntity member, NativeBehavior behavior) {
       behavior.writeToDataSink(sink);
     });
 
-    sink.writeMemberMap(nativeFieldLoadBehavior, (NativeBehavior behavior) {
+    sink.writeMemberMap(nativeFieldLoadBehavior,
+        (MemberEntity member, NativeBehavior behavior) {
       behavior.writeToDataSink(sink);
     });
-    sink.writeMemberMap(nativeFieldStoreBehavior, (NativeBehavior behavior) {
+    sink.writeMemberMap(nativeFieldStoreBehavior,
+        (MemberEntity member, NativeBehavior behavior) {
       behavior.writeToDataSink(sink);
     });
 
diff --git a/pkg/compiler/lib/src/js_model/closure.dart b/pkg/compiler/lib/src/js_model/closure.dart
index 2ee5d5c..32f7f78 100644
--- a/pkg/compiler/lib/src/js_model/closure.dart
+++ b/pkg/compiler/lib/src/js_model/closure.dart
@@ -49,12 +49,13 @@
       JsToElementMap elementMap, DataSource source) {
     source.begin(tag);
     // TODO(johnniwinther): Support shared [ScopeInfo].
-    Map<MemberEntity, ScopeInfo> scopeMap =
-        source.readMemberMap(() => new ScopeInfo.readFromDataSource(source));
+    Map<MemberEntity, ScopeInfo> scopeMap = source.readMemberMap(
+        (MemberEntity member) => new ScopeInfo.readFromDataSource(source));
     Map<ir.TreeNode, CapturedScope> capturedScopesMap = source
         .readTreeNodeMap(() => new CapturedScope.readFromDataSource(source));
-    Map<MemberEntity, CapturedScope> capturedScopeForSignatureMap = source
-        .readMemberMap(() => new CapturedScope.readFromDataSource(source));
+    Map<MemberEntity, CapturedScope> capturedScopeForSignatureMap =
+        source.readMemberMap((MemberEntity member) =>
+            new CapturedScope.readFromDataSource(source));
     Map<ir.LocalFunction, ClosureRepresentationInfo>
         localClosureRepresentationMap = source.readTreeNodeMap(
             () => new ClosureRepresentationInfo.readFromDataSource(source));
@@ -67,13 +68,15 @@
   @override
   void writeToDataSink(DataSink sink) {
     sink.begin(tag);
-    sink.writeMemberMap(
-        _scopeMap, (ScopeInfo info) => info.writeToDataSink(sink));
+    sink.writeMemberMap(_scopeMap,
+        (MemberEntity member, ScopeInfo info) => info.writeToDataSink(sink));
     sink.writeTreeNodeMap(_capturedScopesMap, (CapturedScope scope) {
       scope.writeToDataSink(sink);
     });
-    sink.writeMemberMap(_capturedScopeForSignatureMap,
-        (CapturedScope scope) => scope.writeToDataSink(sink));
+    sink.writeMemberMap(
+        _capturedScopeForSignatureMap,
+        (MemberEntity member, CapturedScope scope) =>
+            scope.writeToDataSink(sink));
     sink.writeTreeNodeMap(_localClosureRepresentationMap,
         (ClosureRepresentationInfo info) {
       info.writeToDataSink(sink);
diff --git a/pkg/compiler/lib/src/js_model/element_map.dart b/pkg/compiler/lib/src/js_model/element_map.dart
index 3fb71a4..0a88fbe 100644
--- a/pkg/compiler/lib/src/js_model/element_map.dart
+++ b/pkg/compiler/lib/src/js_model/element_map.dart
@@ -120,11 +120,14 @@
   List<DartType> getDartTypes(List<ir.DartType> types);
 
   /// Returns the definition information for [member].
-  MemberDefinition getMemberDefinition(covariant MemberEntity member);
+  MemberDefinition getMemberDefinition(MemberEntity member);
+
+  /// Returns the [ir.Member] containing the definition of [member], if any.
+  ir.Member getMemberContextNode(MemberEntity member);
 
   /// Returns the type of `this` in [member], or `null` if member is defined in
   /// a static context.
-  InterfaceType getMemberThisType(covariant MemberEntity member);
+  InterfaceType getMemberThisType(MemberEntity member);
 
   /// Returns how [member] has access to type variables of the this type
   /// returned by [getMemberThisType].
diff --git a/pkg/compiler/lib/src/js_model/element_map_impl.dart b/pkg/compiler/lib/src/js_model/element_map_impl.dart
index e24352c..15e56a5 100644
--- a/pkg/compiler/lib/src/js_model/element_map_impl.dart
+++ b/pkg/compiler/lib/src/js_model/element_map_impl.dart
@@ -437,8 +437,8 @@
     source.end(typeVariableDataTag);
 
     source.begin(nestedClosuresTag);
-    _nestedClosureMap.addAll(
-        source.readMemberMap(() => source.readMembers<IndexedFunction>()));
+    _nestedClosureMap.addAll(source.readMemberMap(
+        (MemberEntity member) => source.readMembers<IndexedFunction>()));
     source.end(nestedClosuresTag);
 
     source.end(tag);
@@ -548,7 +548,10 @@
     sink.end(typeVariableDataTag);
 
     sink.begin(nestedClosuresTag);
-    sink.writeMemberMap(_nestedClosureMap, sink.writeMembers);
+    sink.writeMemberMap(
+        _nestedClosureMap,
+        (MemberEntity member, List<IndexedFunction> functions) =>
+            sink.writeMembers(functions));
     sink.end(nestedClosuresTag);
 
     sink.end(tag);
@@ -1673,6 +1676,33 @@
   }
 
   @override
+  ir.Member getMemberContextNode(MemberEntity member) {
+    ir.Member getParentMember(ir.TreeNode node) {
+      while (node != null) {
+        if (node is ir.Member) {
+          return node;
+        }
+        node = node.parent;
+      }
+      return null;
+    }
+
+    MemberDefinition definition = getMemberDefinition(member);
+    switch (definition.kind) {
+      case MemberKind.regular:
+      case MemberKind.constructor:
+      case MemberKind.constructorBody:
+        return definition.node;
+      case MemberKind.closureCall:
+      case MemberKind.closureField:
+      case MemberKind.signature:
+      case MemberKind.generatorBody:
+        return getParentMember(definition.node);
+    }
+    throw new UnsupportedError('Unexpected member kind ${definition}');
+  }
+
+  @override
   ClassDefinition getClassDefinition(ClassEntity cls) {
     return getClassDefinitionInternal(cls);
   }
diff --git a/pkg/compiler/lib/src/js_model/env.dart b/pkg/compiler/lib/src/js_model/env.dart
index 9017330..ac096b1 100644
--- a/pkg/compiler/lib/src/js_model/env.dart
+++ b/pkg/compiler/lib/src/js_model/env.dart
@@ -678,7 +678,7 @@
     MemberDefinition definition =
         new MemberDefinition.readFromDataSource(source);
     StaticTypeCache staticTypes =
-        new StaticTypeCache.readFromDataSource(source);
+        new StaticTypeCache.readFromDataSource(source, node);
     source.end(tag);
     return new FunctionDataImpl(node, functionNode, definition, staticTypes);
   }
@@ -689,7 +689,7 @@
     sink.begin(tag);
     sink.writeMemberNode(node);
     definition.writeToDataSink(sink);
-    staticTypes.writeToDataSink(sink);
+    staticTypes.writeToDataSink(sink, node);
     sink.end(tag);
   }
 
@@ -877,7 +877,7 @@
     MemberDefinition definition =
         new MemberDefinition.readFromDataSource(source);
     StaticTypeCache staticTypes =
-        new StaticTypeCache.readFromDataSource(source);
+        new StaticTypeCache.readFromDataSource(source, node);
     source.end(tag);
     return new JConstructorDataImpl(
         node, functionNode, definition, staticTypes);
@@ -890,7 +890,7 @@
     sink.writeMemberNode(node);
     definition.writeToDataSink(sink);
     assert(constructorBody == null);
-    staticTypes.writeToDataSink(sink);
+    staticTypes.writeToDataSink(sink, node);
     sink.end(tag);
   }
 
@@ -940,7 +940,7 @@
     MemberDefinition definition =
         new MemberDefinition.readFromDataSource(source);
     StaticTypeCache staticTypes =
-        new StaticTypeCache.readFromDataSource(source);
+        new StaticTypeCache.readFromDataSource(source, node);
     source.end(tag);
     return new ConstructorBodyDataImpl(
         node, functionNode, definition, staticTypes);
@@ -952,7 +952,7 @@
     sink.begin(tag);
     sink.writeMemberNode(node);
     definition.writeToDataSink(sink);
-    staticTypes.writeToDataSink(sink);
+    staticTypes.writeToDataSink(sink, node);
     sink.end(tag);
   }
 
@@ -988,7 +988,7 @@
     MemberDefinition definition =
         new MemberDefinition.readFromDataSource(source);
     StaticTypeCache staticTypes =
-        new StaticTypeCache.readFromDataSource(source);
+        new StaticTypeCache.readFromDataSource(source, node);
     source.end(tag);
     return new JFieldDataImpl(node, definition, staticTypes);
   }
@@ -999,7 +999,7 @@
     sink.begin(tag);
     sink.writeMemberNode(node);
     definition.writeToDataSink(sink);
-    staticTypes.writeToDataSink(sink);
+    staticTypes.writeToDataSink(sink, node);
     sink.end(tag);
   }
 
diff --git a/pkg/compiler/lib/src/js_model/js_world.dart b/pkg/compiler/lib/src/js_model/js_world.dart
index 3515770..b1beeb8 100644
--- a/pkg/compiler/lib/src/js_model/js_world.dart
+++ b/pkg/compiler/lib/src/js_model/js_world.dart
@@ -173,8 +173,8 @@
     elementMap.lateOutputUnitDataBuilder =
         new LateOutputUnitDataBuilder(outputUnitData);
 
-    Map<MemberEntity, MemberAccess> memberAccess =
-        source.readMemberMap(() => new MemberAccess.readFromDataSource(source));
+    Map<MemberEntity, MemberAccess> memberAccess = source.readMemberMap(
+        (MemberEntity member) => new MemberAccess.readFromDataSource(source));
 
     source.end(tag);
 
@@ -228,7 +228,9 @@
     closureDataLookup.writeToDataSink(sink);
     outputUnitData.writeToDataSink(sink);
     sink.writeMemberMap(
-        memberAccess, (MemberAccess access) => access.writeToDataSink(sink));
+        memberAccess,
+        (MemberEntity member, MemberAccess access) =>
+            access.writeToDataSink(sink));
     sink.end(tag);
   }
 
diff --git a/pkg/compiler/lib/src/serialization/abstract_sink.dart b/pkg/compiler/lib/src/serialization/abstract_sink.dart
index a1af8e8..484d24f 100644
--- a/pkg/compiler/lib/src/serialization/abstract_sink.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_sink.dart
@@ -39,6 +39,9 @@
 
   final Map<String, int> tagFrequencyMap;
 
+  ir.Member _currentMemberContext;
+  _MemberData _currentMemberData;
+
   AbstractDataSink({this.useDataKinds: false, this.tagFrequencyMap}) {
     _dartTypeWriter = new DartTypeWriter(this);
     _dartTypeNodeWriter = new DartTypeNodeWriter(this);
@@ -73,6 +76,24 @@
   }
 
   @override
+  void inMemberContext(ir.Member context, void f()) {
+    ir.Member oldMemberContext = _currentMemberContext;
+    _MemberData oldMemberData = _currentMemberData;
+    _currentMemberContext = context;
+    _currentMemberData = null;
+    f();
+    _currentMemberData = oldMemberData;
+    _currentMemberContext = oldMemberContext;
+  }
+
+  _MemberData get currentMemberData {
+    assert(_currentMemberContext != null,
+        "DataSink has no current member context.");
+    return _currentMemberData ??= _memberData[_currentMemberContext] ??=
+        new _MemberData(_currentMemberContext);
+  }
+
+  @override
   void writeCached<E>(E value, void f(E value)) {
     IndexedSink sink = _generalCaches[E] ??= new IndexedSink<E>(this);
     sink.write(value, (v) => f(v));
@@ -221,10 +242,58 @@
   @override
   void writeTreeNode(ir.TreeNode value) {
     _writeDataKind(DataKind.treeNode);
-    _writeTreeNode(value);
+    _writeTreeNode(value, null);
   }
 
-  void _writeTreeNode(ir.TreeNode value) {
+  @override
+  void writeTreeNodeInContext(ir.TreeNode value) {
+    writeTreeNodeInContextInternal(value, currentMemberData);
+  }
+
+  void writeTreeNodeInContextInternal(
+      ir.TreeNode value, _MemberData memberData) {
+    _writeDataKind(DataKind.treeNode);
+    _writeTreeNode(value, memberData);
+  }
+
+  @override
+  void writeTreeNodeOrNullInContext(ir.TreeNode value) {
+    writeBool(value != null);
+    if (value != null) {
+      writeTreeNodeInContextInternal(value, currentMemberData);
+    }
+  }
+
+  @override
+  void writeTreeNodesInContext(Iterable<ir.TreeNode> values,
+      {bool allowNull: false}) {
+    if (values == null) {
+      assert(allowNull);
+      writeInt(0);
+    } else {
+      writeInt(values.length);
+      for (ir.TreeNode value in values) {
+        writeTreeNodeInContextInternal(value, currentMemberData);
+      }
+    }
+  }
+
+  @override
+  void writeTreeNodeMapInContext<V>(Map<ir.TreeNode, V> map, void f(V value),
+      {bool allowNull: false}) {
+    if (map == null) {
+      assert(allowNull);
+      writeInt(0);
+    } else {
+      writeInt(map.length);
+      map.forEach((ir.TreeNode key, V value) {
+        writeTreeNodeInContextInternal(key, currentMemberData);
+        f(value);
+      });
+    }
+  }
+
+  void _writeTreeNode(ir.TreeNode value, _MemberData memberData) {
     if (value is ir.Class) {
       _writeEnumInternal(_TreeNodeKind.cls);
       _writeClassNode(value);
@@ -234,39 +303,43 @@
     } else if (value is ir.VariableDeclaration &&
         value.parent is ir.FunctionDeclaration) {
       _writeEnumInternal(_TreeNodeKind.functionDeclarationVariable);
-      _writeTreeNode(value.parent);
+      _writeTreeNode(value.parent, memberData);
     } else if (value is ir.FunctionNode) {
       _writeEnumInternal(_TreeNodeKind.functionNode);
-      _writeFunctionNode(value);
+      _writeFunctionNode(value, memberData);
     } else if (value is ir.TypeParameter) {
       _writeEnumInternal(_TreeNodeKind.typeParameter);
-      _writeTypeParameter(value);
+      _writeTypeParameter(value, memberData);
     } else if (value is ConstantReference) {
       _writeEnumInternal(_TreeNodeKind.constant);
-      _writeTreeNode(value.expression);
+      _writeTreeNode(value.expression, memberData);
       _ConstantNodeIndexerVisitor indexer = new _ConstantNodeIndexerVisitor();
       value.expression.constant.accept(indexer);
       _writeIntInternal(indexer.getIndex(value.constant));
     } else {
       _writeEnumInternal(_TreeNodeKind.node);
-      ir.TreeNode member = value;
-      while (member is! ir.Member) {
-        if (member == null) {
-          throw new UnsupportedError("No enclosing member of TreeNode "
-              "$value (${value.runtimeType})");
+      if (memberData == null) {
+        ir.TreeNode member = value;
+        while (member is! ir.Member) {
+          if (member == null) {
+            throw new UnsupportedError("No enclosing member of TreeNode "
+                "$value (${value.runtimeType})");
+          }
+          member = member.parent;
         }
-        member = member.parent;
+        _writeMemberNode(member);
+        memberData = _memberData[member] ??= new _MemberData(member);
       }
-      _writeMemberNode(member);
-      _MemberData memberData = _memberData[member] ??= new _MemberData(member);
       int index = memberData.getIndexByTreeNode(value);
       assert(
-          index != null, "No TreeNode index found for ${value.runtimeType}.");
+          index != null,
+          "No TreeNode index found for ${value.runtimeType} "
+          "found in ${memberData}.");
       _writeIntInternal(index);
     }
   }
 
-  void _writeFunctionNode(ir.FunctionNode value) {
+  void _writeFunctionNode(ir.FunctionNode value, _MemberData memberData) {
     ir.TreeNode parent = value.parent;
     if (parent is ir.Procedure) {
       _writeEnumInternal(_FunctionNodeKind.procedure);
@@ -276,10 +349,10 @@
       _writeMemberNode(parent);
     } else if (parent is ir.FunctionExpression) {
       _writeEnumInternal(_FunctionNodeKind.functionExpression);
-      _writeTreeNode(parent);
+      _writeTreeNode(parent, memberData);
     } else if (parent is ir.FunctionDeclaration) {
       _writeEnumInternal(_FunctionNodeKind.functionDeclaration);
-      _writeTreeNode(parent);
+      _writeTreeNode(parent, memberData);
     } else {
       throw new UnsupportedError(
           "Unsupported FunctionNode parent ${parent.runtimeType}");
@@ -289,10 +362,10 @@
   @override
   void writeTypeParameterNode(ir.TypeParameter value) {
     _writeDataKind(DataKind.typeParameterNode);
-    _writeTypeParameter(value);
+    _writeTypeParameter(value, null);
   }
 
-  void _writeTypeParameter(ir.TypeParameter value) {
+  void _writeTypeParameter(ir.TypeParameter value, _MemberData memberData) {
     ir.TreeNode parent = value.parent;
     if (parent is ir.Class) {
       _writeEnumInternal(_TypeParameterKind.cls);
@@ -300,7 +373,7 @@
       _writeIntInternal(parent.typeParameters.indexOf(value));
     } else if (parent is ir.FunctionNode) {
       _writeEnumInternal(_TypeParameterKind.functionNode);
-      _writeFunctionNode(parent);
+      _writeFunctionNode(parent, memberData);
       _writeIntInternal(parent.typeParameters.indexOf(value));
     } else {
       throw new UnsupportedError(
@@ -438,7 +511,8 @@
       case ConstantValueKind.CONSTRUCTED:
         ConstructedConstantValue constant = value;
         writeDartType(constant.type);
-        writeMemberMap(constant.fields, writeConstant);
+        writeMemberMap(constant.fields,
+            (MemberEntity member, ConstantValue value) => writeConstant(value));
         break;
       case ConstantValueKind.TYPE:
         TypeConstantValue constant = value;
diff --git a/pkg/compiler/lib/src/serialization/abstract_source.dart b/pkg/compiler/lib/src/serialization/abstract_source.dart
index a2fa583..a69e834 100644
--- a/pkg/compiler/lib/src/serialization/abstract_source.dart
+++ b/pkg/compiler/lib/src/serialization/abstract_source.dart
@@ -22,6 +22,9 @@
 
   Map<Type, IndexedSource> _generalCaches = {};
 
+  ir.Member _currentMemberContext;
+  _MemberData _currentMemberData;
+
   AbstractDataSource({this.useDataKinds: false}) {
     _stringIndex = new IndexedSource<String>(this);
     _uriIndex = new IndexedSource<Uri>(this);
@@ -92,6 +95,24 @@
   }
 
   @override
+  T inMemberContext<T>(ir.Member context, T f()) {
+    ir.Member oldMemberContext = _currentMemberContext;
+    _MemberData oldMemberData = _currentMemberData;
+    _currentMemberContext = context;
+    _currentMemberData = null;
+    T result = f();
+    _currentMemberData = oldMemberData;
+    _currentMemberContext = oldMemberContext;
+    return result;
+  }
+
+  _MemberData get currentMemberData {
+    assert(_currentMemberContext != null,
+        "DataSink has no current member context.");
+    return _currentMemberData ??= _getMemberData(_currentMemberContext);
+  }
+
+  @override
   E readCached<E>(E f()) {
     IndexedSource source = _generalCaches[E] ??= new IndexedSource<E>(this);
     return source.read(f);
@@ -322,11 +343,11 @@
       case MemberContextKind.cls:
         _ClassData cls = _readClassData();
         String name = _readString();
-        return cls.lookupMember(name);
+        return cls.lookupMemberDataByName(name);
       case MemberContextKind.library:
         _LibraryData library = _readLibraryData();
         String name = _readString();
-        return library.lookupMember(name);
+        return library.lookupMemberDataByName(name);
     }
     throw new UnsupportedError("Unsupported _MemberKind $kind");
   }
@@ -340,7 +361,7 @@
   _ClassData _readClassData() {
     _LibraryData library = _readLibraryData();
     String name = _readString();
-    return library.lookupClass(name);
+    return library.lookupClassByName(name);
   }
 
   @override
@@ -419,7 +440,64 @@
   @override
   ir.TreeNode readTreeNode() {
     _checkDataKind(DataKind.treeNode);
-    return _readTreeNode();
+    return _readTreeNode(null);
+  }
+
+  _MemberData _getMemberData(ir.Member node) {
+    _LibraryData libraryData =
+        componentLookup.getLibraryDataByUri(node.enclosingLibrary.importUri);
+    if (node.enclosingClass != null) {
+      _ClassData classData = libraryData.lookupClassByNode(node.enclosingClass);
+      return classData.lookupMemberDataByNode(node);
+    } else {
+      return libraryData.lookupMemberDataByNode(node);
+    }
+  }
+
+  @override
+  ir.TreeNode readTreeNodeInContext() {
+    return readTreeNodeInContextInternal(currentMemberData);
+  }
+
+  ir.TreeNode readTreeNodeInContextInternal(_MemberData memberData) {
+    _checkDataKind(DataKind.treeNode);
+    return _readTreeNode(memberData);
+  }
+
+  @override
+  ir.TreeNode readTreeNodeOrNullInContext() {
+    bool hasValue = readBool();
+    if (hasValue) {
+      return readTreeNodeInContextInternal(currentMemberData);
+    }
+    return null;
+  }
+
+  @override
+  List<E> readTreeNodesInContext<E extends ir.TreeNode>(
+      {bool emptyAsNull: false}) {
+    int count = readInt();
+    if (count == 0 && emptyAsNull) return null;
+    List<E> list = new List<E>(count);
+    for (int i = 0; i < count; i++) {
+      ir.TreeNode node = readTreeNodeInContextInternal(currentMemberData);
+      list[i] = node;
+    }
+    return list;
+  }
+
+  @override
+  Map<K, V> readTreeNodeMapInContext<K extends ir.TreeNode, V>(V f(),
+      {bool emptyAsNull: false}) {
+    int count = readInt();
+    if (count == 0 && emptyAsNull) return null;
+    Map<K, V> map = {};
+    for (int i = 0; i < count; i++) {
+      ir.TreeNode node = readTreeNodeInContextInternal(currentMemberData);
+      V value = f();
+      map[node] = value;
+    }
+    return map;
   }
 
   @override
@@ -493,7 +571,8 @@
       case ConstantValueKind.CONSTRUCTED:
         InterfaceType type = readDartType();
         Map<FieldEntity, ConstantValue> fields =
-            readMemberMap<FieldEntity, ConstantValue>(() => readConstant());
+            readMemberMap<FieldEntity, ConstantValue>(
+                (MemberEntity member) => readConstant());
         return new ConstructedConstantValue(type, fields);
       case ConstantValueKind.TYPE:
         DartType representedType = readDartType();
@@ -524,7 +603,7 @@
     throw new UnsupportedError("Unexpexted constant value kind ${kind}.");
   }
 
-  ir.TreeNode _readTreeNode() {
+  ir.TreeNode _readTreeNode(_MemberData memberData) {
     _TreeNodeKind kind = _readEnumInternal(_TreeNodeKind.values);
     switch (kind) {
       case _TreeNodeKind.cls:
@@ -532,32 +611,34 @@
       case _TreeNodeKind.member:
         return _readMemberData().node;
       case _TreeNodeKind.functionDeclarationVariable:
-        ir.FunctionDeclaration functionDeclaration = _readTreeNode();
+        ir.FunctionDeclaration functionDeclaration = _readTreeNode(memberData);
         return functionDeclaration.variable;
       case _TreeNodeKind.functionNode:
-        return _readFunctionNode();
+        return _readFunctionNode(memberData);
       case _TreeNodeKind.typeParameter:
-        return _readTypeParameter();
+        return _readTypeParameter(memberData);
       case _TreeNodeKind.constant:
         // TODO(johnniwinther): Support serialization within a member context
         // and use this to temporarily cache constant node indices.
-        ir.ConstantExpression expression = _readTreeNode();
+        ir.ConstantExpression expression = _readTreeNode(memberData);
         _ConstantNodeIndexerVisitor indexer = new _ConstantNodeIndexerVisitor();
         expression.constant.accept(indexer);
         ir.Constant constant = indexer.getConstant(_readIntInternal());
         return new ConstantReference(expression, constant);
       case _TreeNodeKind.node:
-        _MemberData data = _readMemberData();
+        if (memberData == null) {
+          memberData = _readMemberData();
+        }
         int index = _readIntInternal();
-        ir.TreeNode treeNode = data.getTreeNodeByIndex(index);
+        ir.TreeNode treeNode = memberData.getTreeNodeByIndex(index);
         assert(treeNode != null,
-            "No TreeNode found for index $index in ${data.node}.$_errorContext");
+            "No TreeNode found for index $index in ${memberData.node}.$_errorContext");
         return treeNode;
     }
     throw new UnsupportedError("Unexpected _TreeNodeKind $kind");
   }
 
-  ir.FunctionNode _readFunctionNode() {
+  ir.FunctionNode _readFunctionNode(_MemberData memberData) {
     _FunctionNodeKind kind = _readEnumInternal(_FunctionNodeKind.values);
     switch (kind) {
       case _FunctionNodeKind.procedure:
@@ -567,10 +648,10 @@
         ir.Constructor constructor = _readMemberData().node;
         return constructor.function;
       case _FunctionNodeKind.functionExpression:
-        ir.FunctionExpression functionExpression = _readTreeNode();
+        ir.FunctionExpression functionExpression = _readTreeNode(memberData);
         return functionExpression.function;
       case _FunctionNodeKind.functionDeclaration:
-        ir.FunctionDeclaration functionDeclaration = _readTreeNode();
+        ir.FunctionDeclaration functionDeclaration = _readTreeNode(memberData);
         return functionDeclaration.function;
     }
     throw new UnsupportedError("Unexpected _FunctionNodeKind $kind");
@@ -579,17 +660,17 @@
   @override
   ir.TypeParameter readTypeParameterNode() {
     _checkDataKind(DataKind.typeParameterNode);
-    return _readTypeParameter();
+    return _readTypeParameter(null);
   }
 
-  ir.TypeParameter _readTypeParameter() {
+  ir.TypeParameter _readTypeParameter(_MemberData memberData) {
     _TypeParameterKind kind = _readEnumInternal(_TypeParameterKind.values);
     switch (kind) {
       case _TypeParameterKind.cls:
         ir.Class cls = _readClassData().node;
         return cls.typeParameters[_readIntInternal()];
       case _TypeParameterKind.functionNode:
-        ir.FunctionNode functionNode = _readFunctionNode();
+        ir.FunctionNode functionNode = _readFunctionNode(memberData);
         return functionNode.typeParameters[_readIntInternal()];
     }
     throw new UnsupportedError("Unexpected _TypeParameterKind kind $kind");
diff --git a/pkg/compiler/lib/src/serialization/member_data.dart b/pkg/compiler/lib/src/serialization/member_data.dart
index f088210..867a271 100644
--- a/pkg/compiler/lib/src/serialization/member_data.dart
+++ b/pkg/compiler/lib/src/serialization/member_data.dart
@@ -55,27 +55,46 @@
   final ir.Library node;
 
   /// Cache of [_ClassData] for classes in this library.
-  Map<String, _ClassData> _classes;
+  Map<String, _ClassData> _classesByName;
+  Map<ir.Class, _ClassData> _classesByNode;
 
   /// Cache of [ir.Typedef] nodes for typedefs in this library.
   Map<String, ir.Typedef> _typedefs;
 
   /// Cache of [_MemberData] for members in this library.
-  Map<String, _MemberData> _members;
+  Map<String, _MemberData> _membersByName;
+  Map<ir.Member, _MemberData> _membersByNode;
 
   _LibraryData(this.node);
 
-  /// Returns the [_ClassData] for the class [name] in this library.
-  _ClassData lookupClass(String name) {
-    if (_classes == null) {
-      _classes = {};
+  void _ensureClasses() {
+    if (_classesByName == null) {
+      _classesByName = {};
+      _classesByNode = {};
       for (ir.Class cls in node.classes) {
-        assert(!_classes.containsKey(cls.name),
-            "Duplicate class '${cls.name}' in $_classes trying to add $cls.");
-        _classes[cls.name] = new _ClassData(cls);
+        assert(
+            !_classesByName.containsKey(cls.name),
+            "Duplicate class '${cls.name}' in $_classesByName "
+            "trying to add $cls.");
+        assert(
+            !_classesByNode.containsKey(cls),
+            "Duplicate class '${cls.name}' in $_classesByNode "
+            "trying to add $cls.");
+        _classesByNode[cls] = _classesByName[cls.name] = new _ClassData(cls);
       }
     }
-    return _classes[name];
+  }
+
+  /// Returns the [_ClassData] for the class [name] in this library.
+  _ClassData lookupClassByName(String name) {
+    _ensureClasses();
+    return _classesByName[name];
+  }
+
+  /// Returns the [_ClassData] for the class [node] in this library.
+  _ClassData lookupClassByNode(ir.Class node) {
+    _ensureClasses();
+    return _classesByNode[node];
   }
 
   ir.Typedef lookupTypedef(String name) {
@@ -92,20 +111,37 @@
     return _typedefs[name];
   }
 
-  /// Returns the [_MemberData] for the member uniquely identified by [name] in
-  /// this library.
-  _MemberData lookupMember(String name) {
-    if (_members == null) {
-      _members = {};
+  void _ensureMembers() {
+    if (_membersByName == null) {
+      _membersByName = {};
+      _membersByNode = {};
       for (ir.Member member in node.members) {
         String name = _computeMemberName(member);
         if (name == null) continue;
-        assert(!_members.containsKey(name),
-            "Duplicate member '$name' in $_members trying to add $member.");
-        _members[name] = new _MemberData(member);
+        assert(
+            !_membersByName.containsKey(name),
+            "Duplicate member '$name' in $_membersByName "
+            "trying to add $member.");
+        assert(
+            !_membersByNode.containsKey(member),
+            "Duplicate member '$name' in $_membersByNode "
+            "trying to add $member.");
+        _membersByNode[member] = _membersByName[name] = new _MemberData(member);
       }
     }
-    return _members[name];
+  }
+
+  /// Returns the [_MemberData] for the member uniquely identified by [name] in
+  /// this library.
+  _MemberData lookupMemberDataByName(String name) {
+    _ensureMembers();
+    return _membersByName[name];
+  }
+
+  /// Returns the [_MemberData] for the member [node] in this library.
+  _MemberData lookupMemberDataByNode(ir.Member node) {
+    _ensureMembers();
+    return _membersByNode[node];
   }
 
   @override
@@ -118,24 +154,42 @@
   final ir.Class node;
 
   /// Cache of [_MemberData] for members in this class.
-  Map<String, _MemberData> _members;
+  Map<String, _MemberData> _membersByName;
+  Map<ir.Member, _MemberData> _membersByNode;
 
   _ClassData(this.node);
 
-  /// Returns the [_MemberData] for the member uniquely identified by [name] in
-  /// this class.
-  _MemberData lookupMember(String name) {
-    if (_members == null) {
-      _members = {};
+  void _ensureMembers() {
+    if (_membersByName == null) {
+      _membersByName = {};
+      _membersByNode = {};
       for (ir.Member member in node.members) {
         String name = _computeMemberName(member);
         if (name == null) continue;
-        assert(!_members.containsKey(name),
-            "Duplicate member '$name' in $_members trying to add $member.");
-        _members[name] = new _MemberData(member);
+        assert(
+            !_membersByName.containsKey(name),
+            "Duplicate member '$name' in $_membersByName "
+            "trying to add $member.");
+        assert(
+            !_membersByNode.containsKey(member),
+            "Duplicate member '$name' in $_membersByNode "
+            "trying to add $member.");
+        _membersByNode[member] = _membersByName[name] = new _MemberData(member);
       }
     }
-    return _members[name];
+  }
+
+  /// Returns the [_MemberData] for the member uniquely identified by [name] in
+  /// this class.
+  _MemberData lookupMemberDataByName(String name) {
+    _ensureMembers();
+    return _membersByName[name];
+  }
+
+  /// Returns the [_MemberData] for the member [node] in this class.
+  _MemberData lookupMemberDataByNode(ir.Member node) {
+    _ensureMembers();
+    return _membersByNode[node];
   }
 
   @override
diff --git a/pkg/compiler/lib/src/serialization/mixins.dart b/pkg/compiler/lib/src/serialization/mixins.dart
index 2de3e8a..c65a05c 100644
--- a/pkg/compiler/lib/src/serialization/mixins.dart
+++ b/pkg/compiler/lib/src/serialization/mixins.dart
@@ -142,14 +142,14 @@
   }
 
   @override
-  Map<K, V> readMemberMap<K extends MemberEntity, V>(V f(),
+  Map<K, V> readMemberMap<K extends MemberEntity, V>(V f(MemberEntity member),
       {bool emptyAsNull: false}) {
     int count = readInt();
     if (count == 0 && emptyAsNull) return null;
     Map<K, V> map = {};
     for (int i = 0; i < count; i++) {
       MemberEntity member = readMember();
-      V value = f();
+      V value = f(member);
       map[member] = value;
     }
     return map;
@@ -516,7 +516,8 @@
   }
 
   @override
-  void writeMemberMap<V>(Map<MemberEntity, V> map, void f(V value),
+  void writeMemberMap<V>(
+      Map<MemberEntity, V> map, void f(MemberEntity member, V value),
       {bool allowNull: false}) {
     if (map == null) {
       assert(allowNull);
@@ -525,7 +526,7 @@
       writeInt(map.length);
       map.forEach((MemberEntity member, V value) {
         writeMember(member);
-        f(value);
+        f(member, value);
       });
     }
   }
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index 92e6ad0..425f6d2 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -187,6 +187,35 @@
   void writeTreeNodeMap<V>(Map<ir.TreeNode, V> map, void f(V value),
       {bool allowNull: false});
 
+  /// Writes a reference to the kernel tree node [value] in the known [context]
+  /// to this data sink.
+  void writeTreeNodeInContext(ir.TreeNode value);
+
+  /// Writes a reference to the potentially `null` kernel tree node [value] in
+  /// the known [context] to this data sink.
+  ///
+  /// This is a convenience method to be used together with
+  /// [DataSource.readTreeNodeOrNullInContext].
+  void writeTreeNodeOrNullInContext(ir.TreeNode value);
+
+  /// Writes references to the kernel tree node [values] in the known [context]
+  /// to this data sink. If [allowNull] is `true`, [values] is allowed to be
+  /// `null`.
+  ///
+  /// This is a convenience method to be used together with
+  /// [DataSource.readTreeNodesInContext].
+  void writeTreeNodesInContext(Iterable<ir.TreeNode> values,
+      {bool allowNull: false});
+
+  /// Writes the [map] from references to kernel tree nodes to [V] values in the
+  /// known [context] to this data sink, calling [f] to write each value to the
+  /// data sink. If [allowNull] is `true`, [map] is allowed to be `null`.
+  ///
+  /// This is a convenience method to be used together with
+  /// [DataSource.readTreeNodeMapInContext].
+  void writeTreeNodeMapInContext<V>(Map<ir.TreeNode, V> map, void f(V value),
+      {bool allowNull: false});
+
   /// Writes a reference to the kernel type parameter node [value] to this data
   /// sink.
   void writeTypeParameterNode(ir.TypeParameter value);
@@ -297,7 +326,8 @@
   ///
   /// This is a convenience method to be used together with
   /// [DataSource.readMemberMap].
-  void writeMemberMap<V>(Map<MemberEntity, V> map, void f(V value),
+  void writeMemberMap<V>(
+      Map<MemberEntity, V> map, void f(MemberEntity member, V value),
       {bool allowNull: false});
 
   /// Writes a reference to the local [value] to this data sink.
@@ -406,6 +436,11 @@
   /// Register a [CodegenWriter] with this data sink to support serialization
   /// of codegen only data.
   void registerCodegenWriter(CodegenWriter writer);
+
+  /// Invoke [f] in the context of [member]. This sets up support for
+  /// serialization of `ir.TreeNode`s using the `writeTreeNode*InContext`
+  /// methods.
+  void inMemberContext(ir.Member member, void f());
 }
 
 /// Interface for deserialization.
@@ -446,6 +481,11 @@
   /// for deserialization of codegen only data.
   void deregisterCodegenReader(CodegenReader reader);
 
+  /// Invoke [f] in the context of [member]. This sets up support for
+  /// deserialization of `ir.TreeNode`s using the `readTreeNode*InContext`
+  /// methods.
+  T inMemberContext<T>(ir.Member member, T f());
+
   /// Reads a reference to an [E] value from this data source. If the value has
   /// not yet been deserialized, [f] is called to deserialize the value itself.
   E readCached<E>(E f());
@@ -577,6 +617,33 @@
   Map<K, V> readTreeNodeMap<K extends ir.TreeNode, V>(V f(),
       {bool emptyAsNull: false});
 
+  /// Reads a reference to a kernel tree node in the known [context] from this
+  /// data source.
+  ir.TreeNode readTreeNodeInContext();
+
+  /// Reads a reference to a potentially `null` kernel tree node in the known
+  /// [context] from this data source.
+  ir.TreeNode readTreeNodeOrNullInContext();
+
+  /// Reads a list of references to kernel tree nodes in the known [context]
+  /// from this data source. If [emptyAsNull] is `true`, `null` is returned
+  /// instead of an empty list.
+  ///
+  /// This is a convenience method to be used together with
+  /// [DataSink.writeTreeNodesInContext].
+  List<E> readTreeNodesInContext<E extends ir.TreeNode>(
+      {bool emptyAsNull: false});
+
+  /// Reads a map from kernel tree nodes to [V] values in the known [context]
+  /// from this data source, calling [f] to read each value from the data
+  /// source. If [emptyAsNull] is `true`, `null` is returned instead of an empty
+  /// map.
+  ///
+  /// This is a convenience method to be used together with
+  /// [DataSink.writeTreeNodeMapInContext].
+  Map<K, V> readTreeNodeMapInContext<K extends ir.TreeNode, V>(V f(),
+      {bool emptyAsNull: false});
+
   /// Reads a reference to a kernel type parameter node from this data source.
   ir.TypeParameter readTypeParameterNode();
 
@@ -668,7 +735,7 @@
   ///
   /// This is a convenience method to be used together with
   /// [DataSink.writeMemberMap].
-  Map<K, V> readMemberMap<K extends MemberEntity, V>(V f(),
+  Map<K, V> readMemberMap<K extends MemberEntity, V>(V f(MemberEntity member),
       {bool emptyAsNull: false});
 
   /// Reads a reference to a local from this data source.
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 61771d5..f9affee 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -29,7 +29,7 @@
   InferredData inferredData = results.inferredData;
   closedWorld.writeToDataSink(sink);
   inferredData.writeToDataSink(sink);
-  results.writeToDataSink(sink);
+  results.writeToDataSink(sink, closedWorld.elementMap);
   sink.close();
 }
 
@@ -45,7 +45,7 @@
   InferredData newInferredData =
       new InferredData.readFromDataSource(source, newClosedWorld);
   return new GlobalTypeInferenceResults.readFromDataSource(
-      source, newClosedWorld, newInferredData);
+      source, newClosedWorld.elementMap, newClosedWorld, newInferredData);
 }
 
 class SerializationTask extends CompilerTask {
@@ -134,7 +134,9 @@
       sink.registerEntityWriter(entityWriter);
       sink.registerCodegenWriter(new CodegenWriterImpl(closedWorld));
       sink.writeMemberMap(
-          results, (CodegenResult result) => result.writeToDataSink(sink));
+          results,
+          (MemberEntity member, CodegenResult result) =>
+              result.writeToDataSink(sink));
       sink.close();
     });
   }
@@ -155,7 +157,7 @@
         DataSource source = new BinarySourceImpl(dataInput.data);
         backendStrategy.prepareCodegenReader(source);
         Map<MemberEntity, CodegenResult> codegenResults =
-            source.readMemberMap(() {
+            source.readMemberMap((MemberEntity member) {
           List<ModularName> modularNames = [];
           List<ModularExpression> modularExpressions = [];
           CodegenReader reader = new CodegenReaderImpl(