forward language experiments args from the runner executable to the frontend server compiler (#1513)

diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md
index 6e607cf..e990855 100644
--- a/pkgs/test/CHANGELOG.md
+++ b/pkgs/test/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 1.17.3
+
+* Forward experiment args from the runner executable to the compiler with the
+  new vm test loading strategy.
+
 ## 1.17.2
 
 * Fix a windows issue with the new loading strategy.
diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml
index 46d17a6..f202459 100644
--- a/pkgs/test/pubspec.yaml
+++ b/pkgs/test/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test
-version: 1.17.2
+version: 1.17.3
 description: >-
   A full featured library for writing and running Dart tests across platforms.
 repository: https://github.com/dart-lang/test/blob/master/pkgs/test
@@ -34,7 +34,7 @@
   yaml: ^3.0.0
   # Use an exact version until the test_api and test_core package are stable.
   test_api: 0.4.0
-  test_core: 0.3.22
+  test_core: 0.3.23
 
 dev_dependencies:
   fake_async: ^1.0.0
diff --git a/pkgs/test/test/runner/runner_test.dart b/pkgs/test/test/runner/runner_test.dart
index b924952..a0633cf 100644
--- a/pkgs/test/test/runner/runner_test.dart
+++ b/pkgs/test/test/runner/runner_test.dart
@@ -840,4 +840,40 @@
       });
     });
   });
+
+  group('language experiments', () {
+    group('are inherited from the executable arguments', () {
+      setUp(() async {
+        await d.file('test.dart', '''
+// @dart=2.10
+import 'package:test/test.dart';
+
+// Compile time error if the experiment is enabled
+int x;
+
+void main() {
+  test('x is null', () {
+    expect(x, isNull);
+  });
+}
+''').create();
+      });
+
+      for (var platform in ['vm', 'chrome']) {
+        test('on the $platform platform', () async {
+          var test = await runTest(['test.dart', '-p', platform],
+              vmArgs: ['--enable-experiment=non-nullable']);
+
+          await expectLater(test.stdout, emitsThrough(contains('int x;')));
+          await test.shouldExit(1);
+
+          // Test that they can be removed on subsequent runs as well
+          test = await runTest(['test.dart', '-p', platform]);
+          await expectLater(
+              test.stdout, emitsThrough(contains('+1: All tests passed!')));
+          await test.shouldExit(0);
+        });
+      }
+    });
+  });
 }
diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md
index 0e8265f..c483115 100644
--- a/pkgs/test_core/CHANGELOG.md
+++ b/pkgs/test_core/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.3.23
+
+* Forward experiment args from the runner executable to the compiler with the
+  new vm test loading strategy.
+
 ## 0.3.22
 
 * Fix a windows issue with the new loading strategy.
diff --git a/pkgs/test_core/lib/src/runner/compiler_pool.dart b/pkgs/test_core/lib/src/runner/compiler_pool.dart
index 1c34f6a..c712886 100644
--- a/pkgs/test_core/lib/src/runner/compiler_pool.dart
+++ b/pkgs/test_core/lib/src/runner/compiler_pool.dart
@@ -11,6 +11,7 @@
 
 import '../util/io.dart';
 import '../util/package_config.dart';
+import '../util/dart.dart';
 import 'configuration.dart';
 import 'suite.dart';
 
@@ -63,7 +64,7 @@
         if (Platform.isWindows) dart2jsPath += '.bat';
 
         var args = [
-          for (var experiment in _enabledExperiments)
+          for (var experiment in enabledExperiments)
             '--enable-experiment=$experiment',
           '--enable-asserts',
           wrapperPath,
@@ -140,23 +141,3 @@
     });
   }
 }
