Add flag to emit kernel immediately after invoking the CFE

This will be used initially to create the 3-way split in the compiler. Later
this will:
- include our transformation for super mixin calls
- remove the need to emit the .dill after global analysis
- eventually get replaced by a modular step that only builds a portion of the
.dill file.

Change-Id: Iebf2bd6d023716f04dc542ae9b2a85919159c4c0
Reviewed-on: https://dart-review.googlesource.com/c/85840
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
diff --git a/pkg/compiler/lib/src/commandline_options.dart b/pkg/compiler/lib/src/commandline_options.dart
index 982a2de..391cdd9 100644
--- a/pkg/compiler/lib/src/commandline_options.dart
+++ b/pkg/compiler/lib/src/commandline_options.dart
@@ -74,6 +74,7 @@
 
   static const String readData = '--read-data';
   static const String writeData = '--write-data';
+  static const String cfeOnly = '--cfe-only';
 
   static const String serverMode = '--server-mode';
 
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 71225ec..85cc18e 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -168,8 +168,8 @@
     enqueuer = backend.makeEnqueuer();
 
     tasks = [
-      kernelLoader =
-          new KernelLoaderTask(options, provider, reporter, measurer),
+      kernelLoader = new KernelLoaderTask(
+          options, provider, _outputProvider, reporter, measurer),
       kernelFrontEndTask,
       globalInference = new GlobalTypeInferenceTask(this),
       constants = backend.constantCompilerTask,
@@ -255,6 +255,7 @@
       if (compilationFailed && !options.generateCodeWithCompileTimeErrors) {
         return;
       }
+      if (options.cfeOnly) return;
       _mainLibraryUri = result.rootLibraryUri;
 
       frontendStrategy.registerLoadedLibraries(result);
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index b97806b..dfab97f 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -263,6 +263,10 @@
     compilationStrategy = CompilationStrategy.fromData;
   }
 
+  void setCfeOnly(String argument) {
+    compilationStrategy = CompilationStrategy.toKernel;
+  }
+
   void setWriteData(String argument) {
     if (compilationStrategy == CompilationStrategy.fromData) {
       fail("Cannot read and write serialized simultaneously.");
@@ -333,6 +337,7 @@
     new OptionHandler('--libraries-spec=.+', setLibrarySpecificationUri),
     new OptionHandler('${Flags.readData}|${Flags.readData}=.+', setReadData),
     new OptionHandler('${Flags.writeData}|${Flags.writeData}=.+', setWriteData),
+    new OptionHandler(Flags.cfeOnly, setCfeOnly),
     new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
     new OptionHandler('-O.*', setOptimizationLevel),
     new OptionHandler(Flags.allowMockCompilation, ignoreOption),
@@ -492,6 +497,10 @@
     case CompilationStrategy.direct:
       out ??= currentDirectory.resolve('out.js');
       break;
+    case CompilationStrategy.toKernel:
+      out ??= currentDirectory.resolve('out.dill');
+      options.add(Flags.cfeOnly);
+      break;
     case CompilationStrategy.toData:
       out ??= currentDirectory.resolve('out.dill');
       writeDataUri ??= currentDirectory.resolve('$out.data');
@@ -544,6 +553,18 @@
           }
         }
         break;
+      case CompilationStrategy.toKernel:
+        int dartCharactersRead = inputProvider.dartCharactersRead;
+        int dataBytesWritten = outputProvider.totalDataWritten;
+        print('Compiled '
+            '${_formatCharacterCount(dartCharactersRead)} characters Dart to '
+            '${_formatCharacterCount(dataBytesWritten)} kernel bytes in '
+            '${_formatDurationAsSeconds(wallclock.elapsed)} seconds');
+        String input = uriPathToNative(scriptName);
+        String dillOutput =
+            relativize(currentDirectory, out, Platform.isWindows);
+        print('Dart file ($input) compiled to ${dillOutput}.');
+        break;
       case CompilationStrategy.toData:
         int dartCharactersRead = inputProvider.dartCharactersRead;
         int dataBytesWritten = outputProvider.totalDataWritten;
@@ -1007,4 +1028,4 @@
   });
 }
 
-enum CompilationStrategy { direct, toData, fromData }
+enum CompilationStrategy { direct, toKernel, toData, fromData }
diff --git a/pkg/compiler/lib/src/kernel/loader.dart b/pkg/compiler/lib/src/kernel/loader.dart
index 9df6d22..4d14f55 100644
--- a/pkg/compiler/lib/src/kernel/loader.dart
+++ b/pkg/compiler/lib/src/kernel/loader.dart
@@ -9,6 +9,7 @@
 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;
@@ -18,6 +19,7 @@
 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;
@@ -31,6 +33,7 @@
   final DiagnosticReporter _reporter;
 
   final api.CompilerInput _compilerInput;
+  final api.CompilerOutput _compilerOutput;
 
   final CompilerOptions _options;
 
@@ -43,8 +46,8 @@
   /// This is used for testing.
   bool forceSerialization = false;
 
