[dart2js] Add dart2js target to build summaries.

Change-Id: I1ef0b3f87124333e44cb15001c556b1767ddf28f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/219841
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/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index ae463a8..d8985b7 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -93,7 +93,7 @@
   int get enabledConstructorTearOffLowerings => ConstructorTearOffLowering.all;
 
   @override
-  List<String> get extraRequiredLibraries => _requiredLibraries[name]!;
+  List<String> get extraRequiredLibraries => requiredLibraries[name]!;
 
   @override
   List<String> get extraIndexedLibraries => const [
@@ -222,49 +222,86 @@
 
 // TODO(sigmund): this "extraRequiredLibraries" needs to be removed...
 // compile-platform should just specify which libraries to compile instead.
-const _requiredLibraries = <String, List<String>>{
+const requiredLibraries = <String, List<String>>{
   'dart2js': [
+    'dart:_async_await_error_codes',
     'dart:_dart2js_runtime_metrics',
     'dart:_foreign_helper',
+    'dart:_http',
     'dart:_interceptors',
     'dart:_internal',
+    'dart:_js',
     'dart:_js_annotations',
     'dart:_js_embedded_names',
     'dart:_js_helper',
     'dart:_js_names',
+    'dart:_js_primitives',
     'dart:_late_helper',
+    'dart:_metadata',
     'dart:_native_typed_data',
+    'dart:_recipe_syntax',
+    'dart:_rti',
     'dart:async',
     'dart:collection',
+    'dart:convert',
+    'dart:developer',
     'dart:html',
     'dart:html_common',
     'dart:indexed_db',
     'dart:io',
+    'dart:isolate',
     'dart:js',
     'dart:js_util',
+    'dart:math',
     'dart:svg',
+    'dart:typed_data',
     'dart:web_audio',
     'dart:web_gl',
   ],
   'dart2js_server': [
+    'dart:_async_await_error_codes',
     'dart:_dart2js_runtime_metrics',
     'dart:_foreign_helper',
+    'dart:_http',
     'dart:_interceptors',
     'dart:_internal',
+    'dart:_js',
     'dart:_js_annotations',
     'dart:_js_embedded_names',
     'dart:_js_helper',
     'dart:_js_names',
+    'dart:_js_primitives',
     'dart:_late_helper',
     'dart:_native_typed_data',
+    'dart:_recipe_syntax',
+    'dart:_rti',
     'dart:async',
     'dart:collection',
+    'dart:convert',
+    'dart:developer',
     'dart:io',
+    'dart:isolate',
     'dart:js',
     'dart:js_util',
+    'dart:math',
+    'dart:typed_data',
   ]
 };
 
+/// Extends the Dart2jsTarget to transform outlines to meet the requirements
+/// of summaries in bazel and package-build.
+class Dart2jsSummaryTarget extends Dart2jsTarget with SummaryMixin {
+  @override
+  final List<Uri> sources;
+
+  @override
+  final bool excludeNonSources;
+
+  Dart2jsSummaryTarget(String name, this.sources, this.excludeNonSources,
+      TargetFlags targetFlags)
+      : super(name, targetFlags);
+}
+
 class Dart2jsConstantsBackend extends ConstantsBackend {
   @override
   final bool supportsUnevaluatedConstants;
diff --git a/pkg/compiler/tool/modular_test_suite.dart b/pkg/compiler/tool/modular_test_suite.dart
index 6195548..1593208 100644
--- a/pkg/compiler/tool/modular_test_suite.dart
+++ b/pkg/compiler/tool/modular_test_suite.dart
@@ -9,6 +9,7 @@
 import 'dart:async';
 
 import 'package:compiler/src/commandline_options.dart';
+import 'package:compiler/src/kernel/dart2js_target.dart';
 import 'package:front_end/src/compute_platform_binaries_location.dart'
     show computePlatformBinariesLocation;
 import 'package:modular_test/src/io_pipeline.dart';
@@ -32,36 +33,26 @@
   _packageConfig = await loadPackageConfigUri(packageConfigUri);
   await _resolveScripts();
   await Future.wait([
+    // TODO(joshualitt): Factor modular steps into a library so we can test
+    // modular analysis alongside the existing pipeline.
     runSuite(
         sdkRoot.resolve('tests/modular/'),
         'tests/modular',
         _options,
         IOPipeline([
-          SourceToDillStep(),
-          ModularAnalysisStep(),
-          ComputeClosedWorldStep(useModularAnalysis: true),
+          OutlineDillCompilationStep(),
+          FullDillCompilationStep(),
+          ComputeClosedWorldStep(useModularAnalysis: false),
           GlobalAnalysisStep(),
           Dart2jsCodegenStep(codeId0),
           Dart2jsCodegenStep(codeId1),
           Dart2jsEmissionStep(),
           RunD8(),
         ], cacheSharedModules: true)),
-    runSuite(
-        sdkRoot.resolve('tests/modular/'),
-        'tests/modular',
-        _options,
-        IOPipeline([
-          SourceToDillStep(),
-          ComputeClosedWorldStep(useModularAnalysis: false),
-          LegacyGlobalAnalysisStep(),
-          LegacyDart2jsCodegenStep(codeId0),
-          LegacyDart2jsCodegenStep(codeId1),
-          LegacyDart2jsEmissionStep(),
-          RunD8(),
-        ], cacheSharedModules: true))
   ]);
 }
 
+const dillSummaryId = DataId("sdill");
 const dillId = DataId("dill");
 const modularUpdatedDillId = DataId("mdill");
 const modularDataId = DataId("mdata");
@@ -86,27 +77,21 @@
   return '{${fields.join(',')}}';
 }
 
-// Step that compiles sources in a module to a .dill file.
-class SourceToDillStep implements IOModularStep {
-  @override
-  List<DataId> get resultData => const [dillId];
+abstract class CFEStep implements IOModularStep {
+  final String stepName;
+
+  CFEStep(this.stepName);
 
   @override
   bool get needsSources => true;
 
   @override
-  List<DataId> get dependencyDataNeeded => const [dillId];
-
-  @override
-  List<DataId> get moduleDataNeeded => const [];
-
-  @override
   bool get onlyOnMain => false;
 
   @override
   Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
       List<String> flags) async {
-    if (_options.verbose) print("\nstep: source-to-dill on $module");
+    if (_options.verbose) print("\nstep: $stepName on $module");
 
     // We use non file-URI schemes for representeing source locations in a
     // root-agnostic way. This allows us to refer to file across modules and
@@ -181,44 +166,45 @@
         .writeAsString('$packagesContents');
 
     List<String> sources;
-    List<String> extraArgs;
+    List<String> extraArgs = ['--packages-file', '$rootScheme:/.packages'];
     if (module.isSdk) {
       // When no flags are passed, we can skip compilation and reuse the
       // platform.dill created by build.py.
       if (flags.isEmpty) {
         var platform = computePlatformBinariesLocation()
             .resolve("dart2js_platform_unsound.dill");
-        var destination = root.resolveUri(toUri(module, dillId));
+        var destination = root.resolveUri(toUri(module, outputData));
         if (_options.verbose) {
           print('command:\ncp $platform $destination');
         }
         await File.fromUri(platform).copy(destination.toFilePath());
         return;
       }
-      sources = ['dart:core'];
-      extraArgs = ['--libraries-file', '$rootScheme:///sdk/lib/libraries.json'];
+      sources = requiredLibraries['dart2js'] + ['dart:core'];
+      extraArgs += [
+        '--libraries-file',
+        '$rootScheme:///sdk/lib/libraries.json'
+      ];
       assert(transitiveDependencies.isEmpty);
     } else {
       sources = module.sources.map(sourceToImportUri).toList();
-      extraArgs = ['--packages-file', '$rootScheme:/.packages'];
     }
 
     // TODO(joshualitt): Ensure the kernel worker has some way to specify
     // --no-sound-null-safety
     List<String> args = [
       _kernelWorkerScript,
-      '--no-summary-only',
-      '--target',
-      'dart2js',
+      ...stepArguments,
+      '--exclude-non-sources',
       '--multi-root',
       '$root',
       '--multi-root-scheme',
       rootScheme,
       ...extraArgs,
       '--output',
-      '${toUri(module, dillId)}',
+      '${toUri(module, outputData)}',
       ...(transitiveDependencies
-          .expand((m) => ['--input-linked', '${toUri(m, dillId)}'])),
+          .expand((m) => ['--input-summary', '${toUri(m, inputData)}'])),
       ...(sources.expand((String uri) => ['--source', uri])),
       ...(flags.expand((String flag) => ['--enable-experiment', flag])),
     ];
@@ -228,12 +214,73 @@
     _checkExitCode(result, this, module);
   }
 
+  List<String> get stepArguments;
+
+  DataId get inputData;
+
+  DataId get outputData;
+
   @override
   void notifyCached(Module module) {
-    if (_options.verbose) print("\ncached step: source-to-dill on $module");
+    if (_options.verbose) print("\ncached step: $stepName on $module");
   }
 }
 
