[dart2js] Decouple closed world and serialization.

Change-Id: Idf9420eb1d461b9a2cff90354cfbfee601b75ae8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330861
Reviewed-by: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/common/codegen.dart b/pkg/compiler/lib/src/common/codegen.dart
index 0d0d556..c46e74e 100644
--- a/pkg/compiler/lib/src/common/codegen.dart
+++ b/pkg/compiler/lib/src/common/codegen.dart
@@ -8,11 +8,8 @@
 
 import '../common/elements.dart';
 import '../constants/values.dart';
-import '../deferred_load/output_unit.dart' show OutputUnit;
 import '../elements/entities.dart';
 import '../elements/types.dart' show DartType, InterfaceType;
-import '../inferrer/abstract_value_domain.dart';
-import '../inferrer/types.dart';
 import '../io/source_information.dart';
 import '../js/js.dart' as js;
 import '../js_backend/backend.dart';
@@ -25,8 +22,6 @@
 import '../js_backend/type_reference.dart' show TypeReference;
 import '../js_emitter/js_emitter.dart' show Emitter;
 import '../js_model/elements.dart';
-import '../js_model/js_world.dart';
-import '../js_model/type_recipe.dart' show TypeRecipe;
 import '../native/behavior.dart';
 import '../serialization/serialization.dart';
 import '../universe/feature.dart';
@@ -401,8 +396,8 @@
     return CodegenResult(
         code,
         _worldImpact,
-        js.DeferredExpressionData(_names.isEmpty ? [] : _names,
-            _expressions.isEmpty ? [] : _expressions));
+        js.DeferredExpressionData(_names.isEmpty ? const [] : _names,
+            _expressions.isEmpty ? const [] : _expressions));
   }
 }
 
@@ -411,13 +406,10 @@
 /// This is used in the non-modular codegen enqueuer driving code generation.
 class OnDemandCodegenResults extends CodegenResults {
   @override
-  final GlobalTypeInferenceResults globalTypeInferenceResults;
-  @override
   final CodegenInputs codegenInputs;
   final FunctionCompiler _functionCompiler;
 
-  OnDemandCodegenResults(this.globalTypeInferenceResults, this.codegenInputs,
-      this._functionCompiler);
+  OnDemandCodegenResults(this.codegenInputs, this._functionCompiler);
 
   @override
   CodegenResult getCodegenResults(MemberEntity member) {
@@ -441,7 +433,7 @@
     js.Fun? code = source.readJsNodeOrNull() as js.Fun?;
     CodegenImpact impact = CodegenImpact.readFromDataSource(source);
     final deferredExpressionData =
-        js.DeferredExpressionData.readFromDataSource(source);
+        js.DeferredExpressionRegistry.readDataFromDataSource(source);
     source.end(tag);
     if (code != null) {
       code = code.withAnnotation(deferredExpressionData) as js.Fun;
@@ -452,10 +444,11 @@
   /// Writes the [CodegenResult] object to [sink].
   void writeToDataSink(DataSinkWriter sink) {
     sink.begin(tag);
-    deferredExpressionData.prepareForSerialization();
-    sink.writeJsNodeOrNull(code);
+    final registry = js.DeferredExpressionRegistry();
+    sink.withDeferredExpressionRegistry(
+        registry, () => sink.writeJsNodeOrNull(code));
     impact.writeToDataSink(sink);
-    deferredExpressionData.writeToDataSink(sink);
+    registry.writeToDataSink(sink);
     sink.end(tag);
   }
 
@@ -796,15 +789,14 @@
 /// them in the AST.
 class JsNodeSerializer implements js.NodeVisitor<void> {
   final DataSinkWriter sink;
-  final js.DeferredExpressionData deferredExpressionData;
+  final js.DeferredExpressionRegistry? _registry;
 
-  JsNodeSerializer._(this.sink, this.deferredExpressionData);
+  JsNodeSerializer._(this.sink, this._registry);
 
   static void writeToDataSink(DataSinkWriter sink, js.Node node,
-      js.DeferredExpressionData deferredExpressionData) {
+      js.DeferredExpressionRegistry? registry) {
     sink.begin(JsNodeTags.tag);
-    JsNodeSerializer serializer =
-        JsNodeSerializer._(sink, deferredExpressionData);
+    JsNodeSerializer serializer = JsNodeSerializer._(sink, registry);
     serializer.visit(node);
     sink.end(JsNodeTags.tag);
   }
@@ -981,7 +973,7 @@
         node.writeToDataSink(sink);
         _writeInfo(node);
       }, identity: true);
-      deferredExpressionData.registerModularName(node);
+      _registry?.registerModularName(node);
       sink.end(JsNodeTags.modularName);
     } else if (node is AsyncName) {
       sink.writeEnum(JsNodeKind.asyncName);
@@ -1077,7 +1069,7 @@
         node.writeToDataSink(sink);
         _writeInfo(node);
       }, identity: true);
-      deferredExpressionData.registerModularExpression(node);
+      _registry?.registerModularExpression(node);
       sink.end(JsNodeTags.modularExpression);
     } else if (node is TypeReference) {
       sink.writeEnum(JsNodeKind.typeReference);
@@ -1086,7 +1078,7 @@
         node.writeToDataSink(sink);
         _writeInfo(node);
       }, identity: true);
-      deferredExpressionData.registerTypeReference(node);
+      _registry?.registerTypeReference(node);
       sink.end(JsNodeTags.typeReference);
     } else if (node is StringReference) {
       sink.writeEnum(JsNodeKind.stringReference);
@@ -1095,7 +1087,7 @@
         node.writeToDataSink(sink);
         _writeInfo(node);
       }, identity: true);
