Version 2.12.0-103.0.dev

Merge commit '97baa6efd187c0eaec6a1dfd53984300588a0101' into 'dev'
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 5e8f2a9..287c5dd 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -32,34 +32,129 @@
 /// analytics logic, it has been moved here.
 Future<void> runDartdev(List<String> args, SendPort port) async {
   VmInteropHandler.initialize(port);
-  if (args.contains('run')) {
-    // These flags have a format that can't be handled by package:args, so while
-    // they are valid flags we'll assume the VM has verified them by this point.
-    args = args
-        .where(
-          (element) => !(element.contains('--observe') ||
-              element.contains('--enable-vm-service')),
-        )
-        .toList();
+
+  int result;
+
+  // The exit code for the dartdev process; null indicates that it has not been
+  // set yet. The value is set in the catch and finally blocks below.
+  int exitCode;
+
+  // Any caught non-UsageExceptions when running the sub command.
+  Object exception;
+  StackTrace stackTrace;
+
+  // The Analytics instance used to report information back to Google Analytics;
+  // see lib/src/analytics.dart.
+  final analytics = createAnalyticsInstance(
+    args.contains('--disable-dartdev-analytics'),
+  );
+
+  // If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
+  // the user is on a terminal, and the machine is not a bot, then print the
+  // disclosure and set analytics.disclosureShownOnTerminal to true.
+  if (analytics is DartdevAnalytics &&
+      !analytics.disclosureShownOnTerminal &&
+      io.stdout.hasTerminal &&
+      !isBot()) {
+    print(analyticsNoticeOnFirstRunMessage);
+    analytics.disclosureShownOnTerminal = true;
   }
 
-  // Finally, call the runner to execute the command; see DartdevRunner.
+  // When `--disable-analytics` or `--enable-analytics` are called we perform
+  // the respective intention and print any notices to standard out and exit.
+  if (args.contains('--disable-analytics')) {
+    // This block also potentially catches the case of (disableAnalytics &&
+    // enableAnalytics), in which we favor the disabling of analytics.
+    analytics.enabled = false;
 
-  final runner = DartdevRunner(args);
-  var exitCode = 1;
+    // Alert the user that analytics has been disabled.
+    print(analyticsDisabledNoticeMessage);
+    VmInteropHandler.exit(0);
+    return;
+  } else if (args.contains('--enable-analytics')) {
+    analytics.enabled = true;
+
+    // Alert the user again that anonymous data will be collected.
+    print(analyticsNoticeOnFirstRunMessage);
+    VmInteropHandler.exit(0);
+    return;
+  }
+
   try {
-    exitCode = await runner.run(args);
-  } on UsageException catch (e) {
-    // TODO(sigurdm): It is unclear when a UsageException gets to here, and
-    // when it is in DartdevRunner.runCommand.
-    io.stderr.writeln('$e');
-    exitCode = 64;
+    final runner = DartdevRunner(args, analytics);
+
+    // Run can't be called with the '--disable-dartdev-analytics' flag; remove
+    // it if it is contained in args.
+    if (args.contains('--disable-dartdev-analytics')) {
+      args = List.from(args)..remove('--disable-dartdev-analytics');
+    }
+
+    if (args.contains('run')) {
+      // These flags have a format that can't be handled by package:args, so while
+      // they are valid flags we'll assume the VM has verified them by this point.
+      args = args
+          .where(
+            (element) => !(element.contains('--observe') ||
+                element.contains('--enable-vm-service')),
+          )
+          .toList();
+    }
+
+    // If ... help pub ... is in the args list, remove 'help', and add '--help'
+    // to the end of the list. This will make it possible to use the help
+    // command to access subcommands of pub such as `dart help pub publish`; see
+    // https://github.com/dart-lang/sdk/issues/42965.
+    if (PubUtils.shouldModifyArgs(args, runner.commands.keys.toList())) {
+      args = PubUtils.modifyArgs(args);
+    }
+
+    // Finally, call the runner to execute the command; see DartdevRunner.
+    result = await runner.run(args);
+  } catch (e, st) {
+    if (e is UsageException) {
+      io.stderr.writeln('$e');
+      exitCode = 64;
+    } else {
+      // Set the exception and stack trace only for non-UsageException cases:
+      exception = e;
+      stackTrace = st;
+      io.stderr.writeln('$e');
+      io.stderr.writeln('$st');
+      exitCode = 1;
+    }
   } finally {
+    // Set the exitCode, if it wasn't set in the catch block above.
+    exitCode ??= result ?? 0;
+
+    // Send analytics before exiting
+    if (analytics.enabled) {
+      // And now send the exceptions and events to Google Analytics:
+      if (exception != null) {
+        unawaited(
+          analytics.sendException(
+              '${exception.runtimeType}\n${sanitizeStacktrace(stackTrace)}',
+              fatal: true),
+        );
+      }
+
+      await analytics.waitForLastPing(
+          timeout: const Duration(milliseconds: 200));
+    }
+
+    // Set the enabled flag in the analytics object to true. Note: this will not
+    // enable the analytics unless the disclosure was shown (terminal detected),
+    // and the machine is not detected to be a bot.
+    if (analytics.firstRun) {
+      analytics.enabled = true;
+    }
+    analytics.close();
     VmInteropHandler.exit(exitCode);
   }
 }
 
 class DartdevRunner extends CommandRunner<int> {
+  final Analytics analytics;
+
   @override
   final ArgParser argParser = ArgParser(
     usageLineLength: dartdevUsageLineLength,
@@ -69,7 +164,8 @@
   static const String dartdevDescription =
       'A command-line utility for Dart development';
 
-  DartdevRunner(List<String> args) : super('dart', '$dartdevDescription.') {
+  DartdevRunner(List<String> args, this.analytics)
+      : super('dart', '$dartdevDescription.') {
     final bool verbose = args.contains('-v') || args.contains('--verbose');
 
     argParser.addFlag('verbose',
@@ -84,9 +180,12 @@
     argParser.addFlag('diagnostics',
         negatable: false, help: 'Show tool diagnostic output.', hide: !verbose);
 
+    // A hidden flag to disable analytics on this run, this constructor can be
+    // called with this flag, but should be removed before run() is called as
+    // the flag has not been added to all sub-commands.
     argParser.addFlag(
-      'analytics',
-      negatable: true,
+      'disable-dartdev-analytics',
+      negatable: false,
       help: 'Disable anonymous analytics for this `dart *` run',
       hide: true,
     );
@@ -113,38 +212,7 @@
   @override
   Future<int> runCommand(ArgResults topLevelResults) async {
     final stopwatch = Stopwatch()..start();
-    // The Analytics instance used to report information back to Google Analytics;
-    // see lib/src/analytics.dart.
-    final analytics = createAnalyticsInstance(!topLevelResults['analytics']);
-
-    // If we have not printed the analyticsNoticeOnFirstRunMessage to stdout,
-    // the user is on a terminal, and the machine is not a bot, then print the
-    // disclosure and set analytics.disclosureShownOnTerminal to true.
-    if (analytics is DartdevAnalytics &&
-        !analytics.disclosureShownOnTerminal &&
-        io.stdout.hasTerminal &&
-        !isBot()) {
-      print(analyticsNoticeOnFirstRunMessage);
-      analytics.disclosureShownOnTerminal = true;
-    }
-
-    // When `--disable-analytics` or `--enable-analytics` are called we perform
-    // the respective intention and print any notices to standard out and exit.
-    if (topLevelResults['disable-analytics']) {
-      // This block also potentially catches the case of (disableAnalytics &&
-      // enableAnalytics), in which we favor the disabling of analytics.
-      analytics.enabled = false;
-
-      // Alert the user that analytics has been disabled.
-      print(analyticsDisabledNoticeMessage);
-      return 0;
-    } else if (topLevelResults['enable-analytics']) {
-      analytics.enabled = true;
-
-      // Alert the user again that anonymous data will be collected.
-      print(analyticsNoticeOnFirstRunMessage);
-      return 0;
-    }
+    assert(!topLevelResults.arguments.contains('--disable-dartdev-analytics'));
 
     if (topLevelResults.command == null &&
         topLevelResults.arguments.isNotEmpty) {
@@ -154,12 +222,14 @@
         io.stderr.writeln(
             "Error when reading '$firstArg': No such file or directory.");
         // This is the exit code used by the frontend.
-        return 254;
+        VmInteropHandler.exit(254);
       }
     }
 
+    isDiagnostics = topLevelResults['diagnostics'];
+
     final Ansi ansi = Ansi(Ansi.terminalSupportsAnsi);
-    log = topLevelResults['diagnostics']
+    log = isDiagnostics
         ? Logger.verbose(ansi: ansi)
         : Logger.standard(ansi: ansi);
 
@@ -177,15 +247,8 @@
       analytics.sendScreenView(path),
     );
 
-    // The exit code for the dartdev process; null indicates that it has not been
-    // set yet. The value is set in the catch and finally blocks below.
-    int exitCode;
-
-    // Any caught non-UsageExceptions when running the sub command.
-    Object exception;
-    StackTrace stackTrace;
     try {
-      exitCode = await super.runCommand(topLevelResults);
+      final exitCode = await super.runCommand(topLevelResults);
 
       if (path != null && analytics.enabled) {
         // Send the event to analytics
@@ -205,16 +268,8 @@
           ),
         );
       }
-    } on UsageException catch (e) {
-      io.stderr.writeln('$e');
-      exitCode = 64;
-    } catch (e, st) {
-      // Set the exception and stack trace only for non-UsageException cases:
-      exception = e;
-      stackTrace = st;
-      io.stderr.writeln('$e');
-      io.stderr.writeln('$st');
-      exitCode = 1;
+
+      return exitCode;
     } finally {
       stopwatch.stop();
       if (analytics.enabled) {
@@ -226,32 +281,6 @@
           ),
         );
       }
-      // Set the exitCode, if it wasn't set in the catch block above.
-      exitCode ??= 0;
-
-      // Send analytics before exiting
-      if (analytics.enabled) {
-        // And now send the exceptions and events to Google Analytics:
-        if (exception != null) {
-          unawaited(
-            analytics.sendException(
-                '${exception.runtimeType}\n${sanitizeStacktrace(stackTrace)}',
-                fatal: true),
-          );
-        }
-
-        await analytics.waitForLastPing(
-            timeout: const Duration(milliseconds: 200));
-      }
-
-      // Set the enabled flag in the analytics object to true. Note: this will not
-      // enable the analytics unless the disclosure was shown (terminal detected),
-      // and the machine is not detected to be a bot.
-      if (analytics.firstRun) {
-        analytics.enabled = true;
-      }
-      analytics.close();
-      return exitCode;
     }
   }
 }
diff --git a/pkg/dartdev/lib/src/analytics.dart b/pkg/dartdev/lib/src/analytics.dart
index c9b1edf..14b9fb2 100644
--- a/pkg/dartdev/lib/src/analytics.dart
+++ b/pkg/dartdev/lib/src/analytics.dart
@@ -57,7 +57,7 @@
   }
 
   if (disableAnalytics) {
-    // Dartdev tests pass a hidden 'no-analytics' flag which is
+    // Dartdev tests pass a hidden 'disable-dartdev-analytics' flag which is
     // handled here.
     //
     // Also, stdout.hasTerminal is checked; if there is no terminal we infer
diff --git a/pkg/dartdev/lib/src/utils.dart b/pkg/dartdev/lib/src/utils.dart
index 32a9c8e..88b80ed 100644
--- a/pkg/dartdev/lib/src/utils.dart
+++ b/pkg/dartdev/lib/src/utils.dart
@@ -38,6 +38,31 @@
   return s;
 }
 
