Changes to bazel worker to support modular testing.

This includes:
 * accepting null values like sdk-summary or .packages (which are not used when building kernel for the sdk itself)
 * allow enabling language experiments
 * only exclude-non-sources on the non-incremental code path if that's requested

Change-Id: I08eeb643676f1f1406f0f3030c341d68179d42a2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103574
Reviewed-by: Nate Bosch <nbosch@google.com>
diff --git a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
index 3606b3d..525f56b 100644
--- a/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
+++ b/pkg/front_end/lib/src/api_unstable/bazel_worker.dart
@@ -11,10 +11,13 @@
 
 import 'package:kernel/target/targets.dart' show Target;
 
-import '../api_prototype/compiler_options.dart' show CompilerOptions;
+import '../api_prototype/compiler_options.dart'
+    show CompilerOptions, parseExperimentalFlags;
 
 import '../api_prototype/diagnostic_message.dart' show DiagnosticMessageHandler;
 
+import '../api_prototype/experimental_flags.dart' show ExperimentalFlag;
+
 import '../api_prototype/file_system.dart' show FileSystem;
 
 import '../base/processed_options.dart' show ProcessedOptions;
@@ -41,6 +44,8 @@
 
 export 'compiler_state.dart' show InitializedCompilerState;
 
+import 'util.dart' show equalMaps;
+
 /// Initializes the compiler for a modular build.
 ///
 /// Re-uses cached components from [_workerInputCache], and reloads them
@@ -54,6 +59,7 @@
     Map<Uri, List<int>> workerInputDigests,
     Target target,
     FileSystem fileSystem,