-      deferredExpressionData.registerStringReference(node);
+      _registry?.registerStringReference(node);
       sink.end(JsNodeTags.stringReference);
     } else if (node is DeferredHolderExpression) {
       sink.writeEnum(JsNodeKind.deferredHolderExpression);
@@ -1104,7 +1096,7 @@
         node.writeToDataSink(sink);
         _writeInfo(node);
       }, identity: true);
-      deferredExpressionData.registerDeferredHolderExpression(node);
+      _registry?.registerDeferredHolderExpression(node);
       sink.end(JsNodeTags.deferredHolderExpression);
     } else {
       throw UnsupportedError(
@@ -1977,60 +1969,6 @@
   }
 }
 
-class CodegenReaderImpl implements CodegenReader {
-  final JClosedWorld closedWorld;
-
-  CodegenReaderImpl(this.closedWorld);
-
-  @override
-  AbstractValue readAbstractValue(DataSourceReader source) {
-    return closedWorld.abstractValueDomain
-        .readAbstractValueFromDataSource(source);
-  }
-
-  @override
-  js.Node readJsNode(DataSourceReader source) {
-    return JsNodeDeserializer.readFromDataSource(source);
-  }
-
-  @override
-  OutputUnit readOutputUnitReference(DataSourceReader source) {
-    return closedWorld.outputUnitData.outputUnits[source.readInt()];
-  }
-
-  @override
-  TypeRecipe readTypeRecipe(DataSourceReader source) {
-    return TypeRecipe.readFromDataSource(source);
-  }
-}
-
-class CodegenWriterImpl implements CodegenWriter {
-  final JClosedWorld closedWorld;
-  final js.DeferredExpressionData deferredExpressionData;
-
-  CodegenWriterImpl(this.closedWorld, this.deferredExpressionData);
-
-  @override
-  void writeAbstractValue(DataSinkWriter sink, AbstractValue value) {
-    closedWorld.abstractValueDomain.writeAbstractValueToDataSink(sink, value);
-  }
-
-  @override
-  void writeJsNode(DataSinkWriter sink, js.Node node) {
-    JsNodeSerializer.writeToDataSink(sink, node, deferredExpressionData);
-  }
-
-  @override
-  void writeOutputUnitReference(DataSinkWriter sink, OutputUnit value) {
-    sink.writeInt(closedWorld.outputUnitData.outputUnits.indexOf(value));
-  }
-
-  @override
-  void writeTypeRecipe(DataSinkWriter sink, TypeRecipe recipe) {
-    recipe.writeToDataSink(sink);
-  }
-}
-
 enum ModularNameKind {
   rtiField,
   className,
@@ -2296,7 +2234,6 @@
 
 /// Interface for reading the code generation results for all [MemberEntity]s.
 abstract class CodegenResults {
-  GlobalTypeInferenceResults get globalTypeInferenceResults;
   CodegenInputs get codegenInputs;
   CodegenResult getCodegenResults(MemberEntity member);
 }
@@ -2306,14 +2243,11 @@
 /// This is used for modular code generation.
 class DeserializedCodegenResults extends CodegenResults {
   @override
-  final GlobalTypeInferenceResults globalTypeInferenceResults;
-  @override
   final CodegenInputs codegenInputs;
 
   final Map<MemberEntity, CodegenResult> _map;
 
-  DeserializedCodegenResults(
-      this.globalTypeInferenceResults, this.codegenInputs, this._map);
+  DeserializedCodegenResults(this.codegenInputs, this._map);
 
   @override
   CodegenResult getCodegenResults(MemberEntity member) {
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index aabfe03..2fc3e76 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -36,6 +36,7 @@
 import 'elements/entities.dart';
 import 'enqueue.dart' show Enqueuer;
 import 'environment.dart';
+import 'inferrer/abstract_value_domain.dart';
 import 'inferrer/abstract_value_strategy.dart';
 import 'inferrer/computable.dart' show ComputableAbstractValueStrategy;
 import 'inferrer/powersets/powersets.dart' show PowersetStrategy;
@@ -522,18 +523,13 @@
   }
 
   int runCodegenEnqueuer(
-      CodegenResults codegenResults, SourceLookup sourceLookup) {
-    GlobalTypeInferenceResults globalInferenceResults =
-        codegenResults.globalTypeInferenceResults;
-    JClosedWorld closedWorld = globalInferenceResults.closedWorld;
+      CodegenResults codegenResults,
+      InferredData inferredData,
+      SourceLookup sourceLookup,
+      JClosedWorld closedWorld) {
     CodegenInputs codegenInputs = codegenResults.codegenInputs;
     CodegenEnqueuer codegenEnqueuer = backendStrategy.createCodegenEnqueuer(
-        enqueueTask,
-        closedWorld,
-        globalInferenceResults,
-        codegenInputs,
-        codegenResults,
-        sourceLookup)
+        enqueueTask, closedWorld, codegenInputs, codegenResults, sourceLookup)
       ..onEmptyForTesting = onCodegenQueueEmptyForTesting;
     if (retainDataForTesting) {
       codegenEnqueuerForTesting = codegenEnqueuer;
@@ -550,8 +546,8 @@
       codegenWorldForTesting = codegenWorld;
     }
     reporter.log('Emitting JavaScript');
-    int programSize = backendStrategy.assembleProgram(closedWorld,
-        globalInferenceResults.inferredData, codegenInputs, codegenWorld);
+    int programSize = backendStrategy.assembleProgram(
+        closedWorld, inferredData, codegenInputs, codegenWorld);
 
     backendStrategy.onCodegenEnd(codegenInputs);
 
@@ -684,16 +680,19 @@
     CodegenInputs codegenInputs = initializeCodegen(globalTypeInferenceResults);
     CodegenResults codegenResults;
     if (!stage.shouldReadCodegenShards) {
-      codegenResults = OnDemandCodegenResults(globalTypeInferenceResults,
+      codegenResults = OnDemandCodegenResults(
           codegenInputs, backendStrategy.functionCompiler);
       if (stage == Dart2JSStage.codegenSharded) {
         serializationTask.serializeCodegen(
-            backendStrategy, codegenResults, indices);
+            backendStrategy,
+            globalTypeInferenceResults.closedWorld.abstractValueDomain,
+            codegenResults,
+            indices);
       }
     } else {
       codegenResults = await serializationTask.deserializeCodegen(
           backendStrategy,
-          globalTypeInferenceResults,
+          globalTypeInferenceResults.closedWorld,
           codegenInputs,
           useDeferredSourceReads,
           sourceLookup,
@@ -733,13 +732,20 @@
         await produceGlobalTypeInferenceResults(
             closedWorld!, output.component, indices);
     if (shouldStopAfterGlobalTypeInference) return;
+    closedWorld = globalTypeInferenceResults.closedWorld;
 
     // Allow the original references to these to be GCed and only hold
     // references to them if we are actually running the dump info task later.
-    JClosedWorld? closedWorldForDumpInfo;
     SerializationIndices? indicesForDumpInfo;
-    if (options.dumpInfoWriteUri != null || options.dumpInfoReadUri != null) {
-      closedWorldForDumpInfo = closedWorld;
+    GlobalTypeInferenceResults? globalTypeInferenceResultsForDumpInfo;
+    AbstractValueDomain? abstractValueDomainForDumpInfo;
+    OutputUnitData? outputUnitDataForDumpInfo;
+    if (options.dumpInfoWriteUri != null ||
+        options.dumpInfoReadUri != null ||
+        options.dumpInfo) {
+      globalTypeInferenceResultsForDumpInfo = globalTypeInferenceResults;
+      abstractValueDomainForDumpInfo = closedWorld.abstractValueDomain;
+      outputUnitDataForDumpInfo = closedWorld.outputUnitData;
       indicesForDumpInfo = indices;
     }
 
@@ -748,33 +754,43 @@
     CodegenResults codegenResults = await produceCodegenResults(
         globalTypeInferenceResults, sourceLookup, indices);
     if (shouldStopAfterCodegen) return;
+    final inferredData = globalTypeInferenceResults.inferredData;
 
     if (options.dumpInfoReadUri != null) {
       final dumpInfoData =
           await serializationTask.deserializeDumpInfoProgramData(
-              backendStrategy, closedWorldForDumpInfo!, indicesForDumpInfo!);
-      await runDumpInfo(codegenResults, dumpInfoData);
+              backendStrategy,
+              abstractValueDomainForDumpInfo!,
+              outputUnitDataForDumpInfo!,
+              indicesForDumpInfo!);
+      await runDumpInfo(
+          codegenResults, globalTypeInferenceResultsForDumpInfo!, dumpInfoData);
     } else {
       // Link.
-      final programSize = runCodegenEnqueuer(codegenResults, sourceLookup);
+      final programSize = runCodegenEnqueuer(
+          codegenResults, inferredData, sourceLookup, closedWorld);
       if (options.dumpInfo || options.dumpInfoWriteUri != null) {
         final dumpInfoData = DumpInfoProgramData.fromEmitterResults(
             backendStrategy, dumpInfoRegistry, programSize);
         dumpInfoRegistry.clear();
         if (options.dumpInfo) {
-          await runDumpInfo(codegenResults, dumpInfoData);
+          await runDumpInfo(codegenResults,
+              globalTypeInferenceResultsForDumpInfo!, dumpInfoData);
         } else {
-          serializationTask.serializeDumpInfoProgramData(backendStrategy,
-              dumpInfoData, closedWorldForDumpInfo!, indicesForDumpInfo!);
+          serializationTask.serializeDumpInfoProgramData(
+              backendStrategy,
+              dumpInfoData,
+              abstractValueDomainForDumpInfo!,
+              indicesForDumpInfo!);
         }
       }
     }
   }
 
-  Future<void> runDumpInfo(CodegenResults codegenResults,
+  Future<void> runDumpInfo(
+      CodegenResults codegenResults,
+      GlobalTypeInferenceResults globalTypeInferenceResults,
       DumpInfoProgramData dumpInfoProgramData) async {
-    GlobalTypeInferenceResults globalTypeInferenceResults =
-        codegenResults.globalTypeInferenceResults;
     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
 
     DumpInfoStateData dumpInfoState;
diff --git a/pkg/compiler/lib/src/deferred_load/output_unit.dart b/pkg/compiler/lib/src/deferred_load/output_unit.dart
index a116e6f..e9551f8 100644
--- a/pkg/compiler/lib/src/deferred_load/output_unit.dart
+++ b/pkg/compiler/lib/src/deferred_load/output_unit.dart
@@ -22,6 +22,8 @@
 ///
 /// We never create two OutputUnits sharing the same set of [imports].
 class OutputUnit implements Comparable<OutputUnit> {
+  static const String tag = 'output-unit';
+
   /// `true` if this output unit is for the main output file.
   final bool isMainOutput;
 
@@ -33,6 +35,23 @@
 
   OutputUnit(this.isMainOutput, this.name, this.imports);
 
+  void writeToDataSink(DataSinkWriter sink) {
+    sink.begin(tag);
+    sink.writeBool(isMainOutput);
+    sink.writeString(name);
+    sink.writeImports(imports);
+    sink.end(tag);
+  }
+
+  static OutputUnit readFromDataSource(DataSourceReader source) {
+    source.begin(tag);
+    final isMainOutput = source.readBool();
+    final name = source.readString();
+    final imports = source.readImports().toSet();
+    source.end(tag);
+    return OutputUnit(isMainOutput, name, imports);
+  }
+
   @override
   int compareTo(OutputUnit other) {
     if (identical(this, other)) return 0;
@@ -167,12 +186,8 @@
   factory OutputUnitData.readFromDataSource(DataSourceReader source) {
     source.begin(tag);
     bool isProgramSplit = source.readBool();
-    List<OutputUnit> outputUnits = source.readList(() {
-      bool isMainOutput = source.readBool();
-      String name = source.readString();
-      Set<ImportEntity> importSet = source.readImports().toSet();
-      return OutputUnit(isMainOutput, name, importSet);
-    });
+    List<OutputUnit> outputUnits =
+        source.readList(source.readOutputUnitReference);
     OutputUnit mainOutputUnit = outputUnits[source.readInt()];
 
     Map<ClassEntity, OutputUnit> classToUnit = source.readClassMap(() {
@@ -219,9 +234,7 @@
     Map<OutputUnit, int> outputUnitIndices = {};
     sink.writeList(outputUnits, (OutputUnit outputUnit) {
       outputUnitIndices[outputUnit] = outputUnitIndices.length;
-      sink.writeBool(outputUnit.isMainOutput);
-      sink.writeString(outputUnit.name);
-      sink.writeImports(outputUnit.imports);
+      sink.writeOutputUnitReference(outputUnit);
     });
     sink.writeInt(outputUnitIndices[mainOutputUnit]!);
     sink.writeClassMap(_classToUnit, (OutputUnit outputUnit) {
diff --git a/pkg/compiler/lib/src/dump_info.dart b/pkg/compiler/lib/src/dump_info.dart
index 53fdd3b..bcb43a5 100644
--- a/pkg/compiler/lib/src/dump_info.dart
+++ b/pkg/compiler/lib/src/dump_info.dart
@@ -23,7 +23,8 @@
 import 'common/ram_usage.dart';
 import 'constants/values.dart'
     show ConstantValue, ConstantValueKind, DeferredGlobalConstantValue;
-import 'deferred_load/output_unit.dart' show OutputUnit, deferredPartFileName;
+import 'deferred_load/output_unit.dart'
+    show OutputUnit, OutputUnitData, deferredPartFileName;
 import 'elements/entities.dart';
 import 'elements/entity_utils.dart' as entity_utils;
 import 'elements/names.dart';
@@ -231,7 +232,7 @@
   }
 
   factory DumpInfoProgramData.readFromDataSource(
-      DataSourceReader source, JClosedWorld closedWorld,
+      DataSourceReader source, OutputUnitData outputUnitData,
       {required bool includeCodeText}) {
     final programSize = source.readInt();
     final outputUnitSizesLength = source.readInt();
@@ -275,7 +276,7 @@
           .readList(() => ConstantUse.readFromDataSource(source))
           .forEach((use) {
         assert(use.value.kind == ConstantValueKind.DEFERRED_GLOBAL);
-        closedWorld.outputUnitData.registerConstantDeferredUse(
+        outputUnitData.registerConstantDeferredUse(
             use.value as DeferredGlobalConstantValue);
       });
       return impactBuilder;
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index 62e2f61..87088ec 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -203,34 +203,15 @@
   return null;
 }
 
-/// Contains pointers to deferred expressions within a portion of the AST.
-///
-/// These objects are attached nodes that have been deserialized but whose
-/// bodies are kept in a serialized state. This allows us to skip deserializing
-/// the entire function body when we are trying to link these deferred
-/// expressions. A [DeferredExpressionData] will be added to
-/// [Node.annotations] for these functions. Visitors that just need these
-/// deferred expressions should then check the annotations for a [Node] and
-/// process them accordingly rather than deserializing all the [Node] children.
-class DeferredExpressionData {
-  final List<ModularName> modularNames;
-  final List<ModularExpression> modularExpressions;
-  final List<TypeReference> typeReferences;
-  final List<StringReference> stringReferences;
-  final List<DeferredHolderExpression> deferredHolderExpressions;
+class DeferredExpressionRegistry {
+  final List<ModularName> modularNames = [];
+  final List<ModularExpression> modularExpressions = [];
+  final List<TypeReference> typeReferences = [];
+  final List<StringReference> stringReferences = [];
+  final List<DeferredHolderExpression> deferredHolderExpressions = [];
 
-  DeferredExpressionData(this.modularNames, this.modularExpressions)
-      : typeReferences = [],
-        stringReferences = [],
-        deferredHolderExpressions = [];
-  DeferredExpressionData._(
-      this.modularNames,
-      this.modularExpressions,
-      this.typeReferences,
-      this.stringReferences,
-      this.deferredHolderExpressions);
-
-  factory DeferredExpressionData.readFromDataSource(DataSourceReader source) {
+  static DeferredExpressionData readDataFromDataSource(
+      DataSourceReader source) {
     final modularNames =
         source.readListOrNull(() => source.readJsNode() as ModularName) ??
             const [];
@@ -250,11 +231,8 @@
         typeReferences, stringReferences, deferredHolderExpressions);
   }
 
-  bool _serializing = false;
-
   void writeToDataSink(DataSinkWriter sink) {
     // Set [_serializing] so that we don't re-register nodes.
-    _serializing = true;
     sink.writeList(modularNames, (ModularName node) => sink.writeJsNode(node));
     sink.writeList(
         modularExpressions, (ModularExpression node) => sink.writeJsNode(node));
@@ -264,40 +242,57 @@
         stringReferences, (StringReference node) => sink.writeJsNode(node));
     sink.writeList(deferredHolderExpressions,
         (DeferredHolderExpression node) => sink.writeJsNode(node));
-    _serializing = false;
-  }
-
-  void prepareForSerialization() {
-    modularNames.clear();
-    modularExpressions.clear();
   }
 
   void registerModularName(ModularName node) {
-    if (_serializing) return;
     modularNames.add(node);
   }
 
   void registerModularExpression(ModularExpression node) {
-    if (_serializing) return;
     modularExpressions.add(node);
   }
 
   void registerTypeReference(TypeReference node) {
-    if (_serializing) return;
     typeReferences.add(node);
   }
 
   void registerStringReference(StringReference node) {
-    if (_serializing) return;
     stringReferences.add(node);
   }
 
   void registerDeferredHolderExpression(DeferredHolderExpression node) {
-    if (_serializing) return;
     deferredHolderExpressions.add(node);
   }
 }
 
+/// Contains pointers to deferred expressions within a portion of the AST.
+///
+/// These objects are attached nodes that have been deserialized but whose
+/// bodies are kept in a serialized state. This allows us to skip deserializing
+/// the entire function body when we are trying to link these deferred
+/// expressions. A [DeferredExpressionData] will be added to
+/// [Node.annotations] for these functions. Visitors that just need these
+/// deferred expressions should then check the annotations for a [Node] and
+/// process them accordingly rather than deserializing all the [Node] children.
+class DeferredExpressionData {
+  final List<ModularName> modularNames;
+  final List<ModularExpression> modularExpressions;
+  final List<TypeReference> typeReferences;
+  final List<StringReference> stringReferences;
+  final List<DeferredHolderExpression> deferredHolderExpressions;
+
+  DeferredExpressionData(this.modularNames, this.modularExpressions)
+      : typeReferences = const [],
+        stringReferences = const [],
+        deferredHolderExpressions = const [];
+  DeferredExpressionData._(
+      this.modularNames,
+      this.modularExpressions,
+      this.typeReferences,
+      this.stringReferences,
+      this.deferredHolderExpressions);
+}
+
 /// A code [Block] that has not been fully deserialized but instead holds a
 /// [Deferrable] to get the enclosed statements.
 ///
diff --git a/pkg/compiler/lib/src/js_model/js_strategy.dart b/pkg/compiler/lib/src/js_model/js_strategy.dart
index 6384790..c84eba8 100644
--- a/pkg/compiler/lib/src/js_model/js_strategy.dart
+++ b/pkg/compiler/lib/src/js_model/js_strategy.dart
@@ -237,7 +237,6 @@
   CodegenEnqueuer createCodegenEnqueuer(
       CompilerTask task,
       JClosedWorld closedWorld,
-      GlobalTypeInferenceResults globalInferenceResults,
       CodegenInputs codegen,
       CodegenResults codegenResults,
       SourceLookup sourceLookup) {
@@ -245,8 +244,7 @@
         closedWorld.interceptorData,
         closedWorld.commonElements,
         closedWorld.nativeData);
-    _onCodegenEnqueuerStart(
-        globalInferenceResults, codegen, oneShotInterceptorData);
+    _onCodegenEnqueuerStart(closedWorld, codegen, oneShotInterceptorData);
     ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
     CommonElements commonElements = closedWorld.commonElements;
     BackendImpacts impacts = BackendImpacts(commonElements, _compiler.options);
@@ -261,7 +259,7 @@
             oneShotInterceptorData),
         KernelCodegenWorkItemBuilder(
             this,
-            closedWorld,
+            closedWorld.abstractValueDomain,
             codegenResults,
             // TODO(johnniwinther): Avoid the need for a [ComponentLookup]. This
             // is caused by some type masks holding a kernel node for using in
@@ -283,11 +281,8 @@
   }
 
   /// Called before the compiler starts running the codegen enqueuer.
-  void _onCodegenEnqueuerStart(
-      GlobalTypeInferenceResults globalTypeInferenceResults,
-      CodegenInputs codegen,
+  void _onCodegenEnqueuerStart(JClosedWorld closedWorld, CodegenInputs codegen,
       OneShotInterceptorData oneShotInterceptorData) {
-    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
     FixedNames fixedNames = codegen.fixedNames;
     _namer = _compiler.options.enableMinification
         ? _compiler.options.useFrequencyNamer
@@ -324,7 +319,7 @@
 
   WorldImpact generateCode(
       WorkItem work,
-      JClosedWorld closedWorld,
+      AbstractValueDomain abstractValueDomain,
       CodegenResults codegenResults,
       ComponentLookup componentLookup,
       SourceLookup sourceLookup) {
@@ -337,14 +332,13 @@
       DataSinkWriter sink = DataSinkWriter(
           ObjectDataSink(data), _compiler.options, indices,
           useDataKinds: useDataKinds);
-      sink.registerCodegenWriter(
-          CodegenWriterImpl(closedWorld, result.deferredExpressionData));
+      sink.registerAbstractValueDomain(abstractValueDomain);
       result.writeToDataSink(sink);
       sink.close();
       DataSourceReader source = DataSourceReader(
           ObjectDataSource(data), _compiler.options, indices,
           useDataKinds: useDataKinds);
-      source.registerCodegenReader(CodegenReaderImpl(closedWorld));
+      source.registerAbstractValueDomain(abstractValueDomain);
       source.registerComponentLookup(componentLookup);
       source.registerSourceLookup(sourceLookup);
       result = CodegenResult.readFromDataSource(source);
@@ -430,25 +424,25 @@
 
 class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
   final JsBackendStrategy _backendStrategy;
-  final JClosedWorld _closedWorld;
+  final AbstractValueDomain _abstractValueDomain;
   final CodegenResults _codegenResults;
   final ComponentLookup _componentLookup;
   final SourceLookup _sourceLookup;
 
-  KernelCodegenWorkItemBuilder(this._backendStrategy, this._closedWorld,
+  KernelCodegenWorkItemBuilder(this._backendStrategy, this._abstractValueDomain,
       this._codegenResults, this._componentLookup, this._sourceLookup);
 
   @override
   WorkItem? createWorkItem(MemberEntity entity) {
     if (entity.isAbstract) return null;
-    return KernelCodegenWorkItem(_backendStrategy, _closedWorld,
+    return KernelCodegenWorkItem(_backendStrategy, _abstractValueDomain,
         _codegenResults, _componentLookup, _sourceLookup, entity);
   }
 }
 
 class KernelCodegenWorkItem extends WorkItem {
   final JsBackendStrategy _backendStrategy;
-  final JClosedWorld _closedWorld;
+  final AbstractValueDomain _abstractValueDomain;
   final CodegenResults _codegenResults;
   final ComponentLookup _componentLookup;
   final SourceLookup _sourceLookup;
@@ -457,7 +451,7 @@
 
   KernelCodegenWorkItem(
       this._backendStrategy,
-      this._closedWorld,
+      this._abstractValueDomain,
       this._codegenResults,
       this._componentLookup,
       this._sourceLookup,
@@ -465,8 +459,8 @@
 
   @override
   WorldImpact run() {
-    return _backendStrategy.generateCode(
-        this, _closedWorld, _codegenResults, _componentLookup, _sourceLookup);
+    return _backendStrategy.generateCode(this, _abstractValueDomain,
+        _codegenResults, _componentLookup, _sourceLookup);
   }
 }
 
diff --git a/pkg/compiler/lib/src/serialization/serialization.dart b/pkg/compiler/lib/src/serialization/serialization.dart
index b119412..2028ef7 100644
--- a/pkg/compiler/lib/src/serialization/serialization.dart
+++ b/pkg/compiler/lib/src/serialization/serialization.dart
@@ -7,6 +7,7 @@
 import 'package:kernel/ast.dart' as ir;
 import '../closure.dart';
 import '../common.dart';
+import '../common/codegen.dart';
 import '../constants/constant_system.dart' as constant_system;
 import '../constants/values.dart';
 import '../deferred_load/output_unit.dart' show OutputUnit;
diff --git a/pkg/compiler/lib/src/serialization/sink.dart b/pkg/compiler/lib/src/serialization/sink.dart
index f048578..cf9186a 100644
--- a/pkg/compiler/lib/src/serialization/sink.dart
+++ b/pkg/compiler/lib/src/serialization/sink.dart
@@ -68,7 +68,8 @@
 
   final Map<Type, IndexedSink> _generalCaches = {};
 
-  late CodegenWriter _codegenWriter;
+  late AbstractValueDomain _abstractValueDomain;
+  js.DeferredExpressionRegistry? _deferredExpressionRegistry;
 
   final Map<String, int>? tagFrequencyMap;
 
@@ -1183,28 +1184,30 @@
 
   /// Writes an abstract [value] to this data sink.
   ///
-  /// This feature is only available a [CodegenWriter] has been registered.
+  /// This feature is only available a [AbstractValueDomain] has been
+  /// registered.
   void writeAbstractValue(AbstractValue value) {
-    _codegenWriter.writeAbstractValue(this, value);
+    _abstractValueDomain.writeAbstractValueToDataSink(this, value);
   }
 
   /// Writes a reference to the output unit [value] to this data sink.
-  ///
-  /// This feature is only available a [CodegenWriter] has been registered.
   void writeOutputUnitReference(OutputUnit value) {
-    _codegenWriter.writeOutputUnitReference(this, value);
+    writeCached<OutputUnit>(value, (v) => v.writeToDataSink(this));
+  }
+
+  void withDeferredExpressionRegistry(
+      js.DeferredExpressionRegistry registry, void Function() f) {
+    _deferredExpressionRegistry = registry;
+    f();
+    _deferredExpressionRegistry = null;
   }
 
   /// Writes a js node [value] to this data sink.
-  ///
-  /// This feature is only available a [CodegenWriter] has been registered.
   void writeJsNode(js.Node value) {
-    _codegenWriter.writeJsNode(this, value);
+    JsNodeSerializer.writeToDataSink(this, value, _deferredExpressionRegistry);
   }
 
   /// Writes a potentially `null` js node [value] to this data sink.
-  ///
-  /// This feature is only available a [CodegenWriter] has been registered.
   void writeJsNodeOrNull(js.Node? value) {
     writeBool(value != null);
     if (value != null) {
@@ -1213,16 +1216,14 @@
   }
 
   /// Writes TypeRecipe [value] to this data sink.
-  ///
-  /// This feature is only available a [CodegenWriter] has been registered.
   void writeTypeRecipe(TypeRecipe value) {
-    _codegenWriter.writeTypeRecipe(this, value);
+    value.writeToDataSink(this);
   }
 
-  /// Register a [CodegenWriter] with this data sink to support serialization
-  /// of codegen only data.
-  void registerCodegenWriter(CodegenWriter writer) {
-    _codegenWriter = writer;
+  /// Register a [AbstractValueDomain] with this data sink to support
+  /// serialization of abstract values.
+  void registerAbstractValueDomain(AbstractValueDomain domain) {
+    _abstractValueDomain = domain;
   }
 
   /// Invoke [f] in the context of [member]. This sets up support for
diff --git a/pkg/compiler/lib/src/serialization/source.dart b/pkg/compiler/lib/src/serialization/source.dart
index 7086aa7..a141c35 100644
--- a/pkg/compiler/lib/src/serialization/source.dart
+++ b/pkg/compiler/lib/src/serialization/source.dart
@@ -65,7 +65,7 @@
   final ValueInterner? interner;
   final SerializationIndices importedIndices;
   ComponentLookup? _componentLookup;
-  CodegenReader? _codegenReader;
+  AbstractValueDomain? _abstractValueDomain;
   SourceLookup? _sourceLookup;
 
   late final IndexedSource<String> _stringIndex;
@@ -138,9 +138,10 @@
 
   SourceLookup get sourceLookup => _sourceLookup!;
 
-  /// deserialization of codegen only data.
-  void registerCodegenReader(CodegenReader reader) {
-    _codegenReader = reader;
+  /// Registers a [AbstractValueDomain] with this data source to support
+  /// deserialization of abstract values.
+  void registerAbstractValueDomain(AbstractValueDomain domain) {
+    _abstractValueDomain = domain;
   }
 
   /// Evaluates [f] with [DataSource] for the provided [source] as the
@@ -149,17 +150,17 @@
   E readWithSource<E>(DataSourceReader source, E f()) {
     final lastSource = _sourceReader;
     final lastComponentLookup = _componentLookup;
-    final lastCodegenReader = _codegenReader;
     final lastStartOffset = startOffset;
+    final lastAbstractValueDomain = _abstractValueDomain;
     _sourceReader = source._sourceReader;
     _componentLookup = source._componentLookup;
-    _codegenReader = source._codegenReader;
     startOffset = source.startOffset;
+    _abstractValueDomain = source._abstractValueDomain;
     final value = f();
     _sourceReader = lastSource;
     _componentLookup = lastComponentLookup;
-    _codegenReader = lastCodegenReader;
     startOffset = lastStartOffset;
+    _abstractValueDomain = lastAbstractValueDomain;
     return value;
   }
 
@@ -1417,39 +1418,25 @@
   }
 
   /// Reads an [AbstractValue] from this data source.
-  ///
-  /// This feature is only available a [CodegenReader] has been registered.
   AbstractValue readAbstractValue() {
     assert(
-        _codegenReader != null,
+        _abstractValueDomain != null,
         "Can not deserialize an AbstractValue "
-        "without a registered codegen reader.");
-    return _codegenReader!.readAbstractValue(this);
+        "without a registered AbstractValueDomain.");
+    return _abstractValueDomain!.readAbstractValueFromDataSource(this);
   }
 
   /// Reads a reference to an [OutputUnit] from this data source.
-  ///
-  /// This feature is only available a [CodegenReader] has been registered.
   OutputUnit readOutputUnitReference() {
-    assert(
-        _codegenReader != null,
-        "Can not deserialize an OutputUnit reference "
-        "without a registered codegen reader.");
-    return _codegenReader!.readOutputUnitReference(this);
+    return readCached<OutputUnit>(() => OutputUnit.readFromDataSource(this));
   }
 
   /// Reads a [js.Node] value from this data source.
-  ///
-  /// This feature is only available a [CodegenReader] has been registered.
   js.Node readJsNode() {
-    assert(_codegenReader != null,
-        "Can not deserialize a JS node without a registered codegen reader.");
-    return _codegenReader!.readJsNode(this);
+    return JsNodeDeserializer.readFromDataSource(this);
   }
 
   /// Reads a potentially `null` [js.Node] value from this data source.
-  ///
-  /// This feature is only available a [CodegenReader] has been registered.
   js.Node? readJsNodeOrNull() {
     bool hasValue = readBool();
     if (hasValue) {
@@ -1459,12 +1446,8 @@
   }
 
   /// Reads a [TypeRecipe] value from this data source.
-  ///
-  /// This feature is only available a [CodegenReader] has been registered.
   TypeRecipe readTypeRecipe() {
-    assert(_codegenReader != null,
-        "Can not deserialize a TypeRecipe without a registered codegen reader.");
-    return _codegenReader!.readTypeRecipe(this);
+    return TypeRecipe.readFromDataSource(this);
   }
 
   MemberData _getMemberData(ir.Member node) {
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 54b9d55..694b70c 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'package:compiler/src/js/js.dart';
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/binary/ast_from_binary.dart' as ir;
 import 'package:kernel/binary/ast_to_binary.dart' as ir;
@@ -12,10 +11,12 @@
 import '../commandline_options.dart' show Flags;
 import '../common/codegen.dart';
 import '../common/tasks.dart';
+import '../deferred_load/output_unit.dart';
 import '../diagnostics/diagnostic_listener.dart';
 import '../dump_info.dart';
 import '../elements/entities.dart';
 import '../environment.dart';
+import '../inferrer/abstract_value_domain.dart';
 import '../inferrer/abstract_value_strategy.dart';
 import '../inferrer/types.dart';
 import '../io/source_information.dart';
@@ -265,11 +266,11 @@
     });
   }
 
-  void serializeCodegen(JsBackendStrategy backendStrategy,
-      CodegenResults codegenResults, SerializationIndices indices) {
-    GlobalTypeInferenceResults globalTypeInferenceResults =
-        codegenResults.globalTypeInferenceResults;
-    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
+  void serializeCodegen(
+      JsBackendStrategy backendStrategy,
+      AbstractValueDomain domain,
+      CodegenResults codegenResults,
+      SerializationIndices indices) {
     int shard = _options.codegenShard!;
     int shards = _options.codegenShards!;
     Map<MemberEntity, CodegenResult> results = {};
@@ -291,9 +292,8 @@
           DataSinkWriter(BinaryDataSink(dataOutput), _options, indices);
       _reporter.log('Writing data to ${uri}');
       sink.writeMembers(lazyMemberBodies);
+      sink.registerAbstractValueDomain(domain);
       sink.writeMemberMap(results, (MemberEntity member, CodegenResult result) {
-        sink.registerCodegenWriter(
-            CodegenWriterImpl(closedWorld, result.deferredExpressionData));
         sink.writeDeferrable(() => result.writeToDataSink(sink));
       });
       sink.close();
@@ -302,13 +302,12 @@
 
   Future<CodegenResults> deserializeCodegen(
       JsBackendStrategy backendStrategy,
-      GlobalTypeInferenceResults globalTypeInferenceResults,
+      JClosedWorld closedWorld,
       CodegenInputs codegenInputs,
       bool useDeferredSourceReads,
       SourceLookup sourceLookup,
       SerializationIndices indices) async {
     int shards = _options.codegenShards!;
-    JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld;
     Map<MemberEntity, Deferrable<CodegenResult>> results = {};
     for (int shard = 0; shard < shards; shard++) {
       Uri uri = Uri.parse(
@@ -325,15 +324,7 @@
       });
     }
     return DeserializedCodegenResults(
-        globalTypeInferenceResults, codegenInputs, DeferrableValueMap(results));
-  }
-
-  static CodegenResult _readCodegenResult(
-      DataSourceReader source, JClosedWorld closedWorld) {
-    CodegenReader reader = CodegenReaderImpl(closedWorld);
-    source.registerCodegenReader(reader);
-    CodegenResult result = CodegenResult.readFromDataSource(source);
-    return result;
+        codegenInputs, DeferrableValueMap(results));
   }
 
   void _deserializeCodegenInput(
@@ -355,9 +346,10 @@
     source.registerSourceLookup(sourceLookup);
     final lazyMemberBodies = source.readMembers();
     closedWorld.elementMap.registerLazyMemberBodies(lazyMemberBodies);
+    source.registerAbstractValueDomain(closedWorld.abstractValueDomain);
     Map<MemberEntity, Deferrable<CodegenResult>> codegenResults =
         source.readMemberMap((MemberEntity member) {
-      return source.readDeferrableWithArg(_readCodegenResult, closedWorld,
+      return source.readDeferrable(CodegenResult.readFromDataSource,
           cacheData: false);
     });
     _reporter.log('Read ${codegenResults.length} members from ${uri}');
@@ -367,21 +359,21 @@
   void serializeDumpInfoProgramData(
       JsBackendStrategy backendStrategy,
       DumpInfoProgramData dumpInfoProgramData,
-      JClosedWorld closedWorld,
+      AbstractValueDomain abstractValueDomain,
       SerializationIndices indices) {
     final outputUri = _options.dumpInfoWriteUri!;
     api.BinaryOutputSink dataOutput =
         _outputProvider.createBinarySink(outputUri);
     final sink = DataSinkWriter(BinaryDataSink(dataOutput), _options, indices);
-    sink.registerCodegenWriter(
-        CodegenWriterImpl(closedWorld, DeferredExpressionData([], [])));
+    sink.registerAbstractValueDomain(abstractValueDomain);
     dumpInfoProgramData.writeToDataSink(sink);
     sink.close();
   }
 
   Future<DumpInfoProgramData> deserializeDumpInfoProgramData(
       JsBackendStrategy backendStrategy,
-      JClosedWorld closedWorld,
+      AbstractValueDomain abstractValueDomain,
+      OutputUnitData outputUnitData,
       SerializationIndices indices) async {
     final inputUri = _options.dumpInfoReadUri!;
     final dataInput =
@@ -391,8 +383,8 @@
         _options,
         indices);
     backendStrategy.prepareCodegenReader(source);
-    source.registerCodegenReader(CodegenReaderImpl(closedWorld));
-    return DumpInfoProgramData.readFromDataSource(source, closedWorld,
+    source.registerAbstractValueDomain(abstractValueDomain);
+    return DumpInfoProgramData.readFromDataSource(source, outputUnitData,
         includeCodeText: !_options.useDumpInfoBinaryFormat);
   }
 }
diff --git a/pkg/compiler/test/end_to_end/exit_code_test.dart b/pkg/compiler/test/end_to_end/exit_code_test.dart
index 318c0a3..b0d8b91 100644
--- a/pkg/compiler/test/end_to_end/exit_code_test.dart
+++ b/pkg/compiler/test/end_to_end/exit_code_test.dart
@@ -21,8 +21,8 @@
 import 'package:compiler/src/diagnostics/messages.dart';
 import 'package:compiler/src/diagnostics/spannable.dart';
 import 'package:compiler/src/elements/entities.dart';
+import 'package:compiler/src/inferrer/abstract_value_domain.dart';
 import 'package:compiler/src/js_model/js_strategy.dart';
-import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld;
 import 'package:compiler/src/null_compiler_output.dart';
 import 'package:compiler/src/serialization/serialization.dart';
 import 'package:compiler/src/options.dart' show CompilerOptions;
@@ -107,13 +107,13 @@
   @override
   WorldImpact generateCode(
       WorkItem work,
-      JClosedWorld closedWorld,
+      AbstractValueDomain abstractValueDomain,
       CodegenResults codegenResults,
       ComponentLookup componentLookup,
       SourceLookup sourceLookup) {
     compiler.test('Compiler.codegen');
-    return super.generateCode(
-        work, closedWorld, codegenResults, componentLookup, sourceLookup);
+    return super.generateCode(work, abstractValueDomain, codegenResults,
+        componentLookup, sourceLookup);
   }
 }
 
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index 2fa3398..062a6fa 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -30,13 +30,17 @@
 Future<void> generateJavaScriptCode(Compiler compiler,
     GlobalTypeInferenceResults globalTypeInferenceResults) async {
   final codegenInputs = compiler.initializeCodegen(globalTypeInferenceResults);
-  final codegenResults = OnDemandCodegenResults(globalTypeInferenceResults,
+  final codegenResults = OnDemandCodegenResults(
       codegenInputs, compiler.backendStrategy.functionCompiler);
   final programSize = compiler.runCodegenEnqueuer(
-      codegenResults, SourceLookup(compiler.componentForTesting));
+      codegenResults,
+      globalTypeInferenceResults.inferredData,
+      SourceLookup(compiler.componentForTesting),
+      globalTypeInferenceResults.closedWorld);
   if (compiler.options.dumpInfo) {
     await compiler.runDumpInfo(
         codegenResults,
+        globalTypeInferenceResults,
         DumpInfoProgramData.fromEmitterResults(
             compiler.backendStrategy, compiler.dumpInfoRegistry, programSize));
   }