[dart2js] Avoid reserializing the closed world alongside type inference.

This cl adds a temporary flag '--no-closed-world-in-data' in order to
support serializing global inference results with and without the closed
world.

This flag only exists to allow us to roll this into google3. Afterwards,
it will be removed.

Change-Id: I6e9712739edd148edcf43007b6ece35efa688538
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192124
Commit-Queue: Joshua Litt <joshualitt@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index e0c45c8..45b02a0 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -106,6 +106,7 @@
   static const String dillDependencies = '--dill-dependencies';
   static const String readData = '--read-data';
   static const String writeData = '--write-data';
+  static const String noClosedWorldInData = '--no-closed-world-in-data';
   static const String writeClosedWorld = '--write-closed-world';
   static const String readClosedWorld = '--read-closed-world';
   static const String readCodegen = '--read-codegen';
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 571e8ce..687c158 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -236,6 +236,10 @@
         options.readCodegenUri == null;
   }
 
+  bool get onlyPerformCodegen {
+    return options.readClosedWorldUri != null && options.readDataUri != null;
+  }
+
   Future runInternal(Uri uri) async {
     clearState();
     assert(uri != null);
@@ -250,30 +254,32 @@
       GlobalTypeInferenceResults globalTypeInferenceResults =
           performGlobalTypeInference(closedWorld);
       if (options.writeDataUri != null) {
-        serializationTask
-            .serializeGlobalTypeInference(globalTypeInferenceResults);
+        if (options.noClosedWorldInData) {
+          serializationTask
+              .serializeGlobalTypeInference(globalTypeInferenceResults);
+        } else {
+          serializationTask
+              .serializeGlobalTypeInferenceLegacy(globalTypeInferenceResults);
+        }
         return;
       }
       await generateJavaScriptCode(globalTypeInferenceResults);
-    } else if (options.readDataUri != null) {
+    } else if (onlyPerformCodegen) {
       GlobalTypeInferenceResults globalTypeInferenceResults;
-      if (options.readClosedWorldUri != null) {
-        ir.Component component =
-            await serializationTask.deserializeComponentAndUpdateOptions();
-        JsClosedWorld closedWorld =
-            await serializationTask.deserializeClosedWorld(
-                environment, abstractValueStrategy, component);
-        globalTypeInferenceResults =
-            await serializationTask.deserializeGlobalAnalysis(
-                environment, abstractValueStrategy, component, closedWorld);
-      } else {
-        globalTypeInferenceResults = await serializationTask
-            .deserializeGlobalTypeInference(environment, abstractValueStrategy);
-      }
-      if (options.debugGlobalInference) {
-        performGlobalTypeInference(globalTypeInferenceResults.closedWorld);
-        return;
-      }
+      ir.Component component =
+          await serializationTask.deserializeComponentAndUpdateOptions();
+      JsClosedWorld closedWorld =
+          await serializationTask.deserializeClosedWorld(
+              environment, abstractValueStrategy, component);
+      globalTypeInferenceResults =
+          await serializationTask.deserializeGlobalTypeInferenceResults(
+              environment, abstractValueStrategy, component, closedWorld);
+      await generateJavaScriptCode(globalTypeInferenceResults);
+    } else if (options.readDataUri != null) {
+      // TODO(joshualitt) delete and clean up after google3 roll
+      var globalTypeInferenceResults =
+          await serializationTask.deserializeGlobalTypeInferenceLegacy(
+              environment, abstractValueStrategy);
       await generateJavaScriptCode(globalTypeInferenceResults);
     } else {
       KernelResult result = await kernelLoader.load(uri);
@@ -444,14 +450,18 @@
       GlobalTypeInferenceResults results) {
     SerializationStrategy strategy = const BytesInMemorySerializationStrategy();
     List<int> irData = strategy.unpackAndSerializeComponent(results);
-    List worldData = strategy.serializeGlobalTypeInferenceResults(results);
+    List<int> closedWorldData =
+        strategy.serializeClosedWorld(results.closedWorld);
+    List<int> globalTypeInferenceResultsData =
+        strategy.serializeGlobalTypeInferenceResults(results);
     return strategy.deserializeGlobalTypeInferenceResults(
         options,
         reporter,
         environment,
         abstractValueStrategy,
         strategy.deserializeComponent(irData),
-        worldData);
+        closedWorldData,
+        globalTypeInferenceResultsData);
   }
 
   void compileFromKernel(Uri rootLibraryUri, Iterable<Uri> libraries) {
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index d4a9558..b96bd5d 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -277,8 +277,15 @@
     if (argument != Flags.readData) {
       readDataUri = fe.nativeToUri(extractPath(argument, isDirectory: false));
     }
-    if (readStrategy != ReadStrategy.fromCodegen) {
+
+    if (readStrategy == ReadStrategy.fromDart) {
       readStrategy = ReadStrategy.fromData;
+    } else if (readStrategy == ReadStrategy.fromClosedWorld) {
+      readStrategy = ReadStrategy.fromDataAndClosedWorld;
+    } else if (readStrategy == ReadStrategy.fromCodegen) {
+      readStrategy = ReadStrategy.fromCodegenAndData;
+    } else if (readStrategy == ReadStrategy.fromCodegenAndClosedWorld) {
+      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
     }
   }
 
@@ -287,7 +294,16 @@
       readClosedWorldUri =
           fe.nativeToUri(extractPath(argument, isDirectory: false));
     }
