[dart2js] Fix modular analysis test.

Change-Id: I066149a6ae052acf7ec2db90cdeec378f73dcfb3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/221142
Commit-Queue: Joshua Litt <joshualitt@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 3c5f245..49838e5 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -178,8 +178,7 @@
     enqueuer = EnqueueTask(this);
 
     tasks = [
-      kernelLoader = KernelLoaderTask(
-          options, provider, _outputProvider, reporter, measurer),
+      kernelLoader = KernelLoaderTask(options, provider, reporter, measurer),
       kernelFrontEndTask,
       globalInference = GlobalTypeInferenceTask(this),
       deferredLoadTask = frontendStrategy.createDeferredLoadTask(this),
@@ -306,7 +305,6 @@
       if (retainDataForTesting) {
         componentForTesting = result.component;
       }
-      if (options.cfeOnly) return;
 
       frontendStrategy.registerLoadedLibraries(result);
 
@@ -314,12 +312,34 @@
         await runModularAnalysis(result);
       } else {
         List<ModuleData> data;
-        if (options.modularAnalysisInputs != null) {
+        if (options.hasModularAnalysisInputs) {
           data =
               await serializationTask.deserializeModuleData(result.component);
         }
         frontendStrategy.registerModuleData(data);
-        await compileFromKernel(result.rootLibraryUri, result.libraries);
+
+        // After we've deserialized modular data, we set and verify the main
+        // method as well as 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.entryUri != null) {
+            result.setMainAndTrimComponent(options.entryUri);
+          }
+          if (result.component.mainMethod == null) {
+            // TODO(sigmund): move this so that we use the same error template
+            // from the CFE.
+            _reporter.reportError(_reporter.createMessage(NO_LOCATION_SPANNABLE,
+                MessageKind.GENERIC, {'text': "No 'main' method found."}));
+            return;
+          }
+        }
+        if (options.cfeOnly) {
+          await serializationTask.serializeComponent(result.component);
+        } else {
+          await compileFromKernel(result.rootLibraryUri, result.libraries);
+        }
       }
     }
   }
diff --git a/pkg/compiler/lib/src/ir/impact.dart b/pkg/compiler/lib/src/ir/impact.dart
index f3d8702..3e7f7fca 100644
--- a/pkg/compiler/lib/src/ir/impact.dart
+++ b/pkg/compiler/lib/src/ir/impact.dart
@@ -858,4 +858,11 @@
   void visitNullConstant(ir.NullConstant node) {
     registry.registerNullLiteral();
   }
+
+  @override
+  void visitConstructorTearOffConstant(ir.ConstructorTearOffConstant node) {
+    // The CFE encoding of redirecting factories, which dart2js doesn't use,
+    // uses ConstructorTearOff(Constant) to point to its effective target.
+    // However, these should be safe to ignore.
+  }
 }
diff --git a/pkg/compiler/lib/src/kernel/loader.dart b/pkg/compiler/lib/src/kernel/loader.dart
index 57c32f2..dbefdbb 100644
--- a/pkg/compiler/lib/src/kernel/loader.dart
+++ b/pkg/compiler/lib/src/kernel/loader.dart
@@ -9,7 +9,6 @@
 import 'package:front_end/src/fasta/kernel/utils.dart';
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
-import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
 
 import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
 import 'package:kernel/kernel.dart' hide LibraryDependency, Combinator;
@@ -20,7 +19,6 @@
 import '../common/tasks.dart' show CompilerTask, Measurer;
 import '../common.dart';
 import '../options.dart';
-import '../util/sink_adapter.dart';
 
 import 'front_end_adapter.dart';
 import 'dart2js_target.dart' show Dart2jsTarget;
@@ -34,7 +32,6 @@
   final DiagnosticReporter _reporter;
 
   final api.CompilerInput _compilerInput;
-  final api.CompilerOutput _compilerOutput;
 
   final CompilerOptions _options;
 
@@ -47,58 +44,13 @@
   /// This is used for testing.
   bool forceSerialization = false;
 
-  KernelLoaderTask(this._options, this._compilerInput, this._compilerOutput,
-      this._reporter, Measurer measurer)
+  KernelLoaderTask(
+      this._options, this._compilerInput, this._reporter, Measurer measurer)
       : initializedCompilerState = _options.kernelInitializedCompilerState,
         super(measurer);
 
   @override
   String get name => 'kernel loader';