+/// Static util methods used in dartdev to potentially modify the order of the
+/// arguments passed into dartdev.
+class PubUtils {
+  /// If [doModifyArgs] returns true, then this method returns a modified copy
+  /// of the argument list, 'help' is removed from the interior of the list, and
+  /// '--help' is added to the end of the list of arguments. This method returns
+  /// a modified copy of the list, the list itself is not modified.
+  static List<String> modifyArgs(List<String> args) => List.from(args)
+    ..remove('help')
+    ..add('--help');
+
+  /// If ... help pub ..., and no other verb (such as 'analyze') appears before
+  /// the ... help pub ... in the argument list, then return true.
+  static bool shouldModifyArgs(List<String> args, List<String> allCmds) =>
+      args != null &&
+      allCmds != null &&
+      args.isNotEmpty &&
+      allCmds.isNotEmpty &&
+      args.firstWhere((arg) => allCmds.contains(arg), orElse: () => '') ==
+          'help' &&
+      args.contains('help') &&
+      args.contains('pub') &&
+      args.indexOf('help') + 1 == args.indexOf('pub');
+}
+
 extension FileSystemEntityExtension on FileSystemEntity {
   String get name => p.basename(path);
 
diff --git a/pkg/dartdev/test/analytics_test.dart b/pkg/dartdev/test/analytics_test.dart
index 8c74972..26d9822 100644
--- a/pkg/dartdev/test/analytics_test.dart
+++ b/pkg/dartdev/test/analytics_test.dart
@@ -23,7 +23,7 @@
   group('Sending analytics', () {
     test('help', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['help']);
+      final result = p.runSync('help', []);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -51,7 +51,7 @@
     });
     test('create', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['create', '-tpackage-simple', 'name']);
+      final result = p.runSync('create', ['-tpackage-simple', 'name']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -82,7 +82,7 @@
 
     test('pub get', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['pub', 'get', '--dry-run']);
+      final result = p.runSync('pub', ['get', '--dry-run']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -113,7 +113,7 @@
 
     test('format', () {
       final p = project(logAnalytics: true);
-      final result = p.runSync(['format', '-l80']);
+      final result = p.runSync('format', ['-l80']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
@@ -146,8 +146,7 @@
       final p = project(
           mainSrc: 'void main(List<String> args) => print(args)',
           logAnalytics: true);
-      final result = p.runSync([
-        'run',
+      final result = p.runSync('run', [
         '--no-pause-isolates-on-exit',
         '--enable-asserts',
         'lib/main.dart',
@@ -185,8 +184,7 @@
       final p = project(
           mainSrc: 'void main(List<String> args) => print(args);',
           logAnalytics: true);
-      final result = p.runSync([
-        'run',
+      final result = p.runSync('run', [
         '--enable-experiment=non-nullable',
         'lib/main.dart',
       ]);
@@ -223,7 +221,7 @@
           mainSrc: 'void main(List<String> args) => print(args);',
           logAnalytics: true);
       final result = p
-          .runSync(['compile', 'kernel', 'lib/main.dart', '-o', 'main.kernel']);
+          .runSync('compile', ['kernel', 'lib/main.dart', '-o', 'main.kernel']);
       expect(extractAnalytics(result), [
         {
           'hitType': 'screenView',
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index f2d9895..12e1776 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -64,7 +64,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['analyze', '--help']);
+    var result = p.runSync('analyze', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -74,7 +74,7 @@
 
   test('multiple directories', () {
     p = project();
-    var result = p.runSync(['analyze', '/no/such/dir1/', '/no/such/dir2/']);
+    var result = p.runSync('analyze', ['/no/such/dir1/', '/no/such/dir2/']);
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
@@ -84,7 +84,7 @@
 
   test('no such directory', () {
     p = project();
-    var result = p.runSync(['analyze', '/no/such/dir1/']);
+    var result = p.runSync('analyze', ['/no/such/dir1/']);
 
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
@@ -95,7 +95,7 @@
   test('current working directory', () {
     p = project(mainSrc: 'int get foo => 1;\n');
 
-    var result = p.runSync(['analyze'], workingDir: p.dirPath);
+    var result = p.runSync('analyze', [], workingDir: p.dirPath);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -104,7 +104,7 @@
 
   test('no errors', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -113,7 +113,7 @@
 
   test('one error', () {
     p = project(mainSrc: "int get foo => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
@@ -125,7 +125,7 @@
 
   test('two errors', () {
     p = project(mainSrc: "int get foo => 'str';\nint get bar => 'str';\n");
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
@@ -136,7 +136,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', '--fatal-warnings', p.dirPath]);
+    var result = p.runSync('analyze', ['--fatal-warnings', p.dirPath]);
 
     expect(result.exitCode, equals(2));
     expect(result.stderr, isEmpty);
@@ -147,7 +147,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, equals(2));
     expect(result.stderr, isEmpty);
@@ -158,7 +158,7 @@
     p = project(
         mainSrc: _unusedImportCodeSnippet,
         analysisOptions: _unusedImportAnalysisOptions);
-    var result = p.runSync(['analyze', '--no-fatal-warnings', p.dirPath]);
+    var result = p.runSync('analyze', ['--no-fatal-warnings', p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -167,7 +167,7 @@
 
   test('info implicit no --fatal-infos', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'String foo() {}');
-    var result = p.runSync(['analyze', p.dirPath]);
+    var result = p.runSync('analyze', [p.dirPath]);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -176,7 +176,7 @@
 
   test('info --fatal-infos', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'String foo() {}');
-    var result = p.runSync(['analyze', '--fatal-infos', p.dirPath]);
+    var result = p.runSync('analyze', ['--fatal-infos', p.dirPath]);
 
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
@@ -190,7 +190,7 @@
   var one = 1;
   return result;
 }''');
-    var result = p.runSync(['analyze', '--verbose', p.dirPath]);
+    var result = p.runSync('analyze', ['--verbose', p.dirPath]);
 
     expect(result.exitCode, 3);
     expect(result.stderr, isEmpty);
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index f2f5f31..57e669a 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -27,9 +27,8 @@
   test('Implicit --help', () {
     final p = project();
     var result = p.runSync(
-      [
-        'compile',
-      ],
+      'compile',
+      [],
     );
     expect(result.stderr, contains('Compile Dart'));
     expect(result.exitCode, compileErrorExitCode);
@@ -38,7 +37,8 @@
   test('--help', () {
     final p = project();
     final result = p.runSync(
-      ['compile', '--help'],
+      'compile',
+      ['--help'],
     );
     expect(result.stdout, contains('Compile Dart'));
     expect(result.exitCode, 0);
@@ -48,8 +48,8 @@
     final p = project(mainSrc: 'void main() { print("I love jit"); }');
     final outFile = path.join(p.dirPath, 'main.jit');
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'jit-snapshot',
         '-o',
         outFile,
@@ -61,7 +61,7 @@
     expect(File(outFile).existsSync(), true,
         reason: 'File not found: $outFile');
 
-    result = p.runSync(['run', 'main.jit']);
+    result = p.runSync('run', ['main.jit']);
     expect(result.stdout, contains('I love jit'));
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
@@ -73,8 +73,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'lib', 'main.exe'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'exe',
         inFile,
       ],
@@ -102,8 +102,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'myexe'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'exe',
         '--define',
         'life=42',
@@ -134,8 +134,8 @@
     final outFile = path.canonicalize(path.join(p.dirPath, 'main.aot'));
 
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'aot-snapshot',
         '-o',
         'main.aot',
@@ -163,8 +163,8 @@
     final p = project(mainSrc: 'void main() { print("I love kernel"); }');
     final outFile = path.join(p.dirPath, 'main.dill');
     var result = p.runSync(
+      'compile',
       [
-        'compile',
         'kernel',
         '-o',
         outFile,
@@ -176,7 +176,7 @@
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
 
-    result = p.runSync(['run', 'main.dill']);
+    result = p.runSync('run', ['main.dill']);
     expect(result.stdout, contains('I love kernel'));
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
@@ -187,8 +187,7 @@
     final inFile = path.canonicalize(path.join(p.dirPath, p.relativeFilePath));
     final outFile = path.canonicalize(path.join(p.dirPath, 'main.js'));
 
-    final result = p.runSync([
-      'compile',
+    final result = p.runSync('compile', [
       'js',
       '-m',
       '-o',
diff --git a/pkg/dartdev/test/commands/create_test.dart b/pkg/dartdev/test/commands/create_test.dart
index 7f5c846..38a8ebd 100644
--- a/pkg/dartdev/test/commands/create_test.dart
+++ b/pkg/dartdev/test/commands/create_test.dart
@@ -36,7 +36,7 @@
   test('list templates', () {
     p = project();
 
-    ProcessResult result = p.runSync(['create', '--list-templates']);
+    ProcessResult result = p.runSync('create', ['--list-templates']);
     expect(result.exitCode, 0);
 
     String output = result.stdout.toString();
@@ -50,9 +50,7 @@
   test('no directory given', () {
     p = project();
 
-    ProcessResult result = p.runSync([
-      'create',
-    ]);
+    ProcessResult result = p.runSync('create', []);
     expect(result.exitCode, 1);
   });
 
@@ -60,7 +58,7 @@
     p = project();
 
     ProcessResult result = p.runSync(
-        ['create', '--template', CreateCommand.defaultTemplateId, p.dir.path]);
+        'create', ['--template', CreateCommand.defaultTemplateId, p.dir.path]);
     expect(result.exitCode, 73);
   });
 
@@ -68,7 +66,7 @@
     p = project();
 
     ProcessResult result =
-        p.runSync(['create', '--no-pub', '--template', 'foo-bar', p.dir.path]);
+        p.runSync('create', ['--no-pub', '--template', 'foo-bar', p.dir.path]);
     expect(result.exitCode, isNot(0));
   });
 
@@ -78,7 +76,7 @@
       p = project();
 
       ProcessResult result = p
-          .runSync(['create', '--force', '--template', templateId, p.dir.path]);
+          .runSync('create', ['--force', '--template', templateId, p.dir.path]);
       expect(result.exitCode, 0);
 
       String projectName = path.basename(p.dir.path);
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 8a92d7b..40c7860 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -29,9 +29,9 @@
   ProcessResult runFix(List<String> args, {String workingDir}) {
     if (runFromSource) {
       var binary = path.join(Directory.current.path, 'bin', 'dartdev.dart');
-      return p.runSync([binary, 'fix', ...?args], workingDir: workingDir);
+      return p.runSync(binary, ['fix', ...?args], workingDir: workingDir);
     }
-    return p.runSync(['fix', ...args], workingDir: workingDir);
+    return p.runSync('fix', args, workingDir: workingDir);
   }
 
   test('none', () {
@@ -65,7 +65,6 @@
     - prefer_single_quotes
 ''',
     );
-
     var result = runFix(['--apply'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
diff --git a/pkg/dartdev/test/commands/flag_test.dart b/pkg/dartdev/test/commands/flag_test.dart
index 0475ee3..6416ffc 100644
--- a/pkg/dartdev/test/commands/flag_test.dart
+++ b/pkg/dartdev/test/commands/flag_test.dart
@@ -6,6 +6,7 @@
 
 import 'package:args/command_runner.dart';
 import 'package:dartdev/dartdev.dart';
+import 'package:dartdev/src/analytics.dart' show disabledAnalytics;
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -19,7 +20,7 @@
   // For each command description, assert that the values are not empty, don't
   // have trailing white space and end with a period.
   test('description formatting', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       expect(commandKey, isNotEmpty);
@@ -31,7 +32,7 @@
 
   // Assert that all found usageLineLengths are the same and null
   test('argParser usageLineLength', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       if (command.argParser != null) {
@@ -61,7 +62,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['--help']);
+    var result = p.runSync('--help', []);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -79,7 +80,7 @@
 
   test('--help --verbose', () {
     p = project();
-    var result = p.runSync(['--help', '--verbose']);
+    var result = p.runSync('--help', ['--verbose']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, isEmpty);
@@ -89,7 +90,7 @@
 
   test('--help -v', () {
     p = project();
-    var result = p.runSync(['--help', '-v']);
+    var result = p.runSync('--help', ['-v']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, isEmpty);
@@ -99,7 +100,7 @@
 
   test('help', () {
     p = project();
-    var result = p.runSync(['help']);
+    var result = p.runSync('help', []);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -117,7 +118,7 @@
 
   test('help --verbose', () {
     p = project();
-    var result = p.runSync(['help', '--verbose']);
+    var result = p.runSync('help', ['--verbose']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains('migrate '));
@@ -125,7 +126,7 @@
 
   test('help -v', () {
     p = project();
-    var result = p.runSync(['help', '-v']);
+    var result = p.runSync('help', ['-v']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains('migrate '));
diff --git a/pkg/dartdev/test/commands/format_test.dart b/pkg/dartdev/test/commands/format_test.dart
index 6f36795..cded95b 100644
--- a/pkg/dartdev/test/commands/format_test.dart
+++ b/pkg/dartdev/test/commands/format_test.dart
@@ -19,7 +19,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['format', '--help']);
+    var result = p.runSync('format', ['--help']);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Idiomatically format Dart source code.'));
@@ -32,7 +32,7 @@
 
   test('--help --verbose', () {
     p = project();
-    var result = p.runSync(['format', '--help', '--verbose']);
+    var result = p.runSync('format', ['--help', '--verbose']);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Idiomatically format Dart source code.'));
@@ -45,7 +45,7 @@
 
   test('unchanged', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    ProcessResult result = p.runSync(['format', p.relativeFilePath]);
+    ProcessResult result = p.runSync('format', [p.relativeFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, startsWith('Formatted 1 file (0 changed) in '));
@@ -53,7 +53,7 @@
 
   test('formatted', () {
     p = project(mainSrc: 'int get foo =>       1;\n');
-    ProcessResult result = p.runSync(['format', p.relativeFilePath]);
+    ProcessResult result = p.runSync('format', [p.relativeFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(
@@ -65,7 +65,7 @@
   test('unknown file', () {
     p = project(mainSrc: 'int get foo => 1;\n');
     var unknownFilePath = '${p.relativeFilePath}-unknown-file.dart';
-    ProcessResult result = p.runSync(['format', unknownFilePath]);
+    ProcessResult result = p.runSync('format', [unknownFilePath]);
     expect(result.exitCode, 0);
     expect(result.stderr,
         startsWith('No file or directory found at "$unknownFilePath".'));
diff --git a/pkg/dartdev/test/commands/help_test.dart b/pkg/dartdev/test/commands/help_test.dart
index afbaf09..c272e43 100644
--- a/pkg/dartdev/test/commands/help_test.dart
+++ b/pkg/dartdev/test/commands/help_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:args/command_runner.dart';
 import 'package:dartdev/dartdev.dart';
+import 'package:dartdev/src/analytics.dart' show disabledAnalytics;
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -21,14 +22,14 @@
   List<String> _commandsNotTested = <String>[
     'help', // `dart help help` is redundant
   ];
-  DartdevRunner(['--no-analytics'])
+  DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
       .commands
       .forEach((String commandKey, Command command) {
     if (!_commandsNotTested.contains(commandKey)) {
       test('(help $commandKey == $commandKey --help)', () {
         p = project();
-        var result = p.runSync(['help', commandKey]);
-        var verbHelpResult = p.runSync([commandKey, '--help']);
+        var result = p.runSync('help', [commandKey]);
+        var verbHelpResult = p.runSync(commandKey, ['--help']);
 
         expect(result.stdout, contains(verbHelpResult.stdout));
         expect(result.stderr, contains(verbHelpResult.stderr));
@@ -38,15 +39,15 @@
 
   test('(help pub == pub --help)', () {
     p = project();
-    var result = p.runSync(['help', 'pub']);
-    var pubHelpResult = p.runSync(['pub', '--help']);
+    var result = p.runSync('help', ['pub']);
+    var pubHelpResult = p.runSync('pub', ['--help']);
 
     expect(result.stdout, contains(pubHelpResult.stdout));
     expect(result.stderr, contains(pubHelpResult.stderr));
   });
 
   test('(--help flags also have -h abbr)', () {
-    DartdevRunner(['--no-analytics'])
+    DartdevRunner(['--disable-dartdev-analytics'], disabledAnalytics)
         .commands
         .forEach((String commandKey, Command command) {
       var helpOption = command.argParser.options['help'];
diff --git a/pkg/dartdev/test/commands/migrate_test.dart b/pkg/dartdev/test/commands/migrate_test.dart
index 681d550..8101640 100644
--- a/pkg/dartdev/test/commands/migrate_test.dart
+++ b/pkg/dartdev/test/commands/migrate_test.dart
@@ -21,7 +21,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['migrate', '--help']);
+    var result = p.runSync('migrate', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
@@ -34,7 +34,7 @@
   test('directory implicit', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'int get foo => 1;\n');
     var result =
-        p.runSync(['migrate', '--no-web-preview'], workingDir: p.dirPath);
+        p.runSync('migrate', ['--no-web-preview'], workingDir: p.dirPath);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Generating migration suggestions'));
@@ -42,7 +42,7 @@
 
   test('directory explicit', () {
     p = project(mainSrc: dartVersionFilePrefix2_9 + 'int get foo => 1;\n');
-    var result = p.runSync(['migrate', '--no-web-preview', p.dirPath]);
+    var result = p.runSync('migrate', ['--no-web-preview', p.dirPath]);
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('Generating migration suggestions'));
@@ -50,7 +50,7 @@
 
   test('bad directory', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['migrate', 'foo_bar_dir']);
+    var result = p.runSync('migrate', ['foo_bar_dir']);
     expect(result.exitCode, 1);
     expect(result.stderr, contains('foo_bar_dir does not exist'));
     expect(result.stdout, isEmpty);
@@ -58,7 +58,7 @@
 
   test('pub get needs running', () {
     p = project(mainSrc: 'import "package:foo/foo.dart";\n');
-    var result = p.runSync(['migrate', p.dirPath]);
+    var result = p.runSync('migrate', [p.dirPath]);
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
     expect(result.stdout, runPubGet);
@@ -67,7 +67,7 @@
 
   test('non-pub-related error', () {
     p = project(mainSrc: 'var missing = "semicolon"\n');
-    var result = p.runSync(['migrate', p.dirPath]);
+    var result = p.runSync('migrate', [p.dirPath]);
     expect(result.exitCode, 1);
     expect(result.stderr, isEmpty);
     expect(result.stdout, runPubGet);
diff --git a/pkg/dartdev/test/commands/pub_test.dart b/pkg/dartdev/test/commands/pub_test.dart
index e164858..82f00d0 100644
--- a/pkg/dartdev/test/commands/pub_test.dart
+++ b/pkg/dartdev/test/commands/pub_test.dart
@@ -26,7 +26,7 @@
   }
 
   test('implicit --help', () {
-    final result = project().runSync(['pub']);
+    final result = project().runSync('pub', []);
     expect(result, isNotNull);
     expect(result.exitCode, 64);
     expect(result.stderr, contains('Missing subcommand for "dart pub".'));
@@ -35,17 +35,17 @@
   });
 
   test('--help', () {
-    _assertPubHelpInvoked(project().runSync(['pub', '--help']));
+    _assertPubHelpInvoked(project().runSync('pub', ['--help']));
   });
 
   test('-h', () {
-    _assertPubHelpInvoked(project().runSync(['pub', '-h']));
+    _assertPubHelpInvoked(project().runSync('pub', ['-h']));
   });
 
   test('help cache', () {
     p = project();
-    var result = p.runSync(['help', 'pub', 'cache']);
-    var result2 = p.runSync(['pub', 'cache', '--help']);
+    var result = p.runSync('help', ['pub', 'cache']);
+    var result2 = p.runSync('pub', ['cache', '--help']);
 
     expect(result.exitCode, 0);
 
@@ -58,8 +58,8 @@
 
   test('help publish', () {
     p = project();
-    var result = p.runSync(['help', 'pub', 'publish']);
-    var result2 = p.runSync(['pub', 'publish', '--help']);
+    var result = p.runSync('help', ['pub', 'publish']);
+    var result2 = p.runSync('pub', ['publish', '--help']);
 
     expect(result.exitCode, 0);
 
@@ -77,10 +77,10 @@
         "void main() { int? a; a = null; print('a is \$a.'); }");
 
     // run 'pub get'
-    p.runSync(['pub', 'get']);
+    p.runSync('pub', ['get']);
 
     var result = p.runSync(
-        ['pub', 'run', '--enable-experiment=no-non-nullable', 'main.dart']);
+        'pub', ['run', '--enable-experiment=no-non-nullable', 'main.dart']);
 
     expect(result.exitCode, 254);
     expect(result.stdout, isEmpty);
@@ -93,7 +93,7 @@
 
   test('failure', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['pub', 'deps']);
+    var result = p.runSync('pub', ['deps']);
     expect(result.exitCode, 65);
     expect(result.stdout, isEmpty);
     expect(result.stderr, contains('No pubspec.lock file found'));
@@ -101,7 +101,7 @@
 
   test('failure unknown option', () {
     p = project(mainSrc: 'int get foo => 1;\n');
-    var result = p.runSync(['pub', 'deps', '--foo']);
+    var result = p.runSync('pub', ['deps', '--foo']);
     expect(result.exitCode, 64);
     expect(result.stdout, isEmpty);
     expect(result.stderr, startsWith('Could not find an option named "foo".'));
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index 59bffba..9d2125c 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -20,7 +20,7 @@
 
   test('--help', () {
     p = project();
-    var result = p.runSync(['run', '--help']);
+    var result = p.runSync('run', ['--help']);
 
     expect(result.stdout, contains('Run a Dart program.'));
     expect(result.stdout, contains('Debugging options:'));
@@ -30,7 +30,7 @@
 
   test("'Hello World'", () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
-    ProcessResult result = p.runSync(['run', p.relativeFilePath]);
+    ProcessResult result = p.runSync('run', [p.relativeFilePath]);
 
     expect(result.stdout, contains('Hello World'));
     expect(result.stderr, isEmpty);
@@ -40,7 +40,7 @@
   test('no such file', () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
     ProcessResult result =
-        p.runSync(['run', 'no/such/file/${p.relativeFilePath}']);
+        p.runSync('run', ['no/such/file/${p.relativeFilePath}']);
 
     expect(result.stderr, isNotEmpty);
     expect(result.exitCode, isNot(0));
@@ -51,7 +51,7 @@
     // name (package name) will be the name of the temporary directory on disk
     p = project(mainSrc: "void main() { print('Hello World'); }");
     p.file('bin/main.dart', "void main() { print('Hello main.dart'); }");
-    ProcessResult result = p.runSync(['run']);
+    ProcessResult result = p.runSync('run', []);
 
     expect(result.stdout, contains('Hello main.dart'));
     expect(result.stderr, isEmpty);
@@ -62,7 +62,7 @@
   test('missing implicit packageName.dart', () {
     p = project(mainSrc: "void main() { print('Hello World'); }");
     p.file('bin/foo.dart', "void main() { print('Hello main.dart'); }");
-    ProcessResult result = p.runSync(['run']);
+    ProcessResult result = p.runSync('run', []);
 
     expect(result.stdout, isEmpty);
     expect(
@@ -75,8 +75,7 @@
   test('arguments are properly passed', () {
     p = project();
     p.file('main.dart', 'void main(args) { print(args); }');
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--enable-experiment=triple-shift',
       'main.dart',
       'argument1',
@@ -112,7 +111,7 @@
 void main(List<String> args) => print("$b $args");
 ''');
 
-      ProcessResult result = p.runSync(['run', 'bar:main', '--arg1', 'arg2']);
+      ProcessResult result = p.runSync('run', ['bar:main', '--arg1', 'arg2']);
 
       expect(result.stderr, isEmpty);
       expect(result.stdout, contains('FOO BAR [--arg1, arg2]'));
@@ -127,8 +126,7 @@
     p.file('main.dart', 'void main(args) { print(args); }');
     // Test with absolute path
     final name = path.join(p.dirPath, 'main.dart');
-    final result = p.runSync([
-      'run',
+    final result = p.runSync('run', [
       '--enable-experiment=triple-shift',
       name,
       '--argument1',
@@ -146,8 +144,7 @@
     p.file('main.dart', 'void main(args) { print(args); }');
     // Test with File uri
     final name = path.join(p.dirPath, 'main.dart');
-    final result = p.runSync([
-      'run',
+    final result = p.runSync('run', [
       Uri.file(name).toString(),
       '--argument1',
       'argument2',
@@ -170,8 +167,7 @@
     //
     // This test ensures that allowed arguments for dart run which are valid VM
     // arguments are properly handled by the VM.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--observe',
       '--pause-isolates-on-start',
       // This should negate the above flag.
@@ -190,8 +186,7 @@
     expect(result.exitCode, 0);
 
     // Again, with --disable-service-auth-codes.
-    result = p.runSync([
-      'run',
+    result = p.runSync('run', [
       '--observe',
       '--pause-isolates-on-start',
       // This should negate the above flag.
@@ -216,8 +211,7 @@
 
     // Any VM flags not listed under 'dart run help --verbose' should be passed
     // before a dartdev command.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--vm-name=foo',
       p.relativeFilePath,
     ]);
@@ -235,8 +229,7 @@
 
     // Any VM flags not listed under 'dart run help --verbose' should be passed
     // before a dartdev command.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--verbose_gc',
       p.relativeFilePath,
     ]);
@@ -254,8 +247,7 @@
 
     // Ensure --enable-asserts doesn't cause the dartdev isolate to fail to
     // load. Regression test for: https://github.com/dart-lang/sdk/issues/42831
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       '--enable-asserts',
       p.relativeFilePath,
     ]);
@@ -269,8 +261,7 @@
     p = project(mainSrc: 'void main() { assert(false); }');
 
     // Any VM flags passed after the script shouldn't be interpreted by the VM.
-    ProcessResult result = p.runSync([
-      'run',
+    ProcessResult result = p.runSync('run', [
       p.relativeFilePath,
       '--enable-asserts',
     ]);
diff --git a/pkg/dartdev/test/commands/test_test.dart b/pkg/dartdev/test/commands/test_test.dart
index 00be2bc..ff85982 100644
--- a/pkg/dartdev/test/commands/test_test.dart
+++ b/pkg/dartdev/test/commands/test_test.dart
@@ -21,7 +21,7 @@
   test('--help', () {
     p = project();
 
-    final result = p.runSync(['test', '--help']);
+    final result = p.runSync('test', ['--help']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -31,7 +31,7 @@
   test('dart help test', () {
     p = project();
 
-    final result = p.runSync(['help', 'test']);
+    final result = p.runSync('help', ['test']);
 
     expect(result.exitCode, 0);
     expect(result.stdout, contains(' tests in this package'));
@@ -43,7 +43,7 @@
     var pubspec = File(path.join(p.dirPath, 'pubspec.yaml'));
     pubspec.deleteSync();
 
-    var result = p.runSync(['test']);
+    var result = p.runSync('test', []);
 
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('No pubspec.yaml file found'));
@@ -63,7 +63,7 @@
 ''');
 
     // An implicit `pub get` will happen.
-    final result = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains('All tests passed!'));
     expect(result.exitCode, 0);
@@ -86,8 +86,7 @@
 }
 ''');
 
-    final result = p.runSync(['test']);
-    expect(result.exitCode, 65);
+    final result = p.runSync('test', []);
     expect(
       result.stdout,
       contains('You need to add a dependency on package:test'),
@@ -95,10 +94,10 @@
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 65);
 
-    final resultPubAdd = p.runSync(['pub', 'add', 'test']);
+    final resultPubAdd = p.runSync('pub', ['add', 'test']);
 
     expect(resultPubAdd.exitCode, 0);
-    final result2 = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result2 = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result2.stderr, isEmpty);
     expect(result2.stdout, contains('All tests passed!'));
     expect(result2.exitCode, 0);
@@ -118,7 +117,7 @@
 }
 ''');
 
-    final result = p.runSync(['test', '--no-color', '--reporter', 'expanded']);
+    final result = p.runSync('test', ['--no-color', '--reporter', 'expanded']);
     expect(result.exitCode, 0);
     expect(result.stdout, contains('All tests passed!'));
     expect(result.stderr, isEmpty);
@@ -139,13 +138,8 @@
 ''');
 
     final result = p.runSync(
-      [
-        '--enable-experiment=non-nullable',
-        'test',
-        '--no-color',
-        '--reporter',
-        'expanded',
-      ],
+      '--enable-experiment=non-nullable',
+      ['test', '--no-color', '--reporter', 'expanded'],
     );
     expect(result.exitCode, 1);
   });
diff --git a/pkg/dartdev/test/no_such_file_test.dart b/pkg/dartdev/test/no_such_file_test.dart
index b5a3452..1d197d7 100644
--- a/pkg/dartdev/test/no_such_file_test.dart
+++ b/pkg/dartdev/test/no_such_file_test.dart
@@ -14,12 +14,12 @@
   test('Ensure parsing fails after encountering invalid file', () {
     // Regression test for https://github.com/dart-lang/sdk/issues/43991
     p = project();
-    final noArgsResult = p.runSync(['foo.dart']);
+    final noArgsResult = p.runSync('foo.dart', []);
     expect(noArgsResult.stderr, isNotEmpty);
     expect(noArgsResult.stdout, isEmpty);
     expect(noArgsResult.exitCode, 64);
 
-    final argsResult = p.runSync(['foo.dart', '--bar']);
+    final argsResult = p.runSync('foo.dart', ['--bar']);
     expect(argsResult.stderr, noArgsResult.stderr);
     expect(argsResult.stdout, isEmpty);
     expect(argsResult.exitCode, 64);
@@ -29,7 +29,7 @@
       () {
     // Regression test for https://github.com/dart-lang/sdk/issues/43785
     p = project();
-    final result = p.runSync(['--snapshot=abc', 'foo.dart']);
+    final result = p.runSync('--snapshot=abc', ['foo.dart']);
     expect(result.stderr, isNotEmpty);
     expect(result.stderr, contains("Error when reading 'foo.dart':"));
     expect(result.stdout, isEmpty);
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 48dcfd7..a8de016 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -75,15 +75,17 @@
   }
 
   ProcessResult runSync(
-    List<String> arguments, {
+    String command,
+    List<String> args, {
     String workingDir,
   }) {
-    return Process.runSync(
-        Platform.resolvedExecutable,
-        [
-          '--no-analytics',
-          ...arguments,
-        ],
+    var arguments = [
+      command,
+      ...?args,
+    ];
+
+    arguments.add('--disable-dartdev-analytics');
+    return Process.runSync(Platform.resolvedExecutable, arguments,
         workingDirectory: workingDir ?? dir.path,
         environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
   }
diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart
index 199d461..9740dd1 100644
--- a/pkg/dartdev/test/utils_test.dart
+++ b/pkg/dartdev/test/utils_test.dart
@@ -100,6 +100,39 @@
       expect(File('bar.bart').name, 'bar.bart');
     });
   });
+
+  group('PubUtils', () {
+    test('doModifyArgs', () {
+      const allCmds = ['analyze', 'help', 'pub', 'migrate'];
+      expect(PubUtils.shouldModifyArgs(null, null), isFalse);
+      expect(PubUtils.shouldModifyArgs([], null), isFalse);
+      expect(PubUtils.shouldModifyArgs(null, []), isFalse);
+      expect(PubUtils.shouldModifyArgs([], []), isFalse);
+      expect(PubUtils.shouldModifyArgs(['-h'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['--help'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['help'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['pub'], allCmds), isFalse);
+      expect(PubUtils.shouldModifyArgs(['analyze', 'help', 'pub'], allCmds),
+          isFalse);
+
+      expect(PubUtils.shouldModifyArgs(['--some-flag', 'help', 'pub'], allCmds),
+          isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub'], allCmds), isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub', 'publish'], allCmds),
+          isTrue);
+      expect(PubUtils.shouldModifyArgs(['help', 'pub', 'analyze'], allCmds),
+          isTrue);
+    });
+
+    test('modifyArgs', () {
+      expect(PubUtils.modifyArgs(['--some-flag', 'help', 'pub']),
+          orderedEquals(['--some-flag', 'pub', '--help']));
+      expect(PubUtils.modifyArgs(['help', 'pub']),
+          orderedEquals(['pub', '--help']));
+      expect(PubUtils.modifyArgs(['help', 'pub', 'publish']),
+          orderedEquals(['pub', 'publish', '--help']));
+    });
+  });
 }
 
 const String _packageData = '''{
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index ad8d0b8..3a23482 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -808,8 +808,8 @@
             isSynthetic: true,
             isConst: isConst,
             reference: referenceFrom?.reference)
-          ..isNonNullableByDefault =
-              cls.enclosingLibrary.isNonNullableByDefault,
+          ..isNonNullableByDefault = cls.enclosingLibrary.isNonNullableByDefault
+          ..fileUri = cls.fileUri,
         // If the constructor is constant, the default values must be part of
         // the outline expressions. We pass on the original constructor and
         // cloned function nodes to ensure that the default values are computed
@@ -1254,6 +1254,15 @@
     ticker.logMs("Evaluated constants");
     constantCoverageForTesting = coverage;
 
+    coverage.constructorCoverage.forEach((Uri fileUri, Set<Reference> value) {
+      Source source = uriToSource[fileUri];
+      if (source != null && fileUri != null) {
+        source.constantCoverageConstructors ??= new Set<Reference>();
+        source.constantCoverageConstructors.addAll(value);
+      }
+    });
+    ticker.logMs("Added constant coverage");
+
     if (loader.target.context.options
         .isExperimentEnabledGlobally(ExperimentalFlag.valueClass)) {
       valueClass.transformComponent(
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index cafad21..d2d2c47 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -99,6 +99,10 @@
   List<UInt> lineStarts;
 
   List<Byte> importUriUtf8Bytes;
+
+  // List of constructors evaluated *by* this library. Note that these can be
+  // in other libraries.
+  List<ConstructorReference> constructorCoverage;
 }
 
 type String {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 7b9ce66..2ba1722 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -9937,6 +9937,8 @@
 
   final Uri fileUri;
 
+  Set<Reference> constantCoverageConstructors;
+
   String cachedText;
 
   Source(this.lineStarts, this.source, this.importUri, this.fileUri);
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index 5d9958f..9461731 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -841,7 +841,19 @@
       String importUriString = readString();
       Uri importUri =
           importUriString.isEmpty ? null : Uri.parse(importUriString);
-      uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri);
+
+      Set<Reference> coverageConstructors;
+      {
+        int constructorCoverageCount = readUInt30();
+        coverageConstructors =
+            constructorCoverageCount == 0 ? null : new Set<Reference>();
+        for (int j = 0; j < constructorCoverageCount; ++j) {
+          coverageConstructors.add(readMemberReference());
+        }
+      }
+
+      uriToSource[uri] = new Source(lineStarts, sourceCode, importUri, uri)
+        ..constantCoverageConstructors = coverageConstructors;
     }
 
     // Read index.
@@ -861,8 +873,32 @@
       dst.addAll(src);
     } else {
       src.forEach((Uri key, Source value) {
-        if (value.source.isNotEmpty || !dst.containsKey(key)) {
+        Source originalDestinationSource = dst[key];
+        Source mergeFrom;
+        Source mergeTo;
+        if (value.source.isNotEmpty || originalDestinationSource == null) {
           dst[key] = value;
+          mergeFrom = originalDestinationSource;
+          mergeTo = value;
+        } else {
+          mergeFrom = value;
+          mergeTo = originalDestinationSource;
+        }
+
+        // TODO(jensj): Find out what the right thing to do is --- it probably
+        // depends on what we do if read the same library twice - do we merge or
+        // do we overwrite, and should we even support such a thing?
+
+        // Merge coverage. Note that mergeFrom might be null.
+        if (mergeTo.constantCoverageConstructors == null) {
+          mergeTo.constantCoverageConstructors =
+              mergeFrom?.constantCoverageConstructors;
+        } else if (mergeFrom?.constantCoverageConstructors == null) {
+          // Nothing to do.
+        } else {
+          // Bot are non-null: Merge.
+          mergeTo.constantCoverageConstructors
+              .addAll(mergeFrom.constantCoverageConstructors);
         }
       });
     }
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 038a584..20881c8 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -794,19 +794,33 @@
 
       writeByteList(source.source);
 
-      List<int> lineStarts = source.lineStarts;
-      writeUInt30(lineStarts.length);
-      int previousLineStart = 0;
-      for (int j = 0; j < lineStarts.length; ++j) {
-        int lineStart = lineStarts[j];
-        writeUInt30(lineStart - previousLineStart);
-        previousLineStart = lineStart;
+      {
+        List<int> lineStarts = source.lineStarts;
+        writeUInt30(lineStarts.length);
+        int previousLineStart = 0;
+        for (int j = 0; j < lineStarts.length; ++j) {
+          int lineStart = lineStarts[j];
+          writeUInt30(lineStart - previousLineStart);
+          previousLineStart = lineStart;
+        }
       }
 
       String importUriAsString =
           source.importUri == null ? "" : "${source.importUri}";
       outputStringViaBuffer(importUriAsString, buffer);
 
+      {
+        Set<Reference> coverage = source.constantCoverageConstructors;
+        if (coverage == null || coverage.isEmpty) {
+          writeUInt30(0);
+        } else {
+          writeUInt30(coverage.length);
+          for (Reference reference in coverage) {
+            writeNonNullReference(reference);
+          }
+        }
+      }
+
       i++;
     }
 
diff --git a/pkg/vm/lib/transformations/type_flow/transformer.dart b/pkg/vm/lib/transformations/type_flow/transformer.dart
index 68688fd..06a0665 100644
--- a/pkg/vm/lib/transformations/type_flow/transformer.dart
+++ b/pkg/vm/lib/transformations/type_flow/transformer.dart
@@ -1230,6 +1230,12 @@
 
   void transform(Component component) {
     component.transformChildren(this);
+    for (Source source in component.uriToSource.values) {
+      source?.constantCoverageConstructors?.removeWhere((Reference reference) {
+        Member node = reference.asMember;
+        return !shaker.isMemberUsed(node) && !_preserveSpecialMember(node);
+      });
+    }
   }
 
   @override
diff --git a/pkg/vm/test/incremental_compiler_test.dart b/pkg/vm/test/incremental_compiler_test.dart
index 52a98e6..6cd97fc 100644
--- a/pkg/vm/test/incremental_compiler_test.dart
+++ b/pkg/vm/test/incremental_compiler_test.dart
@@ -134,7 +134,9 @@
   /// If [getAllSources] is false it will ask specifically for report
   /// (and thus hits) for "lib1.dart" only.
   Future<Set<int>> collectAndCheckCoverageData(int port, bool getAllSources,
-      {bool resume: true}) async {
+      {bool resume: true,
+      bool onGetAllVerifyCount: true,
+      Set<int> coverageForLines}) async {
     RemoteVm remoteVm = new RemoteVm(port);
 
     // Wait for the script to have finished.
@@ -189,7 +191,7 @@
         }
         i++;
       }
-      if (getAllSources) {
+      if (getAllSources && onGetAllVerifyCount) {
         expect(scriptIdToIndex.length >= 2, isTrue);
       }
 
@@ -226,12 +228,16 @@
             }
           }
           for (int pos in coverage["misses"]) positions.add(pos);
-          for (int pos in range["possibleBreakpoints"]) positions.add(pos);
+          if (range["possibleBreakpoints"] != null) {
+            for (int pos in range["possibleBreakpoints"]) positions.add(pos);
+          }
           Map script = scriptIndexToScript[range["scriptIndex"]];
           Set<int> knownPositions = new Set<int>();
+          Map<int, int> tokenPosToLine = {};
           if (script["tokenPosTable"] != null) {
             for (List tokenPosTableLine in script["tokenPosTable"]) {
               for (int i = 1; i < tokenPosTableLine.length; i += 2) {
+                tokenPosToLine[tokenPosTableLine[i]] = tokenPosTableLine[0];
                 knownPositions.add(tokenPosTableLine[i]);
               }
             }
@@ -244,6 +250,14 @@
                   "line and column.");
             }
           }
+
+          if (coverageForLines != null) {
+            for (int pos in coverage["hits"]) {
+              if (lib1scriptIndices.contains(range["scriptIndex"])) {
+                coverageForLines.add(tokenPosToLine[pos]);
+              }
+            }
+          }
         }
       }
     }
@@ -486,6 +500,243 @@
     });
   });
 
+  group('multiple kernels constant coverage', () {
+    Directory mytest;
+    File main;
+    File lib1;
+    int lineForUnnamedConstructor;
+    int lineForNamedConstructor;
+    Process vm;
+    setUpAll(() {
+      mytest = Directory.systemTemp.createTempSync('incremental');
+      main = new File('${mytest.path}/main.dart')..createSync();
+      main.writeAsStringSync("""
+        // This file - combined with the lib - should have coverage for both
+        // constructors of Foo.
+        import 'lib1.dart' as lib1;
+
+        void testFunction() {
+          const foo = lib1.Foo.named();
+          const foo2 = lib1.Foo.named();
+          if (!identical(foo, foo2)) throw "what?";
+        }
+
+        main() {
+          lib1.testFunction();
+          testFunction();
+          print("main");
+        }
+      """);
+      lib1 = new File('${mytest.path}/lib1.dart')..createSync();
+      lib1.writeAsStringSync("""
+        // Compiling this file should mark the default constructor - but not the
+        // named constructor - as having coverage.
+        class Foo {
+          final int x;
+          const Foo([int? x]) : this.x = x ?? 42;
+          const Foo.named([int? x]) : this.x = x ?? 42;
+        }
+
+        void testFunction() {
+          const foo = Foo();
+          const foo2 = Foo();
+          if (!identical(foo, foo2)) throw "what?";
+        }
+
+        main() {
+          testFunction();
+          print("lib1");
+        }
+      """);
+      lineForUnnamedConstructor = 5;
+      lineForNamedConstructor = 6;
+    });
+
+    tearDownAll(() {
+      try {
+        mytest.deleteSync(recursive: true);
+      } catch (_) {
+        // Ignore errors;
+      }
+      try {
+        vm.kill();
+      } catch (_) {
+        // Ignore errors;
+      }
+    });
+
+    Future<Set<int>> runAndGetLineCoverage(
+        File list, String expectStdoutContains) async {
+      vm = await Process.start(Platform.resolvedExecutable, <String>[
+        "--pause-isolates-on-exit",
+        "--enable-vm-service:0",
+        "--disable-service-auth-codes",
+        "--disable-dart-dev",
+        list.path
+      ]);
+
+      const kObservatoryListening = 'Observatory listening on ';
+      final RegExp observatoryPortRegExp =
+          new RegExp("Observatory listening on http://127.0.0.1:\([0-9]*\)");
+      int port;
+      final splitter = new LineSplitter();
+      Completer<String> portLineCompleter = new Completer<String>();
+      Set<int> coverageLines = {};
+      bool foundExpectedString = false;
+      vm.stdout
+          .transform(utf8.decoder)
+          .transform(splitter)
+          .listen((String s) async {
+        if (s == expectStdoutContains) {
+          foundExpectedString = true;
+        }
+        if (s.startsWith(kObservatoryListening)) {
+          expect(observatoryPortRegExp.hasMatch(s), isTrue);
+          final match = observatoryPortRegExp.firstMatch(s);
+          port = int.parse(match.group(1));
+          await collectAndCheckCoverageData(port, true,
+              onGetAllVerifyCount: false, coverageForLines: coverageLines);
+          if (!portLineCompleter.isCompleted) {
+            portLineCompleter.complete("done");
+          }
+        }
+        print("vm stdout: $s");
+      });
+      vm.stderr.transform(utf8.decoder).transform(splitter).listen((String s) {
+        print("vm stderr: $s");
+      });
+      await portLineCompleter.future;
+      print("Compiler terminated with ${await vm.exitCode} exit code");
+      expect(foundExpectedString, isTrue);
+      return coverageLines;
+    }
+
+    test('compile seperatly, check coverage', () async {
+      Directory dir = mytest.createTempSync();
+
+      // First compile lib, run and verify coverage (un-named constructor
+      // covered, but not the named constructor).
+      // Note that it's called 'lib1' to match with expectations from coverage
+      // collector helper in this file.
+      File libDill = File(p.join(dir.path, p.basename(lib1.path + ".dill")));
+      IncrementalCompiler compiler = new IncrementalCompiler(options, lib1.uri);
+      Component component = await compiler.compile();
+      expect(component.libraries.length, equals(1));
+      expect(component.libraries.single.fileUri, equals(lib1.uri));
+      IOSink sink = libDill.openWrite();
+      BinaryPrinter printer = new BinaryPrinter(sink);
+      printer.writeComponentFile(component);
+      await sink.flush();
+      await sink.close();
+      File list = new File(p.join(dir.path, 'dill.list'))..createSync();
+      list.writeAsStringSync("#@dill\n${libDill.path}\n");
+      Set<int> lineCoverage = await runAndGetLineCoverage(list, "lib1");
+      // Expect coverage for unnamed constructor but not for the named one.
+      expect(
+          lineCoverage.intersection(
+              {lineForUnnamedConstructor, lineForNamedConstructor}),
+          equals({lineForUnnamedConstructor}));
+
+      try {
+        vm.kill();
+      } catch (_) {
+        // Ignore errors;
+      }
+      // Accept the compile to not include the lib again.
+      compiler.accept();
+
+      // Then compile lib, run and verify coverage (un-named constructor
+      // covered, and the named constructor coveraged too).
+      File mainDill = File(p.join(dir.path, p.basename(main.path + ".dill")));
+      component = await compiler.compile(entryPoint: main.uri);
+      expect(component.libraries.length, equals(1));
+      expect(component.libraries.single.fileUri, equals(main.uri));
+      sink = mainDill.openWrite();
+      printer = new BinaryPrinter(sink);
+      printer.writeComponentFile(component);
+      await sink.flush();
+      await sink.close();
+      list.writeAsStringSync("#@dill\n${mainDill.path}\n${libDill.path}\n");
+      lineCoverage = await runAndGetLineCoverage(list, "main");
+
+      // Expect coverage for both unnamed constructor and for the named one.
+      expect(
+          lineCoverage.intersection(
+              {lineForUnnamedConstructor, lineForNamedConstructor}),
+          equals({lineForUnnamedConstructor, lineForNamedConstructor}));
+
+      try {
+        vm.kill();
+      } catch (_) {
+        // Ignore errors;
+      }
+      // Accept the compile to not include the lib again.
+      compiler.accept();
+
+      // Finally, change lib to shift the constructors so the old line numbers
+      // doesn't match. Compile lib by itself, compile lib, run with the old
+      // main and verify coverage is still correct (both un-named constructor
+      // and named constructor (at new line numbers) are covered, and the old
+      // line numbers are not coverage.
+
+      lib1.writeAsStringSync("""
+        //
+        // Shift lines down by five
+        // lines so the original
+        // lines can't be coverred
+        //
+        class Foo {
+          final int x;
+          const Foo([int? x]) : this.x = x ?? 42;
+          const Foo.named([int? x]) : this.x = x ?? 42;
+        }
+
+        void testFunction() {
+          const foo = Foo();
+          const foo2 = Foo();
+          if (!identical(foo, foo2)) throw "what?";
+        }
+
+        main() {
+          testFunction();
+          print("lib1");
+        }
+      """);
+      int newLineForUnnamedConstructor = 8;
+      int newLineForNamedConstructor = 9;
+      compiler.invalidate(lib1.uri);
+      component = await compiler.compile(entryPoint: lib1.uri);
+      expect(component.libraries.length, equals(1));
+      expect(component.libraries.single.fileUri, equals(lib1.uri));
+      sink = libDill.openWrite();
+      printer = new BinaryPrinter(sink);
+      printer.writeComponentFile(component);
+      await sink.flush();
+      await sink.close();
+      list.writeAsStringSync("#@dill\n${mainDill.path}\n${libDill.path}\n");
+      lineCoverage = await runAndGetLineCoverage(list, "main");
+
+      // Expect coverage for both unnamed constructor and for the named one on
+      // the new positions, but no coverage on the old positions.
+      expect(
+          lineCoverage.intersection({
+            lineForUnnamedConstructor,
+            lineForNamedConstructor,
+            newLineForUnnamedConstructor,
+            newLineForNamedConstructor
+          }),
+          equals({newLineForUnnamedConstructor, newLineForNamedConstructor}));
+
+      try {
+        vm.kill();
+      } catch (_) {
+        // Ignore errors;
+      }
+      // Accept the compile to not include the lib again.
+      compiler.accept();
+    });
+  });
+
   group('multiple kernels 2', () {
     Directory mytest;
     File main;
diff --git a/runtime/bin/gen_snapshot.cc b/runtime/bin/gen_snapshot.cc
index 3815b9a..72e10c8 100644
--- a/runtime/bin/gen_snapshot.cc
+++ b/runtime/bin/gen_snapshot.cc
@@ -209,11 +209,15 @@
                           char** argv,
                           CommandLineOptions* vm_options,
                           CommandLineOptions* inputs) {
+  const char* kPrefix = "-";
+  const intptr_t kPrefixLen = strlen(kPrefix);
+
   // Skip the binary name.
   int i = 1;
 
   // Parse out the vm options.
-  while ((i < argc) && OptionProcessor::IsValidFlag(argv[i])) {
+  while ((i < argc) &&
+         OptionProcessor::IsValidFlag(argv[i], kPrefix, kPrefixLen)) {
     if (OptionProcessor::TryProcess(argv[i], vm_options)) {
       i += 1;
       continue;
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 7299ade..76d3b51 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -1140,10 +1140,10 @@
 
   // Parse command line arguments.
   if (app_snapshot == nullptr) {
-    bool success = Options::ParseArguments(
+    int result = Options::ParseArguments(
         argc, argv, vm_run_app_snapshot, &vm_options, &script_name,
         &dart_options, &print_flags_seen, &verbose_debug_seen);
-    if (!success) {
+    if (result < 0) {
       if (Options::help_option()) {
         Options::PrintUsage();
         Platform::Exit(0);
diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc
index d019ce4..2e962c1 100644
--- a/runtime/bin/main_options.cc
+++ b/runtime/bin/main_options.cc
@@ -282,26 +282,6 @@
   return true;
 }
 
-// Returns true if arg starts with the characters "--" followed by option, but
-// all '_' in the option name are treated as '-'.
-static bool IsOption(const char* arg, const char* option) {
-  if (arg[0] != '-' || arg[1] != '-') {
-    // Special case first two characters to avoid recognizing __flag.
-    return false;
-  }
-  for (int i = 0; option[i] != '\0'; i++) {
-    auto c = arg[i + 2];
-    if (c == '\0') {
-      // Not long enough.
-      return false;
-    }
-    if ((c == '_' ? '-' : c) != option[i]) {
-      return false;
-    }
-  }
-  return true;
-}
-
 const char* Options::vm_service_server_ip_ = DEFAULT_VM_SERVICE_SERVER_IP;
 int Options::vm_service_server_port_ = INVALID_VM_SERVICE_SERVER_PORT;
 bool Options::ProcessEnableVmServiceOption(const char* arg,
@@ -386,14 +366,17 @@
   return false;
 }
 
-bool Options::ParseArguments(int argc,
-                             char** argv,
-                             bool vm_run_app_snapshot,
-                             CommandLineOptions* vm_options,
-                             char** script_name,
-                             CommandLineOptions* dart_options,
-                             bool* print_flags_seen,
-                             bool* verbose_debug_seen) {
+int Options::ParseArguments(int argc,
+                            char** argv,
+                            bool vm_run_app_snapshot,
+                            CommandLineOptions* vm_options,
+                            char** script_name,
+                            CommandLineOptions* dart_options,
+                            bool* print_flags_seen,
+                            bool* verbose_debug_seen) {
+  const char* kPrefix = "--";
+  const intptr_t kPrefixLen = strlen(kPrefix);
+
   // Store the executable name.
   Platform::SetExecutableName(argv[0]);
 
@@ -412,26 +395,43 @@
       i++;
     } else {
       // Check if this flag is a potentially valid VM flag.
-      if (!OptionProcessor::IsValidFlag(argv[i])) {
+      if (!OptionProcessor::IsValidFlag(argv[i], kPrefix, kPrefixLen)) {
         break;
       }
-      // The following flags are processed as DartDev flags and are not to
+      // The following two flags are processed by both the embedder and
+      // the VM.
+      const char* kPrintFlags1 = "--print-flags";
+      const char* kPrintFlags2 = "--print_flags";
+      const char* kVerboseDebug1 = "--verbose_debug";
+      const char* kVerboseDebug2 = "--verbose-debug";
+
+      // The following two flags are processed as DartDev flags and are not to
       // be treated as if they are VM flags.
-      if (IsOption(argv[i], "print-flags")) {
+      const char* kEnableDartDevAnalytics1 = "--enable-analytics";
+      const char* kEnableDartDevAnalytics2 = "--enable_analytics";
+      const char* kDisableDartDevAnalytics1 = "--disable-analytics";
+      const char* kDisableDartDevAnalytics2 = "--disable_analytics";
+
+      if ((strncmp(argv[i], kPrintFlags1, strlen(kPrintFlags1)) == 0) ||
+          (strncmp(argv[i], kPrintFlags2, strlen(kPrintFlags2)) == 0)) {
         *print_flags_seen = true;
-      } else if (IsOption(argv[i], "verbose-debug")) {
+      } else if ((strncmp(argv[i], kVerboseDebug1, strlen(kVerboseDebug1)) ==
+                  0) ||
+                 (strncmp(argv[i], kVerboseDebug2, strlen(kVerboseDebug2)) ==
+                  0)) {
         *verbose_debug_seen = true;
-      } else if (IsOption(argv[i], "enable-analytics")) {
+      } else if ((strncmp(argv[i], kEnableDartDevAnalytics1,
+                          strlen(kEnableDartDevAnalytics1)) == 0) ||
+                 (strncmp(argv[i], kEnableDartDevAnalytics2,
+                          strlen(kEnableDartDevAnalytics2)) == 0)) {
         enable_dartdev_analytics = true;
         skipVmOption = true;
-      } else if (IsOption(argv[i], "disable-analytics")) {
+      } else if ((strncmp(argv[i], kDisableDartDevAnalytics1,
+                          strlen(kDisableDartDevAnalytics1)) == 0) ||
+                 (strncmp(argv[i], kDisableDartDevAnalytics2,
+                          strlen(kDisableDartDevAnalytics2)) == 0)) {
         disable_dartdev_analytics = true;
         skipVmOption = true;
-      } else if (IsOption(argv[i], "no-analytics")) {
-        // Just add this option even if we don't go to dartdev.
-        // It is irelevant for the vm.
-        dart_options->AddArgument("--no-analytics");
-        skipVmOption = true;
       }
       if (!skipVmOption) {
         temp_vm_options.AddArgument(argv[i]);
@@ -465,6 +465,7 @@
 
   bool implicitly_use_dart_dev = false;
   bool run_script = false;
+
   // Get the script name.
   if (i < argc) {
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -505,19 +506,25 @@
     DartDevIsolate::set_should_run_dart_dev(true);
     // Let DartDev handle the default help message.
     dart_options->AddArgument("help");
-    return true;
+    return 0;
   } else if (!Options::disable_dart_dev() &&
              (enable_dartdev_analytics || disable_dartdev_analytics)) {
     // The analytics flags are a special case as we don't have a target script
     // or DartDev command but we still want to launch DartDev.
     DartDevIsolate::set_should_run_dart_dev(true);
 
-    return true;
+    if (enable_dartdev_analytics) {
+      dart_options->AddArgument("--enable-analytics");
+    }
+    if (disable_dartdev_analytics) {
+      dart_options->AddArgument("--disable-analytics");
+    }
+    return 0;
   }
 
 #endif    // !defined(DART_PRECOMPILED_RUNTIME)
   else {  // NOLINT
-    return false;
+    return -1;
   }
   USE(enable_dartdev_analytics);
   USE(disable_dartdev_analytics);
@@ -540,7 +547,7 @@
     while (tmp_i < argc) {
       // Check if this flag is a potentially valid VM flag. If not, we've likely
       // hit a script name and are done parsing VM flags.
-      if (!OptionProcessor::IsValidFlag(argv[tmp_i])) {
+      if (!OptionProcessor::IsValidFlag(argv[tmp_i], kPrefix, kPrefixLen)) {
         break;
       }
       OptionProcessor::TryProcess(argv[tmp_i], vm_options);
@@ -589,12 +596,14 @@
       first_option = false;
     }
   }
+
+
   // Verify consistency of arguments.
 
   // snapshot_depfile is an alias for depfile. Passing them both is an error.
   if ((snapshot_deps_filename_ != NULL) && (depfile_ != NULL)) {
     Syslog::PrintErr("Specify only one of --depfile and --snapshot_depfile\n");
-    return false;
+    return -1;
   }
   if (snapshot_deps_filename_ != NULL) {
     depfile_ = snapshot_deps_filename_;
@@ -603,25 +612,25 @@
 
   if ((packages_file_ != NULL) && (strlen(packages_file_) == 0)) {
     Syslog::PrintErr("Empty package file name specified.\n");
-    return false;
+    return -1;
   }
   if ((gen_snapshot_kind_ != kNone) && (snapshot_filename_ == NULL)) {
     Syslog::PrintErr(
         "Generating a snapshot requires a filename (--snapshot).\n");
-    return false;
+    return -1;
   }
   if ((gen_snapshot_kind_ == kNone) && (depfile_ != NULL) &&
       (snapshot_filename_ == NULL) && (depfile_output_filename_ == NULL)) {
     Syslog::PrintErr(
         "Generating a depfile requires an output filename"
         " (--depfile-output-filename or --snapshot).\n");
-    return false;
+    return -1;
   }
   if ((gen_snapshot_kind_ != kNone) && vm_run_app_snapshot) {
     Syslog::PrintErr(
         "Specifying an option to generate a snapshot and"
         " run using a snapshot is invalid.\n");
-    return false;
+    return -1;
   }
 
   // If --snapshot is given without --snapshot-kind, default to script snapshot.
@@ -629,7 +638,7 @@
     gen_snapshot_kind_ = kKernel;
   }
 
-  return true;
+  return 0;
 }
 
 }  // namespace bin
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 2ab8598..f900a3c 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -85,15 +85,14 @@
 
 class Options {
  public:
-  // Returns true if argument parsing succeeded. False otherwise.
-  static bool ParseArguments(int argc,
-                             char** argv,
-                             bool vm_run_app_shapshot,
-                             CommandLineOptions* vm_options,
-                             char** script_name,
-                             CommandLineOptions* dart_options,
-                             bool* print_flags_seen,
-                             bool* verbose_debug_seen);
+  static int ParseArguments(int argc,
+                            char** argv,
+                            bool vm_run_app_shapshot,
+                            CommandLineOptions* vm_options,
+                            char** script_name,
+                            CommandLineOptions* dart_options,
+                            bool* print_flags_seen,
+                            bool* verbose_debug_seen);
 
 #define STRING_OPTION_GETTER(flag, variable)                                   \
   static const char* variable() { return variable##_; }
diff --git a/runtime/bin/options.cc b/runtime/bin/options.cc
index 6d4e355..1eed113 100644
--- a/runtime/bin/options.cc
+++ b/runtime/bin/options.cc
@@ -9,8 +9,12 @@
 
 OptionProcessor* OptionProcessor::first_ = NULL;
 
-bool OptionProcessor::IsValidFlag(const char* name) {
-  return name[0] == '-' && name[1] == '-' && name[2] != '\0';
+bool OptionProcessor::IsValidFlag(const char* name,
+                                  const char* prefix,
+                                  intptr_t prefix_length) {
+  const intptr_t name_length = strlen(name);
+  return ((name_length > prefix_length) &&
+          (strncmp(name, prefix, prefix_length) == 0));
 }
 
 const char* OptionProcessor::ProcessOption(const char* option,
diff --git a/runtime/bin/options.h b/runtime/bin/options.h
index ef92579..4bcd7de 100644
--- a/runtime/bin/options.h
+++ b/runtime/bin/options.h
@@ -22,8 +22,9 @@
 
   virtual ~OptionProcessor() {}
 
-  // Returns true if name starts with "--".
-  static bool IsValidFlag(const char* name);
+  static bool IsValidFlag(const char* name,
+                          const char* prefix,
+                          intptr_t prefix_length);
 
   virtual bool Process(const char* option, CommandLineOptions* options) = 0;
 
diff --git a/runtime/observatory/tests/service/get_source_report_const_coverage_lib.dart b/runtime/observatory/tests/service/get_source_report_const_coverage_lib.dart
new file mode 100644
index 0000000..b0f078d
--- /dev/null
+++ b/runtime/observatory/tests/service/get_source_report_const_coverage_lib.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.
+
+import "get_source_report_const_coverage_test.dart";
+
+void testFunction() {
+  const namedFoo = Foo.named3();
+  const namedFoo2 = Foo.named3();
+  const namedIdentical = identical(namedFoo, namedFoo2);
+  print("namedIdentical: $namedIdentical");
+}
diff --git a/runtime/observatory/tests/service/get_source_report_const_coverage_test.dart b/runtime/observatory/tests/service/get_source_report_const_coverage_test.dart
new file mode 100644
index 0000000..32cb035
--- /dev/null
+++ b/runtime/observatory/tests/service/get_source_report_const_coverage_test.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+import 'get_source_report_const_coverage_lib.dart' as lib;
+
+const String filename = "get_source_report_const_coverage_test";
+const Set<int> expectedLinesHit = {20, 22, 26};
+const Set<int> expectedLinesNotHit = {24};
+
+class Foo {
+  final int x;
+  // Expect this constructor to be coverage by coverage.
+  const Foo([int? x]) : this.x = x ?? 42;
+  // Expect this constructor to be coverage by coverage too.
+  const Foo.named1([int? x]) : this.x = x ?? 42;
+  // Expect this constructor to *NOT* be coverage by coverage.
+  const Foo.named2([int? x]) : this.x = x ?? 42;
+  // Expect this constructor to be coverage by coverage too (from lib).
+  const Foo.named3([int? x]) : this.x = x ?? 42;
+}
+
+void testFunction() {
+  const foo = Foo();
+  const foo2 = Foo();
+  const fooIdentical = identical(foo, foo2);
+  print(fooIdentical);
+
+  const namedFoo = Foo.named1();
+  const namedFoo2 = Foo.named1();
+  const namedIdentical = identical(namedFoo, namedFoo2);
+  print(fooIdentical);
+
+  debugger();
+
+  // That this is called after (or at all) is not relevent for the code
+  // coverage of constants.
+  lib.testFunction();
+
+  print("Done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    final stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    final List<Script> scripts = await isolate.getScripts();
+    Script? foundScript;
+    for (Script script in scripts) {
+      if (script.uri.contains(filename)) {
+        foundScript = script;
+        break;
+      }
+    }
+
+    Set<int> hits;
+    {
+      // Get report for everything; then collect for this library.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      hits = getHitsFor(coverage, filename);
+      await foundScript!.load();
+      final Set<int> lines = {};
+      for (int hit in hits) {
+        // We expect every hit to be translatable to line
+        // (i.e. tokenToLine to return non-null).
+        lines.add(foundScript.tokenToLine(hit)!);
+      }
+      print("Token position hits: $hits --- line hits: $lines");
+      expect(lines.intersection(expectedLinesHit), equals(expectedLinesHit));
+      expect(lines.intersection(expectedLinesNotHit), isEmpty);
+    }
+    {
+      // Now get report for the this file only.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+        'scriptId': foundScript.id!
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      final Set<int> localHits = getHitsFor(coverage, filename);
+      expect(localHits.length, equals(hits.length));
+      expect(hits.toList()..sort(), equals(localHits.toList()..sort()));
+      print(localHits);
+    }
+  },
+];
+
+Set<int> getHitsFor(Map coverage, String uriContains) {
+  final List scripts = coverage["scripts"];
+  final Set<int> scriptIdsWanted = {};
+  for (int i = 0; i < scripts.length; i++) {
+    final Map script = scripts[i];
+    final String scriptUri = script["uri"];
+    if (scriptUri.contains(uriContains)) {
+      scriptIdsWanted.add(i);
+    }
+  }
+  final List ranges = coverage["ranges"];
+  final Set<int> hits = {};
+  for (int i = 0; i < ranges.length; i++) {
+    final Map range = ranges[i];
+    if (scriptIdsWanted.contains(range["scriptIndex"])) {
+      if (range["coverage"] != null) {
+        for (int hit in range["coverage"]["hits"]) {
+          hits.add(hit);
+        }
+      }
+    }
+  }
+  return hits;
+}
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory/tests/service/get_source_report_test.dart b/runtime/observatory/tests/service/get_source_report_test.dart
index adea481..a1d5206 100644
--- a/runtime/observatory/tests/service/get_source_report_test.dart
+++ b/runtime/observatory/tests/service/get_source_report_test.dart
@@ -91,7 +91,7 @@
     final numRanges = coverage['ranges'].length;
     expect(coverage['type'], equals('SourceReport'));
 
-    expect(numRanges, equals(10));
+    expect(numRanges, equals(11));
     expect(coverage['ranges'][0], equals(expectedRange));
     expect(coverage['scripts'].length, 1);
     expect(
@@ -106,7 +106,7 @@
     };
     coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
     expect(coverage['type'], equals('SourceReport'));
-    expect(coverage['ranges'].length, 11);
+    expect(coverage['ranges'].length, 12);
     expect(allRangesCompiled(coverage), isTrue);
 
     // One function
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 7a297eb..c92c5e5 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -109,6 +109,7 @@
 get_allocation_samples_test: Skip, Timeout
 get_isolate_after_language_error_test: CompileTimeError
 get_object_rpc_test: SkipByDesign
+get_source_report_const_coverage_test: SkipByDesign
 get_source_report_test: Skip, Timeout
 get_source_report_with_mixin_test: Skip, Timeout
 get_stack_limit_rpc_test: Skip, Timeout
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_lib.dart b/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_lib.dart
new file mode 100644
index 0000000..b0f078d
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_lib.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2020, 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.
+
+import "get_source_report_const_coverage_test.dart";
+
+void testFunction() {
+  const namedFoo = Foo.named3();
+  const namedFoo2 = Foo.named3();
+  const namedIdentical = identical(namedFoo, namedFoo2);
+  print("namedIdentical: $namedIdentical");
+}
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_test.dart b/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_test.dart
new file mode 100644
index 0000000..7c6af16
--- /dev/null
+++ b/runtime/observatory_2/tests/service_2/get_source_report_const_coverage_test.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2020, 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.
+
+import 'package:observatory_2/service_io.dart';
+import 'package:test/test.dart';
+import 'test_helper.dart';
+import 'service_test_common.dart';
+import 'dart:developer';
+
+import 'get_source_report_const_coverage_lib.dart' as lib;
+
+const String filename = "get_source_report_const_coverage_test";
+const Set<int> expectedLinesHit = {20, 22, 26};
+const Set<int> expectedLinesNotHit = {24};
+
+class Foo {
+  final int x;
+  // Expect this constructor to be coverage by coverage.
+  const Foo([int x]) : this.x = x ?? 42;
+  // Expect this constructor to be coverage by coverage too.
+  const Foo.named1([int x]) : this.x = x ?? 42;
+  // Expect this constructor to *NOT* be coverage by coverage.
+  const Foo.named2([int x]) : this.x = x ?? 42;
+  // Expect this constructor to be coverage by coverage too (from lib).
+  const Foo.named3([int x]) : this.x = x ?? 42;
+}
+
+void testFunction() {
+  const foo = Foo();
+  const foo2 = Foo();
+  const fooIdentical = identical(foo, foo2);
+  print(fooIdentical);
+
+  const namedFoo = Foo.named1();
+  const namedFoo2 = Foo.named1();
+  const namedIdentical = identical(namedFoo, namedFoo2);
+  print(fooIdentical);
+
+  debugger();
+
+  // That this is called after (or at all) is not relevent for the code
+  // coverage of constants.
+  lib.testFunction();
+
+  print("Done");
+}
+
+var tests = <IsolateTest>[
+  hasStoppedAtBreakpoint,
+  (Isolate isolate) async {
+    final stack = await isolate.getStack();
+
+    // Make sure we are in the right place.
+    expect(stack.type, equals('Stack'));
+    expect(stack['frames'].length, greaterThanOrEqualTo(1));
+    expect(stack['frames'][0].function.name, equals('testFunction'));
+
+    final List<Script> scripts = await isolate.getScripts();
+    Script foundScript;
+    for (Script script in scripts) {
+      if (script.uri.contains(filename)) {
+        foundScript = script;
+        break;
+      }
+    }
+
+    Set<int> hits;
+    {
+      // Get report for everything; then collect for this library.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      hits = getHitsFor(coverage, filename);
+      await foundScript.load();
+      final Set<int> lines = {};
+      for (int hit in hits) {
+        // We expect every hit to be translatable to line
+        // (i.e. tokenToLine to return non-null).
+        lines.add(foundScript.tokenToLine(hit));
+      }
+      print("Token position hits: $hits --- line hits: $lines");
+      expect(lines.intersection(expectedLinesHit), equals(expectedLinesHit));
+      expect(lines.intersection(expectedLinesNotHit), isEmpty);
+    }
+    {
+      // Now get report for the this file only.
+      final Map<String, Object> params = {
+        'reports': ['Coverage'],
+        'scriptId': foundScript.id
+      };
+      final coverage =
+          await isolate.invokeRpcNoUpgrade('getSourceReport', params);
+      final Set<int> localHits = getHitsFor(coverage, filename);
+      expect(localHits.length, equals(hits.length));
+      expect(hits.toList()..sort(), equals(localHits.toList()..sort()));
+      print(localHits);
+    }
+  },
+];
+
+Set<int> getHitsFor(Map coverage, String uriContains) {
+  final List scripts = coverage["scripts"];
+  final Set<int> scriptIdsWanted = {};
+  for (int i = 0; i < scripts.length; i++) {
+    final Map script = scripts[i];
+    final String scriptUri = script["uri"];
+    if (scriptUri.contains(uriContains)) {
+      scriptIdsWanted.add(i);
+    }
+  }
+  final List ranges = coverage["ranges"];
+  final Set<int> hits = {};
+  for (int i = 0; i < ranges.length; i++) {
+    final Map range = ranges[i];
+    if (scriptIdsWanted.contains(range["scriptIndex"])) {
+      if (range["coverage"] != null) {
+        for (int hit in range["coverage"]["hits"]) {
+          hits.add(hit);
+        }
+      }
+    }
+  }
+  return hits;
+}
+
+main(args) => runIsolateTests(args, tests, testeeConcurrent: testFunction);
diff --git a/runtime/observatory_2/tests/service_2/get_source_report_test.dart b/runtime/observatory_2/tests/service_2/get_source_report_test.dart
index 97606de..78ea471 100644
--- a/runtime/observatory_2/tests/service_2/get_source_report_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_source_report_test.dart
@@ -91,7 +91,7 @@
     final numRanges = coverage['ranges'].length;
     expect(coverage['type'], equals('SourceReport'));
 
-    expect(numRanges, equals(10));
+    expect(numRanges, equals(11));
     expect(coverage['ranges'][0], equals(expectedRange));
     expect(coverage['scripts'].length, 1);
     expect(
@@ -106,7 +106,7 @@
     };
     coverage = await isolate.invokeRpcNoUpgrade('getSourceReport', params);
     expect(coverage['type'], equals('SourceReport'));
-    expect(coverage['ranges'].length, 11);
+    expect(coverage['ranges'].length, 12);
     expect(allRangesCompiled(coverage), isTrue);
 
     // One function
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index d8996be..e8d4784 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -109,6 +109,7 @@
 get_allocation_samples_test: Skip, Timeout
 get_isolate_after_language_error_test: CompileTimeError
 get_object_rpc_test: SkipByDesign
+get_source_report_const_coverage_test: SkipByDesign
 get_source_report_test: Skip, Timeout
 get_source_report_with_mixin_test: Skip, Timeout
 get_stack_limit_rpc_test: Skip, Timeout
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index e706302..6cc0183 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2807,6 +2807,33 @@
   return H.DartString(reader_.BufferAt(ReaderOffset()), size, Heap::kOld);
 }
 
+ExternalTypedDataPtr KernelReaderHelper::GetConstantCoverageFor(
+    intptr_t index) {
+  AlternativeReadingScope alt(&reader_);
+  SetOffset(GetOffsetForSourceInfo(index));
+  SkipBytes(ReadUInt());                         // skip uri.
+  SkipBytes(ReadUInt());                         // skip source.
+  const intptr_t line_start_count = ReadUInt();  // read number of line start
+                                                 // entries.
+  for (intptr_t i = 0; i < line_start_count; ++i) {
+    ReadUInt();
+  }
+
+  SkipBytes(ReadUInt());  // skip import uri.
+
+  intptr_t start_offset = ReaderOffset();
+
+  // Read past "constant coverage constructors".
+  const intptr_t constant_coverage_constructors = ReadUInt();
+  for (intptr_t i = 0; i < constant_coverage_constructors; ++i) {
+    ReadUInt();
+  }
+
+  intptr_t end_offset = ReaderOffset();
+
+  return reader_.ExternalDataFromTo(start_offset, end_offset);
+}
+
 intptr_t ActiveClass::MemberTypeParameterCount(Zone* zone) {
   ASSERT(member != NULL);
   if (member->IsFactory()) {
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index d725cf7..28877d0 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -1244,6 +1244,7 @@
   const String& GetSourceFor(intptr_t index);
   TypedDataPtr GetLineStartsFor(intptr_t index);
   String& SourceTableImportUriFor(intptr_t index, uint32_t binaryVersion);
+  ExternalTypedDataPtr GetConstantCoverageFor(intptr_t index);
 
   Zone* zone_;
   TranslationHelper& translation_helper_;
@@ -1282,6 +1283,8 @@
   friend class ObfuscationProhibitionsMetadataHelper;
   friend class LoadingUnitsMetadataHelper;
   friend bool NeedsDynamicInvocationForwarder(const Function& function);
+  friend ArrayPtr CollectConstConstructorCoverageFrom(
+      const Script& interesting_script);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(KernelReaderHelper);
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index e4dae18..156a5c4 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -501,7 +501,7 @@
 static constexpr dart::compiler::target::word Pointer_InstanceSize = 12;
 static constexpr dart::compiler::target::word ReceivePort_InstanceSize = 20;
 static constexpr dart::compiler::target::word RegExp_InstanceSize = 60;
-static constexpr dart::compiler::target::word Script_InstanceSize = 56;
+static constexpr dart::compiler::target::word Script_InstanceSize = 64;
 static constexpr dart::compiler::target::word SendPort_InstanceSize = 24;
 static constexpr dart::compiler::target::word SignatureData_InstanceSize = 12;
 static constexpr dart::compiler::target::word SingleTargetCache_InstanceSize =
@@ -1022,7 +1022,7 @@
 static constexpr dart::compiler::target::word Pointer_InstanceSize = 24;
 static constexpr dart::compiler::target::word ReceivePort_InstanceSize = 40;
 static constexpr dart::compiler::target::word RegExp_InstanceSize = 120;
-static constexpr dart::compiler::target::word Script_InstanceSize = 96;
+static constexpr dart::compiler::target::word Script_InstanceSize = 104;
 static constexpr dart::compiler::target::word SendPort_InstanceSize = 24;
 static constexpr dart::compiler::target::word SignatureData_InstanceSize = 24;
 static constexpr dart::compiler::target::word SingleTargetCache_InstanceSize =
@@ -1534,7 +1534,7 @@
 static constexpr dart::compiler::target::word Pointer_InstanceSize = 12;
 static constexpr dart::compiler::target::word ReceivePort_InstanceSize = 20;
 static constexpr dart::compiler::target::word RegExp_InstanceSize = 60;
-static constexpr dart::compiler::target::word Script_InstanceSize = 56;
+static constexpr dart::compiler::target::word Script_InstanceSize = 64;
 static constexpr dart::compiler::target::word SendPort_InstanceSize = 24;
 static constexpr dart::compiler::target::word SignatureData_InstanceSize = 12;
 static constexpr dart::compiler::target::word SingleTargetCache_InstanceSize =
@@ -2056,7 +2056,7 @@
 static constexpr dart::compiler::target::word Pointer_InstanceSize = 24;
 static constexpr dart::compiler::target::word ReceivePort_InstanceSize = 40;
 static constexpr dart::compiler::target::word RegExp_InstanceSize = 120;
-static constexpr dart::compiler::target::word Script_InstanceSize = 96;
+static constexpr dart::compiler::target::word Script_InstanceSize = 104;
 static constexpr dart::compiler::target::word SendPort_InstanceSize = 24;
 static constexpr dart::compiler::target::word SignatureData_InstanceSize = 24;
 static constexpr dart::compiler::target::word SingleTargetCache_InstanceSize =
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 7c53e72..07932e6 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -347,6 +347,36 @@
   script.set_debug_positions(array_object);
 }
 
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ArrayPtr CollectConstConstructorCoverageFrom(const Script& interesting_script) {
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  interesting_script.LookupSourceAndLineStarts(zone);
+  TranslationHelper helper(thread);
+  helper.InitFromScript(interesting_script);
+
+  ExternalTypedData& data =
+      ExternalTypedData::Handle(zone, interesting_script.constant_coverage());
+
+  KernelReaderHelper kernel_reader(zone, &helper, interesting_script, data, 0);
+
+  // Read "constant coverage constructors".
+  const intptr_t constant_coverage_constructors = kernel_reader.ReadUInt();
+  const Array& constructors =
+      Array::Handle(Array::New(constant_coverage_constructors));
+  for (intptr_t i = 0; i < constant_coverage_constructors; ++i) {
+    NameIndex kernel_name = kernel_reader.ReadCanonicalNameReference();
+    Class& klass = Class::ZoneHandle(
+        zone,
+        helper.LookupClassByKernelClass(helper.EnclosingName(kernel_name)));
+    const Function& target = Function::ZoneHandle(
+        zone, helper.LookupConstructorByKernelConstructor(klass, kernel_name));
+    constructors.SetAt(i, target);
+  }
+  return constructors.raw();
+}
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+
 ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field) {
   ASSERT(field.is_static() && field.is_const());
 
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 9b0d8de..4b55de9 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -195,6 +195,10 @@
 
 void CollectTokenPositionsFor(const Script& script);
 
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+ArrayPtr CollectConstConstructorCoverageFrom(const Script& interesting_script);
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+
 ObjectPtr EvaluateStaticConstFieldInitializer(const Field& field);
 ObjectPtr EvaluateMetadata(const Field& metadata_field,
                            bool is_annotations_offset);
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index e37f325..a8cafe4 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -2099,6 +2099,10 @@
   const String& uri_string = helper_.SourceTableUriFor(index);
   const String& import_uri_string =
       helper_.SourceTableImportUriFor(index, program_->binary_version());
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+  ExternalTypedData& constant_coverage =
+      ExternalTypedData::Handle(Z, helper_.GetConstantCoverageFor(index));
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
 
   String& sources = String::Handle(Z);
   TypedData& line_starts = TypedData::Handle(Z);
@@ -2144,6 +2148,9 @@
   script.set_kernel_script_index(index);
   script.set_kernel_program_info(kernel_program_info_);
   script.set_line_starts(line_starts);
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+  script.set_constant_coverage(constant_coverage);
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   script.set_debug_positions(Array::null_array());
   return script.raw();
 }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a709577..bae9d93f 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11162,6 +11162,16 @@
   StorePointer(&raw_ptr()->line_starts_, value.raw());
 }
 
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+void Script::set_constant_coverage(const ExternalTypedData& value) const {
+  StorePointer(&raw_ptr()->constant_coverage_, value.raw());
+}
+
+ExternalTypedDataPtr Script::constant_coverage() const {
+  return raw_ptr()->constant_coverage_;
+}
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+
 void Script::set_debug_positions(const Array& value) const {
   StorePointer(&raw_ptr()->debug_positions_, value.raw());
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 808d9e1..584e180 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4501,6 +4501,12 @@
 
   TypedDataPtr line_starts() const;
 
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+  ExternalTypedDataPtr constant_coverage() const;
+
+  void set_constant_coverage(const ExternalTypedData& value) const;
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+
   void set_line_starts(const TypedData& value) const;
 
   void set_debug_positions(const Array& value) const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 6ef0636..bbfc8f7 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1253,6 +1253,9 @@
   StringPtr resolved_url_;
   ArrayPtr compile_time_constants_;
   TypedDataPtr line_starts_;
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
+  ExternalTypedDataPtr constant_coverage_;
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
   ArrayPtr debug_positions_;
   KernelProgramInfoPtr kernel_program_info_;
   StringPtr source_;
@@ -1284,8 +1287,17 @@
                kLazyLookupSourceAndLineStartsSize>;
   uint8_t flags_;
 
-  intptr_t kernel_script_index_;
   int64_t load_timestamp_;
+  int32_t kernel_script_index_;
+#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) &&                 \
+    defined(ARCH_IS_32_BIT)
+  // MSVC aligns sizeof(ScriptLayout) by 8 on 32-bit because rgw structure
+  // contains int64_t member, while clang does not.
+  // This member is thus needed to ensure consistent struct size across all
+  // compilers.
+  int32_t padding_;
+#endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) &&          \
+        // defined(ARCH_IS_32_BIT)
 };
 
 class LibraryLayout : public ObjectLayout {
diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc
index 84f5495..3f074ff 100644
--- a/runtime/vm/source_report.cc
+++ b/runtime/vm/source_report.cc
@@ -565,6 +565,19 @@
 
     // Visit all closures for this isolate.
     VisitClosures(&ranges);
+
+    // Output constant coverage if coverage is requested.
+    if (IsReportRequested(kCoverage)) {
+      // Find all scripts. We need to go though all scripts because a script
+      // (even one we don't want) can add coverage to another library (i.e.
+      // potentially one we want).
+      DirectChainedHashMap<ScriptTableTrait> local_script_table;
+      GrowableArray<ScriptTableEntry*> local_script_table_entries;
+      CollectAllScripts(&local_script_table, &local_script_table_entries);
+      CollectConstConstructorCoverageFromScripts(&local_script_table_entries,
+                                                 &ranges);
+      CleanupCollectedScripts(&local_script_table, &local_script_table_entries);
+    }
   }
 
   // Print the script table.
@@ -572,5 +585,102 @@
   PrintScriptTable(&scripts);
 }
 
+void SourceReport::CollectAllScripts(
+    DirectChainedHashMap<ScriptTableTrait>* local_script_table,
+    GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
+  ScriptTableEntry wrapper;
+  const GrowableObjectArray& libs = GrowableObjectArray::Handle(
+      zone(), thread()->isolate()->object_store()->libraries());
+  Library& lib = Library::Handle(zone());
+  Script& scriptRef = Script::Handle(zone());
+  for (int i = 0; i < libs.Length(); i++) {
+    lib ^= libs.At(i);
+    const Array& scripts = Array::Handle(zone(), lib.LoadedScripts());
+    for (intptr_t j = 0; j < scripts.Length(); j++) {
+      scriptRef ^= scripts.At(j);
+      const String& url = String::Handle(zone(), scriptRef.url());
+      wrapper.key = &url;
+      wrapper.script = &Script::Handle(zone(), scriptRef.raw());
+      ScriptTableEntry* pair = local_script_table->LookupValue(&wrapper);
+      if (pair != NULL) {
+        // Existing one.
+        continue;
+      }
+      // New one. Insert.
+      ScriptTableEntry* tmp = new ScriptTableEntry();
+      tmp->key = &url;
+      tmp->index = next_script_index_++;
+      tmp->script = wrapper.script;
+      local_script_table_entries->Add(tmp);
+      local_script_table->Insert(tmp);
+    }
+  }
+}
+
+void SourceReport::CleanupCollectedScripts(
+    DirectChainedHashMap<ScriptTableTrait>* local_script_table,
+    GrowableArray<ScriptTableEntry*>* local_script_table_entries) {
+  for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
+    delete local_script_table_entries->operator[](i);
+    local_script_table_entries->operator[](i) = NULL;
+  }
+  local_script_table_entries->Clear();
+  local_script_table->Clear();
+}
+
+void SourceReport::CollectConstConstructorCoverageFromScripts(
+    GrowableArray<ScriptTableEntry*>* local_script_table_entries,
+    JSONArray* ranges) {
+  // Now output the wanted constant coverage.
+  for (intptr_t i = 0; i < local_script_table_entries->length(); i++) {
+    const Script* script = local_script_table_entries->At(i)->script;
+    bool script_ok = true;
+    if (script_ != NULL && !script_->IsNull()) {
+      if (script->raw() != script_->raw()) {
+        // This is the wrong script.
+        script_ok = false;
+      }
+    }
+
+    // Whether we want *this* script or not we need to look at the constant
+    // constructor coverage. Any of those could be in a script we *do* want.
+    {
+      Script& scriptRef = Script::Handle(zone());
+      const Array& constructors =
+          Array::Handle(kernel::CollectConstConstructorCoverageFrom(*script));
+      intptr_t constructors_count = constructors.Length();
+      Function& constructor = Function::Handle(zone());
+      Code& code = Code::Handle(zone());
+      for (intptr_t i = 0; i < constructors_count; i++) {
+        constructor ^= constructors.At(i);
+        // Check if we want coverage for this constructor.
+        if (ShouldSkipFunction(constructor)) {
+          continue;
+        }
+        scriptRef ^= constructor.script();
+        code ^= constructor.unoptimized_code();
+        const TokenPosition begin_pos = constructor.token_pos();
+        const TokenPosition end_pos = constructor.end_token_pos();
+        JSONObject range(ranges);
+        range.AddProperty("scriptIndex", GetScriptIndex(scriptRef));
+        range.AddProperty("compiled",
+                          !code.IsNull());  // Does this make a difference?
+        range.AddProperty("startPos", begin_pos);
+        range.AddProperty("endPos", end_pos);
+
+        JSONObject cov(&range, "coverage");
+        {
+          JSONArray hits(&cov, "hits");
+          hits.AddValue(begin_pos);
+        }
+        {
+          JSONArray misses(&cov, "misses");
+          // No misses
+        }
+      }
+    }
+  }
+}
+
 }  // namespace dart
 #endif  // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/source_report.h b/runtime/vm/source_report.h
index 0a3ddb1..4ec20c9 100644
--- a/runtime/vm/source_report.h
+++ b/runtime/vm/source_report.h
@@ -112,6 +112,18 @@
     }
   };
 
+  void CollectAllScripts(
+      DirectChainedHashMap<ScriptTableTrait>* local_script_table,
+      GrowableArray<ScriptTableEntry*>* local_script_table_entries);
+
+  void CleanupCollectedScripts(
+      DirectChainedHashMap<ScriptTableTrait>* local_script_table,
+      GrowableArray<ScriptTableEntry*>* local_script_table_entries);
+
+  void CollectConstConstructorCoverageFromScripts(
+      GrowableArray<ScriptTableEntry*>* local_script_table_entries,
+      JSONArray* ranges);
+
   intptr_t report_set_;
   CompileMode compile_mode_;
   Thread* thread_;
diff --git a/tools/VERSION b/tools/VERSION
index b66ea8d..7c1de33 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 102
+PRERELEASE 103
 PRERELEASE_PATCH 0
\ No newline at end of file