-  KernelLoaderTask(
-      this._options, this._compilerInput, this._reporter, Measurer measurer)
+  KernelLoaderTask(this._options, this._compilerInput, this._compilerOutput,
+      this._reporter, Measurer measurer)
       : initializedCompilerState = _options.kernelInitializedCompilerState,
         super(measurer);
 
@@ -78,6 +81,20 @@
             resolvedUri);
       }
       if (component == null) return null;
+
+      if (_options.cfeOnly) {
+        measureSubtask('serialize dill', () {
+          _reporter.log('Writing dill to ${_options.outputUri}');
+          api.BinaryOutputSink dillOutput =
+              _compilerOutput.createBinarySink(_options.outputUri);
+          BinaryOutputSinkAdapter irSink =
+              new BinaryOutputSinkAdapter(dillOutput);
+          BinaryPrinter printer = new BinaryPrinter(irSink);
+          printer.writeComponentFile(component);
+          irSink.close();
+        });
+      }
+
       if (forceSerialization) {
         // TODO(johnniwinther): Remove this when #34942 is fixed.
         List<int> data = serializeComponent(component);
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 0c05a18..ed6fcc8 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -62,6 +62,10 @@
   /// If this is set, the compilation stops after type inference.
   Uri writeDataUri;
 
+  /// Whether to run only the CFE and emit the generated kernel file in
+  /// [outputUri].
+  bool cfeOnly = false;
+
   /// Resolved constant "environment" values passed to the compiler via the `-D`
   /// flags.
   Map<String, String> environment = const <String, String>{};
@@ -332,7 +336,8 @@
       ..verbose = _hasOption(options, Flags.verbose)
       ..showInternalProgress = _hasOption(options, Flags.progress)
       ..readDataUri = _extractUriOption(options, '${Flags.readData}=')
-      ..writeDataUri = _extractUriOption(options, '${Flags.writeData}=');
+      ..writeDataUri = _extractUriOption(options, '${Flags.writeData}=')
+      ..cfeOnly = _hasOption(options, Flags.cfeOnly);
   }
 
   void validate() {
diff --git a/pkg/compiler/lib/src/serialization/strategies.dart b/pkg/compiler/lib/src/serialization/strategies.dart
index f4a03db..3b59676 100644
--- a/pkg/compiler/lib/src/serialization/strategies.dart
+++ b/pkg/compiler/lib/src/serialization/strategies.dart
@@ -10,7 +10,6 @@
 import 'package:kernel/ast.dart' as ir;
 import 'package:kernel/binary/ast_from_binary.dart' show BinaryBuilder;
 
-import '../../compiler_new.dart' as api;
 import '../diagnostics/diagnostic_listener.dart';
 import '../environment.dart';
 import '../js_model/js_world.dart';
@@ -18,6 +17,7 @@
 import '../source_file_provider.dart';
 import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
+import '../util/sink_adapter.dart';
 import 'serialization.dart';
 import 'task.dart';
 
@@ -130,19 +130,3 @@
         abstractValueStrategy, component, source);
   }
 }
-
-class BinaryOutputSinkAdapter implements Sink<List<int>> {
-  api.BinaryOutputSink output;
-
-  BinaryOutputSinkAdapter(this.output);
-
-  @override
-  void add(List<int> data) {
-    output.write(data);
-  }
-
-  @override
-  void close() {
-    output.close();
-  }
-}
diff --git a/pkg/compiler/lib/src/serialization/task.dart b/pkg/compiler/lib/src/serialization/task.dart
index b2ce9e1..761d99e 100644
--- a/pkg/compiler/lib/src/serialization/task.dart
+++ b/pkg/compiler/lib/src/serialization/task.dart
@@ -16,7 +16,7 @@
 import '../options.dart';
 import '../types/abstract_value_domain.dart';
 import '../types/types.dart';
-import 'strategies.dart';
+import '../util/sink_adapter.dart';
 import 'serialization.dart';
 
 void serializeGlobalTypeInferenceResults(
@@ -53,6 +53,9 @@
 
   void serialize(GlobalTypeInferenceResults results) {
     measureSubtask('serialize dill', () {
+      // TODO(sigmund): remove entirely: we will do this immediately as soon as
+      // we get the component in the kernel/loader.dart task once we refactor
+      // how we apply our modular kernel transformation for super mixin calls.
       compiler.reporter.log('Writing dill to ${compiler.options.outputUri}');
       api.BinaryOutputSink dillOutput =
           compiler.outputProvider.createBinarySink(compiler.options.outputUri);
diff --git a/pkg/compiler/lib/src/util/sink_adapter.dart b/pkg/compiler/lib/src/util/sink_adapter.dart
new file mode 100644
index 0000000..685d611
--- /dev/null
+++ b/pkg/compiler/lib/src/util/sink_adapter.dart
@@ -0,0 +1,17 @@
+import '../../compiler_new.dart' as api;
+
+class BinaryOutputSinkAdapter implements Sink<List<int>> {
+  api.BinaryOutputSink output;
+
+  BinaryOutputSinkAdapter(this.output);
+
+  @override
+  void add(List<int> data) {
+    output.write(data);
+  }
+
+  @override
+  void close() {
+    output.close();
+  }
+}