-    readStrategy = ReadStrategy.fromClosedWorld;
+
+    if (readStrategy == ReadStrategy.fromDart) {
+      readStrategy = ReadStrategy.fromClosedWorld;
+    } else if (readStrategy == ReadStrategy.fromData) {
+      readStrategy = ReadStrategy.fromDataAndClosedWorld;
+    } else if (readStrategy == ReadStrategy.fromCodegen) {
+      readStrategy = ReadStrategy.fromCodegenAndClosedWorld;
+    } else if (readStrategy == ReadStrategy.fromCodegenAndData) {
+      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
+    }
   }
 
   void setDillDependencies(String argument) {
@@ -318,7 +334,16 @@
       readCodegenUri =
           fe.nativeToUri(extractPath(argument, isDirectory: false));
     }
-    readStrategy = ReadStrategy.fromCodegen;
+
+    if (readStrategy == ReadStrategy.fromDart) {
+      readStrategy = ReadStrategy.fromCodegen;
+    } else if (readStrategy == ReadStrategy.fromClosedWorld) {
+      readStrategy = ReadStrategy.fromCodegenAndClosedWorld;
+    } else if (readStrategy == ReadStrategy.fromData) {
+      readStrategy = ReadStrategy.fromCodegenAndData;
+    } else if (readStrategy == ReadStrategy.fromDataAndClosedWorld) {
+      readStrategy = ReadStrategy.fromCodegenAndClosedWorldAndData;
+    }
   }
 
   void setWriteData(String argument) {
@@ -466,6 +491,7 @@
     new OptionHandler('${Flags.dillDependencies}=.+', setDillDependencies),
     new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
     new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
+    new OptionHandler(Flags.noClosedWorldInData, passThrough),
     new OptionHandler('${Flags.readClosedWorld}|${Flags.readClosedWorld}=.+',
         setReadClosedWorld),
     new OptionHandler('${Flags.writeClosedWorld}|${Flags.writeClosedWorld}=.+',
@@ -709,7 +735,9 @@
       if (readStrategy == ReadStrategy.fromCodegen) {
         fail("Cannot read and write serialized codegen simultaneously.");
       }
-      if (readStrategy != ReadStrategy.fromData) {
+      // TODO(joshualitt) cleanup after google3 roll.
+      if (readStrategy != ReadStrategy.fromData &&
+          readStrategy != ReadStrategy.fromDataAndClosedWorld) {
         fail("Can only write serialized codegen from serialized data.");
       }
       if (codegenShards == null) {
@@ -737,6 +765,8 @@
       options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
       break;
     case ReadStrategy.fromData:
+      // TODO(joshualitt): fail after Google3 roll.
+      // fail("Must read from closed world and data.");
       readDataUri ??= Uri.base.resolve('$scriptName.data');
       options.add('${Flags.readData}=${readDataUri}');
       break;
@@ -747,6 +777,8 @@
       options.add('${Flags.readData}=${readDataUri}');
       break;
     case ReadStrategy.fromCodegen:
+    case ReadStrategy.fromCodegenAndData:
+      // TODO(joshualitt): fall through to fail after google3 roll.
       readDataUri ??= Uri.base.resolve('$scriptName.data');
       options.add('${Flags.readData}=${readDataUri}');
       readCodegenUri ??= Uri.base.resolve('$scriptName.code');
@@ -760,6 +792,9 @@
       options.add('${Flags.codegenShards}=$codegenShards');
       break;
     case ReadStrategy.fromCodegenAndClosedWorld:
+      fail("Must read from closed world, data, and codegen");
+      break;
+    case ReadStrategy.fromCodegenAndClosedWorldAndData:
       readClosedWorldUri ??= Uri.base.resolve('$scriptName.world');
       options.add('${Flags.readClosedWorld}=${readClosedWorldUri}');
       readDataUri ??= Uri.base.resolve('$scriptName.data');
@@ -816,6 +851,8 @@
         summary = 'Data files $input and $dataInput ';
         break;
       case ReadStrategy.fromData:
+        // TODO(joshualitt): fail after google3 roll.
+        //fail("Must read from closed world and data.");
         inputName = 'bytes data';
         inputSize = inputProvider.dartCharactersRead;
         String dataInput =
@@ -832,6 +869,8 @@
         summary = 'Data files $input, $worldInput, and $dataInput ';
         break;
       case ReadStrategy.fromCodegen:
+      case ReadStrategy.fromCodegenAndData:
+        // TODO(joshualitt): Fall through to fail after google3 roll.
         inputName = 'bytes data';
         inputSize = inputProvider.dartCharactersRead;
         String dataInput =
@@ -842,6 +881,9 @@
             '${codeInput}[0-${codegenShards - 1}] ';
         break;
       case ReadStrategy.fromCodegenAndClosedWorld:
+        fail("Must read from closed world, data, and codegen");
+        break;
+      case ReadStrategy.fromCodegenAndClosedWorldAndData:
         inputName = 'bytes data';
         inputSize = inputProvider.dartCharactersRead;
         String worldInput =
@@ -1373,12 +1415,17 @@
   });
 }
 
+// TODO(joshualitt): Clean up the combinatorial explosion of read strategies.
+// Right now only fromClosedWorld, fromDataAndClosedWorld, and
+// fromCodegenAndClosedWorldAndData are valid.
 enum ReadStrategy {
   fromDart,
   fromClosedWorld,
   fromData,
   fromDataAndClosedWorld,
   fromCodegen,
-  fromCodegenAndClosedWorld
+  fromCodegenAndClosedWorld,
+  fromCodegenAndData,
+  fromCodegenAndClosedWorldAndData,
 }
 enum WriteStrategy { toKernel, toClosedWorld, toData, toCodegen, toJs }
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 03edd91..ce9cddb 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -77,6 +77,11 @@
   /// If this is set, the compilation stops after type inference.
   Uri writeDataUri;
 
+  /// Serialize data without the closed world.
+  /// TODO(joshualitt) make this the default right after landing in Google3 and
+  /// clean up.
+  bool noClosedWorldInData = false;
+
   /// Location from which the serialized closed world is read.
   ///
   /// If this is set, the [entryPoint] is expected to be a .dill file and the
@@ -542,6 +547,7 @@
           _extractUriListOption(options, '${Flags.dillDependencies}')
       ..readDataUri = _extractUriOption(options, '${Flags.readData}=')
       ..writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
+      ..noClosedWorldInData = _hasOption(options, Flags.noClosedWorldInData)
       ..readClosedWorldUri =
           _extractUriOption(options, '${Flags.readClosedWorld}=')
       ..writeClosedWorldUri =
diff --git a/pkg/compiler/lib/src/serialization/strategies.dart b/pkg/compiler/lib/src/serialization/strategies.dart
index 37fbe89..9269eed 100644
--- a/pkg/compiler/lib/src/serialization/strategies.dart
+++ b/pkg/compiler/lib/src/serialization/strategies.dart
@@ -49,7 +49,8 @@
       Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
-      List<T> data);
+      List<T> closedWorldData,
+      List<T> globalTypeInferenceResultsData);
 
   List<T> serializeClosedWorld(JsClosedWorld closedWorld);
 