+// Step that compiles sources in a module to a summary .dill file.
+class OutlineDillCompilationStep extends CFEStep {
+  @override
+  List<DataId> get resultData => const [dillSummaryId];
+
+  @override
+  bool get needsSources => true;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [];
+
+  @override
+  List<String> get stepArguments =>
+      ['--target', 'dart2js_summary', '--summary-only'];
+
+  @override
+  DataId get inputData => dillSummaryId;
+
+  @override
+  DataId get outputData => dillSummaryId;
+
+  OutlineDillCompilationStep() : super('outline-dill-compilation');
+}
+
+// Step that compiles sources in a module to a .dill file.
+class FullDillCompilationStep extends CFEStep {
+  @override
+  List<DataId> get resultData => const [dillId];
+
+  @override
+  bool get needsSources => true;
+
+  @override
+  List<DataId> get dependencyDataNeeded => const [dillSummaryId];
+
+  @override
+  List<DataId> get moduleDataNeeded => const [];
+
+  // TODO(joshualitt): we need a --no-summary argument to cfe.
+  @override
+  List<String> get stepArguments =>
+      ['--target', 'dart2js', '--no-summary-only'];
+
+  @override
+  DataId get inputData => dillSummaryId;
+
+  @override
+  DataId get outputData => dillId;
+
+  FullDillCompilationStep() : super('full-dill-compilation');
+}
+
 class ModularAnalysisStep implements IOModularStep {
   @override
   List<DataId> get resultData => const [modularDataId, modularUpdatedDillId];
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 5746a68..99bfae9 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -329,6 +329,7 @@
 destination
 destinations
 destroy
+destructive
 deterministic
 dev
 device
@@ -620,6 +621,7 @@
 int64
 int8
 integrate
+intends
 intentionally
 interested
 interim
diff --git a/pkg/frontend_server/lib/compute_kernel.dart b/pkg/frontend_server/lib/compute_kernel.dart
index a75573e..9b60711 100644
--- a/pkg/frontend_server/lib/compute_kernel.dart
+++ b/pkg/frontend_server/lib/compute_kernel.dart
@@ -62,6 +62,7 @@
         'flutter',
         'flutter_runner',
         'dart2js',
+        'dart2js_summary',
         'ddc',
       ],
       help: 'Build kernel for the vm, flutter, flutter_runner, dart2js or ddc')