+    Iterable<String> experiments,
     bool outlineOnly) async {
   final List<int> sdkDigest = workerInputDigests[sdkSummary];
   if (sdkDigest == null) {
@@ -66,10 +72,13 @@
   Map<Uri, WorkerInputComponent> workerInputCache =
       oldState?.workerInputCache ?? new Map<Uri, WorkerInputComponent>();
   bool startOver = false;
+  Map<ExperimentalFlag, bool> experimentalFlags =
+      parseExperimentalFlags(experiments, (e) => throw e);
 
   if (oldState == null ||
       oldState.incrementalCompiler == null ||
-      oldState.incrementalCompiler.outlineOnly != outlineOnly) {
+      oldState.incrementalCompiler.outlineOnly != outlineOnly ||
+      !equalMaps(oldState.options.experimentalFlags, experimentalFlags)) {
     // No - or immediately not correct - previous state.
     startOver = true;
 
@@ -184,7 +193,8 @@
     List<Uri> summaryInputs,
     List<Uri> linkedInputs,
     Target target,
-    FileSystem fileSystem) async {
+    FileSystem fileSystem,
+    Iterable<String> experiments) async {
   // TODO(sigmund): use incremental compiler when it supports our use case.
   // Note: it is common for the summary worker to invoke the compiler with the
   // same input summary URIs, but with different contents, so we'd need to be
@@ -198,7 +208,8 @@
     ..linkedDependencies = linkedInputs
     ..target = target
     ..fileSystem = fileSystem
-    ..environmentDefines = const {};
+    ..environmentDefines = const {}
+    ..experimentalFlags = parseExperimentalFlags(experiments, (e) => throw e);
 
   ProcessedOptions processedOpts = new ProcessedOptions(options: options);
 
diff --git a/pkg/front_end/lib/src/api_unstable/dart2js.dart b/pkg/front_end/lib/src/api_unstable/dart2js.dart
index 8ec174d..e6f8ac6 100644
--- a/pkg/front_end/lib/src/api_unstable/dart2js.dart
+++ b/pkg/front_end/lib/src/api_unstable/dart2js.dart
@@ -32,6 +32,8 @@
 
 import 'compiler_state.dart' show InitializedCompilerState;
 
+import 'util.dart' show equalLists, equalMaps;
+
 export '../api_prototype/compiler_options.dart'
     show CompilerOptions, parseExperimentalFlags;
 
@@ -116,30 +118,13 @@
     Map<ExperimentalFlag, bool> experimentalFlags,
     bool verify: false,
     bool enableAsserts: false}) {
-  bool mapEqual(Map<ExperimentalFlag, bool> a, Map<ExperimentalFlag, bool> b) {
-    if (a == null || b == null) return a == b;
-    if (a.length != b.length) return false;
-    for (var flag in a.keys) {
-      if (!b.containsKey(flag) || a[flag] != b[flag]) return false;
-    }
-    return true;
-  }
-
-  bool listEqual(List<Uri> a, List<Uri> b) {
-    if (a.length != b.length) return false;
-    for (int i = 0; i < a.length; ++i) {
-      if (a[i] != b[i]) return false;
-    }
-    return true;
-  }
-
   linkedDependencies.sort((a, b) => a.toString().compareTo(b.toString()));
 
   if (oldState != null &&
       oldState.options.packagesFileUri == packagesFileUri &&
       oldState.options.librariesSpecificationUri == librariesSpecificationUri &&
-      listEqual(oldState.options.linkedDependencies, linkedDependencies) &&
-      mapEqual(oldState.options.experimentalFlags, experimentalFlags)) {
+      equalLists(oldState.options.linkedDependencies, linkedDependencies) &&
+      equalMaps(oldState.options.experimentalFlags, experimentalFlags)) {
     return oldState;
   }
 
diff --git a/pkg/front_end/lib/src/api_unstable/ddc.dart b/pkg/front_end/lib/src/api_unstable/ddc.dart
index 3230a6b..852cd60 100644
--- a/pkg/front_end/lib/src/api_unstable/ddc.dart
+++ b/pkg/front_end/lib/src/api_unstable/ddc.dart
@@ -29,6 +29,8 @@
 import 'compiler_state.dart'
     show InitializedCompilerState, WorkerInputComponent, digestsEqual;
 
+import 'util.dart' show equalLists, equalMaps;
+
 export '../api_prototype/compiler_options.dart' show CompilerOptions;
 
 export '../api_prototype/diagnostic_message.dart' show DiagnosticMessage;
@@ -79,29 +81,13 @@
     {FileSystem fileSystem,
     Map<ExperimentalFlag, bool> experiments}) async {
   inputSummaries.sort((a, b) => a.toString().compareTo(b.toString()));
-  bool listEqual(List<Uri> a, List<Uri> b) {
-    if (a.length != b.length) return false;
-    for (int i = 0; i < a.length; ++i) {
-      if (a[i] != b[i]) return false;
-    }
-    return true;
-  }
-
-  bool mapEqual(Map<ExperimentalFlag, bool> a, Map<ExperimentalFlag, bool> b) {
-    if (a == null || b == null) return a == b;
-    if (a.length != b.length) return false;
-    for (var flag in a.keys) {
-      if (!b.containsKey(flag) || a[flag] != b[flag]) return false;
-    }
-    return true;
-  }
 
   if (oldState != null &&
       oldState.options.sdkSummary == sdkSummary &&
       oldState.options.packagesFileUri == packagesFile &&
       oldState.options.librariesSpecificationUri == librariesSpecificationUri &&
-      listEqual(oldState.options.inputSummaries, inputSummaries) &&
-      mapEqual(oldState.options.experimentalFlags, experiments)) {
+      equalLists(oldState.options.inputSummaries, inputSummaries) &&
+      equalMaps(oldState.options.experimentalFlags, experiments)) {
     // Reuse old state.
 
     // These libraries are marked external when compiling. If not un-marking
diff --git a/pkg/front_end/lib/src/api_unstable/util.dart b/pkg/front_end/lib/src/api_unstable/util.dart
new file mode 100644
index 0000000..146ddd0
--- /dev/null
+++ b/pkg/front_end/lib/src/api_unstable/util.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2019, 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.
+
+bool equalLists<T>(List<T> a, List<T> b) {
+  if (identical(a, b)) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+  for (int i = 0; i < a.length; ++i) {
+    if (a[i] != b[i]) return false;
+  }
+  return true;
+}
+
+bool equalMaps<K, V>(Map<K, V> a, Map<K, V> b) {
+  if (identical(a, b)) return true;
+  if (a == null || b == null) return false;
+  if (a.length != b.length) return false;
+  for (K key in a.keys) {
+    if (!b.containsKey(key) || a[key] != b[key]) return false;
+  }
+  return true;
+}
diff --git a/utils/bazel/kernel_worker.dart b/utils/bazel/kernel_worker.dart
index 8ebfaee..2c873ee 100644
--- a/utils/bazel/kernel_worker.dart
+++ b/utils/bazel/kernel_worker.dart
@@ -128,7 +128,9 @@
   ..addOption('output')
   ..addFlag('reuse-compiler-result', defaultsTo: false)
   ..addFlag('use-incremental-compiler', defaultsTo: false)
-  ..addFlag('track-widget-creation', defaultsTo: false);
+  ..addFlag('track-widget-creation', defaultsTo: false)
+  ..addMultiOption('enable-experiment',
+      help: 'Enable a language experiment when invoking the CFE.');
 
 class ComputeKernelResult {
   final bool succeeded;
@@ -167,8 +169,7 @@
   if (multiRoots.isEmpty) multiRoots.add(Uri.base);
   var fileSystem = new MultiRootFileSystem(parsedArgs['multi-root-scheme'],
       multiRoots, fe.StandardFileSystem.instance);
-  var sources =
-      (parsedArgs['source'] as List<String>).map(Uri.base.resolve).toList();
+  var sources = (parsedArgs['source'] as List<String>).map(_toUri).toList();
   var excludeNonSources = parsedArgs['exclude-non-sources'] as bool;
 
   var summaryOnly = parsedArgs['summary-only'] as bool;
@@ -222,19 +223,11 @@
       out.writeln('error: unsupported target: $targetName');
   }
 
-  // TODO(sigmund,jakemac): make it mandatory. We allow null while we migrate
-  // existing clients of this tool.
-  var librariesSpec = parsedArgs['libraries-file'] == null
-      ? null
-      : Uri.base.resolve(parsedArgs['libraries-file']);
+  List<Uri> linkedInputs =
+      (parsedArgs['input-linked'] as List<String>).map(_toUri).toList();
 
-  List<Uri> linkedInputs = (parsedArgs['input-linked'] as List<String>)
-      .map(Uri.base.resolve)
-      .toList();
-
-  List<Uri> summaryInputs = (parsedArgs['input-summary'] as List<String>)
-      .map(Uri.base.resolve)
-      .toList();
+  List<Uri> summaryInputs =
+      (parsedArgs['input-summary'] as List<String>).map(_toUri).toList();
 
   fe.InitializedCompilerState state;
   bool usingIncrementalCompiler = false;
@@ -253,27 +246,30 @@
       inputDigests[uri] = input.digest;
     }
 
+    // TODO(sigmund): add support for experiments with the incremental compiler.
     state = await fe.initializeIncrementalCompiler(
         previousState,
-        Uri.base.resolve(parsedArgs['dart-sdk-summary']),
-        Uri.base.resolve(parsedArgs['packages-file']),
-        librariesSpec,
+        _toUri(parsedArgs['dart-sdk-summary']),
+        _toUri(parsedArgs['packages-file']),
+        _toUri(parsedArgs['libraries-file']),
         summaryInputs,
         inputDigests,
         target,
         fileSystem,
+        (parsedArgs['enable-experiment'] as List<String>),
         summaryOnly);
   } else {
     state = await fe.initializeCompiler(
         // TODO(sigmund): pass an old state once we can make use of it.
         null,
-        Uri.base.resolve(parsedArgs['dart-sdk-summary']),
-        librariesSpec,
-        Uri.base.resolve(parsedArgs['packages-file']),
+        _toUri(parsedArgs['dart-sdk-summary']),
+        _toUri(parsedArgs['libraries-file']),
+        _toUri(parsedArgs['packages-file']),
         summaryInputs,
         linkedInputs,
         target,
-        fileSystem);
+        fileSystem,
+        (parsedArgs['enable-experiment'] as List<String>));
   }
 
   void onDiagnostic(fe.DiagnosticMessage message) {
@@ -308,7 +304,9 @@
     Component component =
         await fe.compileComponent(state, sources, onDiagnostic);
     kernel = fe.serializeComponent(component,
-        filter: (library) => sources.contains(library.importUri),
+        filter: excludeNonSources
+            ? (library) => sources.contains(library.importUri)
+            : null,
         includeOffsets: true);
   }
 
@@ -365,3 +363,8 @@
     }
   }
 }
+
+Uri _toUri(String uriString) {
+  if (uriString == null) return null;
+  return Uri.base.resolve(uriString);
+}