@@ -83,10 +84,28 @@
       Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
-      List<int> data) {
-    DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+      List<int> closedWorldData,
+      List<int> globalTypeInferenceResultsData) {
+    DataSource closedWorldSource =
+        BinarySourceImpl(closedWorldData, useDataKinds: useDataKinds);
+    DataSource globalTypeInferenceResultsSource = BinarySourceImpl(
+        globalTypeInferenceResultsData,
+        useDataKinds: useDataKinds);
+    JsClosedWorld closedWorld = deserializeClosedWorldFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorldSource);
+    return deserializeGlobalTypeInferenceResultsFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorld,
+        globalTypeInferenceResultsSource);
   }
 
   @override
@@ -134,10 +153,28 @@
       Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
-      List<int> data) {
-    DataSource source = new BinarySourceImpl(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+      List<int> closedWorldData,
+      List<int> globalTypeInferenceResultsData) {
+    DataSource closedWorldSource =
+        BinarySourceImpl(closedWorldData, useDataKinds: useDataKinds);
+    DataSource globalTypeInferenceResultsSource = BinarySourceImpl(
+        globalTypeInferenceResultsData,
+        useDataKinds: useDataKinds);
+    JsClosedWorld closedWorld = deserializeClosedWorldFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorldSource);
+    return deserializeGlobalTypeInferenceResultsFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorld,
+        globalTypeInferenceResultsSource);
   }
 
   @override
