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