[dart2js] Add a 'modular analysis' phase.

Change-Id: Idda666911a15e1726a15db65eb0cce2733851fdc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/237633
Reviewed-by: Mark Zhou <markzipan@google.com>
Commit-Queue: Joshua Litt <joshualitt@google.com>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 918c9fa..7abb1f8 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -50,6 +50,7 @@
 import 'null_compiler_output.dart' show NullCompilerOutput;
 import 'options.dart' show CompilerOptions;
 import 'phase/load_kernel.dart' as load_kernel;
+import 'phase/modular_analysis.dart' as modular_analysis;
 import 'serialization/task.dart';
 import 'serialization/serialization.dart';
 import 'serialization/strategies.dart';
@@ -322,7 +323,11 @@
       programSplitConstraintsData = constraintParser.read(programSplitJson);
     }
 
-    if (onlyPerformGlobalTypeInference) {
+    if (options.cfeOnly) {
+      await runLoadKernel();
+    } else if (options.modularMode) {
+      await runModularAnalysis();
+    } else if (onlyPerformGlobalTypeInference) {
       ir.Component component =
           await serializationTask.deserializeComponentAndUpdateOptions();
       var closedWorldAndIndices =
@@ -357,48 +362,25 @@
       await generateJavaScriptCode(globalTypeInferenceResults,
           indices: closedWorldAndIndices.indices);
     } else {
-      final input = load_kernel.Input(options, provider, reporter,
-          initializedCompilerState, forceSerializationForTesting);
-      load_kernel.Output output =
-          await loadKernelTask.measure(() async => load_kernel.run(input));
+      load_kernel.Output output = await runLoadKernel();
       if (output == null || compilationFailed) return;
-      reporter.log("Kernel load complete");
-      if (retainDataForTesting) {
-        componentForTesting = output.component;
-      }
 
       ir.Component component = output.component;
       List<Uri> libraries = output.libraries;
       frontendStrategy.registerLoadedLibraries(component, libraries);
-
-      if (options.modularMode) {
-        await runModularAnalysis(component, output.moduleLibraries);
-      } else {
-        List<ModuleData> data;
-        if (options.hasModularAnalysisInputs) {
-          data = await serializationTask.deserializeModuleData(component);
-        }
-        frontendStrategy.registerModuleData(data);
-
-        // After we've deserialized modular data, we trim the component of any
-        // unnecessary dependencies.
-        // Note: It is critical we wait to trim the dill until after we've
-        // deserialized modular data because some of this data may reference
-        // 'trimmed' elements.
-        if (options.fromDill) {
-          if (options.dumpUnusedLibraries) {
-            dumpUnusedLibraries(component, libraries);
-          }
-          if (options.entryUri != null) {
-            component = trimComponent(component, libraries);
-          }
-        }
-        if (options.cfeOnly) {
-          await serializationTask.serializeComponent(component);
-        } else {
-          await compileFromKernel(output.rootLibraryUri, libraries);
-        }
+      List<ModuleData> data;
+      if (options.hasModularAnalysisInputs) {
+        data = await serializationTask.deserializeModuleData(component);
       }
+      frontendStrategy.registerModuleData(data);
+
+      // After we've deserialized modular data, we trim the component of any
+      // unnecessary dependencies.
+      // Note: It is critical we wait to trim the dill until after we've
+      // deserialized modular data because some of this data may reference
+      // 'trimmed' elements.
+      dumpUnusedLibrariesAndTrimComponent(component, libraries);
+      await compileFromKernel(output.rootLibraryUri, libraries);
     }
   }
 
@@ -498,19 +480,54 @@
     return closedWorld;
   }
 