@@ -186,10 +223,28 @@
       Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
-      List<Object> data) {
-    DataSource source = new ObjectSource(data, useDataKinds: useDataKinds);
-    return deserializeGlobalTypeInferenceResultsFromSource(options, reporter,
-        environment, abstractValueStrategy, component, source);
+      List<Object> closedWorldData,
+      List<Object> globalTypeInferenceResultsData) {
+    DataSource closedWorldSource =
+        ObjectSource(closedWorldData, useDataKinds: useDataKinds);
+    DataSource globalTypeInferenceResultsSource = ObjectSource(
+        globalTypeInferenceResultsData,
+        useDataKinds: useDataKinds);
+    JsClosedWorld closedWorld = deserializeClosedWorldFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorldSource);
+    return deserializeGlobalTypeInferenceResultsFromSource(
+        options,
+        reporter,
+        environment,
+        abstractValueStrategy,
+        component,
+        closedWorld,
+        globalTypeInferenceResultsSource);
   }
 
   @override
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index 6e5c4c8..cfc2b0d 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -19,6 +19,7 @@
 import '../js_backend/backend.dart';
 import '../js_backend/inferred_data.dart';
 import '../js_model/js_world.dart';
+import '../js_model/element_map_impl.dart';
 import '../js_model/locals.dart';
 import '../options.dart';
 import '../util/sink_adapter.dart';
@@ -30,40 +31,50 @@
   JsClosedWorld closedWorld = results.closedWorld;
   GlobalLocalsMap globalLocalsMap = results.globalLocalsMap;
   InferredData inferredData = results.inferredData;
-  closedWorld.writeToDataSink(sink);
   globalLocalsMap.writeToDataSink(sink);
   inferredData.writeToDataSink(sink);
   results.writeToDataSink(sink, closedWorld.elementMap);
   sink.close();
 }
 
-GlobalTypeInferenceResults deserializeGlobalAnalysisFromSource(
-    CompilerOptions options,
-    DiagnosticReporter reporter,
-    Environment environment,
-    AbstractValueStrategy abstractValueStrategy,
-    ir.Component component,
-    JsClosedWorld newClosedWorld,
-    DataSource source) {
-  GlobalLocalsMap newGlobalLocalsMap = GlobalLocalsMap.readFromDataSource(
-      newClosedWorld.closureDataLookup.getEnclosingMember, source);
-  InferredData newInferredData =
-      InferredData.readFromDataSource(source, newClosedWorld);
-  return GlobalTypeInferenceResults.readFromDataSource(
-      source,
-      newClosedWorld.elementMap,
-      newClosedWorld,
-      newGlobalLocalsMap,
-      newInferredData);
-}
-
 GlobalTypeInferenceResults deserializeGlobalTypeInferenceResultsFromSource(
     CompilerOptions options,
     DiagnosticReporter reporter,
     Environment environment,
     AbstractValueStrategy abstractValueStrategy,
     ir.Component component,
+    JsClosedWorld closedWorld,
     DataSource source) {
+  source.registerComponentLookup(ComponentLookup(component));
+  source.registerEntityLookup(ClosedEntityLookup(closedWorld.elementMap));
+  GlobalLocalsMap globalLocalsMap = GlobalLocalsMap.readFromDataSource(
+      closedWorld.closureDataLookup.getEnclosingMember, source);
+  InferredData inferredData =
+      InferredData.readFromDataSource(source, closedWorld);
+  return GlobalTypeInferenceResults.readFromDataSource(source,
+      closedWorld.elementMap, closedWorld, globalLocalsMap, inferredData);
+}
+
+void serializeGlobalTypeInferenceResultsToSinkLegacy(
+    GlobalTypeInferenceResults results, DataSink sink) {
+  JsClosedWorld closedWorld = results.closedWorld;
+  GlobalLocalsMap globalLocalsMap = results.globalLocalsMap;
+  InferredData inferredData = results.inferredData;
+  closedWorld.writeToDataSink(sink);
+  globalLocalsMap.writeToDataSink(sink);
+  inferredData.writeToDataSink(sink);
+  results.writeToDataSink(sink, closedWorld.elementMap);
+  sink.close();
+}
+
+GlobalTypeInferenceResults
+    deserializeGlobalTypeInferenceResultsFromSourceLegacy(
+        CompilerOptions options,
+        DiagnosticReporter reporter,
+        Environment environment,
+        AbstractValueStrategy abstractValueStrategy,
+        ir.Component component,
+        DataSource source) {
   JsClosedWorld newClosedWorld = new JsClosedWorld.readFromDataSource(
       options, reporter, environment, abstractValueStrategy, component, source);
   GlobalLocalsMap newGlobalLocalsMap = GlobalLocalsMap.readFromDataSource(
@@ -213,23 +224,7 @@
     });
   }
 