-  Library findEntryLibrary(Component component, Uri entryUri) {
-    var entryLibrary = component.libraries
-        .firstWhere((l) => l.fileUri == entryUri, orElse: () => null);
-    if (entryLibrary == null) {
-      throw ArgumentError('Entry uri $entryUri not found in dill.');
-    }
-    return entryLibrary;
-  }
-
-  ir.Reference findMainMethod(Library entryLibrary) {
-    var mainMethod = entryLibrary.procedures
-        .firstWhere((p) => p.name.text == 'main', orElse: () => null);
-
-    // In some cases, a main method is defined in another file, and then
-    // exported. In these cases, we search for the main method in
-    // [additionalExports].
-    ir.Reference mainMethodReference;
-    if (mainMethod == null) {
-      mainMethodReference = entryLibrary.additionalExports.firstWhere(
-          (p) => p.canonicalName.name == 'main',
-          orElse: () => null);
-    } else {
-      mainMethodReference = mainMethod.reference;
-    }
-    if (mainMethodReference == null) {
-      throw ArgumentError(
-          'Entry uri ${entryLibrary.fileUri} has no main method.');
-    }
-    return mainMethodReference;
-  }
-
-  Set<Library> computeRequiredLibraries(Library entryLibrary) {
-    var toVisit = [entryLibrary];
-    var visited = <Library>{entryLibrary};
-    while (toVisit.isNotEmpty) {
-      var library = toVisit.removeLast();
-      for (var dependency in library.dependencies) {
-        var target = dependency.targetLibrary;
-        if (visited.add(target)) {
-          toVisit.add(target);
-        }
-      }
-    }
-    return visited;
-  }
 
   /// Loads an entire Kernel [Component] from a file on disk.
   Future<KernelResult> load() {
@@ -116,9 +68,6 @@
       var resolvedUri = _options.compilationTarget;
       ir.Component component;
       List<Uri> moduleLibraries = const [];
-      var isDill = resolvedUri.path.endsWith('.dill') ||
-          resolvedUri.path.endsWith('.gdill') ||
-          resolvedUri.path.endsWith('.mdill');
 
       void inferNullSafetyMode(bool isSound) {
         if (_options.nullSafetyMode == NullSafetyMode.unspecified) {
@@ -131,7 +80,7 @@
         assert(_options.nullSafetyMode != NullSafetyMode.unspecified);
       }
 
-      if (isDill) {
+      if (_options.fromDill) {
         component = ir.Component();
         Future<void> read(Uri uri) async {
           api.Input input = await _compilerInput.readFromUri(uri,
@@ -141,15 +90,6 @@
 
         await read(resolvedUri);
 
-        // If an entryUri is supplied, we use it to manually select the main
-        // method.
-        Library entryLibrary;
-        if (_options.entryUri != null) {
-          entryLibrary = findEntryLibrary(component, _options.entryUri);
-          var mainMethod = findMainMethod(entryLibrary);
-          component.setMainMethodAndMode(mainMethod, true, component.mode);
-        }
-
         if (_options.modularMode) {
           moduleLibraries =
               component.libraries.map((lib) => lib.importUri).toList();
@@ -182,38 +122,12 @@
           if (platformUri != resolvedUri) await read(platformUri);
         }
 
-        // Concatenate dills, trim the resulting monolithic dill, and then
-        // reset the main method.
-        var mainMethod = component.mainMethodName;
-        var mainMode = component.mode;
+        // Concatenate dills.
         if (_options.dillDependencies != null) {
           for (Uri dependency in _options.dillDependencies) {
             await read(dependency);
           }
         }
-        if (entryLibrary != null) {
-          var requiredLibraries = computeRequiredLibraries(entryLibrary);
-          for (var library in component.libraries) {
-            if (library.importUri.scheme == 'dart') {
-              requiredLibraries.add(library);
-            }
-          }
-          component = ir.Component(
-              libraries: requiredLibraries.toList(),
-              uriToSource: component.uriToSource,
-              nameRoot: component.root);
-        }
-        component.setMainMethodAndMode(mainMethod, true, mainMode);
-
-        // This is not expected to be null when creating a whole-program .dill
-        // file, but needs to be checked for modular inputs.
-        if (component.mainMethod == null && !_options.modularMode) {
-          // TODO(sigmund): move this so that we use the same error template
-          // from the CFE.
-          _reporter.reportError(_reporter.createMessage(NO_LOCATION_SPANNABLE,
-              MessageKind.GENERIC, {'text': "No 'main' method found."}));
-          return null;
-        }
       } else {
         bool verbose = false;
         Target target =
@@ -266,18 +180,6 @@
         validateNullSafetyMode();
       }
 
-      if (_options.cfeOnly) {
-        measureSubtask('serialize dill', () {
-          _reporter.log('Writing dill to ${_options.outputUri}');
-          api.BinaryOutputSink dillOutput =
-              _compilerOutput.createBinarySink(_options.outputUri);
-          BinaryOutputSinkAdapter irSink = BinaryOutputSinkAdapter(dillOutput);
-          BinaryPrinter printer = BinaryPrinter(irSink);
-          printer.writeComponentFile(component);
-          irSink.close();
-        });
-      }
-
       if (forceSerialization) {
         // TODO(johnniwinther): Remove this when #34942 is fixed.
         List<int> data = serializeComponent(component);
@@ -324,7 +226,7 @@
 
 /// Result of invoking the CFE to produce the kernel IR.
 class KernelResult {
-  final ir.Component component;
+  ir.Component component;
 
   /// The [Uri] of the root library containing main.
   /// Note: rootLibraryUri will be null for some modules, for example in the
@@ -348,6 +250,55 @@
   KernelResult(this.component, this.rootLibraryUri, this.libraries,
       this.moduleLibraries);
 
+  static Library _findEntryLibrary(Component component, Uri entryUri) {
+    var entryLibrary = component.libraries
+        .firstWhere((l) => l.fileUri == entryUri, orElse: () => null);
+    if (entryLibrary == null) {
+      throw ArgumentError('Entry uri $entryUri not found in dill.');
+    }
+    return entryLibrary;
+  }
+
+  static ir.Reference _findMainMethod(Library entryLibrary) {
+    var mainMethod = entryLibrary.procedures
+        .firstWhere((p) => p.name.text == 'main', orElse: () => null);
+
+    // In some cases, a main method is defined in another file, and then
+    // exported. In these cases, we search for the main method in
+    // [additionalExports].
+    ir.Reference mainMethodReference;
+    if (mainMethod == null) {
+      mainMethodReference = entryLibrary.additionalExports.firstWhere(
+          (p) => p.canonicalName.name == 'main',
+          orElse: () => null);
+    } else {
+      mainMethodReference = mainMethod.reference;
+    }
+    if (mainMethodReference == null) {
+      throw ArgumentError(
+          'Entry uri ${entryLibrary.fileUri} has no main method.');
+    }
+    return mainMethodReference;
+  }
+
+  void setMainAndTrimComponent(Uri entryUri) {
+    var entryLibrary = _findEntryLibrary(component, entryUri);
+    var mainMethod = _findMainMethod(entryLibrary);
+    var irLibraryMap = <Uri, Library>{};
+    var irLibraries = <Library>[];
+    for (var library in component.libraries) {
+      irLibraryMap[library.importUri] = library;
+    }
+    for (var library in libraries) {
+      irLibraries.add(irLibraryMap[library]);
+    }
+    component = ir.Component(
+        libraries: irLibraries,
+        uriToSource: component.uriToSource,
+        nameRoot: component.root);
+    component.setMainMethodAndMode(mainMethod, true, component.mode);
+  }
+
   @override
   String toString() =>
       'root=$rootLibraryUri,libraries=$libraries,module=$moduleLibraries';
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index e6db1d9..79d15b9 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -160,6 +160,13 @@
   /// Returns the compilation target specified by these options.
   Uri? get compilationTarget => inputDillUri ?? entryUri;
 
+  bool get fromDill {
+    var targetPath = compilationTarget!.path;
+    return targetPath.endsWith('.dill') ||
+        targetPath.endsWith('.gdill') ||
+        targetPath.endsWith('.mdill');
+  }
+
   /// Location of the package configuration file.
   ///
   /// If not null then [packageRoot] should be null.
@@ -185,6 +192,8 @@
 
   List<Uri>? modularAnalysisInputs;
 
+  bool get hasModularAnalysisInputs => modularAnalysisInputs != null;
+
   /// Location from which serialized inference data is read.
   ///
   /// If this is set, the [entryUri] is expected to be a .dill file and the