-  void runModularAnalysis(
-      ir.Component component, Iterable<Uri> moduleLibraries) {
+  Future<load_kernel.Output> runLoadKernel() async {
+    final input = load_kernel.Input(options, provider, reporter,
+        initializedCompilerState, forceSerializationForTesting);
+    load_kernel.Output output =
+        await loadKernelTask.measure(() async => load_kernel.run(input));
+    if (output == null || compilationFailed) return null;
+    reporter.log("Kernel load complete");
+    if (retainDataForTesting) {
+      componentForTesting = output.component;
+    }
+
+    if (options.cfeOnly) {
+      ir.Component component = output.component;
+      dumpUnusedLibrariesAndTrimComponent(component, output.libraries);
+      await serializationTask.serializeComponent(component);
+    }
+    return output;
+  }
+
+  void dumpUnusedLibrariesAndTrimComponent(
+      ir.Component component, List<Uri> libraries) {
+    if (options.fromDill) {
+      if (options.dumpUnusedLibraries) {
+        dumpUnusedLibraries(component, libraries);
+      }
+      if (options.entryUri != null) {
+        component = trimComponent(component, libraries);
+      }
+    }
+  }
+
+  void runModularAnalysis() async {
+    load_kernel.Output output = await runLoadKernel();
+    if (output == null || compilationFailed) return;
+
+    ir.Component component = output.component;
+    List<Uri> libraries = output.libraries;
+    Set<Uri> moduleLibraries = output.moduleLibraries.toSet();
     _userCodeLocations
         .addAll(moduleLibraries.map((module) => CodeLocation(module)));
-    selfTask.measureSubtask('runModularAnalysis', () {
-      var included = moduleLibraries.toSet();
-      var elementMap = frontendStrategy.elementMap;
-      var moduleData = computeModuleData(
-          component, included, options, reporter, environment, elementMap);
-      if (compilationFailed) return;
-      serializationTask.testModuleSerialization(moduleData, component);
-      serializationTask.serializeModuleData(moduleData, component, included);
-    });
+    final input = modular_analysis.Input(
+        options, reporter, environment, component, libraries, moduleLibraries);
+    ModuleData moduleData = await selfTask.measureSubtask(
+        'runModularAnalysis', () async => modular_analysis.run(input));
+    if (compilationFailed) return;
+    serializationTask.testModuleSerialization(moduleData, component);
+    serializationTask.serializeModuleData(
+        moduleData, component, moduleLibraries);
   }
 
   GlobalTypeInferenceResults performGlobalTypeInference(
diff --git a/pkg/compiler/lib/src/ir/modular.dart b/pkg/compiler/lib/src/ir/modular.dart
index c143853..bdb89a3 100644
--- a/pkg/compiler/lib/src/ir/modular.dart
+++ b/pkg/compiler/lib/src/ir/modular.dart
@@ -12,8 +12,6 @@
 import '../diagnostics/diagnostic_listener.dart';
 import '../diagnostics/messages.dart';
 import '../diagnostics/source_span.dart';
-import '../kernel/element_map.dart';
-import '../environment.dart';
 import '../ir/impact_data.dart';
 import '../ir/static_type.dart';
 import '../js_backend/annotations.dart';
@@ -92,40 +90,6 @@
   return ModularMemberData(scopeModel, impactBuilderData);
 }
 
-ModuleData computeModuleData(
-    ir.Component component,
-    Set<Uri> includedLibraries,
-    CompilerOptions options,
-    DiagnosticReporter reporter,
-    Environment environment,
-    KernelToElementMap elementMap) {
-  var classHierarchy = elementMap.classHierarchy;
-  var typeEnvironment = elementMap.typeEnvironment;
-  var constantEvaluator = elementMap.constantEvaluator;
-  var result = <ir.Member, ImpactBuilderData>{};
-  void computeForMember(ir.Member member) {
-    var scopeModel = ScopeModel.from(member, constantEvaluator);
-    var annotations = processMemberAnnotations(
-        options, reporter, member, computePragmaAnnotationDataFromIr(member));
-    result[member] = computeModularMemberData(member,
-            options: options,
-            typeEnvironment: typeEnvironment,
-            classHierarchy: classHierarchy,
-            scopeModel: scopeModel,
-            annotations: annotations)
-        .impactBuilderData;
-  }
-
-  for (var library in component.libraries) {
-    if (!includedLibraries.contains(library.importUri)) continue;
-    library.members.forEach(computeForMember);
-    for (var cls in library.classes) {
-      cls.members.forEach(computeForMember);
-    }
-  }
-  return ModuleData(result);
-}
-
 void reportLocatedMessage(DiagnosticReporter reporter,
     ir.LocatedMessage message, List<ir.LocatedMessage> context) {
   DiagnosticMessage diagnosticMessage =
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index 403ff04..a0a1e69 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -38,7 +38,6 @@
 import '../js_backend/namer.dart';
 import '../js_backend/native_data.dart';
 import '../js_model/locals.dart';
-import '../kernel/kernel_strategy.dart';
 import '../kernel/dart2js_target.dart';
 import '../native/behavior.dart';
 import '../options.dart';
@@ -58,6 +57,9 @@
   @override
   final DiagnosticReporter reporter;
   final Environment _environment;
+  final NativeBasicDataBuilder nativeBasicDataBuilder =
+      NativeBasicDataBuilder();
+  NativeBasicData _nativeBasicData;
   KCommonElements _commonElements;
   KernelElementEnvironment _elementEnvironment;
   DartTypeConverter _typeConverter;
@@ -101,12 +103,10 @@
   final Map<ir.TreeNode, Local> localFunctionMap = {};
 
   BehaviorBuilder _nativeBehaviorBuilder;
-  final KernelFrontendStrategy _frontendStrategy;
 
   Map<KMember, Map<ir.Expression, TypeMap>> typeMapsForTesting;
 
-  KernelToElementMap(
-      this.reporter, this._environment, this._frontendStrategy, this.options) {
+  KernelToElementMap(this.reporter, this._environment, this.options) {
     _elementEnvironment = KernelElementEnvironment(this);
     _typeConverter = DartTypeConverter(this);
     _types = KernelDartTypes(this, options);
@@ -1422,7 +1422,16 @@
   }
 
   /// NativeBasicData is need for computation of the default super class.
-  NativeBasicData get nativeBasicData => _frontendStrategy.nativeBasicData;
+  NativeBasicData get nativeBasicData {
+    if (_nativeBasicData == null) {
+      _nativeBasicData = nativeBasicDataBuilder.close(elementEnvironment);
+      assert(
+          _nativeBasicData != null,
+          failedAt(NO_LOCATION_SPANNABLE,
+              "NativeBasicData has not been computed yet."));
+    }
+    return _nativeBasicData;
+  }
 
   /// Adds libraries in [component] to the set of libraries.
   ///
diff --git a/pkg/compiler/lib/src/kernel/kernel_strategy.dart b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
index 1a6b1bb..d3c918a 100644
--- a/pkg/compiler/lib/src/kernel/kernel_strategy.dart
+++ b/pkg/compiler/lib/src/kernel/kernel_strategy.dart
@@ -50,9 +50,6 @@
 /// Front end strategy that loads '.dill' files and builds a resolved element
 /// model from kernel IR nodes.
 class KernelFrontendStrategy {
-  final NativeBasicDataBuilder nativeBasicDataBuilder =
-      NativeBasicDataBuilder();
-  NativeBasicData _nativeBasicData;
   final CompilerOptions _options;
   final CompilerTask _compilerTask;
   KernelToElementMap _elementMap;
@@ -84,7 +81,7 @@
   KernelFrontendStrategy(this._compilerTask, this._options,
       DiagnosticReporter reporter, env.Environment environment) {
     assert(_compilerTask != null);
-    _elementMap = KernelToElementMap(reporter, environment, this, _options);
+    _elementMap = KernelToElementMap(reporter, environment, _options);
     _modularStrategy = KernelModularStrategy(_compilerTask, _elementMap);
     _backendUsageBuilder = BackendUsageBuilderImpl(this);
     noSuchMethodRegistry =
@@ -135,6 +132,7 @@
       CompilerTask task, Compiler compiler) {
     RuntimeTypesNeedBuilder rtiNeedBuilder = _createRuntimeTypesNeedBuilder();
     BackendImpacts impacts = BackendImpacts(commonElements, compiler.options);
+    final nativeBasicData = _elementMap.nativeBasicData;
     _nativeResolutionEnqueuer = NativeResolutionEnqueuer(
         compiler.options,
         elementEnvironment,
@@ -217,24 +215,13 @@
         annotationsData);
   }
 
-  NativeBasicData get nativeBasicData {
-    if (_nativeBasicData == null) {
-      _nativeBasicData = nativeBasicDataBuilder.close(elementEnvironment);
-      assert(
-          _nativeBasicData != null,
-          failedAt(NO_LOCATION_SPANNABLE,
-              "NativeBasicData has not been computed yet."));
-    }
-    return _nativeBasicData;
-  }
-
   /// Registers a set of loaded libraries with this strategy.
   void registerLoadedLibraries(ir.Component component, List<Uri> libraries) {
     _elementMap.addComponent(component);
     _irAnnotationData = processAnnotations(
         ModularCore(component, _elementMap.constantEvaluator));
     _annotationProcessor = KernelAnnotationProcessor(
-        elementMap, nativeBasicDataBuilder, _irAnnotationData);
+        elementMap, elementMap.nativeBasicDataBuilder, _irAnnotationData);
     for (Uri uri in libraries) {
       LibraryEntity library = elementEnvironment.lookupLibrary(uri);
       if (maybeEnableNative(library.canonicalUri)) {
diff --git a/pkg/compiler/lib/src/phase/load_kernel.dart b/pkg/compiler/lib/src/phase/load_kernel.dart
index 1db4a15..17795f7 100644
--- a/pkg/compiler/lib/src/phase/load_kernel.dart
+++ b/pkg/compiler/lib/src/phase/load_kernel.dart
@@ -50,7 +50,7 @@
   /// reachable from the [rootLibraryUri].
   ///
   /// Note that [component] may contain some libraries that are excluded here.
-  final Iterable<Uri> libraries;
+  final List<Uri> libraries;
 
   /// When running only dart2js modular analysis, returns the [Uri]s for
   /// libraries loaded in the input module.
@@ -58,7 +58,7 @@
   /// This excludes other libraries reachable from them that were loaded as
   /// dependencies. The result of [moduleLibraries] is always a subset of
   /// [libraries].
-  final Iterable<Uri> moduleLibraries;
+  final List<Uri> moduleLibraries;
 
   final fe.InitializedCompilerState initializedCompilerState;
 
diff --git a/pkg/compiler/lib/src/phase/modular_analysis.dart b/pkg/compiler/lib/src/phase/modular_analysis.dart
new file mode 100644
index 0000000..d7ba441
--- /dev/null
+++ b/pkg/compiler/lib/src/phase/modular_analysis.dart
@@ -0,0 +1,81 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:kernel/ast.dart' as ir;
+
+import '../common.dart';
+import '../elements/entities.dart';
+import '../environment.dart';
+import '../ir/annotations.dart';
+import '../ir/impact.dart';
+import '../ir/modular.dart';
+import '../ir/scope.dart';
+import '../kernel/dart2js_target.dart';
+import '../kernel/native_basic_data.dart';
+import '../js_backend/annotations.dart';
+import '../kernel/element_map.dart';
+import '../options.dart';
+
+class Input {
+  final CompilerOptions options;
+  final DiagnosticReporter reporter;
+  final Environment environment;
+  final ir.Component component;
+  final List<Uri> libraries;
+  final Set<Uri> moduleLibraries;
+
+  Input(this.options, this.reporter, this.environment, this.component,
+      this.libraries, this.moduleLibraries);
+}
+
+KernelToElementMap _createElementMap(
+    CompilerOptions options,
+    DiagnosticReporter reporter,
+    Environment environment,
+    ir.Component component,
+    List<Uri> libraries) {
+  final elementMap = KernelToElementMap(reporter, environment, options);
+  elementMap.addComponent(component);
+  IrAnnotationData irAnnotationData =
+      processAnnotations(ModularCore(component, elementMap.constantEvaluator));
+  final annotationProcessor = KernelAnnotationProcessor(
+      elementMap, elementMap.nativeBasicDataBuilder, irAnnotationData);
+  for (final uri in libraries) {
+    LibraryEntity library = elementMap.elementEnvironment.lookupLibrary(uri);
+    if (maybeEnableNative(library.canonicalUri)) {
+      annotationProcessor.extractNativeAnnotations(library);
+    }
+    annotationProcessor.extractJsInteropAnnotations(library);
+  }
+  return elementMap;
+}
+
+ModuleData run(Input input) {
+  final options = input.options;
+  final reporter = input.reporter;
+  final elementMap = _createElementMap(
+      options, reporter, input.environment, input.component, input.libraries);
+  final result = <ir.Member, ImpactBuilderData>{};
+  void computeForMember(ir.Member member) {
+    final scopeModel = ScopeModel.from(member, elementMap.constantEvaluator);
+    final annotations = processMemberAnnotations(
+        options, reporter, member, computePragmaAnnotationDataFromIr(member));
+    result[member] = computeModularMemberData(member,
+            options: options,
+            typeEnvironment: elementMap.typeEnvironment,
+            classHierarchy: elementMap.classHierarchy,
+            scopeModel: scopeModel,
+            annotations: annotations)
+        .impactBuilderData;
+  }
+
+  for (final library in input.component.libraries) {
+    if (!input.moduleLibraries.contains(library.importUri)) continue;
+    library.members.forEach(computeForMember);
+    for (final cls in library.classes) {
+      cls.members.forEach(computeForMember);
+    }
+  }
+  return ModuleData(result);
+}
diff --git a/pkg/compiler/test/model/enqueuer_test.dart b/pkg/compiler/test/model/enqueuer_test.dart
index d6a2ac6..ac2f0c3 100644
--- a/pkg/compiler/test/model/enqueuer_test.dart
+++ b/pkg/compiler/test/model/enqueuer_test.dart
@@ -254,7 +254,7 @@
 
     Object createConstraint(ClassEntity cls) {
       return new StrongModeConstraint(compiler.frontendStrategy.commonElements,
-          compiler.frontendStrategy.nativeBasicData, cls);
+          compiler.frontendStrategy.elementMap.nativeBasicData, cls);
     }
 
     for (Impact impact in impacts) {
diff --git a/pkg/compiler/test/model/open_world_test.dart b/pkg/compiler/test/model/open_world_test.dart
index d098a5d..fa190d8 100644
--- a/pkg/compiler/test/model/open_world_test.dart
+++ b/pkg/compiler/test/model/open_world_test.dart
@@ -93,7 +93,7 @@
     commonElements = compiler.frontendStrategy.commonElements;
     ElementEnvironment elementEnvironment =
         compiler.frontendStrategy.elementEnvironment;
-    nativeBasicData = compiler.frontendStrategy.nativeBasicData;
+    nativeBasicData = compiler.frontendStrategy.elementMap.nativeBasicData;
     world = compiler.resolutionWorldBuilderForTesting;
 
     ClassEntity findClass(String name) {