-  Future<GlobalTypeInferenceResults> deserializeGlobalTypeInference(
-      Environment environment,
-      AbstractValueStrategy abstractValueStrategy) async {
-    ir.Component component = await deserializeComponentAndUpdateOptions();
-
-    return await measureIoSubtask('deserialize data', () async {
-      _reporter.log('Reading data from ${_options.readDataUri}');
-      api.Input<List<int>> dataInput = await _provider
-          .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
-      DataSource source =
-          BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
-      return deserializeGlobalTypeInferenceResultsFromSource(_options,
-          _reporter, environment, abstractValueStrategy, component, source);
-    });
-  }
-
-  Future<GlobalTypeInferenceResults> deserializeGlobalAnalysis(
+  Future<GlobalTypeInferenceResults> deserializeGlobalTypeInferenceResults(
       Environment environment,
       AbstractValueStrategy abstractValueStrategy,
       ir.Component component,
@@ -240,8 +235,45 @@
           .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
       DataSource source =
           BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
-      return deserializeGlobalAnalysisFromSource(_options, _reporter,
-          environment, abstractValueStrategy, component, closedWorld, source);
+      return deserializeGlobalTypeInferenceResultsFromSource(
+          _options,
+          _reporter,
+          environment,
+          abstractValueStrategy,
+          component,
+          closedWorld,
+          source);
+    });
+  }
+
+  // TODO(joshualitt) get rid of legacy functions after Google3 roll.
+  void serializeGlobalTypeInferenceLegacy(GlobalTypeInferenceResults results) {
+    JsClosedWorld closedWorld = results.closedWorld;
+    ir.Component component = closedWorld.elementMap.programEnv.mainComponent;
+    serializeComponent(component);
+
+    measureSubtask('serialize data', () {
+      _reporter.log('Writing data to ${_options.writeDataUri}');
+      api.BinaryOutputSink dataOutput =
+          _outputProvider.createBinarySink(_options.writeDataUri);
+      DataSink sink = new BinarySink(new BinaryOutputSinkAdapter(dataOutput));
+      serializeGlobalTypeInferenceResultsToSinkLegacy(results, sink);
+    });
+  }
+
+  Future<GlobalTypeInferenceResults> deserializeGlobalTypeInferenceLegacy(
+      Environment environment,
+      AbstractValueStrategy abstractValueStrategy) async {
+    ir.Component component = await deserializeComponentAndUpdateOptions();
+
+    return await measureIoSubtask('deserialize data', () async {
+      _reporter.log('Reading data from ${_options.readDataUri}');
+      api.Input<List<int>> dataInput = await _provider
+          .readFromUri(_options.readDataUri, inputKind: api.InputKind.binary);
+      DataSource source =
+          BinarySourceImpl(dataInput.data, stringInterner: _stringInterner);
+      return deserializeGlobalTypeInferenceResultsFromSourceLegacy(_options,
+          _reporter, environment, abstractValueStrategy, component, source);
     });
   }
 