-
-/// Parses and returns the currently enabled experiments from
-/// [Platform.executableArguments].
-final List<String> _enabledExperiments = () {
-  var experiments = <String>[];
-  var itr = Platform.executableArguments.iterator;
-  while (itr.moveNext()) {
-    var arg = itr.current;
-    if (arg == '--enable-experiment') {
-      if (!itr.moveNext()) break;
-      experiments.add(itr.current);
-    } else if (arg.startsWith('--enable-experiment=')) {
-      var parts = arg.split('=');
-      if (parts.length == 2) {
-        experiments.addAll(parts[1].split(','));
-      }
-    }
-  }
-  return experiments;
-}();
diff --git a/pkgs/test_core/lib/src/runner/vm/test_compiler.dart b/pkgs/test_core/lib/src/runner/vm/test_compiler.dart
index 8a93351..6498002 100644
--- a/pkgs/test_core/lib/src/runner/vm/test_compiler.dart
+++ b/pkgs/test_core/lib/src/runner/vm/test_compiler.dart
@@ -13,6 +13,7 @@
 import 'package:frontend_server_client/frontend_server_client.dart';
 
 import '../package_version.dart';
+import '../../util/dart.dart';
 import '../../util/package_config.dart';
 
 class CompilationResponse {
@@ -75,8 +76,8 @@
 
   _TestCompilerForLanguageVersion(
       String dillCachePrefix, this._languageVersionComment)
-      : _dillCachePath =
-            '$dillCachePrefix.${base64.encode(utf8.encode(_languageVersionComment.replaceAll(' ', '')))}';
+      : _dillCachePath = '$dillCachePrefix.'
+            '${_dillCacheSuffix(_languageVersionComment, enabledExperiments)}';
 
   String _generateEntrypoint(Uri testUri) {
     return '''
@@ -157,6 +158,7 @@
       testUri.toString(),
       _outputDill.path,
       platformDill,
+      enabledExperiments: enabledExperiments,
       sdkRoot: sdkRoot,
       packagesJson: (await packageConfigUri).toFilePath(),
       printIncrementalDependencies: false,
@@ -173,3 +175,15 @@
         }
       });
 }
+
+/// Computes a unique dill cache suffix for each [languageVersionComment]
+/// and [enabledExperiments] combination.
+String _dillCacheSuffix(
+    String languageVersionComment, List<String> enabledExperiments) {
+  var identifierString =
+      StringBuffer(languageVersionComment.replaceAll(' ', ''));
+  for (var experiment in enabledExperiments) {
+    identifierString.writeln(experiment);
+  }
+  return base64.encode(utf8.encode(identifierString.toString()));
+}
diff --git a/pkgs/test_core/lib/src/util/dart.dart b/pkgs/test_core/lib/src/util/dart.dart
index e6c3753..5a1546c 100644
--- a/pkgs/test_core/lib/src/util/dart.dart
+++ b/pkgs/test_core/lib/src/util/dart.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:convert';
+import 'dart:io';
 import 'dart:isolate';
 
 import 'package:analyzer/dart/ast/ast.dart';
@@ -68,3 +69,23 @@
 
   return file.span(start, contextRunes.offset);
 }
+
+/// Parses and returns the currently enabled experiments from
+/// [Platform.executableArguments].
+final List<String> enabledExperiments = () {
+  var experiments = <String>[];
+  var itr = Platform.executableArguments.iterator;
+  while (itr.moveNext()) {
+    var arg = itr.current;
+    if (arg == '--enable-experiment') {
+      if (!itr.moveNext()) break;
+      experiments.add(itr.current);
+    } else if (arg.startsWith('--enable-experiment=')) {
+      var parts = arg.split('=');
+      if (parts.length == 2) {
+        experiments.addAll(parts[1].split(','));
+      }
+    }
+  }
+  return experiments;
+}();
diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml
index 2c73b95..eb9b671 100644
--- a/pkgs/test_core/pubspec.yaml
+++ b/pkgs/test_core/pubspec.yaml
@@ -1,5 +1,5 @@
 name: test_core
-version: 0.3.22
+version: 0.3.23
 description: A basic library for writing tests and running them on the VM.
 homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_core
 
@@ -13,7 +13,7 @@
   boolean_selector: ^2.1.0
   collection: ^1.15.0
   coverage: ^1.0.0
-  frontend_server_client: ^2.0.0
+  frontend_server_client: ^2.1.0
   glob: ^2.0.0
   io: ^1.0.0
   meta: ^1.3.0