@@ -174,6 +175,14 @@
             'error: --summary-only not supported for the dart2js target');
       }
       break;
+    case 'dart2js_summary':
+      target = new Dart2jsSummaryTarget(
+          'dart2js', sources, excludeNonSources, targetFlags);
+      if (!summaryOnly) {
+        out.writeln(
+            'error: --no-summary-only not supported for the dart2js summary target');
+      }
+      break;
     case 'ddc':
       // TODO(jakemac):If `generateKernel` changes to return a summary
       // component, process the component instead.
@@ -358,47 +367,13 @@
   }
 }
 
-/// Extends the DevCompilerTarget to transform outlines to meet the requirements
-/// of summaries in bazel and package-build.
-///
-/// Build systems like package-build may provide the same input file twice to
-/// the summary worker, but only intends to have it in one output summary.  The
-/// convention is that if it is listed as a source, it is intended to be part of
-/// the output, if the source file was loaded as a dependency, then it was
-/// already included in a different summary.  The transformation below ensures
-/// that the output summary doesn't include those implicit inputs.
-///
-/// Note: this transformation is destructive and is only intended to be used
-/// when generating summaries.
-class DevCompilerSummaryTarget extends DevCompilerTarget {
+class DevCompilerSummaryTarget extends DevCompilerTarget with SummaryMixin {
   final List<Uri> sources;
   final bool excludeNonSources;
 
   DevCompilerSummaryTarget(
       this.sources, this.excludeNonSources, TargetFlags targetFlags)
       : super(targetFlags);
-
-  @override
-  void performOutlineTransformations(Component component) {
-    super.performOutlineTransformations(component);
-    if (!excludeNonSources) return;
-
-    List<Library> libraries = new List.from(component.libraries);
-    component.libraries.clear();
-    Set<Uri> include = sources.toSet();
-    for (var lib in libraries) {
-      if (include.contains(lib.importUri)) {
-        component.libraries.add(lib);
-      } else {
-        // Excluding the library also means that their canonical names will not
-        // be computed as part of serialization, so we need to do that
-        // preemtively here to avoid errors when serializing references to
-        // elements of these libraries.
-        component.root.getChildFromUri(lib.importUri).bindTo(lib.reference);
-        lib.computeCanonicalNames();
-      }
-    }
-  }
 }
 
 Uri toUri(String uriString) {
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 1cb1b2c..14f30a3 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -918,3 +918,44 @@
 
   TestTargetWrapper(Target target, this.flags) : super(target);
 }
+
+/// Extends a Target to transform outlines to meet the requirements
+/// of summaries in bazel and package-build.
+///
+/// Build systems like package-build may provide the same input file twice to
+/// the summary worker, but only intends to have it in one output summary.  The
+/// convention is that if it is listed as a source, it is intended to be part of
+/// the output, if the source file was loaded as a dependency, then it was
+/// already included in a different summary.  The transformation below ensures
+/// that the output summary doesn't include those implicit inputs.
+///
+/// Note: this transformation is destructive and is only intended to be used
+/// when generating summaries.
+mixin SummaryMixin on Target {
+  List<Uri> get sources;
+  bool get excludeNonSources;
+
+  @override
+  void performOutlineTransformations(Component component) {
+    super.performOutlineTransformations(component);
+    if (!excludeNonSources) return;
+
+    List<Library> libraries = new List.from(component.libraries);
+    component.libraries.clear();
+    Set<Uri> include = sources.toSet();
+    for (Library library in libraries) {
+      if (include.contains(library.importUri)) {
+        component.libraries.add(library);
+      } else {
+        // Excluding the library also means that their canonical names will not
+        // be computed as part of serialization, so we need to do that
+        // preemptively here to avoid errors when serializing references to
+        // elements of these libraries.
+        component.root
+            .getChildFromUri(library.importUri)
+            .bindTo(library.reference);
+        library.computeCanonicalNames();
+      }
+    }
+  }
+}