diff --git a/pkg/compiler/test/serialization/on_disk_split_test.dart b/pkg/compiler/test/serialization/on_disk_split_test.dart
index 00ce798..22cedb1 100644
--- a/pkg/compiler/test/serialization/on_disk_split_test.dart
+++ b/pkg/compiler/test/serialization/on_disk_split_test.dart
@@ -17,17 +17,19 @@
     Uri dillUri = dir.uri.resolve('out.dill');
     Uri outUri = dir.uri.resolve('out.js');
     var commonArgs = [
-      Flags.writeData,
       Flags.verbose,
       '--libraries-spec=$sdkLibrariesSpecificationUri',
     ];
     await internalMain([
           'samples-dev/swarm/swarm.dart',
+          Flags.writeClosedWorld,
           '--out=${dillUri}',
         ] +
         commonArgs);
     await internalMain([
           '${dillUri}',
+          Flags.readClosedWorld,
+          Flags.writeData,
           '--out=${outUri}',
         ] +
         commonArgs);
diff --git a/pkg/compiler/test/serialization/serialization_test_helper.dart b/pkg/compiler/test/serialization/serialization_test_helper.dart
index b429960..b7b771c 100644
--- a/pkg/compiler/test/serialization/serialization_test_helper.dart
+++ b/pkg/compiler/test/serialization/serialization_test_helper.dart
@@ -184,7 +184,8 @@
 GlobalTypeInferenceResults cloneInferenceResults(Compiler compiler,
     GlobalTypeInferenceResults results, SerializationStrategy strategy) {
   List<int> irData = strategy.unpackAndSerializeComponent(results);
-
+  List<int> closedWorldData =
+      strategy.serializeClosedWorld(results.closedWorld);
   List<int> worldData = strategy.serializeGlobalTypeInferenceResults(results);
   print('data size: ${worldData.length}');
 
@@ -196,6 +197,7 @@
           compiler.environment,
           compiler.abstractValueStrategy,
           newComponent,
+          closedWorldData,
           worldData);
   List<int> newWorldData =
       strategy.serializeGlobalTypeInferenceResults(newResults);
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 0aa6ebe..780c182 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -54,7 +54,7 @@
         new IOPipeline([
           SourceToDillStep(),
           ComputeClosedWorldStep(),
-          GlobalAnalysisStep(),
+          LegacyGlobalAnalysisStep(),
           LegacyDart2jsCodegenStep(codeId0),
           LegacyDart2jsCodegenStep(codeId1),
           LegacyDart2jsEmissionStep(),
@@ -312,6 +312,8 @@
       for (String flag in flags) '--enable-experiment=$flag',
       '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
       '${Flags.writeData}=${toUri(module, globalDataId)}',
+      // TODO(joshualitt): delete this flag after google3 roll
+      '${Flags.noClosedWorldInData}',
     ];
     var result =
         await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
@@ -425,6 +427,50 @@
   }
 }
 
+// TODO(joshualitt): delete after google3 roll.
+class LegacyGlobalAnalysisStep implements IOModularStep {
+  @override
+  List<DataId> get resultData => const [globalDataId];
+
+  @override
+  bool get needsSources => false;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [updatedDillId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [closedWorldId, updatedDillId];
+
+  @override
+  bool get onlyOnMain => true;
+
+  @override
+  Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
+      List<String> flags) async {
+    if (_options.verbose) print("\nstep: dart2js global analysis on $module");
+    List<String> args = [
+      '--packages=${sdkRoot.toFilePath()}/.packages',
+      _dart2jsScript,
+      // TODO(sigmund): remove this dependency on libraries.json
+      if (_options.useSdk) '--libraries-spec=$_librarySpecForSnapshot',
+      '${toUri(module, updatedDillId)}',
+      for (String flag in flags) '--enable-experiment=$flag',
+      '${Flags.readClosedWorld}=${toUri(module, closedWorldId)}',
+      '${Flags.writeData}=${toUri(module, globalDataId)}',
+    ];
+    var result =
+        await _runProcess(Platform.resolvedExecutable, args, root.toFilePath());
+
+    _checkExitCode(result, this, module);
+  }
+
+  @override
+  void notifyCached(Module module) {
+    if (_options.verbose)
+      print("\ncached step: dart2js global analysis on $module");
+  }
+}
+
 // Step that invokes the dart2js code generation on the main module given the
 // results of the global analysis step and produces one shard of the codegen
 // output.