diff --git a/BUILD.gn b/BUILD.gn index 56c1c6abbff..25f4b6a5c3b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -42,6 +42,7 @@ group("runtime") { deps = [ "runtime/bin:dart", + "runtime/bin:dartvm", "runtime/bin:ffi_test_dynamic_library", "runtime/bin:ffi_test_functions", "runtime/bin:process_test", @@ -60,12 +61,15 @@ group("runtime") { deps += [ "runtime/bin:dartaotruntime", "runtime/bin:dartaotruntime_product", + "utils/dartdev:dartdev_aot_snapshot", "utils/dds:dds_aot", + "utils/dtd:dtd_aot", "utils/kernel-service:frontend_server_aot_product", ] } else { deps += [ "utils/dds:dds", + "utils/dtd:dtd", "utils/kernel-service:frontend_server", ] } @@ -420,7 +424,7 @@ if (is_fuchsia) { } test_binaries = [ - "dart", + "dartvm", "dartaotruntime", "run_vm_tests", ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c0a9acafe1..4fd7856ad41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,25 @@ constraint][language version] lower bound to 3.9 or greater (`sdk: '^3.9.0'`). Container().value('Invocation with missing runtime checks!'); ``` +#### Dart CLI and Dart VM + +- The Dart CLI and Dart VM have been split into two seperate executables. + + The Dart CLI tool has been split out of the VM into it's own embedder which + runs in AOT mode. The pure Dart VM executable is called `dartvm` and + has no Dart CLI functionality in it + + The Dart CLI executable parses the CLI commands and invokes the rest + of the AOT tools in the same process, for the 'run' and 'test' + commands it execs a process which runs `dartvm` + + `dart hello.dart` execs the `dartvm` process and runs 'hello.dart' + + The Dart CLI is not generated for ia32 as we are not shipping a + Dart SDK for ia32 anymore (support to execute the `dartvm` for ia32 + architecture is retained) + + #### Pub - Git dependencies can now be version-solved based on git tags. diff --git a/build/dart/dart_action.gni b/build/dart/dart_action.gni index 7b5d40cffff..8f75a327ac5 100644 --- a/build/dart/dart_action.gni +++ b/build/dart/dart_action.gni @@ -10,7 +10,7 @@ import("../executable_suffix.gni") # - prebuilt_dart_action() # Runs Dart scripts using the downloaded prebuilt Dart SDK. This is the # preferred method of running Dart code during the build as it is much -# faster than using dart_action() in debug and cross builds. +# faster than using dartvm_action() in debug and cross builds. # However, prebuilt_dart_action() should *not* be used to generate snapshots. # # - prebuilt_dartaotruntime_action() @@ -18,7 +18,7 @@ import("../executable_suffix.gni") # preferred method of running Dart AOT code during the build as it avoids # the multiple layers of code that prebuilt_dart_action goes through. # -# - dart_action() +# - dartvm_action() # Runs Dart scripts using the binary built for runtime/bin:dart using the # host toolchain. It should only be used when an artifact agreeing exactly # with the version of the Dart VM being built must be produced, for example @@ -350,10 +350,10 @@ template("_built_tool_action") { # outputs # testonly # visibility -template("dart_action") { +template("dartvm_action") { assert(defined(invoker.script), "script must be defined for $target_name") _built_tool_action(target_name) { - tool = "$_dart_root/runtime/bin:dart" + tool = "$_dart_root/runtime/bin:dartvm" forward_variables_from(invoker, [ "args", diff --git a/build/rbe/rewrapper_dart.py b/build/rbe/rewrapper_dart.py index 11dfbd69ec0..e81969ddefd 100644 --- a/build/rbe/rewrapper_dart.py +++ b/build/rbe/rewrapper_dart.py @@ -269,6 +269,9 @@ trace to find the place to insert the appropriate support. elif arg.endswith('/dart'): self.dart_subdir = os.path.dirname(arg) return self.parse_dart() + elif arg.endswith('/dartvm'): + self.dart_subdir = os.path.dirname(arg) + return self.parse_dart() elif arg.endswith('/dartaotruntime'): self.dart_subdir = os.path.dirname(arg) return self.parse_dartaotruntime() diff --git a/pkg/analysis_server/integration_test/analysis/get_errors_non_standard_sdk_test.dart b/pkg/analysis_server/integration_test/analysis/get_errors_non_standard_sdk_test.dart index d57a21181e7..4903ad8b069 100644 --- a/pkg/analysis_server/integration_test/analysis/get_errors_non_standard_sdk_test.dart +++ b/pkg/analysis_server/integration_test/analysis/get_errors_non_standard_sdk_test.dart @@ -27,6 +27,11 @@ class AnalysisDomainGetErrorsTest return path.join(sdkPath, 'bin', name); } + String executableVmFilePathIn(String sdkPath) { + var name = Platform.isWindows ? 'dartvm.exe' : 'dartvm'; + return path.join(sdkPath, 'bin', name); + } + String serverSnapshotPathIn(String sdkPath) { return path.join( sdkPath, @@ -50,6 +55,10 @@ class AnalysisDomainGetErrorsTest executableFilePathIn(standardSdkPath), ).copySync(executableFilePathIn(sdkPath)); + File( + executableVmFilePathIn(standardSdkPath), + ).copySync(executableVmFilePathIn(sdkPath)); + File( serverSnapshotPathIn(standardSdkPath), ).copySync(serverSnapshotPathIn(sdkPath)); diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart index ab36839d18d..7a55052f0ea 100644 --- a/pkg/dartdev/lib/src/commands/compile.dart +++ b/pkg/dartdev/lib/src/commands/compile.dart @@ -98,19 +98,12 @@ class CompileJSCommand extends CompileSubcommandCommand { } final args = argResults!; var snapshot = sdk.dart2jsAotSnapshot; - var script = sdk.dartAotRuntime; - var useExecProcess = true; if (!Sdk.checkArtifactExists(snapshot, logError: false)) { - // AOT snapshots cannot be generated on IA32, so we need this fallback - // branch until support for IA32 is dropped (https://dartbug.com/49969). - script = sdk.dart2jsSnapshot; - if (!Sdk.checkArtifactExists(script)) { - return genericErrorExitCode; - } - useExecProcess = false; + log.stderr('Error: JS compilation failed'); + log.stderr('Unable to find $snapshot'); + return compileErrorExitCode; } final dart2jsCommand = [ - if (useExecProcess) snapshot, '--libraries-spec=${sdk.librariesJson}', '--cfe-invocation-modes=compile', '--invoker=dart_cli', @@ -119,10 +112,10 @@ class CompileJSCommand extends CompileSubcommandCommand { ]; try { VmInteropHandler.run( - script, + snapshot, dart2jsCommand, packageConfigOverride: null, - useExecProcess: useExecProcess, + useExecProcess: false, ); return 0; } catch (e, st) { @@ -163,28 +156,21 @@ class CompileDDCCommand extends CompileSubcommandCommand { } final args = argResults!; var snapshot = sdk.ddcAotSnapshot; - var script = sdk.dartAotRuntime; - var useExecProcess = true; if (!Sdk.checkArtifactExists(snapshot, logError: false)) { - // AOT snapshots cannot be generated on IA32, so we need this fallback - // branch until support for IA32 is dropped (https://dartbug.com/49969). - script = sdk.ddcSnapshot; - if (!Sdk.checkArtifactExists(script)) { - return genericErrorExitCode; - } - useExecProcess = false; + log.stderr('Error: JS compilation failed'); + log.stderr('Unable to find $snapshot'); + return compileErrorExitCode; } final ddcCommand = [ - if (useExecProcess) snapshot, // Add the remaining arguments. if (args.rest.isNotEmpty) ...args.rest.sublist(0), ]; try { VmInteropHandler.run( - script, + snapshot, ddcCommand, packageConfigOverride: null, - useExecProcess: useExecProcess, + useExecProcess: false, ); return 0; } catch (e, st) { @@ -459,7 +445,7 @@ class CompileJitSnapshotCommand extends CompileSubcommandCommand { log.stdout('Compiling $sourcePath to jit-snapshot file $outputFile.'); // TODO(bkonyi): perform compilation in same process. - return await runProcess([sdk.dart, ...buildArgs]); + return await runProcess([sdk.dartvm, ...buildArgs]); } } diff --git a/pkg/dartdev/lib/src/commands/dart_mcp_server.dart b/pkg/dartdev/lib/src/commands/dart_mcp_server.dart index 142c375e754..8d6f6caaf3a 100644 --- a/pkg/dartdev/lib/src/commands/dart_mcp_server.dart +++ b/pkg/dartdev/lib/src/commands/dart_mcp_server.dart @@ -51,12 +51,11 @@ A stdio based Model Context Protocol (MCP) server to aid in Dart and Flutter dev } try { VmInteropHandler.run( - sdk.dartAotRuntime, + sdk.dartMCPServerAotSnapshot, [ - sdk.dartMCPServerAotSnapshot, ...forwardedArgs, ], - useExecProcess: true, + useExecProcess: false, ); return 0; } catch (e, st) { diff --git a/pkg/dartdev/lib/src/commands/development_service.dart b/pkg/dartdev/lib/src/commands/development_service.dart index f640da4896e..bf80eba3c74 100644 --- a/pkg/dartdev/lib/src/commands/development_service.dart +++ b/pkg/dartdev/lib/src/commands/development_service.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'package:dds/src/arg_parser.dart'; import 'package:path/path.dart'; @@ -37,38 +36,22 @@ class DevelopmentServiceCommand extends DartdevCommand { Future run() async { final sdkDir = dirname(sdk.dart); final fullSdk = sdkDir.endsWith('bin'); - var script = fullSdk - ? sdk.dartAotRuntime - : absolute(sdkDir, 'dartaotruntime${Platform.isWindows ? '.exe' : ''}'); var snapshot = fullSdk ? sdk.ddsAotSnapshot : absolute(sdkDir, 'dds_aot.dart.snapshot'); - var useExecProcess = true; final args = argResults!.arguments; if (!Sdk.checkArtifactExists(snapshot, logError: false)) { - // On ia32 platforms we do not have an AOT snapshot and so we need - // to run the JIT snapshot. - useExecProcess = false; - script = - fullSdk ? sdk.ddsSnapshot : absolute(sdkDir, 'dds.dart.snapshot'); - if (!Sdk.checkArtifactExists(script, logError: false)) { - log.stderr('Error: launching development server failed : ' - 'Unable to find snapshot for the development server'); - return 255; - } + log.stderr('Error: launching development server failed : ' + 'Unable to find snapshot for the development server'); + return 255; } - final ddsCommand = [ - if (useExecProcess) snapshot, - // Add the remaining args. - if (args.isNotEmpty) ...args, - ]; try { VmInteropHandler.run( - script, - ddsCommand, + snapshot, + args, packageConfigOverride: null, - useExecProcess: useExecProcess, + useExecProcess: false, ); return 0; } catch (e, st) { diff --git a/pkg/dartdev/lib/src/commands/language_server.dart b/pkg/dartdev/lib/src/commands/language_server.dart index 9c446dbefc1..f4d95d2bb30 100644 --- a/pkg/dartdev/lib/src/commands/language_server.dart +++ b/pkg/dartdev/lib/src/commands/language_server.dart @@ -54,26 +54,27 @@ For more information about the server's capabilities and configuration, see: args = [...args, '--$protocol=$lsp']; } try { + var script = sdk.analysisServerAotSnapshot; + var useExec = false; if (argResults!.flag(useAotSnapshotFlag)) { - if (!Sdk.checkArtifactExists(sdk.dartAotRuntime)) { + if (!Sdk.checkArtifactExists(sdk.analysisServerAotSnapshot)) { + log.stderr('Error: launching language analysis server failed'); + log.stderr('${sdk.analysisServerAotSnapshot} not found'); return _genericErrorExitCode; } args = [...args]; args.remove('--$useAotSnapshotFlag'); - VmInteropHandler.run( - sdk.dartAotRuntime, - [sdk.analysisServerAotSnapshot, ...args], - useExecProcess: true, - ); } else { args = [...args]; args.remove('--no-$useAotSnapshotFlag'); - VmInteropHandler.run( - sdk.analysisServerSnapshot, - args, - useExecProcess: false, - ); + script = sdk.analysisServerSnapshot; + useExec = true; } + VmInteropHandler.run( + script, + args, + useExecProcess: useExec, + ); return 0; } catch (e, st) { log.stderr('Error: launching language analysis server failed'); @@ -81,7 +82,7 @@ For more information about the server's capabilities and configuration, see: if (verbose) { log.stderr(st.toString()); } - return 255; + return _genericErrorExitCode; } } diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart index 1035d4bbd08..b897714811d 100644 --- a/pkg/dartdev/lib/src/commands/run.dart +++ b/pkg/dartdev/lib/src/commands/run.dart @@ -25,7 +25,6 @@ import '../vm_interop_handler.dart'; import 'compilation_server.dart'; class RunCommand extends DartdevCommand { - static const bool isProductMode = bool.fromEnvironment('dart.vm.product'); static const String cmdName = 'run'; // kErrorExitCode, as defined in runtime/bin/error_exit.h @@ -92,109 +91,101 @@ class RunCommand extends DartdevCommand { argParser.addSeparator( 'Debugging options:', ); - if (!isProductMode) { - argParser - ..addOption( - 'observe', - help: 'The observe flag is a convenience flag used to run a program ' - 'with a set of common options useful for debugging. ' - 'Run `dart help -v run` for details.', - valueHelp: '[[/]]', - ) - ..addFlag( - 'enable-asserts', - help: 'Enable assert statements.', - ) - ..addOption( - 'launch-dds', - hide: true, - help: 'Launch DDS.', - ); + argParser + ..addOption( + 'observe', + help: 'The observe flag is a convenience flag used to run a program ' + 'with a set of common options useful for debugging. ' + 'Run `dart help -v run` for details.', + valueHelp: '[[/]]', + ) + ..addFlag( + 'enable-asserts', + help: 'Enable assert statements.', + ) + ..addOption( + 'launch-dds', + hide: true, + help: 'Launch DDS.', + ); - if (verbose) { - argParser.addSeparator( - verbose ? 'Options implied by --observe are currently:' : ''); - } - argParser - ..addOption( - 'enable-vm-service', - help: 'Enables the VM service and listens on the specified port for ' - 'connections (default port number is 8181, default bind address ' - 'is localhost).', - valueHelp: '[[/]]', - hide: !verbose, - ) - ..addFlag( - 'serve-devtools', - help: 'Serves an instance of the Dart DevTools debugger and profiler ' - 'via the VM service at /devtools.', - defaultsTo: true, - hide: !verbose, - ) - ..addFlag( - 'pause-isolates-on-exit', - help: 'Pause isolates on exit when ' - 'running with --enable-vm-service.', - hide: !verbose, - ) - ..addFlag( - 'pause-isolates-on-unhandled-exceptions', - help: 'Pause isolates when an unhandled exception is encountered ' - 'when running with --enable-vm-service.', - hide: !verbose, - ) - ..addFlag( - 'warn-on-pause-with-no-debugger', - help: - 'Print a warning when an isolate pauses with no attached debugger' - ' when running with --enable-vm-service.', - hide: !verbose, - ) - ..addOption( - 'timeline-streams', - help: 'Enables recording for specific timeline streams.\n' - 'Valid streams include: all, API, Compiler, CompilerVerbose, Dart, ' - 'Debugger, Embedder, GC, Isolate, Microtask, VM.\n' - 'Defaults to "Compiler, Dart, GC, Microtask" when --observe is ' - 'provided.', - valueHelp: 'str1, str2, ...', - hide: !verbose, - ); - - if (verbose) { - argParser.addSeparator('Other debugging options:'); - } - argParser - ..addFlag( - 'pause-isolates-on-start', - help: 'Pause isolates on start when ' - 'running with --enable-vm-service.', - hide: !verbose, - ) - ..addOption( - 'timeline-recorder', - help: 'Selects the timeline recorder to use.\n' - 'Valid recorders include: none, ring, endless, startup, ' - 'systrace, file, callback, perfettofile.\n' - 'Defaults to ring.', - valueHelp: 'recorder', - hide: !verbose, - ) - ..addFlag( - 'profile-microtasks', - hide: !verbose, - negatable: false, - help: 'Record information about each microtask. Information about ' - 'completed microtasks will be written to the "Microtask" ' - 'timeline stream.', - ); - } else { - argParser.addOption('timeline-recorder', - help: 'Selects the timeline recorder to use.\n' - 'Valid recorders include: none, systrace, file, callback.\n' - 'Defaults to none.', - valueHelp: 'recorder'); + if (verbose) { + argParser.addSeparator( + verbose ? 'Options implied by --observe are currently:' : ''); } + argParser + ..addOption( + 'enable-vm-service', + help: 'Enables the VM service and listens on the specified port for ' + 'connections (default port number is 8181, default bind address ' + 'is localhost).', + valueHelp: '[[/]]', + hide: !verbose, + ) + ..addFlag( + 'serve-devtools', + help: 'Serves an instance of the Dart DevTools debugger and profiler ' + 'via the VM service at /devtools.', + defaultsTo: true, + hide: !verbose, + ) + ..addFlag( + 'pause-isolates-on-exit', + help: 'Pause isolates on exit when ' + 'running with --enable-vm-service.', + hide: !verbose, + ) + ..addFlag( + 'pause-isolates-on-unhandled-exceptions', + help: 'Pause isolates when an unhandled exception is encountered ' + 'when running with --enable-vm-service.', + hide: !verbose, + ) + ..addFlag( + 'warn-on-pause-with-no-debugger', + help: + 'Print a warning when an isolate pauses with no attached debugger' + ' when running with --enable-vm-service.', + hide: !verbose, + ) + ..addOption( + 'timeline-streams', + help: 'Enables recording for specific timeline streams.\n' + 'Valid streams include: all, API, Compiler, CompilerVerbose, Dart, ' + 'Debugger, Embedder, GC, Isolate, Microtask, VM.\n' + 'Defaults to "Compiler, Dart, GC, Microtask" when --observe is ' + 'provided.', + valueHelp: 'str1, str2, ...', + hide: !verbose, + ); + + if (verbose) { + argParser.addSeparator('Other debugging options:'); + } + argParser + ..addFlag( + 'pause-isolates-on-start', + help: 'Pause isolates on start when ' + 'running with --enable-vm-service.', + hide: !verbose, + ) + ..addOption( + 'timeline-recorder', + help: 'Selects the timeline recorder to use.\n' + 'Valid recorders include: none, ring, endless, startup, ' + 'systrace, file, callback, perfettofile.\n' + 'Defaults to ring.', + valueHelp: 'recorder', + hide: !verbose, + ) + ..addFlag( + 'profile-microtasks', + hide: !verbose, + negatable: false, + help: 'Record information about each microtask. Information about ' + 'completed microtasks will be written to the "Microtask" ' + 'timeline stream.', + ); argParser.addSeparator('Logging options:'); argParser.addOption( @@ -215,26 +206,24 @@ class RunCommand extends DartdevCommand { help: 'Define an environment declaration.', hide: !verbose, ); - if (!isProductMode) { - argParser - ..addFlag( - 'disable-service-auth-codes', - hide: !verbose, - negatable: false, - help: 'Disables the requirement for an authentication code to ' - 'communicate with the VM service. Authentication codes help ' - 'protect against CSRF attacks, so it is not recommended to ' - 'disable them unless behind a firewall on a secure device.', - ) - ..addFlag( - 'enable-service-port-fallback', - hide: !verbose, - negatable: false, - help: 'When the VM service is told to bind to a particular port, ' - 'fallback to 0 if it fails to bind instead of failing to ' - 'start.', - ); - } + argParser + ..addFlag( + 'disable-service-auth-codes', + hide: !verbose, + negatable: false, + help: 'Disables the requirement for an authentication code to ' + 'communicate with the VM service. Authentication codes help ' + 'protect against CSRF attacks, so it is not recommended to ' + 'disable them unless behind a firewall on a secure device.', + ) + ..addFlag( + 'enable-service-port-fallback', + hide: !verbose, + negatable: false, + help: 'When the VM service is told to bind to a particular port, ' + 'fallback to 0 if it fails to bind instead of failing to ' + 'start.', + ); argParser ..addOption( 'namespace', @@ -271,40 +260,38 @@ class RunCommand extends DartdevCommand { 'supplies a mapping of package names\ninto paths.', ); - if (!isProductMode) { - argParser - ..addOption( - 'write-service-info', - help: 'Outputs information necessary to connect to the VM service to ' - 'specified file in JSON format. Useful for clients which are ' - 'unable to listen to stdout for the Dart VM service listening ' - 'message.', - valueHelp: 'file', + argParser + ..addOption( + 'write-service-info', + help: 'Outputs information necessary to connect to the VM service to ' + 'specified file in JSON format. Useful for clients which are ' + 'unable to listen to stdout for the Dart VM service listening ' + 'message.', + valueHelp: 'file', + hide: !verbose, + ) + ..addFlag('dds', hide: !verbose, - ) - ..addFlag('dds', - hide: !verbose, - help: - 'Use the Dart Development Service (DDS) for enhanced debugging ' - 'functionality. Note: Disabling DDS may break some ' - 'functionality in IDEs and other tooling.', - defaultsTo: true) - ..addFlag('serve-observatory', - hide: !verbose, - help: 'Enable hosting Observatory through the VM Service.', - defaultsTo: true) - ..addFlag( - 'print-dtd', + help: + 'Use the Dart Development Service (DDS) for enhanced debugging ' + 'functionality. Note: Disabling DDS may break some ' + 'functionality in IDEs and other tooling.', + defaultsTo: true) + ..addFlag('serve-observatory', hide: !verbose, - help: 'Prints connection details for the Dart Tooling Daemon (DTD).' - 'Useful for Dart DevTools extension authors working with DTD in the ' - 'extension development environment.', - ) - ..addFlag( - 'debug-dds', - hide: true, - ); - } + help: 'Enable hosting Observatory through the VM Service.', + defaultsTo: true) + ..addFlag( + 'print-dtd', + hide: !verbose, + help: 'Prints connection details for the Dart Tooling Daemon (DTD).' + 'Useful for Dart DevTools extension authors working with DTD in the ' + 'extension development environment.', + ) + ..addFlag( + 'debug-dds', + hide: true, + ); argParser.addExperimentalFlags(verbose: verbose); } @@ -528,6 +515,7 @@ class RunCommand extends DartdevCommand { runArgs, packageConfigOverride: args.option('packages') ?? executable.packageConfig, + useExecProcess: true, ); return 0; } diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart index d08a4927bee..3698559d6aa 100644 --- a/pkg/dartdev/lib/src/commands/test.dart +++ b/pkg/dartdev/lib/src/commands/test.dart @@ -112,6 +112,7 @@ Run "${runner!.executableName} help" to see global options.'''); testExecutable.executable, argsRestNoExperimentOrSuppressAnalytics, packageConfigOverride: testExecutable.packageConfig!, + useExecProcess: true, // TODO(bkonyi): remove once DartDev moves to AOT and this flag can be // provided directly to the process spawned by `dart run` and // `dart test`. diff --git a/pkg/dartdev/lib/src/commands/tooling_daemon.dart b/pkg/dartdev/lib/src/commands/tooling_daemon.dart index 59a14199af1..0c09d0631ed 100644 --- a/pkg/dartdev/lib/src/commands/tooling_daemon.dart +++ b/pkg/dartdev/lib/src/commands/tooling_daemon.dart @@ -33,28 +33,19 @@ class ToolingDaemonCommand extends DartdevCommand { @override Future run() async { - var script = sdk.dartAotRuntime; var snapshot = sdk.dtdAotSnapshot; - var useExecProcess = true; final args = argResults!.arguments; - if (!Sdk.checkArtifactExists(sdk.dtdAotSnapshot, logError: false)) { - // On ia32 platforms we do not have an AOT snapshot and so we need - // to run the JIT snapshot. - useExecProcess = false; - script = sdk.dtdSnapshot; + log.stderr('Error: launching dart tooling daemon failed : ' + 'Unable to find snapshot for the tooling daemon'); + return 255; } - final dtdCommand = [ - if (useExecProcess) snapshot, - // Add the remaining args. - if (args.isNotEmpty) ...args, - ]; try { VmInteropHandler.run( - script, - dtdCommand, + snapshot, + args, packageConfigOverride: null, - useExecProcess: useExecProcess, + useExecProcess : false, ); return 0; } catch (e, st) { diff --git a/pkg/dartdev/lib/src/dds_runner.dart b/pkg/dartdev/lib/src/dds_runner.dart index f387d3303a5..b1cd2e68c7a 100644 --- a/pkg/dartdev/lib/src/dds_runner.dart +++ b/pkg/dartdev/lib/src/dds_runner.dart @@ -22,6 +22,10 @@ class DDSRunner { required bool debugDds, required bool enableServicePortFallback, }) async { + void printError(String details) => stderr.writeln( + 'Could not start the VM service:\n$details', + ); + final sdkDir = dirname(sdk.dart); final fullSdk = sdkDir.endsWith('bin'); var execName = fullSdk @@ -32,14 +36,8 @@ class DDSRunner { : absolute(sdkDir, 'dds_aot.dart.snapshot'); final isAot = Sdk.checkArtifactExists(snapshotName) ? true : false; if (!isAot) { - // On ia32 sdks we do not have an AOT runtime and so we would be - // using the regular executable. - snapshotName = - fullSdk ? sdk.ddsSnapshot : absolute(sdkDir, 'dds.dart.snapshot'); - if (!Sdk.checkArtifactExists(snapshotName)) { - return false; - } - execName = sdk.dart; + printError('Unable to find snapshot for the development server'); + return false; } final process = await Process.start( @@ -81,10 +79,6 @@ class DDSRunner { // DDS will close stderr once it's finished launching. final launchResult = await process.stderr.transform(utf8.decoder).join(); - void printError(String details) => stderr.writeln( - 'Could not start the VM service:\n$details', - ); - try { final result = json.decode(launchResult) as Map; if (result diff --git a/pkg/dartdev/lib/src/generate_kernel.dart b/pkg/dartdev/lib/src/generate_kernel.dart index b84cd60cada..6241512c743 100644 --- a/pkg/dartdev/lib/src/generate_kernel.dart +++ b/pkg/dartdev/lib/src/generate_kernel.dart @@ -142,17 +142,7 @@ Future ensureCompilationServerIsRunning( mode: ProcessStartMode.detachedWithStdio, ); } else { - // AOT snapshots cannot be generated on IA32, so we need this fallback - // branch until support for IA32 is dropped (https://dartbug.com/49969). - frontendServerProcess = await Process.start( - sdk.dart, - [ - sdk.frontendServerSnapshot, - '--resident-info-file-name=${serverInfoFile.absolute.path}' - ], - workingDirectory: homeDir?.path, - mode: ProcessStartMode.detachedWithStdio, - ); + throw StateError('Unable to find snapshot for frontend server'); } final serverOutput = diff --git a/pkg/dartdev/lib/src/processes.dart b/pkg/dartdev/lib/src/processes.dart index 3223161195b..64c3a447748 100644 --- a/pkg/dartdev/lib/src/processes.dart +++ b/pkg/dartdev/lib/src/processes.dart @@ -289,7 +289,8 @@ List _getProcessInfoWindows() { } bool _isProcessDartRelated(ProcessInfo process) { - return process.command == 'dart' || process.command == 'dart.exe'; + return process.command == 'dart' || process.command == 'dart.exe' || + process.command == 'dartvm' || process.command == 'dartvm.exe'; } String _getCommandFrom(String commandLine) { diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart index 845c38d7071..dfd73aed2e7 100644 --- a/pkg/dartdev/lib/src/sdk.dart +++ b/pkg/dartdev/lib/src/sdk.dart @@ -53,6 +53,19 @@ class Sdk { ); } + String get dartvm { + final basename = Platform.isWindows? 'dartvm.exe' : 'dartvm'; + return path.absolute( + _runFromBuildRoot + ? sdkPath + : path.absolute( + sdkPath, + 'bin', + ), + basename, + ); + } + String get dartAotRuntime => _runFromBuildRoot ? path.absolute( sdkPath, @@ -74,10 +87,6 @@ class Sdk { 'analysis_server.dart.snapshot', ); - String get ddcSnapshot => _snapshotPathFor( - 'dartdevc.dart.snapshot', - ); - String get ddcAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'dartdevc_aot_product.dart.snapshot', @@ -86,10 +95,6 @@ class Sdk { 'dartdevc_aot.dart.snapshot', ); - String get dart2jsSnapshot => _snapshotPathFor( - 'dart2js.dart.snapshot', - ); - String get dart2jsAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'dart2js_aot_product.dart.snapshot', @@ -106,18 +111,10 @@ class Sdk { 'dart_mcp_server_aot.dart.snapshot', ); - String get ddsSnapshot => _snapshotPathFor( - 'dds.dart.snapshot', - ); - String get ddsAotSnapshot => _snapshotPathFor( 'dds_aot.dart.snapshot', ); - String get frontendServerSnapshot => _snapshotPathFor( - 'frontend_server.dart.snapshot', - ); - String get frontendServerAotSnapshot => _runFromBuildRoot ? _snapshotPathFor( 'frontend_server_aot_product.dart.snapshot', @@ -126,10 +123,6 @@ class Sdk { 'frontend_server_aot.dart.snapshot', ); - String get dtdSnapshot => _snapshotPathFor( - 'dart_tooling_daemon.dart.snapshot', - ); - String get dtdAotSnapshot => _snapshotPathFor( 'dart_tooling_daemon_aot.dart.snapshot', ); @@ -229,7 +222,7 @@ class Sdk { // non-standard SDK layouts that can involve symlinks (e.g., Brew // installations, google3 tests, etc). if (!checkArtifactExists( - path.join(snapshotsDir, 'dartdev.dart.snapshot'), + path.join(snapshotsDir, 'dartdev_aot.dart.snapshot'), logError: false, )) { return null; diff --git a/pkg/dartdev/test/commands/flag_test.dart b/pkg/dartdev/test/commands/flag_test.dart index 42b1c871c9a..c2814f51d0d 100644 --- a/pkg/dartdev/test/commands/flag_test.dart +++ b/pkg/dartdev/test/commands/flag_test.dart @@ -100,18 +100,10 @@ void help() { test('print Dart CLI help on usage error', () async { p = project(); var result = await p.run(['---help']); - expect(result.exitCode, 255); - expect(result.stdout, contains(DartdevRunner.dartdevDescription)); - expect(result.stderr, isEmpty); - }); - - test('print VM help on usage error when --disable-dart-dev is provided', - () async { - p = project(); - var result = await p.run(['---help', '--disable-dart-dev']); - expect(result.exitCode, 255); - expect(result.stdout, isNot(contains(DartdevRunner.dartdevDescription))); - expect(result.stderr, isEmpty); + expect(result.exitCode, 64); + expect(result.stderr, contains('Could not find an option named "---help"')); + expectUsage(result.stderr); + expect(result.stdout, isEmpty); }); test('help', () async { diff --git a/pkg/dartdev/test/commands/info_linux_test.dart b/pkg/dartdev/test/commands/info_linux_test.dart index dac77adb7ed..2425140c47a 100644 --- a/pkg/dartdev/test/commands/info_linux_test.dart +++ b/pkg/dartdev/test/commands/info_linux_test.dart @@ -47,7 +47,7 @@ void main() { expect(output, contains('providing this information')); expect(output, contains('## Process info')); expect(output, contains('Memory |')); - expect(output, contains('| dart ')); + expect(output, contains(RegExp('| dart(vm)? '))); }); }, timeout: longTimeout); } diff --git a/pkg/dartdev/test/commands/info_macos_test.dart b/pkg/dartdev/test/commands/info_macos_test.dart index 8b5a2ab9389..78695ebc4f6 100644 --- a/pkg/dartdev/test/commands/info_macos_test.dart +++ b/pkg/dartdev/test/commands/info_macos_test.dart @@ -59,7 +59,7 @@ void main() { expect(output, contains('providing this information')); expect(output, contains('## Process info')); expect(output, contains('Memory |')); // Header is aligned right. - expect(output, contains('| dart ')); + expect(output, contains(RegExp('| dart(vm)? '))); }); }, timeout: longTimeout); } diff --git a/pkg/dartdev/test/commands/info_windows_test.dart b/pkg/dartdev/test/commands/info_windows_test.dart index 798159623c1..ba4b29c9261 100644 --- a/pkg/dartdev/test/commands/info_windows_test.dart +++ b/pkg/dartdev/test/commands/info_windows_test.dart @@ -24,7 +24,7 @@ void main() { expect(process.memoryMb, greaterThan(0)); expect(process.cpuPercent, null); expect(process.elapsedTime, null); - expect(process.commandLine, startsWith('dart.exe')); + expect(process.commandLine, startsWith('dart')); } }); diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart index ba8604084f3..ae1d9b7a363 100644 --- a/pkg/dartdev/test/commands/run_test.dart +++ b/pkg/dartdev/test/commands/run_test.dart @@ -838,7 +838,8 @@ main() => print('b:b'); sub = process.stdout.transform(utf8.decoder).listen((event) async { if (event.contains('ready')) { readyCompleter.complete(); - } else if (event.contains(devToolsMessagePrefix)) { + } + if (event.contains(devToolsMessagePrefix)) { await sub.cancel(); completer.complete(); } @@ -928,20 +929,6 @@ void residentRun() { }); }); - test('running dartdev is a prerequisite for passing --resident', () async { - p = project(mainSrc: 'void main() {}'); - final result = await p.run(['--resident', p.relativeFilePath]); - - expect(result.exitCode, 255); - expect( - result.stderr, - contains( - 'Passing the `--resident` flag to `dart` is invalid. It must be passed ' - 'to `dart run`.', - ), - ); - }); - test( 'passing --resident is a prerequisite for passing --resident-compiler-info-file', () async { @@ -1103,8 +1090,7 @@ void residentRun() { final result = await p.run([ 'run', '--resident', - '--$residentCompilerInfoFileOption', - path.relative(serverInfoFile, from: p.dirPath), + '--$residentCompilerInfoFileOption=${path.relative(serverInfoFile, from: p.dirPath)}', p.relativeFilePath, ]); diff --git a/pkg/dartdev/test/sdk_test.dart b/pkg/dartdev/test/sdk_test.dart index 9ef7801da80..a39439dc589 100644 --- a/pkg/dartdev/test/sdk_test.dart +++ b/pkg/dartdev/test/sdk_test.dart @@ -26,11 +26,11 @@ void _sdk() { }); test('dds snapshot', () { - expectSnapshotExists(Sdk().ddsAotSnapshot, Sdk().ddsSnapshot); + expectFileExists(Sdk().ddsAotSnapshot); }); test('dart2js snapshot', () { - expectSnapshotExists(Sdk().dart2jsAotSnapshot, Sdk().dart2jsSnapshot); + expectFileExists(Sdk().dart2jsAotSnapshot); }); } @@ -45,10 +45,6 @@ void expectFileExists(String path) { expect(File(path).existsSync(), isTrue); } -void expectSnapshotExists(String aotpath, String jitpath) { - expect(File(aotpath).existsSync() || File(jitpath).existsSync(), isTrue); -} - void expectDirectoryExists(String path) { expect(Directory(path).existsSync(), isTrue); } diff --git a/pkg/dartdev/test/smoke/implicit_smoke_test.dart b/pkg/dartdev/test/smoke/implicit_smoke_test.dart index a9dc7ad43bd..21094c4037f 100644 --- a/pkg/dartdev/test/smoke/implicit_smoke_test.dart +++ b/pkg/dartdev/test/smoke/implicit_smoke_test.dart @@ -73,7 +73,8 @@ void main() { void onData(event) { if (event.contains(dartVMServiceMsg)) { sawServiceMsg = true; - } else if (event.contains('Observe smoke test!')) { + } + if (event.contains('Observe smoke test!')) { sawProgramMsg = true; } if (sawServiceMsg && sawProgramMsg) { diff --git a/pkg/dartdev/test/smoke/smoke_test.dart b/pkg/dartdev/test/smoke/smoke_test.dart index ea7a1014889..95e3684dbca 100644 --- a/pkg/dartdev/test/smoke/smoke_test.dart +++ b/pkg/dartdev/test/smoke/smoke_test.dart @@ -61,9 +61,9 @@ void main() { script, ], ); + expect(result.exitCode, 0); expect(result.stderr, isEmpty); expect(result.stdout, contains('Smoke test!')); - expect(result.exitCode, 0); } }); @@ -78,7 +78,8 @@ void main() { void onData(event) { if (event.contains(dartVMServiceMsg)) { sawServiceMsg = true; - } else if (event.contains('Observe smoke test!')) { + } + if (event.contains('Observe smoke test!')) { sawProgramMsg = true; } if (sawServiceMsg && sawProgramMsg) { @@ -153,9 +154,9 @@ void main() { script, ], ); + expect(result.exitCode, 0); expect(result.stderr, isEmpty); expect(result.stdout, contains('Smoke test!')); - expect(result.exitCode, 0); }); // This test verifies that an error is thrown when an invalid experiment @@ -171,9 +172,9 @@ void main() { script, ], ); + expect(result.exitCode, 254); expect(result.stderr, isNotEmpty); expect(result.stdout, isEmpty); - expect(result.exitCode, 254); }); }, timeout: Timeout.none, diff --git a/pkg/dds/lib/src/devtools/dtd.dart b/pkg/dds/lib/src/devtools/dtd.dart index 72b06ed5a70..29417e8eaf4 100644 --- a/pkg/dds/lib/src/devtools/dtd.dart +++ b/pkg/dds/lib/src/devtools/dtd.dart @@ -34,14 +34,19 @@ String getDTDSnapshotDir() { runFromBuildRoot = true; } - // Try to locate the DartDev snapshot to determine if we're able to find + // Try to locate the DDS snapshot to determine if we're able to find // the SDK snapshots with this SDK path. This is meant to handle // non-standard SDK layouts that can involve symlinks (e.g., Brew // installations, google3 tests, etc). if (!File( - path.join(snapshotsDir, 'dartdev.dart.snapshot'), + path.join(snapshotsDir, 'dds_aot.dart.snapshot'), ).existsSync()) { - return null; + // We do not have an AOT snpashot and hence look for the JIT snapshot. + if (!File( + path.join(snapshotsDir, 'dds.dart.snapshot'), + ).existsSync()) { + return null; + } } return (sdkPath, runFromBuildRoot); } @@ -68,11 +73,6 @@ Future startDtd({ snapshotDir, 'dart_tooling_daemon_aot.dart.snapshot', ); - final dtdSnapshot = path.absolute( - snapshotDir, - 'dart_tooling_daemon.dart.snapshot', - ); - final completer = Completer(); void completeForError() => completer.complete(null); @@ -124,8 +124,11 @@ Future startDtd({ ); } catch (_, __) { // Spawning an isolate using the AOT snapshot of the tooling daemon failed, - // we are probably in a JIT VM, try again using the JIT snapshot of the - // tooling daemon. + // try again using the JIT snapshot of the tooling daemon. + final dtdSnapshot = path.absolute( + snapshotDir, + 'dart_tooling_daemon.dart.snapshot', + ); try { await Isolate.spawnUri( Uri.file(dtdSnapshot), diff --git a/pkg/dds/test/devtools_server/utils/server_driver.dart b/pkg/dds/test/devtools_server/utils/server_driver.dart index d2ad505d4ce..b48e0b6c45b 100644 --- a/pkg/dds/test/devtools_server/utils/server_driver.dart +++ b/pkg/dds/test/devtools_server/utils/server_driver.dart @@ -78,10 +78,8 @@ class DevToolsServerDriver { int? tryPorts, List additionalArgs = const [], }) async { - final script = - resolveTestRelativePath('devtools_server/utils/serve_devtools.dart'); final args = [ - script.toFilePath(), + 'devtools', '--machine', '--port', '$port', diff --git a/pkg/test_runner/lib/src/process_queue.dart b/pkg/test_runner/lib/src/process_queue.dart index d2a3f8590c7..775de22a52a 100644 --- a/pkg/test_runner/lib/src/process_queue.dart +++ b/pkg/test_runner/lib/src/process_queue.dart @@ -714,6 +714,8 @@ class CommandExecutorImpl implements CommandExecutor { steps.add(() => device.runAdbShellCommand(['mkdir', '-p', deviceTestDir])); steps .add(() => device.pushCachedData("$buildPath/dart", '$devicedir/dart')); + steps.add(() => device + .pushCachedData("$buildPath/dartvm", '$devicedir/dartvm')); steps.add(() => device .runAdbCommand(['push', hostKernelFile, '$deviceTestDir/out.dill'])); diff --git a/pkg/test_runner/lib/src/runtime_configuration.dart b/pkg/test_runner/lib/src/runtime_configuration.dart index a2f27276c6f..b2050ad9dce 100644 --- a/pkg/test_runner/lib/src/runtime_configuration.dart +++ b/pkg/test_runner/lib/src/runtime_configuration.dart @@ -547,7 +547,7 @@ class DartkFuchsiaEmulatorRuntimeConfiguration argument.replaceAll(Directory.current.path, "pkg/data")) .toList(); - var component = "dart_test_component.cm"; + var component = "dartvm_test_component.cm"; if (aot) { component = "dartaotruntime_test_component.cm"; arguments[arguments.length - 1] = diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 6e3a35a6347..dfa1dbc836d 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -59,6 +59,25 @@ config("dart_aotruntime_config") { ] } +config("dartdev_config") { + defines = [] + cflags = [] + defines += [ + "DART_PRECOMPILED_RUNTIME", + "EXCLUDE_CFE_AND_KERNEL_PLATFORM", + ] + if (!dart_debug) { + defines += [ "PRODUCT" ] + if (is_posix) { + cflags = [ + # This is the equivalent from `build/config/BUILDCONFIG.gn` which includes + # `build/config/gcc:symbol_visibility_hidden` in product mode. + "-fvisibility=hidden", + ] + } + } +} + config("add_empty_macho_section_config") { if (is_mac || is_ios) { # We create an empty __space_for_note section in a __CUSTOM segment to diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index 63a919c8130..b3b71b39cdb 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -916,8 +916,6 @@ template("dart_executable") { "error_exit.cc", "error_exit.h", "icu.cc", - "main_options.cc", - "main_options.h", "options.cc", "options.h", "snapshot_utils.cc", @@ -966,7 +964,7 @@ template("dart_executable") { } } -dart_executable("dart") { +dart_executable("dartvm") { extra_deps = [ ":dart_snapshot_cc", "..:libdart_jit", @@ -974,8 +972,7 @@ dart_executable("dart") { ] extra_sources = [ "builtin.cc", - "dartdev_isolate.cc", - "dartdev_isolate.h", + "common_options.h", "dfe.cc", "dfe.h", "gzip.cc", @@ -984,6 +981,8 @@ dart_executable("dart") { "loader.h", "main.cc", "main_impl.cc", + "main_options.cc", + "main_options.h", ] if (!exclude_kernel_service) { extra_deps += [ ":dart_kernel_platform_cc" ] @@ -1007,12 +1006,15 @@ dart_executable("dartaotruntime") { ] extra_sources = [ "builtin.cc", + "common_options.h", "gzip.cc", "gzip.h", "loader.cc", "loader.h", "main.cc", "main_impl.cc", + "main_options.cc", + "main_options.h", "snapshot_empty.cc", ] @@ -1041,12 +1043,15 @@ dart_executable("dartaotruntime_product") { ] extra_sources = [ "builtin.cc", + "common_options.h", "gzip.cc", "gzip.h", "loader.cc", "loader.h", "main.cc", "main_impl.cc", + "main_options.cc", + "main_options.h", "snapshot_empty.cc", ] @@ -1056,6 +1061,40 @@ dart_executable("dartaotruntime_product") { ] } +if (dart_target_arch != "ia32" && dart_target_arch != "x86") { + dart_executable("dart") { + use_product_mode = true + extra_configs = [ + "..:dartdev_config", + "..:add_empty_macho_section_config", + ] + extra_deps = [ + ":shared_object_loaders_product", + "..:libdart_aotruntime_product", + "../platform:libdart_platform_aotruntime_product", + ] + extra_sources = [ + "builtin.cc", + "common_options.h", + "dartdev.cc", + "dartdev.h", + "dartdev_options.cc", + "dartdev_options.h", + "gzip.cc", + "gzip.h", + "loader.cc", + "loader.h", + "snapshot_empty.cc", + ] + } +} else { + copy("dart") { + deps = [ ":dartvm" ] + sources = [ "$root_build_dir/dartvm${executable_suffix}" ] + outputs = [ "$root_build_dir/dart${executable_suffix}" ] + } +} + # This flag is set in runtime/runtime_args.gni # The analyze_snapshot tool is only supported on 64 bit AOT builds running under # linux and android platforms @@ -1079,8 +1118,11 @@ if (build_analyze_snapshot) { extra_sources = [ "analyze_snapshot.cc", "builtin.cc", + "common_options.h", "loader.cc", "loader.h", + "main_options.cc", + "main_options.h", ] if (use_product_mode) { @@ -1206,7 +1248,7 @@ executable("run_vm_tests") { } shared_library("entrypoints_verification_test") { - deps = [ ":dart" ] + deps = [ ":dartvm" ] sources = [ "entrypoints_verification_test.cc" ] if (is_win) { sources += [ "dart_api_win.c" ] @@ -1215,13 +1257,13 @@ shared_library("entrypoints_verification_test") { } shared_library("ffi_test_dynamic_library") { - deps = [ ":dart" ] + deps = [ ":dartvm" ] sources = [ "ffi_test/ffi_test_dynamic_library.cc" ] include_dirs = [ ".." ] } shared_library("ffi_test_functions") { - deps = [ ":dart" ] + deps = [ ":dartvm" ] sources = [ # This file must be compiled in for dynamic linking. @@ -1259,8 +1301,11 @@ if (defined(is_linux) && is_linux && defined(is_asan) && is_asan && extra_sources = [ "../vm/libfuzzer/dart_libfuzzer.cc", "builtin.cc", + "common_options.h", "dfe.cc", "dfe.h", + "main_options.cc", + "main_options.h", ] if (!exclude_kernel_service) { extra_deps += [ ":dart_kernel_platform_cc" ] diff --git a/runtime/bin/common_options.h b/runtime/bin/common_options.h new file mode 100644 index 00000000000..d3756854bd6 --- /dev/null +++ b/runtime/bin/common_options.h @@ -0,0 +1,173 @@ +// Copyright (c) 2025, 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. + +#ifndef RUNTIME_BIN_COMMON_OPTIONS_H_ +#define RUNTIME_BIN_COMMON_OPTIONS_H_ + +#include "include/dart_api.h" +#include "platform/assert.h" +#include "platform/globals.h" +#include "platform/syslog.h" + +namespace dart { +namespace bin { + +static void _PrintVersion() { + Syslog::Print("Dart SDK version: %s\n", Dart_VersionString()); +} + +// clang-format off +static void _PrintUsage() { + Syslog::Print( + "Usage: dart [] []\n" + "\n" + "Executes the Dart script with " + "the given list of .\n" + "\n"); +} + +static void _PrintNonVerboseUsage() { + Syslog::Print( +"Common VM flags:\n" +#if !defined(PRODUCT) +"--enable-asserts\n" +" Enable assert statements.\n" +#endif // !defined(PRODUCT) +"--help or -h\n" +" Display this message (add -v or --verbose for information about\n" +" all VM options).\n" +"--packages=\n" +" Where to find a package spec file.\n" +"--define== or -D=\n" +" Define an environment declaration. To specify multiple declarations,\n" +" use multiple instances of this option.\n" +#if !defined(PRODUCT) +"--observe[=[/]]\n" +" The observe flag is a convenience flag used to run a program with a\n" +" set of options which are often useful for debugging under Dart DevTools.\n" +" These options are currently:\n" +" --enable-vm-service[=[/]]\n" +" --serve-devtools\n" +" --pause-isolates-on-exit\n" +" --pause-isolates-on-unhandled-exceptions\n" +" --warn-on-pause-with-no-debugger\n" +" --timeline-streams=\"Compiler, Dart, GC, Microtask\"\n" +" This set is subject to change.\n" +" Please see these options (--help --verbose) for further documentation.\n" +"--write-service-info=\n" +" Outputs information necessary to connect to the VM service to the\n" +" specified file in JSON format. Useful for clients which are unable to\n" +" listen to stdout for the Dart VM service listening message.\n" +#endif // !defined(PRODUCT) +"--snapshot-kind=\n" +"--snapshot=\n" +" These snapshot options are used to generate a snapshot of the loaded\n" +" Dart script:\n" +" controls the kind of snapshot, it could be\n" +" kernel(default) or app-jit\n" +" specifies the file into which the snapshot is written\n" +"--version\n" +" Print the SDK version.\n"); +} + +static void _PrintVerboseUsage() { + Syslog::Print( +"Supported options:\n" +#if !defined(PRODUCT) +"--enable-asserts\n" +" Enable assert statements.\n" +#endif // !defined(PRODUCT) +"--help or -h\n" +" Display this message (add -v or --verbose for information about\n" +" all VM options).\n" +"--packages=\n" +" Where to find a package spec file.\n" +"--define== or -D=\n" +" Define an environment declaration. To specify multiple declarations,\n" +" use multiple instances of this option.\n" +#if !defined(PRODUCT) +"--observe[=[/]]\n" +" The observe flag is a convenience flag used to run a program with a\n" +" set of options which are often useful for debugging under Dart DevTools.\n" +" These options are currently:\n" +" --enable-vm-service[=[/]]\n" +" --serve-devtools\n" +" --pause-isolates-on-exit\n" +" --pause-isolates-on-unhandled-exceptions\n" +" --warn-on-pause-with-no-debugger\n" +" --timeline-streams=\"Compiler, Dart, GC, Microtask\"\n" +" This set is subject to change.\n" +" Please see these options for further documentation.\n" +"--profile-microtasks\n" +" Record information about each microtask. Information about completed\n" +" microtasks will be written to the \"Microtask\" timeline stream.\n" +#endif // !defined(PRODUCT) +"--version\n" +" Print the VM version.\n" +"\n" +"--trace-loading\n" +" enables tracing of library and script loading\n" +"\n" +#if !defined(PRODUCT) +"--enable-vm-service[=[/]]\n" +" Enables the VM service and listens on specified port for connections\n" +" (default port number is 8181, default bind address is localhost).\n" +"\n" +"--disable-service-auth-codes\n" +" Disables the requirement for an authentication code to communicate with\n" +" the VM service. Authentication codes help protect against CSRF attacks,\n" +" so it is not recommended to disable them unless behind a firewall on a\n" +" secure device.\n" +"\n" +"--enable-service-port-fallback\n" +" When the VM service is told to bind to a particular port, fallback to 0 if\n" +" it fails to bind instead of failing to start.\n" +"\n" +#endif // !defined(PRODUCT) +"--root-certs-file=\n" +" The path to a file containing the trusted root certificates to use for\n" +" secure socket connections.\n" +"--root-certs-cache=\n" +" The path to a cache directory containing the trusted root certificates to\n" +" use for secure socket connections.\n" +#if defined(DART_HOST_OS_LINUX) || \ + defined(DART_HOST_OS_ANDROID) || \ + defined(DART_HOST_OS_FUCHSIA) +"--namespace=\n" +" The path to a directory that dart:io calls will treat as the root of the\n" +" filesystem.\n" +#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) +"\n" +"The following options are only used for VM development and may\n" +"be changed in any future version:\n"); + const char* print_flags = "--print_flags"; + char* error = Dart_SetVMFlags(1, &print_flags); + ASSERT(error == nullptr); +} +// clang-format on + +// 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; +} + +} // namespace bin +} // namespace dart + +#endif // RUNTIME_BIN_COMMON_OPTIONS_H_ diff --git a/runtime/bin/dartdev.cc b/runtime/bin/dartdev.cc new file mode 100644 index 00000000000..aa94be8d04c --- /dev/null +++ b/runtime/bin/dartdev.cc @@ -0,0 +1,1137 @@ +// Copyright (c) 2025, 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. + +#include +#include +#include + +#include +#include + +#include "bin/builtin.h" +#include "bin/console.h" +#include "bin/crashpad.h" +#include "bin/dartdev_options.h" +#include "bin/dartutils.h" +#include "bin/error_exit.h" +#include "bin/eventhandler.h" +#include "bin/exe_utils.h" +#include "bin/file.h" +#include "bin/gzip.h" +#include "bin/isolate_data.h" +#include "bin/loader.h" +#include "bin/lockers.h" +#include "bin/platform.h" +#include "bin/process.h" +#include "bin/snapshot_utils.h" +#include "bin/thread.h" +#include "bin/utils.h" +#if defined(DART_HOST_OS_WINDOWS) +#include "bin/utils_win.h" +#endif +#include "bin/vmservice_impl.h" +#include "include/bin/dart_io_api.h" +#include "include/dart_api.h" +#include "include/dart_embedder_api.h" +#include "include/dart_native_api.h" +#include "include/dart_tools_api.h" +#include "platform/assert.h" +#include "platform/globals.h" +#include "platform/growable_array.h" +#include "platform/hashmap.h" +#include "platform/syslog.h" +#include "platform/text_buffer.h" +#include "platform/utils.h" + +namespace dart { +namespace bin { + +#if defined(DART_PRECOMPILED_RUNTIME) +/** + * Global state used to control and store generation of application snapshots. + */ +static const uint8_t* ignore_vm_snapshot_data = nullptr; +static const uint8_t* ignore_vm_snapshot_instructions = nullptr; +static const uint8_t* app_isolate_snapshot_data = nullptr; +static const uint8_t* app_isolate_snapshot_instructions = nullptr; + +#define SAVE_ERROR_AND_RETURN(result) \ + if (Dart_IsError(result)) { \ + *error = Utils::StrDup(Dart_GetError(result)); \ + return false; \ + } + +#define SAVE_ERROR_AND_EXIT(result) \ + *error = Utils::StrDup(Dart_GetError(result)); \ + if (Dart_IsCompilationError(result)) { \ + *exit_code = kCompilationErrorExitCode; \ + } else if (Dart_IsApiError(result)) { \ + *exit_code = kApiErrorExitCode; \ + } else { \ + *exit_code = kErrorExitCode; \ + } \ + Dart_ExitScope(); \ + Dart_ShutdownIsolate(); \ + return nullptr; + +#define CHECK_RESULT(result) \ + if (Dart_IsError(result)) { \ + SAVE_ERROR_AND_EXIT(result); \ + } + +#define CHECK_RESULT_CLEANUP(result, cleanup) \ + if (Dart_IsError(result)) { \ + delete (cleanup); \ + SAVE_ERROR_AND_EXIT(result); \ + } + +#define DART_DEV_ISOLATE_NAME "dartdev" + +// Helper class to ensure we enter a new Dart Scope and exit it. +class DartScope { + public: + DartScope() { Dart_EnterScope(); } + ~DartScope() { Dart_ExitScope(); } +}; + +static Dart_Handle SetupCoreLibraries(Dart_Isolate isolate, + IsolateData* isolate_data, + bool is_isolate_group_start, + const char** resolved_packages_config) { + auto isolate_group_data = isolate_data->isolate_group_data(); + const auto packages_file = isolate_data->packages_file(); + const auto script_uri = isolate_group_data->script_url; + + Dart_Handle result; + + // Prepare builtin and other core libraries for use to resolve URIs. + // Set up various closures, e.g: printing, timers etc. + // Set up package configuration for URI resolution. + result = DartUtils::PrepareForScriptLoading(false, false, false); + if (Dart_IsError(result)) return result; + + // Setup packages config if specified. + result = DartUtils::SetupPackageConfig(packages_file); + if (Dart_IsError(result)) return result; + if (!Dart_IsNull(result) && resolved_packages_config != nullptr) { + result = Dart_StringToCString(result, resolved_packages_config); + if (Dart_IsError(result)) return result; + ASSERT(*resolved_packages_config != nullptr); + } + + result = Dart_SetEnvironmentCallback(DartUtils::EnvironmentCallback); + if (Dart_IsError(result)) return result; + + // Setup the native resolver as the snapshot does not carry it. + Builtin::SetNativeResolver(Builtin::kBuiltinLibrary); + Builtin::SetNativeResolver(Builtin::kIOLibrary); + Builtin::SetNativeResolver(Builtin::kCLILibrary); + VmService::SetNativeResolver(); + + const char* namespc = Options::namespc(); + result = + DartUtils::SetupIOLibrary(namespc, script_uri, Options::exit_disabled()); + if (Dart_IsError(result)) return result; + + return Dart_Null(); +} + +static bool OnIsolateInitialize(void** child_callback_data, char** error) { + Dart_Isolate isolate = Dart_CurrentIsolate(); + ASSERT(isolate != nullptr); + + auto isolate_group_data = + reinterpret_cast(Dart_CurrentIsolateGroupData()); + + auto isolate_data = new IsolateData(isolate_group_data); + *child_callback_data = isolate_data; + + DartScope scope; // Enter a new scope. + const auto script_uri = isolate_group_data->script_url; + const bool isolate_run_app_snapshot = + isolate_group_data->RunFromAppSnapshot(); + Dart_Handle result = SetupCoreLibraries(isolate, isolate_data, + /*group_start=*/false, + /*resolved_packages_config=*/nullptr); + SAVE_ERROR_AND_RETURN(result); + if (isolate_run_app_snapshot) { + result = Loader::InitForSnapshot(script_uri, isolate_data); + SAVE_ERROR_AND_RETURN(result); + } else { + result = DartUtils::ResolveScript(Dart_NewStringFromCString(script_uri)); + SAVE_ERROR_AND_RETURN(result); + + if (isolate_group_data->kernel_buffer() != nullptr) { + // Various core-library parts will send requests to the Loader to resolve + // relative URIs and perform other related tasks. We need Loader to be + // initialized for this to work because loading from Kernel binary + // bypasses normal source code loading paths that initialize it. + const char* resolved_script_uri = nullptr; + result = Dart_StringToCString(result, &resolved_script_uri); + SAVE_ERROR_AND_RETURN(result); + result = Loader::InitForSnapshot(resolved_script_uri, isolate_data); + SAVE_ERROR_AND_RETURN(result); + } + } + + return true; +} + +static Dart_Isolate IsolateSetupHelper(Dart_Isolate isolate, + bool is_dartdev_isolate, + const char* script_uri, + const char* packages_config, + bool isolate_run_app_snapshot, + Dart_IsolateFlags* flags, + char** error, + int* exit_code) { + Dart_EnterScope(); + + // Set up the library tag handler for the isolate group shared by all + // isolates in the group. + Dart_Handle result = Dart_SetLibraryTagHandler(Loader::LibraryTagHandler); + CHECK_RESULT(result); + result = Dart_SetDeferredLoadHandler(Loader::DeferredLoadHandler); + CHECK_RESULT(result); + + auto isolate_data = reinterpret_cast(Dart_IsolateData(isolate)); + + const char* resolved_packages_config = nullptr; + result = SetupCoreLibraries(isolate, isolate_data, + /*is_isolate_group_start=*/true, + &resolved_packages_config); + CHECK_RESULT(result); + + if (isolate_run_app_snapshot) { + Dart_Handle result = Loader::InitForSnapshot(script_uri, isolate_data); + CHECK_RESULT(result); + } else { + UNREACHABLE(); + } + + // Make the isolate runnable so that it is ready to handle messages. + Dart_ExitScope(); + Dart_ExitIsolate(); + *error = Dart_IsolateMakeRunnable(isolate); + if (*error != nullptr) { + Dart_EnterIsolate(isolate); + Dart_ShutdownIsolate(); + return nullptr; + } + + return isolate; +} + +// Returns newly created Isolate on success, nullptr on failure. +static Dart_Isolate CreateIsolateGroupAndSetupHelper( + bool is_dartdev_isolate, + const char* script_uri, + const char* name, + const char* packages_config, + Dart_IsolateFlags* flags, + void* callback_data, + char** error, + int* exit_code) { + int64_t start = Dart_TimelineGetMicros(); + ASSERT(script_uri != nullptr); + uint8_t* kernel_buffer = nullptr; + intptr_t kernel_buffer_size = 0; + AppSnapshot* app_snapshot = nullptr; + + const uint8_t* isolate_snapshot_data = nullptr; + const uint8_t* isolate_snapshot_instructions = nullptr; + if (is_dartdev_isolate) { + isolate_snapshot_data = app_isolate_snapshot_data; + isolate_snapshot_instructions = app_isolate_snapshot_instructions; + } else { + // AOT: All isolates need to be run from AOT compiled snapshots. + app_snapshot = Snapshot::TryReadAppSnapshot( + script_uri, /*force_load_from_memory*/ false, /*decode_uri*/ true); + if (app_snapshot == nullptr || !app_snapshot->IsAOT()) { + *error = Utils::SCreate( + "The uri(%s) provided to `Isolate.spawnUri()` does not " + "contain a valid AOT snapshot.", + script_uri); + return nullptr; + } + + app_snapshot->SetBuffers( + &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, + &isolate_snapshot_data, &isolate_snapshot_instructions); + } + + bool isolate_run_app_snapshot = true; + + auto isolate_group_data = new IsolateGroupData( + script_uri, /*asset_resolution_base=*/nullptr, packages_config, + app_snapshot, isolate_run_app_snapshot); + if (kernel_buffer != nullptr) { + isolate_group_data->SetKernelBufferNewlyOwned(kernel_buffer, + kernel_buffer_size); + } + + Dart_Isolate isolate = nullptr; + + IsolateData* isolate_data = nullptr; + isolate_data = new IsolateData(isolate_group_data); + isolate = Dart_CreateIsolateGroup(script_uri, name, isolate_snapshot_data, + isolate_snapshot_instructions, flags, + isolate_group_data, isolate_data, error); + Dart_Isolate created_isolate = nullptr; + if (isolate == nullptr) { + delete isolate_data; + delete isolate_group_data; + } else { + created_isolate = IsolateSetupHelper( + isolate, is_dartdev_isolate, script_uri, packages_config, + isolate_run_app_snapshot, flags, error, exit_code); + } + int64_t end = Dart_TimelineGetMicros(); + Dart_RecordTimelineEvent("CreateIsolateGroupAndSetupHelper", start, end, + /*flow_id_count=*/0, nullptr, + Dart_Timeline_Event_Duration, + /*argument_count=*/0, nullptr, nullptr); + return created_isolate; +} + +#undef CHECK_RESULT + +static Dart_Isolate CreateIsolateGroupAndSetup(const char* script_uri, + const char* main, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + void* callback_data, + char** error) { + // The VM should never call the isolate helper with a nullptr flags. + ASSERT(flags != nullptr); + ASSERT(flags->version == DART_FLAGS_CURRENT_VERSION); + ASSERT(package_root == nullptr); + + if (error != nullptr) { + *error = nullptr; + } + +#if defined(DART_HOST_OS_LINUX) + // This would also be true in Linux, except that Google3 overrides the default + // ELF interpreter to one that apparently doesn't create proper mappings. + flags->snapshot_is_dontneed_safe = false; +#else + flags->snapshot_is_dontneed_safe = true; +#endif + + int exit_code = 0; + + if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { + // We do not start the service isolate in the Dart CLI mode of execution. + // If profiling or debugging of the individual tools is desired it is + // possible to do that using the 'dartaotruntime' or 'dartvm' executables. + return nullptr; + } + + bool is_dartdev_isolate = false; + return CreateIsolateGroupAndSetupHelper(is_dartdev_isolate, script_uri, main, + package_config, flags, callback_data, + error, &exit_code); +} + +static void OnIsolateShutdown(void* isolate_group_data, void* isolate_data) { + Dart_EnterScope(); + Dart_Handle sticky_error = Dart_GetStickyError(); + if (!Dart_IsNull(sticky_error) && !Dart_IsFatalError(sticky_error)) { + Syslog::PrintErr("%s\n", Dart_GetError(sticky_error)); + } + Dart_ExitScope(); +} + +static void DeleteIsolateData(void* isolate_group_data, void* callback_data) { + auto isolate_data = reinterpret_cast(callback_data); + delete isolate_data; +} + +static void DeleteIsolateGroupData(void* callback_data) { + auto isolate_group_data = reinterpret_cast(callback_data); + delete isolate_group_data; +} + +static constexpr const char* kStdoutStreamId = "Stdout"; +static constexpr const char* kStderrStreamId = "Stderr"; + +static bool ServiceStreamListenCallback(const char* stream_id) { + if (strcmp(stream_id, kStdoutStreamId) == 0) { + SetCaptureStdout(true); + return true; + } else if (strcmp(stream_id, kStderrStreamId) == 0) { + SetCaptureStderr(true); + return true; + } + return false; +} + +static void ServiceStreamCancelCallback(const char* stream_id) { + if (strcmp(stream_id, kStdoutStreamId) == 0) { + SetCaptureStdout(false); + } else if (strcmp(stream_id, kStderrStreamId) == 0) { + SetCaptureStderr(false); + } else { + UNREACHABLE(); + } +} + +static bool FileModifiedCallback(const char* url, int64_t since) { + auto path = File::UriToPath(url); + if (path == nullptr) { + // If it isn't a file on local disk, we don't know if it has been + // modified. + return true; + } + int64_t data[File::kStatSize]; + File::Stat(nullptr, path.get(), data); + if (data[File::kType] == File::kDoesNotExist) { + return true; + } + return data[File::kModifiedTime] > since; +} + +static void EmbedderInformationCallback(Dart_EmbedderInformation* info) { + info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION; + info->name = "Dart VM"; + Process::GetRSSInformation(&(info->max_rss), &(info->current_rss)); +} + +#define CHECK_RESULT(result) \ + if (Dart_IsError(result)) { \ + const int exit_code = Dart_IsCompilationError(result) \ + ? kCompilationErrorExitCode \ + : kErrorExitCode; \ + ErrorExit(exit_code, "%s\n", Dart_GetError(result)); \ + } + +static bool CheckForInvalidPath(const char* path) { + // TODO(zichangguo): "\\?\" is a prefix for paths on Windows. + // Arguments passed are parsed as an URI. "\\?\" causes problems as a part + // of URIs. This is a temporary workaround to prevent VM from crashing. + // Issue: https://github.com/dart-lang/sdk/issues/42779 + if (strncmp(path, R"(\\?\)", 4) == 0) { + Syslog::PrintErr(R"(\\?\ prefix is not supported)"); + return false; + } + return true; +} + +class DartDev { + public: + // Return codes from dartdev. + // Note: keep in sync with pkg/dartdev/lib/vm_interop_handler.dart + typedef enum { + DartDev_Result_Unknown = -1, + DartDev_Result_Run = 1, + DartDev_Result_RunExec = 2, + DartDev_Result_Exit = 3, + } DartDev_Result; + + static CStringUniquePtr ResolvedDartVmPath() { +#if defined(DART_HOST_OS_WINDOWS) + const char* filename = "dartvm.exe"; +#else + const char* filename = "dartvm"; +#endif // defined(DART_HOST_OS_WINDOWS) + auto try_resolve_path = [&](CStringUniquePtr dir_prefix) { + // |dir_prefix| includes the last path separator. + // Assume 'dartvm' and 'dart' executables are in the same directory. + char* dartvm_path = Utils::SCreate("%s%s", dir_prefix.get(), filename); + if (File::Exists(nullptr, dartvm_path)) { + return CStringUniquePtr(dartvm_path); + } + free(dartvm_path); + return CStringUniquePtr(nullptr); + }; + + auto result = + try_resolve_path(EXEUtils::GetDirectoryPrefixFromResolvedExeName()); + if (result == nullptr) { + result = + try_resolve_path(EXEUtils::GetDirectoryPrefixFromUnresolvedExeName()); + } + return result; + } + + static CStringUniquePtr ResolvedSnapshotPath() { + const char* filename = "dartdev_aot.dart.snapshot"; + + auto try_resolve_path = [&](CStringUniquePtr dir_prefix) { + // |dir_prefix| includes the last path separator. + // First assume we're in dart-sdk/bin. + char* snapshot_path = + Utils::SCreate("%ssnapshots/%s", dir_prefix.get(), filename); + if (File::Exists(nullptr, snapshot_path)) { + return CStringUniquePtr(snapshot_path); + } + free(snapshot_path); + + // If we're not in dart-sdk/bin, we might be in one of the $SDK/out*/ + // directories, Try to use a snapshot from that directory. + snapshot_path = Utils::SCreate("%s%s", dir_prefix.get(), filename); + if (File::Exists(nullptr, snapshot_path)) { + return CStringUniquePtr(snapshot_path); + } + free(snapshot_path); + return CStringUniquePtr(nullptr); + }; + + auto result = + try_resolve_path(EXEUtils::GetDirectoryPrefixFromResolvedExeName()); + if (result == nullptr) { + result = + try_resolve_path(EXEUtils::GetDirectoryPrefixFromUnresolvedExeName()); + } + return result; + } + + // Invoke the Dart VM directly bypassing dartdev. + static void InvokeDartVM(int argc, char** argv, bool argv_converted) { + auto dartvm_path = DartDev::ResolvedDartVmPath(); + char* exec_name = dartvm_path.get(); + if (exec_name == nullptr || !CheckForInvalidPath(exec_name)) { + // Free environment if any. + Syslog::PrintErr("Unable to locate the Dart VM executable"); + Options::Cleanup(); + Platform::Exit(kErrorExitCode); + } + int idx = 0; + char err_msg[256]; + err_msg[0] = '\0'; + intptr_t num_args = argc + 2; + char** exec_argv = new char*[num_args]; + exec_argv[idx] = Utils::StrDup(exec_name); + const size_t kPathBufSize = PATH_MAX + 1; + char dart_path[kPathBufSize]; + Platform::ResolveExecutablePathInto(dart_path, kPathBufSize); + idx += 1; + exec_argv[idx] = Utils::SCreate("--executable_name=%s", dart_path); + for (intptr_t i = 1; i < argc; ++i) { +#if defined(DART_HOST_OS_WINDOWS) + exec_argv[i + idx] = StringUtilsWin::ArgumentEscape(argv[i]); +#else + exec_argv[i + idx] = Utils::StrDup(argv[i]); +#endif // defined(DART_HOST_OS_WINDOWS) + } + // Null terminate the exec_argv array. + exec_argv[num_args - 1] = nullptr; + + // Exec the script to be run and pass the arguments. + int ret = + Process::Exec(nullptr, exec_name, const_cast(exec_argv), + (num_args - 1), nullptr, err_msg, sizeof(err_msg)); + // Exec process done. + if (ret != 0) { + Syslog::PrintErr("%s\n", err_msg); + } + // Free copied argument strings if converted. + if (argv_converted) { + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + } + for (int i = 0; i < argc; i++) { + free(exec_argv[i]); + } + delete[] exec_argv; + + // Free environment if any. + Options::Cleanup(); + Platform::Exit(ret); + } + + // Process the DartDev_Result_Run result message produced by + // VmInteropHandler in pkg/dartdev/lib/src/vm_interop_handler.dart + static void RunResultCallback(Dart_CObject* message) { + result_ = DartDev_Result_Run; + ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); + auto item2 = GetArrayItem(message, 2); + + ASSERT(item2->type == Dart_CObject_kString || + item2->type == Dart_CObject_kNull); + + auto item3 = GetArrayItem(message, 3); + + // ignoring mark_main_isolate_as_system_isolate + ASSERT(item3->type == Dart_CObject_kBool); + + script_name_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); + + ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray); + Dart_CObject* args = GetArrayItem(message, 4); + argc_ = args->value.as_array.length; + Dart_CObject** dart_args = args->value.as_array.values; + + auto deleter = [](char** args) { + for (intptr_t i = 0; i < argc_; ++i) { + free(args[i]); + } + delete[] args; + }; + argv_ = + std::unique_ptr(new char*[argc_], deleter); + for (intptr_t i = 0; i < argc_; ++i) { + argv_[i] = Utils::StrDup(dart_args[i]->value.as_string); + } + } + + // Process the DartDev_Result_RunExec result message produced by + // VmInteropHandler in pkg/dartdev/lib/src/vm_interop_handler.dart + static void RunExecResultCallback(Dart_CObject* message) { + result_ = DartDev_Result_RunExec; + auto dartvm_path = DartDev::ResolvedDartVmPath(); + char* exec_name = dartvm_path.get(); + if (exec_name == nullptr || !CheckForInvalidPath(exec_name)) { + Syslog::PrintErr("Unable to locate the Dart VM executable"); + Platform::Exit(kErrorExitCode); + } + ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); + auto item2 = GetArrayItem(message, 2); + + ASSERT(item2->type == Dart_CObject_kString || + item2->type == Dart_CObject_kNull); + + package_config_override_ = nullptr; + + if (item2->type == Dart_CObject_kString) { + package_config_override_ = Utils::StrDup(item2->value.as_string); + } + + intptr_t num_vm_options = dart_vm_options_->count(); + const char** vm_options = dart_vm_options_->arguments(); + ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray); + Dart_CObject* args = GetArrayItem(message, 4); + intptr_t argc = args->value.as_array.length; + Dart_CObject** dart_args = args->value.as_array.values; + auto item3 = GetArrayItem(message, 3); + ASSERT(item3->type == Dart_CObject_kBool); + const bool mark_main_isolate_as_system_isolate = item3->value.as_bool; + auto deleter = [](char** args) { + for (intptr_t i = 0; i < argc_; ++i) { + free(args[i]); + } + delete[] args; + }; + // Total count of arguments to be passed to the script being execed. + if (mark_main_isolate_as_system_isolate) { + argc_ = argc + num_vm_options + 4; + } else { + argc_ = argc + num_vm_options + 3; + } + + // Array of arguments to be passed to the script being execed. + argv_ = std::unique_ptr(new char*[argc_ + 1], + deleter); + + intptr_t idx = 0; + // Copy in name of the executable to run (should be the dart vm). + script_name_ = Utils::StrDup(exec_name); + argv_[idx++] = script_name_; + // Copy in VM options if any. + // Copy in any vm options that need to be passed to the execed process. + for (intptr_t i = 0; i < num_vm_options; ++i) { + argv_[i + idx] = Utils::StrDup(vm_options[i]); + } + idx += num_vm_options; + { + const size_t kPathBufSize = PATH_MAX + 1; + char dart_path[kPathBufSize]; + Platform::ResolveExecutablePathInto(dart_path, kPathBufSize); + argv_[idx++] = Utils::SCreate("--executable_name=%s", dart_path); + } + if (mark_main_isolate_as_system_isolate) { + argv_[idx++] = Utils::StrDup("--mark_main_isolate_as_system_isolate"); + } + // Copy in name of the script to run. + argv_[idx++] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); + // Copy in the dart options that need to be passed to the script. + for (intptr_t i = 0; i < argc; ++i) { + argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string); + } + // Null terminate the argv array. + argv_[argc + idx] = nullptr; + } + + // Process the DartDev_Result_Exit result message produced by + // VmInteropHandler in pkg/dartdev/lib/src/vm_interop_handler.dart + static void ExitResultCallback(Dart_CObject* message) { + ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32); + int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32; + + // If we're given a non-zero exit code, DartDev is signaling for us to + // shutdown. + Process::SetGlobalExitCode(dartdev_exit_code); + + // If DartDev hasn't signaled for us to do anything else, we can assume + // there's nothing else for the VM to run and that we can exit. + if (result_ == DartDev_Result_Unknown || dartdev_exit_code != 0) { + result_ = DartDev_Result_Exit; + } + // Notify the dartdev runner that it can proceed with processing + // the result from execution of dartdev. + { + MonitorLocker locker(monitor_); + locker.Notify(); + } + } + + // Callback that processes the result from execution of dartdev + // + static void ResultCallback(Dart_Port dest_port_id, Dart_CObject* message) { + // These messages are produced in + // pkg/dartdev/lib/src/vm_interop_handler.dart. + ASSERT(message->type == Dart_CObject_kArray); + int32_t type = GetArrayItem(message, 0)->value.as_int32; + switch (type) { + case DartDev_Result_Run: { + RunResultCallback(message); + break; + } + case DartDev_Result_RunExec: { + RunExecResultCallback(message); + break; + } + case DartDev_Result_Exit: { + ExitResultCallback(message); + break; + } + default: + UNREACHABLE(); + } + } + + static void RunDartDev(const char* script_name, + CommandLineOptions* dart_vm_options, + CommandLineOptions* dart_options) { + ASSERT(script_name != nullptr); + const char* base_name = strrchr(script_name, '/'); + if (base_name == nullptr) { + base_name = script_name; + } else { + base_name++; // Skip '/'. + } + const intptr_t kMaxNameLength = 64; + char name[kMaxNameLength]; + Utils::SNPrint(name, kMaxNameLength, "dart:%s", base_name); + Platform::SetProcessName(name); + + dart_vm_options_ = dart_vm_options; + + // Call CreateIsolateGroupAndSetup which creates an isolate and loads up + // the specified application script. + Dart_IsolateFlags flags; + Dart_IsolateFlagsInitialize(&flags); +#if defined(DART_HOST_OS_LINUX) + // This would also be true in Linux, except that Google3 overrides the + // default ELF interpreter to one that apparently doesn't create proper + // mappings. + flags.snapshot_is_dontneed_safe = false; +#else + flags.snapshot_is_dontneed_safe = true; +#endif + flags.is_system_isolate = true; + SpawnIsolate(script_name, "_startIsolate", + /*is_dartdev_isolate*/ true, + /*package_config_override*/ nullptr, &flags, dart_options); + + // Wait for the callbacks on the native port to be done before we + // proceed. + { + MonitorLocker locker(monitor_); + while (result_ == DartDev_Result_Unknown) { + locker.Wait(); + } + } + + /* Process the result returned by the dartdev isolate. */ + switch (result_) { + case DartDev_Result_Run: { + flags.is_system_isolate = false; + SpawnIsolate(script_name_, "_startMainIsolate", + /*is_dartdev_isolate*/ false, package_config_override_, + &flags, dart_options); + free(script_name_); + free(package_config_override_); + break; + } + case DartDev_Result_RunExec: { + RunExec(script_name_); + break; + } + case DartDev_Result_Exit: { + // Nothing to do here, the process will terminate with the exit code + // set earlier. + break; + } + default: + UNREACHABLE(); + } + } + + private: + static void SpawnIsolate(const char* script_name, + const char* entry_point, + bool is_dartdev_isolate, + const char* package_config_override, + Dart_IsolateFlags* flags, + CommandLineOptions* dart_options) { + // Start a new Isolate with the specified AOT snapshot. + int exit_code = 0; + char* error = nullptr; + + Dart_Isolate isolate = CreateIsolateGroupAndSetupHelper( + is_dartdev_isolate, script_name, "main", + Options::packages_file() == nullptr ? package_config_override + : Options::packages_file(), + flags, nullptr /* callback_data */, &error, &exit_code); + + if (isolate == nullptr) { + if (error != nullptr) { + Syslog::PrintErr("Isolate spawning failed: %s\n", error); + free(error); + } + error = nullptr; + Process::TerminateExitCodeHandler(); + error = Dart_Cleanup(); + if (error != nullptr) { + Syslog::PrintErr("Dart_Cleanup failed: %s\n", error); + free(error); + } + dart::embedder::Cleanup(); + Platform::Exit((exit_code != 0) ? exit_code : kErrorExitCode); + } + + Dart_EnterIsolate(isolate); + ASSERT(isolate == Dart_CurrentIsolate()); + ASSERT(isolate != nullptr); + Dart_Handle result; + + Dart_EnterScope(); + + Dart_Handle send_port = Dart_Null(); + Dart_Port send_port_id = ILLEGAL_PORT; + if (is_dartdev_isolate) { + // Create a SendPort that DartDev can use to communicate its results over. + send_port_id = + Dart_NewNativePort(DART_DEV_ISOLATE_NAME, ResultCallback, false); + ASSERT(send_port_id != ILLEGAL_PORT); + send_port = Dart_NewSendPort(send_port_id); + CHECK_RESULT(send_port); + } + + // Lookup the library of the root script. + Dart_Handle root_lib = Dart_RootLibrary(); + + if (Dart_IsNull(root_lib)) { + ErrorExit(kErrorExitCode, "Unable to find root library for '%s'\n", + script_name); + } + + // Create a closure for the main entry point which is in the exported + // namespace of the root library or invoke a getter of the same name + // in the exported namespace and return the resulting closure. + Dart_Handle main_closure = + Dart_GetField(root_lib, Dart_NewStringFromCString("main")); + CHECK_RESULT(main_closure); + if (!Dart_IsClosure(main_closure)) { + ErrorExit(kErrorExitCode, "Unable to find 'main' in root library '%s'\n", + script_name); + } + + Dart_Handle isolate_lib = + Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate")); + if (is_dartdev_isolate) { + const intptr_t kNumIsolateArgs = 4; + Dart_Handle isolate_args[kNumIsolateArgs]; + isolate_args[0] = main_closure; // entryPoint + isolate_args[1] = dart_options->CreateRuntimeOptions(); // args + isolate_args[2] = send_port; + isolate_args[3] = Dart_True(); // isSpawnUri + result = Dart_Invoke(isolate_lib, Dart_NewStringFromCString(entry_point), + kNumIsolateArgs, isolate_args); + } else { + // Call _startIsolate in the isolate library to enable dispatching the + // initial startup message. + dart_options->Reset(); + dart_options->AddArguments(const_cast(argv_.get()), argc_); + const intptr_t kNumIsolateArgs = 2; + Dart_Handle isolate_args[kNumIsolateArgs]; + isolate_args[0] = main_closure; // entryPoint + isolate_args[1] = dart_options->CreateRuntimeOptions(); // args + result = Dart_Invoke(isolate_lib, Dart_NewStringFromCString(entry_point), + kNumIsolateArgs, isolate_args); + } + CHECK_RESULT(result); + + // Keep handling messages until the last active receive port is closed. + result = Dart_RunLoop(); + CHECK_RESULT(result); + + if (is_dartdev_isolate) { + // DartDev is done processing the command. Close the native port, this + // will ensure we exit from the event handler loop and exit dartdev + // isolate. + Dart_CloseNativePort(send_port_id); + } + Dart_ExitScope(); + + // Shutdown the isolate. + Dart_ShutdownIsolate(); + } + + static void RunExec(const char* script_name) { + // Exec the JIT Dart VM with the specified script file. + char err_msg[256]; + err_msg[0] = '\0'; + int ret = Process::Exec(nullptr, script_name, + const_cast(argv_.get()), argc_, + nullptr, err_msg, sizeof(err_msg)); + if (ret != 0) { + Syslog::PrintErr("%s.\n", err_msg); + Process::SetGlobalExitCode(ret); + } else { + Process::SetGlobalExitCode(ret); + } + } + + static Dart_CObject* GetArrayItem(Dart_CObject* message, intptr_t index) { + return message->value.as_array.values[index]; + } + + static Monitor* monitor_; + static DartDev_Result result_; + static char* script_name_; + static char* package_config_override_; + static CommandLineOptions* dart_vm_options_; + static std::unique_ptr argv_; + static intptr_t argc_; +}; + +Monitor* DartDev::monitor_ = new Monitor(); +DartDev::DartDev_Result DartDev::result_ = DartDev::DartDev_Result_Unknown; +char* DartDev::script_name_ = nullptr; +char* DartDev::package_config_override_ = nullptr; +CommandLineOptions* DartDev::dart_vm_options_ = nullptr; +std::unique_ptr DartDev::argv_ = + std::unique_ptr(nullptr, [](char**) {}); +intptr_t DartDev::argc_ = 0; + +#undef CHECK_RESULT + +static void FreeConvertedArgs(int argc, char** argv, bool argv_converted) { + // Free copied argument strings if converted. + if (argv_converted) { + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + } +} + +void main(int argc, char** argv) { +#if !defined(DART_HOST_OS_WINDOWS) + // Very early so any crashes during startup can also be symbolized. + EXEUtils::LoadDartProfilerSymbols(argv[0]); +#endif + + const int EXTRA_VM_ARGUMENTS = 10; + // An invocation line is as follows + // dart + + // vm-opts : Set of VM options that need to be passed to the AOT runtime + // running in this dartdev process. + CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS); + + // vm-opts-to-cmd : Set of VM options that need to be passed to the runtime + // that executes the dartdev command. + CommandLineOptions dart_vm_options(argc + EXTRA_VM_ARGUMENTS); + + // dart-options : Set of options to be passed to the Dart program + CommandLineOptions dart_options(argc + EXTRA_VM_ARGUMENTS); + + // Perform platform specific initialization. + if (!Platform::Initialize()) { + Syslog::PrintErr("Initialization failed\n"); + Platform::Exit(kErrorExitCode); + } + + // Save the console state so we can restore it at shutdown. + Console::SaveConfig(); + + // On Windows, the argv strings are code page encoded and not + // utf8. We need to convert them to utf8. + bool argv_converted = ShellUtils::GetUtf8Argv(argc, argv); + + // When running from the command line we assume that we are optimizing for + // throughput, and therefore use a larger new gen semi space size and a faster + // new gen growth factor unless others have been specified. + if (kWordSize <= 4) { + vm_options.AddArgument("--new_gen_semi_max_size=16"); + } else { + vm_options.AddArgument("--new_gen_semi_max_size=32"); + } + vm_options.AddArgument("--new_gen_growth_factor=4"); + + // Parse command line arguments. + bool skip_dartdev; + bool success = Options::ParseDartDevArguments( + argc, argv, &vm_options, &dart_vm_options, &dart_options, &skip_dartdev); + if (!success) { + FreeConvertedArgs(argc, argv, argv_converted); + if (Options::help_option()) { + Options::PrintUsage(); + Platform::Exit(0); + } else if (Options::version_option()) { + Options::PrintVersion(); + Platform::Exit(0); + } else { + Options::PrintUsage(); + Platform::Exit(kErrorExitCode); + } + } + if (skip_dartdev) { + // We are skipping execution of dartdev as no Dart command line has + // been specified, in this case we directly exec the Dart JIT VM to + // run the specified script and pass in the original command line + // arguments. + DartDev::InvokeDartVM(argc, argv, argv_converted); + } + + DartUtils::SetEnvironment(Options::environment()); + + if (Options::suppress_core_dump()) { + Platform::SetCoreDumpResourceLimit(0); + } + + Loader::InitOnce(); + + // Setup script_name to point to the dartdev AOT snapshot. + auto dartdev_path = DartDev::ResolvedSnapshotPath(); + char* script_name = dartdev_path.get(); + if (script_name == nullptr || !CheckForInvalidPath(script_name)) { + Syslog::PrintErr("Unable to find AOT snapshot for dartdev\n"); + FreeConvertedArgs(argc, argv, argv_converted); + Platform::Exit(kErrorExitCode); + } + AppSnapshot* app_snapshot = Snapshot::TryReadAppSnapshot( + script_name, /*force_load_from_memory*/ false, /*decode_uri*/ false); + if (app_snapshot == nullptr || !app_snapshot->IsAOT()) { + Syslog::PrintErr("%s is not an AOT snapshot\n", script_name); + FreeConvertedArgs(argc, argv, argv_converted); + if (app_snapshot != nullptr) { + delete app_snapshot; + } + Platform::Exit(kErrorExitCode); + } + app_snapshot->SetBuffers( + &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, + &app_isolate_snapshot_data, &app_isolate_snapshot_instructions); + + vm_options.AddArgument("--precompilation"); + + char* error = nullptr; + if (!dart::embedder::InitOnce(&error)) { + Syslog::PrintErr("dartdev embedder initialization failed: %s\n", error); + free(error); + FreeConvertedArgs(argc, argv, argv_converted); + delete app_snapshot; + Platform::Exit(kErrorExitCode); + } + + error = Dart_SetVMFlags(vm_options.count(), vm_options.arguments()); + if (error != nullptr) { + for (int i = 0; i < argc; i++) { + Syslog::PrintErr("argv[%d]: %s\n", i, argv[i]); + } + Syslog::PrintErr("Setting VM flags failed: %s\n", error); + free(error); + FreeConvertedArgs(argc, argv, argv_converted); + delete app_snapshot; + Platform::Exit(kErrorExitCode); + } + + // Initialize the Dart VM. + Dart_InitializeParams init_params; + memset(&init_params, 0, sizeof(init_params)); + init_params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; + init_params.vm_snapshot_data = ignore_vm_snapshot_data; + init_params.vm_snapshot_instructions = ignore_vm_snapshot_instructions; + init_params.create_group = CreateIsolateGroupAndSetup; + init_params.initialize_isolate = OnIsolateInitialize; + init_params.shutdown_isolate = OnIsolateShutdown; + init_params.cleanup_isolate = DeleteIsolateData; + init_params.cleanup_group = DeleteIsolateGroupData; + init_params.file_open = DartUtils::OpenFile; + init_params.file_read = DartUtils::ReadFile; + init_params.file_write = DartUtils::WriteFile; + init_params.file_close = DartUtils::CloseFile; + init_params.entropy_source = DartUtils::EntropySource; + init_params.start_kernel_isolate = false; +#if defined(DART_HOST_OS_FUCHSIA) + init_params.vmex_resource = ZX_HANDLE_INVALID; +#endif + + error = Dart_Initialize(&init_params); + if (error != nullptr) { + dart::embedder::Cleanup(); + Syslog::PrintErr("VM initialization failed: %s\n", error); + free(error); + FreeConvertedArgs(argc, argv, argv_converted); + delete app_snapshot; + Platform::Exit(kErrorExitCode); + } + + Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, + &ServiceStreamCancelCallback); + Dart_SetFileModifiedCallback(&FileModifiedCallback); + Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); + + // Run dartdev as the main isolate. + // The result from the running the dartdev isolate could result in one of + // these options + // - Exit the process due to some command parsing errors + // - Run the Dart script in a JIT mode by execing the JIT runtime + // - Run the Dart AOT snapshot by creating a new Isolate + DartDev::RunDartDev(script_name, &dart_vm_options, &dart_options); + + // Terminate process exit-code handler. + Process::TerminateExitCodeHandler(); + + error = Dart_Cleanup(); + if (error != nullptr) { + Syslog::PrintErr("VM cleanup failed: %s\n", error); + free(error); + } + const intptr_t global_exit_code = Process::GlobalExitCode(); + dart::embedder::Cleanup(); + + delete app_snapshot; + + // Free copied argument strings if converted. + if (argv_converted) { + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + } + + // Free environment if any. + Options::Cleanup(); + + Platform::Exit(global_exit_code); +} +#else +void main(int argc, char** argv) { + Platform::Exit(kErrorExitCode); +} +#endif // defined(DART_PRECOMPILED_RUNTIME) + +} // namespace bin +} // namespace dart + +int main(int argc, char** argv) { + dart::bin::main(argc, argv); + UNREACHABLE(); +} diff --git a/runtime/bin/dartdev_isolate.cc b/runtime/bin/dartdev_isolate.cc deleted file mode 100644 index 5602d6a18e2..00000000000 --- a/runtime/bin/dartdev_isolate.cc +++ /dev/null @@ -1,415 +0,0 @@ -// 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. - -#include "bin/dartdev_isolate.h" - -#if !defined(DART_PRECOMPILED_RUNTIME) - -#include -#include - -#include "bin/directory.h" -#include "bin/error_exit.h" -#include "bin/exe_utils.h" -#include "bin/file.h" -#include "bin/lockers.h" -#include "bin/main_options.h" -#include "bin/platform.h" -#include "bin/process.h" -#include "include/dart_embedder_api.h" -#include "platform/utils.h" - -#define CHECK_RESULT(result) \ - if (Dart_IsError(result)) { \ - ProcessError(Dart_GetError(result), kErrorExitCode); \ - if (send_port_id != ILLEGAL_PORT) { \ - Dart_CloseNativePort(send_port_id); \ - } \ - Dart_ExitScope(); \ - Dart_ShutdownIsolate(); \ - return; \ - } - -namespace dart { -namespace bin { - -DartDevIsolate::DartDevRunner DartDevIsolate::runner_ = - DartDevIsolate::DartDevRunner(); -bool DartDevIsolate::should_run_dart_dev_ = false; -bool DartDevIsolate::print_usage_error_ = false; -CommandLineOptions* DartDevIsolate::vm_options_ = nullptr; -Monitor* DartDevIsolate::DartDevRunner::monitor_ = new Monitor(); -DartDevIsolate::DartDev_Result DartDevIsolate::DartDevRunner::result_ = - DartDevIsolate::DartDev_Result_Unknown; -char** DartDevIsolate::DartDevRunner::script_ = nullptr; -char** DartDevIsolate::DartDevRunner::package_config_override_ = nullptr; -std::unique_ptr - DartDevIsolate::DartDevRunner::argv_ = - std::unique_ptr(nullptr, [](char**) {}); -intptr_t DartDevIsolate::DartDevRunner::argc_ = 0; - -bool DartDevIsolate::ShouldParseCommand(const char* script_uri) { - // If script_uri is a known DartDev command, we should not try to run it. - // - // Otherwise if script_uri is not a file path or of a known URI scheme, we - // assume this is a mistyped DartDev command. - // - // This should be kept in sync with the commands in - // `pkg/dartdev/lib/dartdev.dart`. - return ( - (strcmp(script_uri, "analyze") == 0) || - (strcmp(script_uri, "compilation-server") == 0) || - (strcmp(script_uri, "build") == 0) || - (strcmp(script_uri, "compile") == 0) || - (strcmp(script_uri, "create") == 0) || - (strcmp(script_uri, "development-service") == 0) || - (strcmp(script_uri, "devtools") == 0) || - (strcmp(script_uri, "doc") == 0) || (strcmp(script_uri, "fix") == 0) || - (strcmp(script_uri, "format") == 0) || - (strcmp(script_uri, "info") == 0) || - (strcmp(script_uri, "mcp-server") == 0) || - (strcmp(script_uri, "pub") == 0) || (strcmp(script_uri, "run") == 0) || - (strcmp(script_uri, "test") == 0) || (strcmp(script_uri, "info") == 0) || - (strcmp(script_uri, "language-server") == 0) || - (strcmp(script_uri, "tooling-daemon") == 0) || - (!File::ExistsUri(nullptr, script_uri) && - (strncmp(script_uri, "http://", 7) != 0) && - (strncmp(script_uri, "https://", 8) != 0) && - (strncmp(script_uri, "file://", 7) != 0) && - (strncmp(script_uri, "package:", 8) != 0) && - (strncmp(script_uri, "google3://", 10) != 0))); -} - -bool DartDevIsolate::ShouldParseVMOptions(const char* command) { - // If command is 'run' or 'test' parse the VM options as we need to pass - // it down to the VM that executes these commands. - return (strcmp(command, "run") == 0) || (strcmp(command, "test") == 0); -} - -CStringUniquePtr DartDevIsolate::TryResolveArtifactPath(const char* filename) { - auto try_resolve_path = [&](CStringUniquePtr dir_prefix) { - // First assume we're in dart-sdk/bin. - char* snapshot_path = - Utils::SCreate("%ssnapshots/%s", dir_prefix.get(), filename); - if (File::Exists(nullptr, snapshot_path)) { - return CStringUniquePtr(snapshot_path); - } - free(snapshot_path); - - // If we're not in dart-sdk/bin, we might be in one of the $SDK/out/* - // directories. Try to use a snapshot from a previously built SDK. - snapshot_path = Utils::SCreate("%s%s", dir_prefix.get(), filename); - if (File::Exists(nullptr, snapshot_path)) { - return CStringUniquePtr(snapshot_path); - } - free(snapshot_path); - return CStringUniquePtr(nullptr); - }; - - // Try to find the artifact using the resolved EXE path first. This can fail - // if the Dart SDK file structure is faked using symlinks and the actual - // artifacts are spread across directories on the file system (e.g., some - // google3 execution environments). - auto result = - try_resolve_path(EXEUtils::GetDirectoryPrefixFromResolvedExeName()); - if (result == nullptr) { - result = - try_resolve_path(EXEUtils::GetDirectoryPrefixFromUnresolvedExeName()); - } - - return result; -} - -CStringUniquePtr DartDevIsolate::TryResolveDartDevSnapshotPath() { - return TryResolveArtifactPath("dartdev.dart.snapshot"); -} - -void DartDevIsolate::DartDevRunner::Run( - Dart_IsolateGroupCreateCallback create_isolate, - char** packages_file, - char** script, - CommandLineOptions* dart_options) { - create_isolate_ = create_isolate; - dart_options_ = dart_options; - package_config_override_ = packages_file; - script_ = script; - - // We've encountered an error during preliminary argument parsing so we'll - // output the standard help message and exit with an error code. - if (print_usage_error_) { - dart_options_->Reset(); - dart_options_->AddArgument("--help"); - } - - MonitorLocker locker(monitor_); - Thread::Start("DartDev Runner", RunCallback, reinterpret_cast(this)); - monitor_->WaitMicros(Monitor::kNoTimeout); - - if (result_ == DartDevIsolate::DartDev_Result_Run) { - // Clear the DartDev dart_options and replace them with the processed - // options provided by DartDev. - dart_options_->Reset(); - dart_options_->AddArguments(const_cast(argv_.get()), argc_); - } -} - -static Dart_CObject* GetArrayItem(Dart_CObject* message, intptr_t index) { - return message->value.as_array.values[index]; -} - -void DartDevIsolate::DartDevRunner::DartDevResultCallback( - Dart_Port dest_port_id, - Dart_CObject* message) { - // These messages are produced in pkg/dartdev/lib/src/vm_interop_handler.dart. - ASSERT(message->type == Dart_CObject_kArray); - int32_t type = GetArrayItem(message, 0)->value.as_int32; - switch (type) { - case DartDevIsolate::DartDev_Result_Run: { - result_ = DartDevIsolate::DartDev_Result_Run; - ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); - auto item2 = GetArrayItem(message, 2); - - ASSERT(item2->type == Dart_CObject_kString || - item2->type == Dart_CObject_kNull); - - auto item3 = GetArrayItem(message, 3); - - ASSERT(item3->type == Dart_CObject_kBool); - const bool mark_main_isolate_as_system_isolate = item3->value.as_bool; - if (mark_main_isolate_as_system_isolate) { - Options::set_mark_main_isolate_as_system_isolate(true); - } - - if (*script_ != nullptr) { - free(*script_); - } - if (*package_config_override_ != nullptr) { - free(*package_config_override_); - *package_config_override_ = nullptr; - } - *script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); - - if (item2->type == Dart_CObject_kString) { - *package_config_override_ = Utils::StrDup(item2->value.as_string); - } - - ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray); - Dart_CObject* args = GetArrayItem(message, 4); - argc_ = args->value.as_array.length; - Dart_CObject** dart_args = args->value.as_array.values; - - auto deleter = [](char** args) { - for (intptr_t i = 0; i < argc_; ++i) { - free(args[i]); - } - delete[] args; - }; - argv_ = - std::unique_ptr(new char*[argc_], deleter); - for (intptr_t i = 0; i < argc_; ++i) { - argv_[i] = Utils::StrDup(dart_args[i]->value.as_string); - } - break; - } - case DartDevIsolate::DartDev_Result_RunExec: { - result_ = DartDevIsolate::DartDev_Result_RunExec; - ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); - auto item2 = GetArrayItem(message, 2); - - ASSERT(item2->type == Dart_CObject_kString || - item2->type == Dart_CObject_kNull); - - auto item3 = GetArrayItem(message, 3); - - ASSERT(item3->type == Dart_CObject_kBool); - const bool mark_main_isolate_as_system_isolate = item3->value.as_bool; - if (mark_main_isolate_as_system_isolate) { - Options::set_mark_main_isolate_as_system_isolate(true); - } - - if (*script_ != nullptr) { - free(*script_); - } - if (*package_config_override_ != nullptr) { - free(*package_config_override_); - *package_config_override_ = nullptr; - } - *script_ = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); - - if (item2->type == Dart_CObject_kString) { - *package_config_override_ = Utils::StrDup(item2->value.as_string); - } - - intptr_t num_vm_options = 0; - const char** vm_options = nullptr; - ASSERT(GetArrayItem(message, 4)->type == Dart_CObject_kArray); - Dart_CObject* args = GetArrayItem(message, 4); - intptr_t argc = args->value.as_array.length; - Dart_CObject** dart_args = args->value.as_array.values; - - if (vm_options_ != nullptr) { - num_vm_options = vm_options_->count(); - vm_options = vm_options_->arguments(); - } - auto deleter = [](char** args) { - for (intptr_t i = 0; i < argc_; ++i) { - free(args[i]); - } - delete[] args; - }; - // Total count of arguments to be passed to the script being execed. - argc_ = argc + num_vm_options + 1; - - // Array of arguments to be passed to the script being execed. - argv_ = std::unique_ptr(new char*[argc_ + 1], - deleter); - - intptr_t idx = 0; - // Copy in name of the script to run (dartaotruntime). - argv_[0] = Utils::StrDup(GetArrayItem(message, 1)->value.as_string); - idx += 1; - // Copy in any vm options that need to be passed to the execed process. - for (intptr_t i = 0; i < num_vm_options; ++i) { - argv_[i + idx] = Utils::StrDup(vm_options[i]); - } - idx += num_vm_options; - // Copy in the dart options that need to be passed to the command. - for (intptr_t i = 0; i < argc; ++i) { - argv_[i + idx] = Utils::StrDup(dart_args[i]->value.as_string); - } - // Null terminate the argv array. - argv_[argc + idx] = nullptr; - - // Exec the script to be run and pass the arguments. - char err_msg[256]; - err_msg[0] = '\0'; - int ret = Process::Exec(nullptr, *script_, - const_cast(argv_.get()), argc_, - nullptr, err_msg, sizeof(err_msg)); - if (ret != 0) { - ProcessError(err_msg, kErrorExitCode); - } - break; - } - case DartDevIsolate::DartDev_Result_Exit: { - ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kInt32); - int32_t dartdev_exit_code = GetArrayItem(message, 1)->value.as_int32; - - // If we're given a non-zero exit code, DartDev is signaling for us to - // shutdown. - int32_t exit_code = - print_usage_error_ ? kErrorExitCode : dartdev_exit_code; - Process::SetGlobalExitCode(exit_code); - - // If DartDev hasn't signaled for us to do anything else, we can assume - // there's nothing else for the VM to run and that we can exit. - if (result_ == DartDevIsolate::DartDev_Result_Unknown) { - result_ = DartDevIsolate::DartDev_Result_Exit; - } - - // DartDev is done processing the command. Unblock the main thread and - // continue the launch procedure. - DartDevRunner::monitor_->Notify(); - break; - } - default: - UNREACHABLE(); - } -} - -void DartDevIsolate::DartDevRunner::RunCallback(uword args) { - MonitorLocker locker_(DartDevRunner::monitor_); - DartDevRunner* runner = reinterpret_cast(args); - - // Hardcode flags to match those used to generate the DartDev snapshot. - Dart_IsolateFlags flags; - Dart_IsolateFlagsInitialize(&flags); - flags.enable_asserts = false; - flags.use_field_guards = true; - flags.use_osr = true; - flags.is_system_isolate = true; - flags.branch_coverage = false; - flags.coverage = false; - - char* error = nullptr; - Dart_Isolate dartdev_isolate = runner->create_isolate_( - DART_DEV_ISOLATE_NAME, DART_DEV_ISOLATE_NAME, nullptr, - runner->packages_file_, &flags, /* callback_data */ nullptr, - const_cast(&error)); - - if (dartdev_isolate == nullptr) { - ProcessError(error, kErrorExitCode); - free(error); - return; - } - - Dart_EnterIsolate(dartdev_isolate); - Dart_EnterScope(); - - // Retrieve the DartDev entrypoint. - Dart_Port send_port_id = ILLEGAL_PORT; - Dart_Handle root_lib = Dart_RootLibrary(); - Dart_Handle main_closure = - Dart_GetField(root_lib, Dart_NewStringFromCString("main")); - CHECK_RESULT(main_closure); - - if (!Dart_IsClosure(main_closure)) { - ProcessError("Unable to find 'main' in root library 'dartdev'", - kErrorExitCode); - Dart_ExitScope(); - Dart_ShutdownIsolate(); - return; - } - - // Create a SendPort that DartDev can use to communicate its results over. - send_port_id = - Dart_NewNativePort(DART_DEV_ISOLATE_NAME, DartDevResultCallback, false); - ASSERT(send_port_id != ILLEGAL_PORT); - Dart_Handle send_port = Dart_NewSendPort(send_port_id); - CHECK_RESULT(send_port); - - const intptr_t kNumIsolateArgs = 4; - Dart_Handle isolate_args[kNumIsolateArgs]; - isolate_args[0] = main_closure; // entryPoint - isolate_args[1] = runner->dart_options_->CreateRuntimeOptions(); // args - isolate_args[2] = send_port; // message - isolate_args[3] = Dart_True(); // isSpawnUri - - Dart_Handle isolate_lib = - Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate")); - Dart_Handle result = - Dart_Invoke(isolate_lib, Dart_NewStringFromCString("_startIsolate"), - kNumIsolateArgs, isolate_args); - CHECK_RESULT(result); - CHECK_RESULT(Dart_RunLoop()); - - Dart_CloseNativePort(send_port_id); - - Dart_ExitScope(); - Dart_ShutdownIsolate(); -} - -void DartDevIsolate::DartDevRunner::ProcessError(const char* msg, - int32_t exit_code) { - Syslog::PrintErr("%s.\n", msg); - Process::SetGlobalExitCode(exit_code); - result_ = DartDevIsolate::DartDev_Result_Exit; - DartDevRunner::monitor_->Notify(); -} - -DartDevIsolate::DartDev_Result DartDevIsolate::RunDartDev( - Dart_IsolateGroupCreateCallback create_isolate, - char** packages_file, - char** script, - CommandLineOptions* vm_options, - CommandLineOptions* dart_options) { - vm_options_ = vm_options; - runner_.Run(create_isolate, packages_file, script, dart_options); - return runner_.result(); -} - -} // namespace bin -} // namespace dart - -#endif // if !defined(DART_PRECOMPILED_RUNTIME) diff --git a/runtime/bin/dartdev_isolate.h b/runtime/bin/dartdev_isolate.h deleted file mode 100644 index c5e476db8e5..00000000000 --- a/runtime/bin/dartdev_isolate.h +++ /dev/null @@ -1,118 +0,0 @@ -// 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. - -#ifndef RUNTIME_BIN_DARTDEV_ISOLATE_H_ -#define RUNTIME_BIN_DARTDEV_ISOLATE_H_ - -#if !defined(DART_PRECOMPILED_RUNTIME) - -#include - -#include "bin/thread.h" -#include "include/dart_api.h" -#include "include/dart_native_api.h" -#include "platform/globals.h" -#include "platform/utils.h" - -#define DART_DEV_ISOLATE_NAME "dartdev" - -namespace dart { -namespace bin { - -class CommandLineOptions; - -class DartDevIsolate { - public: - // Note: keep in sync with pkg/dartdev/lib/vm_interop_handler.dart - typedef enum { - DartDev_Result_Unknown = -1, - DartDev_Result_Run = 1, - DartDev_Result_RunExec = 2, - DartDev_Result_Exit = 3, - } DartDev_Result; - - // Returns true if there does not exist a file at |script_uri| or the URI is - // not an HTTP resource. - static bool ShouldParseCommand(const char* script_uri); - - // Returns true if VM options need to be recorded and passed to the VM - // that executes the command (this is true only for dart CL commands like - // 'run' and 'test'. - static bool ShouldParseVMOptions(const char* command); - - static void set_should_run_dart_dev(bool enable) { - should_run_dart_dev_ = enable; - } - - static void PrintUsageErrorOnRun() { - set_should_run_dart_dev(true); - print_usage_error_ = true; - } - - static bool should_run_dart_dev() { return should_run_dart_dev_; } - - // Attempts to find the path of the DartDev snapshot. - static CStringUniquePtr TryResolveDartDevSnapshotPath(); - - // Starts a DartDev instance in a new isolate and runs it to completion. - // - // Returns true if the VM should run the result in `script`, in which case - // `script` and `dart_options` will have been repopulated with the correct - // values. - static DartDev_Result RunDartDev( - Dart_IsolateGroupCreateCallback create_isolate, - char** packages_file, - char** script, - CommandLineOptions* vm_options, - CommandLineOptions* dart_options); - - protected: - class DartDevRunner { - public: - DartDevRunner() {} - - void Run(Dart_IsolateGroupCreateCallback create_isolate, - char** package_config_override_, - char** script, - CommandLineOptions* dart_options); - - DartDev_Result result() const { return result_; } - - private: - static void DartDevResultCallback(Dart_Port dest_port_id, - Dart_CObject* message); - static void RunCallback(uword arg); - static void ProcessError(const char* msg, int32_t exit_code); - - static DartDev_Result result_; - static char** script_; - static char** package_config_override_; - static std::unique_ptr argv_; - static intptr_t argc_; - static Monitor* monitor_; - - Dart_IsolateGroupCreateCallback create_isolate_; - CommandLineOptions* dart_options_; - const char* packages_file_; - - DISALLOW_ALLOCATION(); - }; - - private: - static CStringUniquePtr TryResolveArtifactPath(const char* filename); - - static DartDevRunner runner_; - static bool should_run_dart_dev_; - static bool print_usage_error_; - static CommandLineOptions* vm_options_; - - DISALLOW_ALLOCATION(); - DISALLOW_IMPLICIT_CONSTRUCTORS(DartDevIsolate); -}; - -} // namespace bin -} // namespace dart - -#endif // !defined(DART_PRECOMPILED_RUNTIME) -#endif // RUNTIME_BIN_DARTDEV_ISOLATE_H_ diff --git a/runtime/bin/dartdev_options.cc b/runtime/bin/dartdev_options.cc new file mode 100644 index 00000000000..5a9a14479fa --- /dev/null +++ b/runtime/bin/dartdev_options.cc @@ -0,0 +1,378 @@ +// Copyright (c) 2025, 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. + +#include "bin/dartdev_options.h" + +#include +#include +#include + +#include "bin/common_options.h" +#include "bin/error_exit.h" +#include "bin/file_system_watcher.h" +#include "bin/platform.h" +#include "bin/socket.h" +#include "bin/utils.h" +#include "include/dart_api.h" +#include "platform/assert.h" +#include "platform/globals.h" +#include "platform/hashmap.h" +#include "platform/syslog.h" + +namespace dart { +namespace bin { + +#if defined(DART_PRECOMPILED_RUNTIME) + +static bool PotentialDartdevCommand(const char* script_uri) { + // If script_uri is a known DartDev command, we should not try to run it. + // + // Otherwise if script_uri is not a file path or of a known URI scheme, we + // assume this is a mistyped DartDev command. + // + // This should be kept in sync with the commands in + // `pkg/dartdev/lib/dartdev.dart`. + return ( + (strcmp(script_uri, "analyze") == 0) || + (strcmp(script_uri, "compilation-server") == 0) || + (strcmp(script_uri, "build") == 0) || + (strcmp(script_uri, "compile") == 0) || + (strcmp(script_uri, "create") == 0) || + (strcmp(script_uri, "development-service") == 0) || + (strcmp(script_uri, "devtools") == 0) || + (strcmp(script_uri, "doc") == 0) || (strcmp(script_uri, "fix") == 0) || + (strcmp(script_uri, "format") == 0) || + (strcmp(script_uri, "info") == 0) || + (strcmp(script_uri, "mcp-server") == 0) || + (strcmp(script_uri, "pub") == 0) || (strcmp(script_uri, "run") == 0) || + (strcmp(script_uri, "test") == 0) || (strcmp(script_uri, "info") == 0) || + (strcmp(script_uri, "language-server") == 0) || + (strcmp(script_uri, "tooling-daemon") == 0) || + (!File::ExistsUri(nullptr, script_uri) && + (strncmp(script_uri, "http://", 7) != 0) && + (strncmp(script_uri, "https://", 8) != 0) && + (strncmp(script_uri, "file://", 7) != 0) && + (strncmp(script_uri, "package:", 8) != 0) && + (strncmp(script_uri, "google3://", 10) != 0))); +} + +#define OPTION_FIELD(variable) Options::variable##_ + +#define STRING_OPTION_DEFINITION(name, variable) \ + const char* OPTION_FIELD(variable) = nullptr; \ + DEFINE_STRING_OPTION(name, OPTION_FIELD(variable)) +STRING_OPTIONS_LIST(STRING_OPTION_DEFINITION) +#undef STRING_OPTION_DEFINITION + +#define BOOL_OPTION_DEFINITION(name, variable) \ + bool OPTION_FIELD(variable) = false; \ + DEFINE_BOOL_OPTION(name, OPTION_FIELD(variable)) +BOOL_OPTIONS_LIST(BOOL_OPTION_DEFINITION) +#undef BOOL_OPTION_DEFINITION + +#define SHORT_BOOL_OPTION_DEFINITION(short_name, long_name, variable) \ + bool OPTION_FIELD(variable) = false; \ + DEFINE_BOOL_OPTION_SHORT(short_name, long_name, OPTION_FIELD(variable)) +SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_DEFINITION) +#undef SHORT_BOOL_OPTION_DEFINITION + +#define CB_OPTION_DEFINITION(callback) \ + static bool callback##Helper(const char* arg, CommandLineOptions* o) { \ + return Options::callback(arg, o); \ + } \ + DEFINE_CB_OPTION(callback##Helper) +CB_OPTIONS_LIST(CB_OPTION_DEFINITION) +#undef CB_OPTION_DEFINITION + +// Explicitly handle VM flags that can be parsed by DartDev's run command. +bool Options::ProcessVMOptions(const char* arg, + CommandLineOptions* vm_options) { +#define IS_VM_OPTION(name, arg) \ + if (OptionProcessor::ProcessOption(arg, name) != nullptr) { \ + vm_options->AddArgument(arg); \ + return true; \ + } + +// This is an exhaustive set of VM flags that are accepted by 'dart run' and +// 'dart test' commands, these options need to be collected and passed to +// the dart VM process that is going to run the command. +// +// NOTE: When updating this list of VM flags, be sure to make the corresponding +// changes in pkg/dartdev/lib/src/commands/run.dart. +#define HANDLE_DARTDEV_VM_OPTIONS(V, arg) \ + V("--enable-asserts", arg) \ + V("--pause-isolates-on-exit", arg) \ + V("--no-pause-isolates-on-exit", arg) \ + V("--pause-isolates-on-start", arg) \ + V("--no-pause-isolates-on-start", arg) \ + V("--pause-isolates-on-unhandled-exception", arg) \ + V("--no-pause-isolates-on-unhandled-exception", arg) \ + V("--warn-on-pause-with-no-debugger", arg) \ + V("--no-warn-on-pause-with-no-debugger", arg) \ + V("--timeline-streams", arg) \ + V("--timeline-recorder", arg) \ + V("--dds", arg) \ + V("--no-dds", arg) \ + V("--profiler", arg) \ + V("--disable-service-auth-codes", arg) \ + V("--write-service-info", arg) \ + V("--enable-service-port-fallback", arg) \ + V("--disable-service-auth-codes", arg) \ + V("--serve-observatory", arg) \ + V("--print-dtd", arg) \ + V("--packages", arg) \ + V("--resident", arg) \ + V("--resident-server-info-file", arg) \ + V("--resident-compiler-info-file", arg) \ + V("--observe", arg) \ + V("--enable-vm-service", arg) \ + V("--serve-devtools", arg) \ + V("--no-serve-devtools", arg) \ + V("--serve-observatory", arg) \ + V("--no-serve-observatory", arg) \ + V("--profile-microtasks", arg) \ + V("--enable-experiment", arg) + HANDLE_DARTDEV_VM_OPTIONS(IS_VM_OPTION, arg); + +#undef IS_VM_OPTION +#undef HANDLE_DARTDEV_VM_OPTIONS + + return false; +} + +bool Options::ParseDartDevArguments(int argc, + char** argv, + CommandLineOptions* vm_options, + CommandLineOptions* dart_vm_options, + CommandLineOptions* dart_options, + bool* skip_dartdev) { + // First figure out if a dartdev command has been explicitly specified. + *skip_dartdev = false; + int tmp_i = 1; + while (tmp_i < argc) { + // Check if this flag is a potentially valid VM flag, we skip over all + // VM flags until we see a command or a script file. + if (!OptionProcessor::IsValidFlag(argv[tmp_i]) && + !OptionProcessor::IsValidShortFlag(argv[tmp_i])) { + break; + } + tmp_i++; + } + if (tmp_i < argc) { + // Check if we have a dartdev command. + if (!PotentialDartdevCommand(argv[tmp_i])) { + // We don't have a dartdev command so skip dartdev and execute the + // script directly. + *skip_dartdev = true; + return true; + } + } + + bool enable_dartdev_analytics = false; + bool disable_dartdev_analytics = false; + char* packages_argument = nullptr; + + // First parse out the vm options into dart_vm_options so that it can be + // passed down to the 'run' and 'test' commands. + // Start processing arguments after argv[0] which would be the executable. + int i = 1; + while (i < argc) { + bool skipVmOption = false; + if (!OptionProcessor::TryProcess(argv[i], dart_vm_options)) { + // Check if this flag is a potentially valid VM flag. + if (!OptionProcessor::IsValidFlag(argv[i])) { + break; + } + // The following flags are processed as DartDev flags and are not to + // be treated as if they are VM flags. + if (IsOption(argv[i], "enable-analytics")) { + enable_dartdev_analytics = true; + skipVmOption = true; + } else if (IsOption(argv[i], "disable-analytics")) { + disable_dartdev_analytics = true; + skipVmOption = true; + } else if (IsOption(argv[i], "disable-telemetry")) { + disable_dartdev_analytics = true; + skipVmOption = true; + } else if (IsOption(argv[i], "suppress-analytics")) { + dart_options->AddArgument("--suppress-analytics"); + skipVmOption = true; + } else if (IsOption(argv[i], "no-analytics")) { + // Just add this option even if we don't go to dartdev. + // It is irrelevant for the vm. + dart_options->AddArgument("--no-analytics"); + skipVmOption = true; + } else if (IsOption(argv[i], "serve-observatory")) { + // This flag is currently set by default in vmservice_io.dart, so we + // ignore it. --no-serve-observatory is a VM flag so we don't need to + // handle that case here. + skipVmOption = true; + } else if (IsOption(argv[i], "print-dtd-uri")) { + skipVmOption = true; + } else if (IsOption(argv[i], "executable-name")) { + skipVmOption = true; + } else if (IsOption(argv[i], "enable-experiment")) { + dart_options->AddArgument(argv[i]); + } + } + if (!skipVmOption) { + dart_vm_options->AddArgument(argv[i]); + } + if (IsOption(argv[i], "packages")) { + packages_argument = argv[i]; + } + i++; + } + + // The arguments to the VM are at positions 1 through i-1 in argv. + Platform::SetExecutableArguments(i, argv); + + // If we have exhausted all the arguments and haven't see a dartdev + // command then we set up some scenarios where it still makes sense + // to start up dartdev and have it process the options. + if (i >= argc) { + // Handles following invocation arguments: + // - dart help + // - dart --help + // - dart + if (((Options::help_option() && !Options::verbose_option()) || + (argc == 1))) { + // Let DartDev handle the default help message. + dart_options->AddArgument("help"); + return true; + } + // Handles cases where only analytics flags are provided. We need to launch + // DartDev for this. + else if (enable_dartdev_analytics || disable_dartdev_analytics) { // NOLINT + // 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. + dart_options->AddArgument(enable_dartdev_analytics + ? "--enable-analytics" + : "--disable-analytics"); + return true; + } + // If it is not '--version' and '--help' we will launch DartDev + // to print its help message and set an error exit code. + else if (!Options::help_option() && !Options::version_option()) { // NOLINT + // Pass in an invalid option so that dartdev prints the help message + // and exits with an error exit code. + dart_options->Reset(); + dart_options->AddArgument(argv[argc - 1]); + dart_options->AddArgument("help"); + return true; + } + return false; + } + + USE(enable_dartdev_analytics); + USE(disable_dartdev_analytics); + USE(packages_argument); + + // Record the dartdev command. + dart_options->AddArgument(argv[i++]); + + // Bring any --packages option into the dartdev command + if (packages_argument != nullptr) { + dart_options->AddArgument(packages_argument); + dart_vm_options->AddArgument(packages_argument); + } + + // Scan remaining arguments and separate them into + // dart_vm_options (vm options to be passed to the dart process executing + // the dartdev command) or dart_options (options to be passed to the + // executing dart script). + bool script_seen = false; + while (i < argc) { + if (!IsOption(argv[i], "disable-dart-dev")) { + if (!script_seen) { + // We scan for VM options that are passed to the 'run' and 'test' + // command. These options are accepted by both the VM and dartdev + // commands and need to be carried over to the VM running the app for + // these commands. + if (Options::ProcessVMOptions(argv[i], dart_vm_options)) { + // dartdev isn't able to parse these options properly. Since it + // doesn't need to use the values from these options, just strip them + // from the argument list passed to dartdev. + if (!IsOption(argv[i], "observe") && + !IsOption(argv[i], "enable-vm-service")) { + dart_options->AddArgument(argv[i]); + } + } else { + if (!OptionProcessor::IsValidFlag(argv[i]) && + !OptionProcessor::IsValidShortFlag(argv[i])) { + script_seen = true; + } + dart_options->AddArgument(argv[i]); + } + } else { + dart_options->AddArgument(argv[i]); + } + } else { + Syslog::PrintErr( + "Attempted to use --disable-dart-dev with a Dart CLI command.\n"); + return false; + } + i++; + } + + // Store the executable name. + Platform::SetExecutableName(argv[0]); + + // Verify consistency of arguments. + if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) { + Syslog::PrintErr("Empty package file name specified.\n"); + return false; + } + + return true; +} + +void Options::PrintVersion() { + _PrintVersion(); +} + +// clang-format off +void Options::PrintUsage() { + _PrintUsage(); + if (!Options::verbose_option()) { + _PrintNonVerboseUsage(); + } else { + _PrintVerboseUsage(); + } +} +// clang-format on + +dart::SimpleHashMap* Options::environment_ = nullptr; +bool Options::ProcessEnvironmentOption(const char* arg, + CommandLineOptions* vm_options) { + return OptionProcessor::ProcessEnvironmentOption(arg, vm_options, + &Options::environment_); +} + +void Options::Cleanup() { + DestroyEnvironment(); +} + +void Options::DestroyEnvironment() { + if (environment_ != nullptr) { + for (SimpleHashMap::Entry* p = environment_->Start(); p != nullptr; + p = environment_->Next(p)) { + free(p->key); + free(p->value); + } + delete environment_; + environment_ = nullptr; + } +} + +char** Options::GetEnvArguments(int* argc) { + return nullptr; +} + +void Options::DestroyEnvArgv() {} + +#endif // defined(DART_PRECOMPILED_RUNTIME) + +} // namespace bin +} // namespace dart diff --git a/runtime/bin/dartdev_options.h b/runtime/bin/dartdev_options.h new file mode 100644 index 00000000000..912fa7f5bd9 --- /dev/null +++ b/runtime/bin/dartdev_options.h @@ -0,0 +1,140 @@ +// Copyright (c) 2025, 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. + +#ifndef RUNTIME_BIN_DARTDEV_OPTIONS_H_ +#define RUNTIME_BIN_DARTDEV_OPTIONS_H_ + +#include "bin/dartutils.h" +#include "bin/options.h" +#include "platform/globals.h" +#include "platform/growable_array.h" +#include "platform/hashmap.h" + +namespace dart { +namespace bin { + +#if defined(DART_PRECOMPILED_RUNTIME) + +// A list of options taking string arguments. Organized as: +// V(flag_name, field_name) +// The value of the flag can then be accessed with Options::field_name(). +#define STRING_OPTIONS_LIST(V) \ + V(packages, packages_file) \ + V(namespace, namespc) + +// As STRING_OPTIONS_LIST but for boolean valued options. The default value is +// always false, and the presence of the flag switches the value to true. +#define BOOL_OPTIONS_LIST(V) \ + V(disable_exit, exit_disabled) \ + V(version, version_option) \ + V(suppress_core_dump, suppress_core_dump) + +// Boolean flags that have a short form. +#define SHORT_BOOL_OPTIONS_LIST(V) \ + V(h, help, help_option) \ + V(v, verbose, verbose_option) + +// Callbacks passed to DEFINE_CB_OPTION(). +#define CB_OPTIONS_LIST(V) \ + V(ProcessEnvironmentOption) \ + V(ProcessVMOptions) + +enum VerbosityLevel { + kError, + kWarning, + kInfo, + kAll, +}; + +static const char* const kVerbosityLevelNames[] = { + "error", "warning", "info", "all", nullptr, +}; + +class Options { + public: + // Returns true if argument parsing succeeded. False otherwise. + static bool ParseDartDevArguments(int argc, + char** argv, + CommandLineOptions* vm_options, + CommandLineOptions* dart_vm_options, + CommandLineOptions* dart_options, + bool* skip_dartdev); + +#define STRING_OPTION_GETTER(flag, variable) \ + static const char* variable() { return variable##_; } + STRING_OPTIONS_LIST(STRING_OPTION_GETTER) +#undef STRING_OPTION_GETTER + +#define BOOL_OPTION_GETTER(flag, variable) \ + static bool variable() { return variable##_; } + BOOL_OPTIONS_LIST(BOOL_OPTION_GETTER) +#undef BOOL_OPTION_GETTER + +#define SHORT_BOOL_OPTION_GETTER(short_name, long_name, variable) \ + static bool variable() { return variable##_; } + SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_GETTER) +#undef SHORT_BOOL_OPTION_GETTER + +// Callbacks have to be public. +#define CB_OPTIONS_DECL(callback) \ + static bool callback(const char* arg, CommandLineOptions* vm_options); + CB_OPTIONS_LIST(CB_OPTIONS_DECL) +#undef CB_OPTIONS_DECL + + static dart::SimpleHashMap* environment() { return environment_; } + + static void PrintUsage(); + static void PrintVersion(); + + static void Cleanup(); + +#if defined(DART_PRECOMPILED_RUNTIME) + // Get the list of options in DART_VM_OPTIONS. + static char** GetEnvArguments(int* argc); +#endif // defined(DART_PRECOMPILED_RUNTIME) + + private: + static void DestroyEnvironment(); +#if defined(DART_PRECOMPILED_RUNTIME) + static void DestroyEnvArgv(); +#endif // defined(DART_PRECOMPILED_RUNTIME) + +#define STRING_OPTION_DECL(flag, variable) static const char* variable##_; + STRING_OPTIONS_LIST(STRING_OPTION_DECL) +#undef STRING_OPTION_DECL + +#define BOOL_OPTION_DECL(flag, variable) static bool variable##_; + BOOL_OPTIONS_LIST(BOOL_OPTION_DECL) +#undef BOOL_OPTION_DECL + +#define SHORT_BOOL_OPTION_DECL(short_name, long_name, variable) \ + static bool variable##_; + SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_DECL) +#undef SHORT_BOOL_OPTION_DECL + + static dart::SimpleHashMap* environment_; + + static char** env_argv_; + static int env_argc_; + +#define OPTION_FRIEND(flag, variable) friend class OptionProcessor_##flag; + STRING_OPTIONS_LIST(OPTION_FRIEND) + BOOL_OPTIONS_LIST(OPTION_FRIEND) +#undef OPTION_FRIEND + +#define SHORT_BOOL_OPTION_FRIEND(short_name, long_name, variable) \ + friend class OptionProcessor_##long_name; + SHORT_BOOL_OPTIONS_LIST(SHORT_BOOL_OPTION_FRIEND) +#undef SHORT_BOOL_OPTION_FRIEND + + DISALLOW_ALLOCATION(); + DISALLOW_IMPLICIT_CONSTRUCTORS(Options); +}; + +#endif // defined(DART_PRECOMPILED_RUNTIME) + +} // namespace bin +} // namespace dart + +#endif // RUNTIME_BIN_DARTDEV_OPTIONS_H_ diff --git a/runtime/bin/dart_test_component.cml b/runtime/bin/dartvm_test_component.cml similarity index 93% rename from runtime/bin/dart_test_component.cml rename to runtime/bin/dartvm_test_component.cml index 3bff2c933ac..463d84e66cf 100644 --- a/runtime/bin/dart_test_component.cml +++ b/runtime/bin/dartvm_test_component.cml @@ -7,7 +7,7 @@ "//runtime/vm-jit.shard.cml", ], program: { - binary: "exe.stripped/dart", + binary: "exe.stripped/dartvm", runner: "elf_test_runner", }, capabilities: [ diff --git a/runtime/bin/main_impl.cc b/runtime/bin/main_impl.cc index 2e4d3c672a9..656205dc4c2 100644 --- a/runtime/bin/main_impl.cc +++ b/runtime/bin/main_impl.cc @@ -15,7 +15,6 @@ #include "bin/builtin.h" #include "bin/console.h" #include "bin/crashpad.h" -#include "bin/dartdev_isolate.h" #include "bin/dartutils.h" #include "bin/dfe.h" #include "bin/error_exit.h" @@ -391,17 +390,6 @@ static Dart_Isolate IsolateSetupHelper(Dart_Isolate isolate, CHECK_RESULT(result); } -#if !defined(DART_PRECOMPILED_RUNTIME) - // Disable pausing the DartDev isolate on start and exit. - const char* isolate_name = nullptr; - result = Dart_StringToCString(Dart_DebugName(), &isolate_name); - CHECK_RESULT(result); - if (strstr(isolate_name, DART_DEV_ISOLATE_NAME) != nullptr) { - Dart_SetShouldPauseOnStart(false); - Dart_SetShouldPauseOnExit(false); - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - #if !defined(DART_PRECOMPILER) NativeAssetsApi native_assets; memset(&native_assets, 0, sizeof(native_assets)); @@ -574,7 +562,7 @@ static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, CHECK_RESULT(result); // We do not spawn the external dds process if DDS is explicitly disabled. - bool wait_for_dds_to_advertise_service = !Options::disable_dds(); + bool wait_for_dds_to_advertise_service = Options::enable_dds(); bool serve_devtools = Options::enable_devtools() || !Options::disable_devtools(); // Load embedder specific bits and return. @@ -606,99 +594,6 @@ static Dart_Isolate CreateAndSetupServiceIsolate(const char* script_uri, #endif // !defined(PRODUCT) } -#if !defined(DART_PRECOMPILED_RUNTIME) - -static Dart_Isolate CreateAndSetupDartDevIsolate(const char* script_uri, - const char* packages_config, - Dart_IsolateFlags* flags, - char** error, - int* exit_code) { - int64_t start = Dart_TimelineGetMicros(); - - auto dartdev_path = DartDevIsolate::TryResolveDartDevSnapshotPath(); - if (dartdev_path.get() == nullptr) { - Syslog::PrintErr( - "Failed to start the Dart CLI isolate. Could not resolve DartDev " - "snapshot or kernel.\n"); - if (error != nullptr && *error != nullptr) { - free(*error); - *error = nullptr; - } - return nullptr; - } - - Dart_Isolate isolate = nullptr; - const uint8_t* isolate_snapshot_data = core_isolate_snapshot_data; - const uint8_t* isolate_snapshot_instructions = - core_isolate_snapshot_instructions; - IsolateGroupData* isolate_group_data = nullptr; - IsolateData* isolate_data = nullptr; - AppSnapshot* app_snapshot = nullptr; - bool isolate_run_app_snapshot = true; - // dartdev isolate uses an app JIT snapshot or uses the dill file. - if (((app_snapshot = Snapshot::TryReadAppSnapshot( - dartdev_path.get(), /*force_load_from_memory=*/false, - /*decode_uri=*/false)) != nullptr) && - app_snapshot->IsJIT()) { - const uint8_t* isolate_snapshot_data = nullptr; - const uint8_t* isolate_snapshot_instructions = nullptr; - const uint8_t* ignore_vm_snapshot_data; - const uint8_t* ignore_vm_snapshot_instructions; - app_snapshot->SetBuffers( - &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, - &isolate_snapshot_data, &isolate_snapshot_instructions); - isolate_group_data = new IsolateGroupData( - DART_DEV_ISOLATE_NAME, /*asset_resolution_base=*/nullptr, - packages_config, app_snapshot, isolate_run_app_snapshot); - isolate_data = new IsolateData(isolate_group_data); - isolate = Dart_CreateIsolateGroup( - DART_DEV_ISOLATE_NAME, DART_DEV_ISOLATE_NAME, isolate_snapshot_data, - isolate_snapshot_instructions, flags, isolate_group_data, isolate_data, - error); - } - - if (isolate == nullptr) { - // dartdev_path was not an application snapshot, try it as a kernel file. - // Clear error from app snapshot and retry from kernel. - if (error != nullptr && *error != nullptr) { - free(*error); - *error = nullptr; - } - isolate_run_app_snapshot = false; - if (app_snapshot != nullptr) { - delete app_snapshot; - } - isolate_group_data = - new IsolateGroupData(DART_DEV_ISOLATE_NAME, nullptr, packages_config, - nullptr, isolate_run_app_snapshot); - uint8_t* application_kernel_buffer = nullptr; - intptr_t application_kernel_buffer_size = 0; - dfe.ReadScript(dartdev_path.get(), nullptr, &application_kernel_buffer, - &application_kernel_buffer_size, /*decode_uri=*/false); - isolate_group_data->SetKernelBufferNewlyOwned( - application_kernel_buffer, application_kernel_buffer_size); - - isolate_data = new IsolateData(isolate_group_data); - isolate = Dart_CreateIsolateGroup( - DART_DEV_ISOLATE_NAME, DART_DEV_ISOLATE_NAME, isolate_snapshot_data, - isolate_snapshot_instructions, flags, isolate_group_data, isolate_data, - error); - } - - Dart_Isolate created_isolate = - IsolateSetupHelper(isolate, false, DART_DEV_ISOLATE_NAME, packages_config, - isolate_run_app_snapshot, flags, error, exit_code); - - int64_t end = Dart_TimelineGetMicros(); - Dart_RecordTimelineEvent("CreateAndSetupDartDevIsolate", start, end, - /*flow_id_count=*/0, nullptr, - Dart_Timeline_Event_Duration, - /*argument_count=*/0, nullptr, nullptr); - return created_isolate; -} - -#endif // !defined(DART_PRECOMPILED_RUNTIME) - // Returns newly created Isolate on success, nullptr on failure. static Dart_Isolate CreateIsolateGroupAndSetupHelper( bool is_main_isolate, @@ -957,13 +852,6 @@ static Dart_Isolate CreateIsolateGroupAndSetup(const char* script_uri, } #endif // !defined(EXCLUDE_CFE_AND_KERNEL_PLATFORM) -#if !defined(DART_PRECOMPILED_RUNTIME) - if (strcmp(script_uri, DART_DEV_ISOLATE_NAME) == 0) { - return CreateAndSetupDartDevIsolate(script_uri, package_config, flags, - error, &exit_code); - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - if (strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { return CreateAndSetupServiceIsolate(script_uri, package_config, flags, error, &exit_code); @@ -1210,13 +1098,11 @@ void main(int argc, char** argv) { char* script_name = nullptr; CStringUniquePtr asset_resolution_base = CStringUniquePtr(); - // Allows the dartdev process to point to the desired package_config. char* package_config_override = nullptr; const int EXTRA_VM_ARGUMENTS = 10; CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS); CommandLineOptions dart_options(argc + EXTRA_VM_ARGUMENTS); bool print_flags_seen = false; - bool verbose_debug_seen = false; // Perform platform specific initialization. if (!Platform::Initialize()) { @@ -1248,39 +1134,36 @@ void main(int argc, char** argv) { } vm_options.AddArgument("--new_gen_growth_factor=4"); - auto parse_arguments = [&](int argc, char** argv, - CommandLineOptions* vm_options, - CommandLineOptions* dart_options, - bool parsing_dart_vm_options) { - bool success = Options::ParseArguments( - argc, argv, vm_run_app_snapshot, parsing_dart_vm_options, vm_options, - &script_name, dart_options, &print_flags_seen, &verbose_debug_seen); - if (!success) { - if (Options::help_option()) { - Options::PrintUsage(); - Platform::Exit(0); - } else if (Options::version_option()) { - Options::PrintVersion(); - Platform::Exit(0); - } else if (print_flags_seen) { - // Will set the VM flags, print them out and then we exit as no - // script was specified on the command line. - char* error = - Dart_SetVMFlags(vm_options->count(), vm_options->arguments()); - if (error != nullptr) { - Syslog::PrintErr("Setting VM flags failed: %s\n", error); - free(error); - Platform::Exit(kErrorExitCode); + auto parse_arguments = + [&](int argc, char** argv, CommandLineOptions* vm_options, + CommandLineOptions* dart_options, bool parsing_dart_vm_options) { + bool success = Options::ParseArguments( + argc, argv, vm_run_app_snapshot, parsing_dart_vm_options, + vm_options, &script_name, dart_options, &print_flags_seen); + if (!success) { + if (Options::help_option()) { + Options::PrintUsage(); + Platform::Exit(0); + } else if (Options::version_option()) { + Options::PrintVersion(); + Platform::Exit(0); + } else if (print_flags_seen) { + // Will set the VM flags, print them out and then we exit as no + // script was specified on the command line. + char* error = + Dart_SetVMFlags(vm_options->count(), vm_options->arguments()); + if (error != nullptr) { + Syslog::PrintErr("Setting VM flags failed: %s\n", error); + free(error); + Platform::Exit(kErrorExitCode); + } + Platform::Exit(0); + } else { + Options::PrintUsage(); + Platform::Exit(kErrorExitCode); + } } - Platform::Exit(0); - } else { - // This usage error case will only be invoked when - // Options::disable_dart_dev() is false. - Options::PrintUsage(); - Platform::Exit(kErrorExitCode); - } - } - }; + }; AppSnapshot* app_snapshot = nullptr; #if defined(DART_PRECOMPILED_RUNTIME) @@ -1378,9 +1261,8 @@ void main(int argc, char** argv) { } }; - // At this point, script_name now points to a script if DartDev is disabled - // or a valid file path was provided as the first non-flag argument. - // Otherwise, script_name can be nullptr if DartDev should be run. + // At this point, script_name now points to a script or a valid file path + // was provided as the first non-flag argument. if (script_name != nullptr) { if (!CheckForInvalidPath(script_name)) { Platform::Exit(0); @@ -1484,36 +1366,21 @@ void main(int argc, char** argv) { &ServiceStreamCancelCallback); Dart_SetFileModifiedCallback(&FileModifiedCallback); Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); - bool ran_dart_dev = false; bool should_run_user_program = true; #if !defined(DART_PRECOMPILED_RUNTIME) - if (DartDevIsolate::should_run_dart_dev() && !Options::disable_dart_dev() && - Options::gen_snapshot_kind() == SnapshotKind::kNone) { - DartDevIsolate::DartDev_Result dartdev_result = DartDevIsolate::RunDartDev( - CreateIsolateGroupAndSetup, &package_config_override, &script_name, - &vm_options, &dart_options); - ASSERT(dartdev_result != DartDevIsolate::DartDev_Result_Unknown); - ran_dart_dev = true; - should_run_user_program = - (dartdev_result == DartDevIsolate::DartDev_Result_Run); - if (should_run_user_program) { - try_load_snapshots_lambda(); - } - } else if (script_name == nullptr && - Options::gen_snapshot_kind() != SnapshotKind::kNone) { + if (script_name == nullptr && + Options::gen_snapshot_kind() != SnapshotKind::kNone) { Syslog::PrintErr( "Snapshot generation should be done using the 'dart compile' " "command.\n"); Platform::Exit(kErrorExitCode); } - - if (!ran_dart_dev && - (Options::resident() || - Options::resident_compiler_info_file_path() != nullptr || + if (!Options::resident() && + (Options::resident_compiler_info_file_path() != nullptr || Options::resident_server_info_file_path() != nullptr)) { Syslog::PrintErr( - "Passing the `--resident` flag to `dart` is invalid. It must be passed " - "to `dart run`.\n"); + "Error: the --resident flag must be passed whenever the " + "--resident-compiler-info-file option is passed.\n"); Platform::Exit(kErrorExitCode); } #endif // !defined(DART_PRECOMPILED_RUNTIME) @@ -1550,9 +1417,6 @@ void main(int argc, char** argv) { delete app_snapshot; free(app_script_uri); - if (ran_dart_dev && script_name != nullptr) { - free(script_name); - } asset_resolution_base.reset(); // Free copied argument strings if converted. diff --git a/runtime/bin/main_options.cc b/runtime/bin/main_options.cc index b94961c7239..0340f58f86c 100644 --- a/runtime/bin/main_options.cc +++ b/runtime/bin/main_options.cc @@ -8,7 +8,7 @@ #include #include -#include "bin/dartdev_isolate.h" +#include "bin/common_options.h" #include "bin/error_exit.h" #include "bin/file_system_watcher.h" #if defined(DART_IO_SECURE_SOCKET_DISABLED) @@ -16,7 +16,6 @@ #else // defined(DART_IO_SECURE_SOCKET_DISABLED) #include "bin/io_service.h" #endif // defined(DART_IO_SECURE_SOCKET_DISABLED) -#include "bin/options.h" #include "bin/platform.h" #include "bin/utils.h" #include "platform/syslog.h" @@ -40,14 +39,13 @@ static const char* const kSnapshotKindNames[] = { nullptr, }; -// These strings must match the enum VerbosityLevel in main_options.h. -static const char* const kVerbosityLevelNames[] = { - "error", "warning", "info", "all", nullptr, -}; - SnapshotKind Options::gen_snapshot_kind_ = kNone; -VerbosityLevel Options::verbosity_ = kAll; -bool Options::enable_vm_service_ = false; + +#if !defined(DART_PRECOMPILED_RUNTIME) +DFE* Options::dfe_ = nullptr; + +DEFINE_STRING_OPTION_CB(dfe, { Options::dfe()->set_frontend_filename(value); }); +#endif // !defined(DART_PRECOMPILED_RUNTIME) #define OPTION_FIELD(variable) Options::variable##_ @@ -85,12 +83,6 @@ ENUM_OPTIONS_LIST(ENUM_OPTION_DEFINITION) CB_OPTIONS_LIST(CB_OPTION_DEFINITION) #undef CB_OPTION_DEFINITION -#if !defined(DART_PRECOMPILED_RUNTIME) -DFE* Options::dfe_ = nullptr; - -DEFINE_STRING_OPTION_CB(dfe, { Options::dfe()->set_frontend_filename(value); }); -#endif // !defined(DART_PRECOMPILED_RUNTIME) - static void hot_reload_test_mode_callback(CommandLineOptions* vm_options) { // Identity reload. vm_options->AddArgument("--identity_reload"); @@ -131,133 +123,175 @@ static void hot_reload_rollback_test_mode_callback( DEFINE_BOOL_OPTION_CB(hot_reload_rollback_test_mode, hot_reload_rollback_test_mode_callback); +bool Options::ParseArguments(int argc, + char** argv, + bool vm_run_app_snapshot, + bool parsing_dart_vm_options, + CommandLineOptions* vm_options, + char** script_name, + CommandLineOptions* dart_options, + bool* print_flags_seen) { + int i = 0; +#if !defined(DART_PRECOMPILED_RUNTIME) + // DART_VM_OPTIONS is only implemented for compiled executables. + ASSERT(!parsing_dart_vm_options); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + if (!parsing_dart_vm_options) { + // Start processing arguments after argv[0] which would be the executable. + i = 1; + } + + CommandLineOptions temp_vm_options(vm_options->max_count()); + // Parse out the vm options. + while (i < argc) { + bool skipVmOption = false; + if (!OptionProcessor::TryProcess(argv[i], &temp_vm_options)) { + // Check if this flag is a potentially valid VM flag. + if (!OptionProcessor::IsValidFlag(argv[i])) { + break; + } + if (IsOption(argv[i], "print-flags")) { + *print_flags_seen = true; + } else if (IsOption(argv[i], "disable-dart-dev")) { + skipVmOption = true; + } + if (!skipVmOption) { + temp_vm_options.AddArgument(argv[i]); + } + } else if (IsOption(argv[i], "profile-microtasks")) { + temp_vm_options.AddArgument(argv[i]); + } + i++; + } + +#if !defined(DART_PRECOMPILED_RUNTIME) + Options::dfe()->set_use_dfe(); +#endif // !defined(DART_PRECOMPILED_RUNTIME) + if (Options::deterministic()) { + // Both an embedder and VM flag. + temp_vm_options.AddArgument("--deterministic"); + } + + Socket::set_short_socket_read(Options::short_socket_read()); + Socket::set_short_socket_write(Options::short_socket_write()); +#if !defined(DART_IO_SECURE_SOCKET_DISABLED) + SSLCertContext::set_root_certs_file(Options::root_certs_file()); + SSLCertContext::set_root_certs_cache(Options::root_certs_cache()); + SSLCertContext::set_long_ssl_cert_evaluation( + Options::long_ssl_cert_evaluation()); + SSLCertContext::set_bypass_trusting_system_roots( + Options::bypass_trusting_system_roots()); +#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) + + FileSystemWatcher::set_delayed_filewatch_callback( + Options::delayed_filewatch_callback()); + + if (Options::deterministic()) { + IOService::set_max_concurrency(1); + } + + // The arguments to the VM are at positions 1 through i-1 in argv. + Platform::SetExecutableArguments(i, argv); + + // Get the script name. + if (i < argc) { + *script_name = Utils::StrDup(argv[i]); + i++; + // Handle argument parsing errors and missing script / command name when not + // processing options set via DART_VM_OPTIONS. + } else if (!parsing_dart_vm_options || Options::help_option() || // NOLINT + Options::version_option()) { // NOLINT + return false; + } + + const char** vm_argv = temp_vm_options.arguments(); + int vm_argc = temp_vm_options.count(); + vm_options->AddArguments(vm_argv, vm_argc); + +#if !defined(DART_PRECOMPILED_RUNTIME) + // If we're parsing DART_VM_OPTIONS, there shouldn't be any script set or + // Dart arguments left to parse. + if (parsing_dart_vm_options) { + ASSERT(i == argc); + return true; + } +#endif // !defined(DART_PRECOMPILED_RUNTIME) + + // Parse out options to be passed to dart main. + while (i < argc) { + dart_options->AddArgument(argv[i]); + i++; + } + if (!parsing_dart_vm_options) { + // Store the executable name. + if (Options::executable_name() != nullptr) { + Platform::SetExecutableName(Options::executable_name()); + Platform::SetResolvedExecutableName(Options::executable_name()); + } else { + Platform::SetExecutableName(argv[0]); + } + } + + // Verify consistency of arguments. + + // snapshot_depfile is an alias for depfile. Passing them both is an error. + if ((snapshot_deps_filename_ != nullptr) && (depfile_ != nullptr)) { + Syslog::PrintErr("Specify only one of --depfile and --snapshot_depfile\n"); + return false; + } + if (snapshot_deps_filename_ != nullptr) { + depfile_ = snapshot_deps_filename_; + snapshot_deps_filename_ = nullptr; + } + + if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) { + Syslog::PrintErr("Empty package file name specified.\n"); + return false; + } + if ((gen_snapshot_kind_ != kNone) && (snapshot_filename_ == nullptr)) { + Syslog::PrintErr( + "Generating a snapshot requires a filename (--snapshot).\n"); + return false; + } + if ((gen_snapshot_kind_ == kNone) && (depfile_ != nullptr) && + (snapshot_filename_ == nullptr) && + (depfile_output_filename_ == nullptr)) { + Syslog::PrintErr( + "Generating a depfile requires an output filename" + " (--depfile-output-filename or --snapshot).\n"); + return false; + } + 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; + } + + // If --snapshot is given without --snapshot-kind, default to script snapshot. + if ((snapshot_filename_ != nullptr) && (gen_snapshot_kind_ == kNone)) { + gen_snapshot_kind_ = kKernel; + } + + return true; +} + +// These strings must match the enum VerbosityLevel in main_options.h. +VerbosityLevel Options::verbosity_ = kAll; +bool Options::enable_vm_service_ = false; +bool Options::enable_dds_ = true; + void Options::PrintVersion() { - Syslog::Print("Dart SDK version: %s\n", Dart_VersionString()); + _PrintVersion(); } // clang-format off void Options::PrintUsage() { - Syslog::Print( - "Usage: dart [] []\n" - "\n" - "Executes the Dart script with " - "the given list of .\n" - "\n"); + _PrintUsage(); if (!Options::verbose_option()) { - Syslog::Print( -"Common VM flags:\n" -#if !defined(PRODUCT) -"--enable-asserts\n" -" Enable assert statements.\n" -#endif // !defined(PRODUCT) -"--help or -h\n" -" Display this message (add -v or --verbose for information about\n" -" all VM options).\n" -"--packages=\n" -" Where to find a package spec file.\n" -"--define== or -D=\n" -" Define an environment declaration. To specify multiple declarations,\n" -" use multiple instances of this option.\n" -#if !defined(PRODUCT) -"--observe[=[/]]\n" -" The observe flag is a convenience flag used to run a program with a\n" -" set of options which are often useful for debugging under Dart DevTools.\n" -" These options are currently:\n" -" --enable-vm-service[=[/]]\n" -" --serve-devtools\n" -" --pause-isolates-on-exit\n" -" --pause-isolates-on-unhandled-exceptions\n" -" --warn-on-pause-with-no-debugger\n" -" --timeline-streams=\"Compiler, Dart, GC, Microtask\"\n" -" This set is subject to change.\n" -" Please see these options (--help --verbose) for further documentation.\n" -"--write-service-info=\n" -" Outputs information necessary to connect to the VM service to the\n" -" specified file in JSON format. Useful for clients which are unable to\n" -" listen to stdout for the Dart VM service listening message.\n" -#endif // !defined(PRODUCT) -"--snapshot-kind=\n" -"--snapshot=\n" -" These snapshot options are used to generate a snapshot of the loaded\n" -" Dart script:\n" -" controls the kind of snapshot, it could be\n" -" kernel(default) or app-jit\n" -" specifies the file into which the snapshot is written\n" -"--version\n" -" Print the SDK version.\n"); + _PrintNonVerboseUsage(); } else { - Syslog::Print( -"Supported options:\n" -#if !defined(PRODUCT) -"--enable-asserts\n" -" Enable assert statements.\n" -#endif // !defined(PRODUCT) -"--help or -h\n" -" Display this message (add -v or --verbose for information about\n" -" all VM options).\n" -"--packages=\n" -" Where to find a package spec file.\n" -"--define== or -D=\n" -" Define an environment declaration. To specify multiple declarations,\n" -" use multiple instances of this option.\n" -#if !defined(PRODUCT) -"--observe[=[/]]\n" -" The observe flag is a convenience flag used to run a program with a\n" -" set of options which are often useful for debugging under Dart DevTools.\n" -" These options are currently:\n" -" --enable-vm-service[=[/]]\n" -" --serve-devtools\n" -" --pause-isolates-on-exit\n" -" --pause-isolates-on-unhandled-exceptions\n" -" --warn-on-pause-with-no-debugger\n" -" --timeline-streams=\"Compiler, Dart, GC, Microtask\"\n" -" This set is subject to change.\n" -" Please see these options for further documentation.\n" -"--profile-microtasks\n" -" Record information about each microtask. Information about completed\n" -" microtasks will be written to the \"Microtask\" timeline stream.\n" -#endif // !defined(PRODUCT) -"--version\n" -" Print the VM version.\n" -"\n" -"--trace-loading\n" -" enables tracing of library and script loading\n" -"\n" -#if !defined(PRODUCT) -"--enable-vm-service[=[/]]\n" -" Enables the VM service and listens on specified port for connections\n" -" (default port number is 8181, default bind address is localhost).\n" -"\n" -"--disable-service-auth-codes\n" -" Disables the requirement for an authentication code to communicate with\n" -" the VM service. Authentication codes help protect against CSRF attacks,\n" -" so it is not recommended to disable them unless behind a firewall on a\n" -" secure device.\n" -"\n" -"--enable-service-port-fallback\n" -" When the VM service is told to bind to a particular port, fallback to 0 if\n" -" it fails to bind instead of failing to start.\n" -"\n" -#endif // !defined(PRODUCT) -"--root-certs-file=\n" -" The path to a file containing the trusted root certificates to use for\n" -" secure socket connections.\n" -"--root-certs-cache=\n" -" The path to a cache directory containing the trusted root certificates to\n" -" use for secure socket connections.\n" -#if defined(DART_HOST_OS_LINUX) || \ - defined(DART_HOST_OS_ANDROID) || \ - defined(DART_HOST_OS_FUCHSIA) -"--namespace=\n" -" The path to a directory that dart:io calls will treat as the root of the\n" -" filesystem.\n" -#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) -"\n" -"The following options are only used for VM development and may\n" -"be changed in any future version:\n"); - const char* print_flags = "--print_flags"; - char* error = Dart_SetVMFlags(1, &print_flags); - ASSERT(error == nullptr); + _PrintVerboseUsage(); } } // clang-format on @@ -294,8 +328,8 @@ void Options::DestroyEnvironment() { // // DART_VM_OPTIONS should contain a list of comma-separated options and flags // with no spaces. Options that support providing multiple values as -// comma-separated lists (e.g., --timeline-streams=Dart,GC,Compiler) are not -// supported and will cause argument parsing to fail. +// comma-separated lists (e.g., --timeline-streams=Dart,GC,Compiler,Microtask) +// are not supported and will cause argument parsing to fail. char** Options::GetEnvArguments(int* argc) { ASSERT(argc != nullptr); const char* env_args_str = std::getenv("DART_VM_OPTIONS"); @@ -389,36 +423,22 @@ bool Options::ExtractPortAndAddress(const char* option_value, 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; -} - +#if !defined(PRODUCT) +static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; +static constexpr int DEFAULT_VM_SERVICE_SERVER_PORT = 8181; +static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; const char* Options::vm_service_server_ip_ = DEFAULT_VM_SERVICE_SERVER_IP; int Options::vm_service_server_port_ = INVALID_VM_SERVICE_SERVER_PORT; +#endif // !defined(PRODUCT) + bool Options::ProcessEnableVmServiceOption(const char* arg, CommandLineOptions* vm_options) { -#if !defined(PRODUCT) const char* value = OptionProcessor::ProcessOption(arg, "--enable-vm-service"); if (value == nullptr) { return false; } +#if !defined(PRODUCT) if (!ExtractPortAndAddress( value, &vm_service_server_port_, &vm_service_server_ip_, DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) { @@ -440,11 +460,11 @@ bool Options::ProcessEnableVmServiceOption(const char* arg, bool Options::ProcessObserveOption(const char* arg, CommandLineOptions* vm_options) { -#if !defined(PRODUCT) const char* value = OptionProcessor::ProcessOption(arg, "--observe"); if (value == nullptr) { return false; } +#if !defined(PRODUCT) if (!ExtractPortAndAddress( value, &vm_service_server_port_, &vm_service_server_ip_, DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) { @@ -471,352 +491,18 @@ bool Options::ProcessObserveOption(const char* arg, #endif // !defined(PRODUCT) } -bool Options::ProcessProfileMicrotasksOption(const char* arg, - CommandLineOptions* vm_options) { -#if !defined(PRODUCT) - constexpr const char* kProfileMicrotasksFlagAsCstr = "--profile-microtasks"; - constexpr const char* kAlternativeProfileMicrotasksFlagAsCstr = - "--profile_microtasks"; - if (strncmp(kProfileMicrotasksFlagAsCstr, arg, - strlen(kProfileMicrotasksFlagAsCstr)) == 0 || - strncmp(kAlternativeProfileMicrotasksFlagAsCstr, arg, - strlen(kAlternativeProfileMicrotasksFlagAsCstr)) == 0) { - profile_microtasks_ = true; - vm_options->AddArgument(kProfileMicrotasksFlagAsCstr); - return true; - } -#endif // !defined(PRODUCT) - return false; -} - -// Explicitly handle VM flags that can be parsed by DartDev's run command. -bool Options::ProcessVMDebuggingOptions(const char* arg, - CommandLineOptions* vm_options) { -#define IS_DEBUG_OPTION(name, arg) \ - if (strncmp(name, arg, strlen(name)) == 0) { \ - vm_options->AddArgument(arg); \ - return true; \ - } - -// This is an exhaustive set of VM flags that are accepted by 'dart run'. Flags -// defined in main_options.h do not need to be handled here as they already -// have handlers generated. -// -// NOTE: When updating this list of VM flags, be sure to make the corresponding -// changes in pkg/dartdev/lib/src/commands/run.dart. -#define HANDLE_DARTDEV_VM_DEBUG_OPTIONS(V, arg) \ - V("--enable-asserts", arg) \ - V("--pause-isolates-on-exit", arg) \ - V("--no-pause-isolates-on-exit", arg) \ - V("--pause-isolates-on-start", arg) \ - V("--no-pause-isolates-on-start", arg) \ - V("--pause-isolates-on-unhandled-exception", arg) \ - V("--no-pause-isolates-on-unhandled-exception", arg) \ - V("--warn-on-pause-with-no-debugger", arg) \ - V("--no-warn-on-pause-with-no-debugger", arg) \ - V("--timeline-streams", arg) \ - V("--timeline-recorder", arg) \ - V("--enable-experiment", arg) - HANDLE_DARTDEV_VM_DEBUG_OPTIONS(IS_DEBUG_OPTION, arg); - -#undef IS_DEBUG_OPTION -#undef HANDLE_DARTDEV_VM_DEBUG_OPTIONS - - return false; -} - -bool Options::ParseArguments(int argc, - char** argv, - bool vm_run_app_snapshot, - bool parsing_dart_vm_options, - CommandLineOptions* vm_options, - char** script_name, - CommandLineOptions* dart_options, - bool* print_flags_seen, - bool* verbose_debug_seen) { - int i = 0; -#if !defined(DART_PRECOMPILED_RUNTIME) - // DART_VM_OPTIONS is only implemented for compiled executables. - ASSERT(!parsing_dart_vm_options); -#endif // !defined(DART_PRECOMPILED_RUNTIME) - if (!parsing_dart_vm_options) { - // Store the executable name. - Platform::SetExecutableName(argv[0]); - - // Start the rest after the executable name. - i = 1; - } - - CommandLineOptions temp_vm_options(vm_options->max_count()); - - bool enable_dartdev_analytics = false; - bool disable_dartdev_analytics = false; - char* packages_argument = nullptr; - - // Parse out the vm options. - while (i < argc) { - bool skipVmOption = false; - if (!OptionProcessor::TryProcess(argv[i], &temp_vm_options)) { - // Check if this flag is a potentially valid VM flag. - if (!OptionProcessor::IsValidFlag(argv[i])) { - break; - } - // The following flags are processed as DartDev flags and are not to - // be treated as if they are VM flags. - if (IsOption(argv[i], "print-flags")) { - *print_flags_seen = true; - } else if (IsOption(argv[i], "verbose-debug")) { - *verbose_debug_seen = true; - } else if (IsOption(argv[i], "enable-analytics")) { - enable_dartdev_analytics = true; - skipVmOption = true; - } else if (IsOption(argv[i], "disable-analytics")) { - disable_dartdev_analytics = true; - skipVmOption = true; - } else if (IsOption(argv[i], "disable-telemetry")) { - disable_dartdev_analytics = true; - skipVmOption = true; - } else if (IsOption(argv[i], "suppress-analytics")) { - dart_options->AddArgument("--suppress-analytics"); - skipVmOption = true; - } else if (IsOption(argv[i], "no-analytics")) { - // Just add this option even if we don't go to dartdev. - // It is irrelevant for the vm. - dart_options->AddArgument("--no-analytics"); - skipVmOption = true; - } else if (IsOption(argv[i], "dds")) { - // This flag is set by default in dartdev, so we ignore it. --no-dds is - // a VM flag as disabling DDS changes how we configure the VM service, - // so we don't need to handle that case here. - skipVmOption = true; - } else if (IsOption(argv[i], "serve-observatory")) { - // This flag is currently set by default in vmservice_io.dart, so we - // ignore it. --no-serve-observatory is a VM flag so we don't need to - // handle that case here. - skipVmOption = true; - } else if (IsOption(argv[i], "print-dtd-uri")) { - skipVmOption = true; - } - if (!skipVmOption) { - temp_vm_options.AddArgument(argv[i]); - } +bool Options::ProcessDdsOption(const char* arg, + CommandLineOptions* vm_options) { + const char* value = OptionProcessor::ProcessOption(arg, "--dds"); + if (value == nullptr) { + value = OptionProcessor::ProcessOption(arg, "--no-dds"); + if (value == nullptr) { + return false; } - if (IsOption(argv[i], "packages")) { - packages_argument = argv[i]; - } - i++; + enable_dds_ = false; + } else { + enable_dds_ = true; } - -#if !defined(DART_PRECOMPILED_RUNTIME) - Options::dfe()->set_use_dfe(); -#else - // DartDev is not supported in AOT. - Options::disable_dart_dev_ = true; -#endif // !defined(DART_PRECOMPILED_RUNTIME) - if (Options::deterministic()) { - // Both an embedder and VM flag. - temp_vm_options.AddArgument("--deterministic"); - } - - Socket::set_short_socket_read(Options::short_socket_read()); - Socket::set_short_socket_write(Options::short_socket_write()); -#if !defined(DART_IO_SECURE_SOCKET_DISABLED) - SSLCertContext::set_root_certs_file(Options::root_certs_file()); - SSLCertContext::set_root_certs_cache(Options::root_certs_cache()); - SSLCertContext::set_long_ssl_cert_evaluation( - Options::long_ssl_cert_evaluation()); - SSLCertContext::set_bypass_trusting_system_roots( - Options::bypass_trusting_system_roots()); -#endif // !defined(DART_IO_SECURE_SOCKET_DISABLED) - - FileSystemWatcher::set_delayed_filewatch_callback( - Options::delayed_filewatch_callback()); - - if (Options::deterministic()) { - IOService::set_max_concurrency(1); - } - - // The arguments to the VM are at positions 1 through i-1 in argv. - Platform::SetExecutableArguments(i, argv); - - bool run_script = false; - // Get the script name. - if (i < argc) { -#if !defined(DART_PRECOMPILED_RUNTIME) - // If the script name is a valid file or a URL, we'll run the script - // directly. Otherwise, this might be a DartDev command and we need to try - // to find the DartDev snapshot so we can forward the command and its - // arguments. - bool is_potential_file_path = !DartDevIsolate::ShouldParseCommand(argv[i]); -#else - bool is_potential_file_path = true; -#endif // !defined(DART_PRECOMPILED_RUNTIME) - if (Options::disable_dart_dev() || - (Options::snapshot_filename() != nullptr) || is_potential_file_path) { - *script_name = Utils::StrDup(argv[i]); - run_script = true; - i++; - } -#if !defined(DART_PRECOMPILED_RUNTIME) - else { // NOLINT - DartDevIsolate::set_should_run_dart_dev(true); - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - } -#if !defined(DART_PRECOMPILED_RUNTIME) - else if (!Options::disable_dart_dev()) { // NOLINT - // Handles following invocation arguments: - // - dart help - // - dart --help - // - dart - if (((Options::help_option() && !Options::verbose_option()) || - (argc == 1))) { - DartDevIsolate::set_should_run_dart_dev(true); - // Let DartDev handle the default help message. - dart_options->AddArgument("help"); - return true; - } - // Handles cases where only analytics flags are provided. We need to start - // the DartDev isolate to set this state. - else if (enable_dartdev_analytics || disable_dartdev_analytics) { // NOLINT - // 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); - dart_options->AddArgument(enable_dartdev_analytics - ? "--enable-analytics" - : "--disable-analytics"); - return true; - } - // Let the VM handle '--version' and '--help --disable-dart-dev'. - // Otherwise, we'll launch the DartDev isolate to print its help message - // and set an error exit code. - else if (!Options::help_option() && !Options::version_option()) { // NOLINT - DartDevIsolate::PrintUsageErrorOnRun(); - return true; - } - return false; - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - // Handle argument parsing errors and missing script / command name when not - // processing options set via DART_VM_OPTIONS. - else if (!parsing_dart_vm_options || Options::help_option() || // NOLINT - Options::version_option()) { // NOLINT - return false; - } - USE(enable_dartdev_analytics); - USE(disable_dartdev_analytics); - USE(packages_argument); - - const char** vm_argv = temp_vm_options.arguments(); - int vm_argc = temp_vm_options.count(); - - vm_options->AddArguments(vm_argv, vm_argc); - -#if !defined(DART_PRECOMPILED_RUNTIME) - // If we're parsing DART_VM_OPTIONS, there shouldn't be any script set or - // Dart arguments left to parse. - if (parsing_dart_vm_options) { - ASSERT(i == argc); - return true; - } - - // If running with dartdev, attempt to parse VM flags which are part of the - // dartdev command (e.g., --enable-vm-service, --observe, etc). - bool record_vm_options = false; - if ((i < argc) && DartDevIsolate::ShouldParseVMOptions(argv[i])) { - record_vm_options = true; - } - if (!run_script && record_vm_options) { - // Skip the command. - int tmp_i = i + 1; - 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]) && - !OptionProcessor::IsValidShortFlag(argv[tmp_i])) { - break; - } - OptionProcessor::TryProcess(argv[tmp_i], vm_options); - tmp_i++; - } - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - - bool first_option = true; - // Parse out options to be passed to dart main. - while (i < argc) { - // dart run isn't able to parse these options properly. Since it doesn't - // need to use the values from these options, just strip them from the - // argument list passed to dart run. - if (!IsOption(argv[i], "observe") && - !IsOption(argv[i], "enable-vm-service")) { - dart_options->AddArgument(argv[i]); - } -#if !defined(DART_PRECOMPILED_RUNTIME) - if (IsOption(argv[i], "disable-dart-dev")) { - Syslog::PrintErr( - "Attempted to use --disable-dart-dev with a Dart CLI command.\n"); - Platform::Exit(kErrorExitCode); - } -#endif // !defined(DART_PRECOMIPLED_RUNTIME) - i++; - // Add DDS specific flags immediately after the dartdev command. - if (first_option) { - // DDS is only enabled for the run command. Make sure we don't pass DDS - // specific flags along with other commands, otherwise argument parsing - // will fail unexpectedly. -#if !defined(DART_PRECOMPILED_RUNTIME) - // Bring any --packages option into the dartdev command - if (DartDevIsolate::should_run_dart_dev() && - packages_argument != nullptr) { - dart_options->AddArgument(packages_argument); - } -#endif // !defined(DART_PRECOMPILED_RUNTIME) - first_option = false; - } - } - - // Verify consistency of arguments. - - // snapshot_depfile is an alias for depfile. Passing them both is an error. - if ((snapshot_deps_filename_ != nullptr) && (depfile_ != nullptr)) { - Syslog::PrintErr("Specify only one of --depfile and --snapshot_depfile\n"); - return false; - } - if (snapshot_deps_filename_ != nullptr) { - depfile_ = snapshot_deps_filename_; - snapshot_deps_filename_ = nullptr; - } - - if ((packages_file_ != nullptr) && (strlen(packages_file_) == 0)) { - Syslog::PrintErr("Empty package file name specified.\n"); - return false; - } - if ((gen_snapshot_kind_ != kNone) && (snapshot_filename_ == nullptr)) { - Syslog::PrintErr( - "Generating a snapshot requires a filename (--snapshot).\n"); - return false; - } - if ((gen_snapshot_kind_ == kNone) && (depfile_ != nullptr) && - (snapshot_filename_ == nullptr) && - (depfile_output_filename_ == nullptr)) { - Syslog::PrintErr( - "Generating a depfile requires an output filename" - " (--depfile-output-filename or --snapshot).\n"); - return false; - } - 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; - } - - // If --snapshot is given without --snapshot-kind, default to script snapshot. - if ((snapshot_filename_ != nullptr) && (gen_snapshot_kind_ == kNone)) { - gen_snapshot_kind_ = kKernel; - } - return true; } diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h index 5b696f74ade..a4e9740f933 100644 --- a/runtime/bin/main_options.h +++ b/runtime/bin/main_options.h @@ -7,6 +7,7 @@ #include "bin/dartutils.h" #include "bin/dfe.h" +#include "bin/options.h" #include "platform/globals.h" #include "platform/growable_array.h" #include "platform/hashmap.h" @@ -27,6 +28,7 @@ namespace bin { V(root_certs_cache, root_certs_cache) \ V(namespace, namespc) \ V(write_service_info, vm_write_service_info_filename) \ + V(executable_name, executable_name) \ /* The purpose of these flags is documented in */ \ /* pkg/dartdev/lib/src/commands/compilation_server.dart. */ \ V(resident_server_info_file, resident_server_info_file_path) \ @@ -44,11 +46,8 @@ namespace bin { V(short_socket_read, short_socket_read) \ V(short_socket_write, short_socket_write) \ V(disable_exit, exit_disabled) \ - V(preview_dart_2, nop_option) \ V(suppress_core_dump, suppress_core_dump) \ V(enable_service_port_fallback, enable_service_port_fallback) \ - V(disable_dart_dev, disable_dart_dev) \ - V(no_dds, disable_dds) \ V(long_ssl_cert_evaluation, long_ssl_cert_evaluation) \ V(bypass_trusting_system_roots, bypass_trusting_system_roots) \ V(delayed_filewatch_callback, delayed_filewatch_callback) \ @@ -85,8 +84,7 @@ namespace bin { V(ProcessEnvironmentOption) \ V(ProcessEnableVmServiceOption) \ V(ProcessObserveOption) \ - V(ProcessProfileMicrotasksOption) \ - V(ProcessVMDebuggingOptions) + V(ProcessDdsOption) // This enum must match the strings in kSnapshotKindNames in main_options.cc. enum SnapshotKind { @@ -95,7 +93,6 @@ enum SnapshotKind { kAppJIT, }; -// This enum must match the strings in kVerbosityLevelNames in main_options.cc. enum VerbosityLevel { kError, kWarning, @@ -103,9 +100,9 @@ enum VerbosityLevel { kAll, }; -static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; -static constexpr int DEFAULT_VM_SERVICE_SERVER_PORT = 8181; -static constexpr int INVALID_VM_SERVICE_SERVER_PORT = -1; +static const char* const kVerbosityLevelNames[] = { + "error", "warning", "info", "all", nullptr, +}; class Options { public: @@ -117,8 +114,7 @@ class Options { CommandLineOptions* vm_options, char** script_name, CommandLineOptions* dart_options, - bool* print_flags_seen, - bool* verbose_debug_seen); + bool* print_flags_seen); #define STRING_OPTION_GETTER(flag, variable) \ static const char* variable() { return variable##_; } @@ -149,21 +145,14 @@ class Options { CB_OPTIONS_LIST(CB_OPTIONS_DECL) #undef CB_OPTIONS_DECL - static bool preview_dart_2() { return true; } - static dart::SimpleHashMap* environment() { return environment_; } static bool enable_vm_service() { return enable_vm_service_; } +#if !defined(PRODUCT) static const char* vm_service_server_ip() { return vm_service_server_ip_; } static int vm_service_server_port() { return vm_service_server_port_; } - - // TODO(bkonyi): remove once DartDev moves to AOT and this flag can be - // provided directly to the process spawned by `dart run` and `dart test`. - // - // See https://github.com/dart-lang/sdk/issues/53576 - static void set_mark_main_isolate_as_system_isolate(bool state) { - mark_main_isolate_as_system_isolate_ = state; - } +#endif // !defined(PRODUCT) + static bool enable_dds() { return enable_dds_; } static Dart_KernelCompilationVerbosityLevel verbosity_level() { return VerbosityLevelToDartAPI(verbosity_); @@ -238,9 +227,13 @@ class Options { } // VM Service argument processing. - static const char* vm_service_server_ip_; static bool enable_vm_service_; +#if !defined(PRODUCT) + static const char* vm_service_server_ip_; static int vm_service_server_port_; +#endif // !defined(PRODUCT) + static bool enable_dds_; + static bool ExtractPortAndAddress(const char* option_value, int* out_port, const char** out_ip, diff --git a/runtime/bin/platform.h b/runtime/bin/platform.h index 74acf310b1e..d8b6409c615 100644 --- a/runtime/bin/platform.h +++ b/runtime/bin/platform.h @@ -72,6 +72,13 @@ class Platform { executable_name_ = executable_name; } static const char* GetExecutableName(); + + // Stores the resolved executable name. + static void SetResolvedExecutableName(const char* resolved_executable_name) { + const char* expect_old_is_null = nullptr; + resolved_executable_name_.compare_exchange_strong(expect_old_is_null, + resolved_executable_name); + } static const char* GetResolvedExecutableName() { if (resolved_executable_name_.load() == nullptr) { // Try to resolve the executable path using platform specific APIs. diff --git a/runtime/bin/process_linux.cc b/runtime/bin/process_linux.cc index 9778dd67611..4b26d9df417 100644 --- a/runtime/bin/process_linux.cc +++ b/runtime/bin/process_linux.cc @@ -519,15 +519,18 @@ class ProcessStarter { ReportChildError(); } close(write_out_[0]); + close(write_out_[1]); if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ReportChildError(); } + close(read_in_[0]); close(read_in_[1]); if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ReportChildError(); } + close(read_err_[0]); close(read_err_[1]); } else { ASSERT(mode_ == kInheritStdio); @@ -595,12 +598,13 @@ class ProcessStarter { environ = program_environment_; } - // Report the final PID and do the exec. - ReportPid(getpid()); // getpid cannot fail. char realpath[PATH_MAX]; if (!FindPathInNamespace(realpath, PATH_MAX)) { ReportChildError(); } + + // Report the final PID and do the exec. + ReportPid(getpid()); // getpid cannot fail. // TODO(dart:io) Test for the existence of execveat, and use it // instead. execvp(realpath, const_cast(program_arguments_)); diff --git a/runtime/bin/process_macos.cc b/runtime/bin/process_macos.cc index 9bae25e65b1..8aebf479c70 100644 --- a/runtime/bin/process_macos.cc +++ b/runtime/bin/process_macos.cc @@ -451,15 +451,18 @@ class ProcessStarter { ReportChildError(); } close(write_out_[0]); + close(write_out_[1]); if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) { ReportChildError(); } + close(read_in_[0]); close(read_in_[1]); if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) { ReportChildError(); } + close(read_err_[0]); close(read_err_[1]); } else { ASSERT(mode_ == kInheritStdio); diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc index a531acf0a1d..cfbfabe4f28 100644 --- a/runtime/bin/process_win.cc +++ b/runtime/bin/process_win.cc @@ -1044,10 +1044,7 @@ int Process::Exec(Namespace* namespc, return -1; } CloseHandle(child_process); - // We exit the process here to simulate the same behaviour as exec on systems - // that support it. - ExitProcess(retval); - return 0; + return retval; } bool Process::Kill(intptr_t id, int signal) { diff --git a/runtime/bin/utils_win.cc b/runtime/bin/utils_win.cc index 99452458f1f..c73d97299bb 100644 --- a/runtime/bin/utils_win.cc +++ b/runtime/bin/utils_win.cc @@ -8,6 +8,7 @@ #include // NOLINT #include // NOLINT #include +#include #include "bin/utils.h" #include "bin/utils_win.h" @@ -188,6 +189,70 @@ const wchar_t* StringUtilsWin::Utf8ToWide(const char* utf8, StringUtilsWin::Utf8ToWide(const_cast(utf8), len, result_len)); } +// This code is identical to the one in process_patch.dart, please ensure +// changes made here are also done in process_patch.dart. +char* StringUtilsWin::ArgumentEscape(const char* argument) { + std::string arg_str(argument); + if (arg_str.empty()) { + return Utils::StrDup(R"("")"); + } + std::string result_str = arg_str; + if (arg_str.find('\t') != std::string::npos || + arg_str.find(' ') != std::string::npos || + arg_str.find('"') != std::string::npos) { + // Produce something that the C runtime on Windows will parse + // back as this string. + + // Replace any number of '\' followed by '"' with + // twice as many '\' followed by '\"'. + char backslash = '\\'; + std::stringstream sb; + size_t nextPos = 0; + size_t quotePos = arg_str.find('"', nextPos); + + while (quotePos != std::string::npos) { + size_t numBackslash = 0; + size_t pos = quotePos - 1; + while (pos != std::string::npos && arg_str[pos] == backslash) { + numBackslash++; + pos--; + } + sb << arg_str.substr(nextPos, quotePos - numBackslash - nextPos); + for (size_t i = 0; i < numBackslash; i++) { + sb << R"(\\)"; + } + sb << R"(\")"; + nextPos = quotePos + 1; + quotePos = arg_str.find('"', nextPos); + } + sb << arg_str.substr(nextPos); + result_str = sb.str(); + + // Add '"' at the beginning and end and replace all '\' at + // the end with two '\'. + std::stringstream sb2; + sb2 << '"'; + sb2 << result_str; + + // Find the last non-backslash character to determine the actual end + // of the string + size_t lastCharPos = arg_str.length() - 1; + while (lastCharPos != std::string::npos && + arg_str[lastCharPos] == backslash) { + sb2 << '\\'; + lastCharPos--; + } + sb2 << '"'; + result_str = sb2.str(); + } + // Allocate memory for the C-style string and copy the content + intptr_t len = result_str.length() + 1; + char* c_str_result = static_cast(malloc(len * sizeof(char))); + if (c_str_result == nullptr) return nullptr; // Allocation failure. + snprintf(c_str_result, len * sizeof(char), "%s", result_str.c_str()); + return c_str_result; +} + bool ShellUtils::GetUtf8Argv(int argc, char** argv) { wchar_t* command_line = GetCommandLineW(); int unicode_argc; diff --git a/runtime/bin/utils_win.h b/runtime/bin/utils_win.h index 101760e7bfc..00b105f22f0 100644 --- a/runtime/bin/utils_win.h +++ b/runtime/bin/utils_win.h @@ -6,6 +6,7 @@ #define RUNTIME_BIN_UTILS_WIN_H_ #include +#include #include #include "platform/utils.h" @@ -43,6 +44,7 @@ class StringUtilsWin { static const wchar_t* Utf8ToWide(const char* utf8, intptr_t len = -1, intptr_t* result_len = nullptr); + static char* ArgumentEscape(const char* argument); private: DISALLOW_ALLOCATION(); diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc index c7bb1f7cdbe..4b040d18386 100644 --- a/runtime/bin/vmservice_impl.cc +++ b/runtime/bin/vmservice_impl.cc @@ -9,7 +9,6 @@ #include "bin/builtin.h" #include "bin/dartutils.h" #include "bin/isolate_data.h" -#include "bin/main_options.h" #include "bin/platform.h" #include "bin/thread.h" #include "bin/utils.h" @@ -35,6 +34,7 @@ namespace bin { } static constexpr const char* kVMServiceIOLibraryUri = "dart:vmservice_io"; +static constexpr const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost"; void NotifyServerState(Dart_NativeArguments args) { Dart_EnterScope(); diff --git a/runtime/tests/vm/dart/exported_symbols_test.dart b/runtime/tests/vm/dart/exported_symbols_test.dart index e0e39547ebd..e1b6ca5f7d9 100644 --- a/runtime/tests/vm/dart/exported_symbols_test.dart +++ b/runtime/tests/vm/dart/exported_symbols_test.dart @@ -4,6 +4,7 @@ import "dart:io"; import "package:expect/expect.dart"; +import 'package:path/path.dart' as p; import "use_flag_test_helper.dart"; @@ -35,7 +36,9 @@ main() { Platform.isMacOS ? "--extern-only" : "--dynamic", "--defined-only", "--format=just-symbols", - Platform.executable, + isAOTRuntime + ? Platform.executable + : p.join(p.dirname(Platform.executable), 'dartvm'), ]); if (result.exitCode != 0) { print("nm failed"); diff --git a/runtime/tests/vm/dart/use_flag_test_helper.dart b/runtime/tests/vm/dart/use_flag_test_helper.dart index 6d12ad92d95..38c14c48811 100644 --- a/runtime/tests/vm/dart/use_flag_test_helper.dart +++ b/runtime/tests/vm/dart/use_flag_test_helper.dart @@ -58,7 +58,7 @@ late final genSnapshot = () { } throw 'Could not find gen_snapshot for build directory $buildDir'; }(); -final dart = path.join(buildDir, 'dart' + (Platform.isWindows ? '.exe' : '')); +final dart = path.join(buildDir, 'dartvm' + (Platform.isWindows ? '.exe' : '')); final dartPrecompiledRuntime = path.join( buildDir, 'dartaotruntime' + (Platform.isWindows ? '.exe' : ''), diff --git a/runtime/tools/entitlements/dart.plist b/runtime/tools/entitlements/dart.plist index eeda4be95dc..3322fe106b9 100644 --- a/runtime/tools/entitlements/dart.plist +++ b/runtime/tools/entitlements/dart.plist @@ -2,7 +2,7 @@ - com.apple.security.cs.allow-jit + com.apple.security.cs.allow-unsigned-executable-memory com.apple.security.cs.disable-library-validation diff --git a/runtime/tools/entitlements/dartvm.plist b/runtime/tools/entitlements/dartvm.plist new file mode 100644 index 00000000000..eeda4be95dc --- /dev/null +++ b/runtime/tools/entitlements/dartvm.plist @@ -0,0 +1,10 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + + + \ No newline at end of file diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 17b62222bd3..136e9e4c686 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -23,6 +23,7 @@ declare_args() { dart_platform_sdk = true # Path to stripped dart binaries relative to build output directory. + dartvm_stripped_binary = "dartvm" dart_stripped_binary = "dart" dart_aotruntime_stripped_binary = "dartaotruntime_product" gen_snapshot_stripped_binary = "gen_snapshot_product" @@ -37,27 +38,23 @@ declare_args() { # ..dart-sdk/ # ....bin/ # ......dart or dart.exe (executable) +# ......dartvm or dartvm.exe (executable) # ......dart.lib (import library for VM native extensions on Windows) # ......dartaotruntime or dartaotruntime.exe (executable) # ......utils/gen_snapshot or utils/gen_snapshot.exe (if not on ia32) # ......snapshots/ -# ........analysis_server.dart.snapshot +# ........analysis_server.dart.snapshot (JIT snapshot) # ........analysis_server_aot.dart.snapshot (AOT snapshot, if not on ia32) # ........dart2bytecode.snapshot (AOT snapshot, for selected targets) -# ........dart2js_aot.dart.snapshot (AOT snapshot, if not on ia32) -# ........dart2js.dart.snapshot (JIT snapshot only on ia32) -# ........dart2wasm_product.snapshot (if not on ia32) -# ........dartdev.dart.snapshot (app-jit snapshot or kernel dill file) -# ........dartdevc_aot.dart.snapshot (AOT snapshot, if not on ia32) -# ........dartdevc.dart.snapshot (JIT snapshot only on ia32) -# ........dds_aot.dart.snapshot (AOT snapshot, if not on ia32) -# ........dds.dart.snapshot (JIT snapshot only on ia32) -# ........dart_tooling_daemon_aot.dart.snapshot (AOT snapshot, if not on ia32) -# ........dart_tooling_daemon.dart.snapshot (JIT snapshot only on ia32) -# ........frontend_server_aot.dart.snapshot (AOT snapshot, if not on ia32) -# ........frontend_server.dart.snapshot (JIT snapshot only on ia32) -# ........gen_kernel_aot.dart.snapshot (if not on ia32) -# ........kernel-service.dart.snapshot +# ........dart2js_aot.dart.snapshot (AOT snapshot) +# ........dart2wasm_product.snapshot (AOT snapshot) +# ........dartdev_aot.dart.snapshot (AOT snapshot) +# ........dartdevc_aot.dart.snapshot (AOT snapshot) +# ........dds_aot.dart.snapshot (AOT snapshot) +# ........dart_tooling_daemon_aot.dart.snapshot (AOT snapshot) +# ........frontend_server_aot.dart.snapshot (AOT snapshot) +# ........gen_kernel_aot.dart.snapshot (AOT snapshot) +# ........kernel-service.dart.snapshot (JIT snapshot) # ........kernel_worker_aot.dart.snapshot (AOT snapshot) # ......resources/ # ........dartdoc/ @@ -112,23 +109,11 @@ _platform_sdk_scripts = [] _full_sdk_scripts = [] # Snapshots that go under bin/snapshots -_platform_sdk_snapshots = [ - [ - "analysis_server", - "../utils/analysis_server", - "analysis_server", - ], - [ - "dartdev", - "../utils/dartdev:dartdev", - "dartdev", - ], - [ - "dart_tooling_daemon", - "../utils/dtd:dtd", - "dart_tooling_daemon", - ], -] +_platform_sdk_snapshots = [ [ + "analysis_server", + "../utils/analysis_server", + "analysis_server", + ] ] if (dart_target_arch != "ia32" && dart_target_arch != "x86") { _platform_sdk_snapshots += [ [ @@ -141,6 +126,11 @@ if (dart_target_arch != "ia32" && dart_target_arch != "x86") { "../utils/kernel-service:frontend_server_aot_product", "frontend_server_aot", ], + [ + "dartdev_aot", + "../utils/dartdev:dartdev_aot", + "dartdev_aot", + ], [ "dds_aot_product", "../utils/dds:dds_aot", @@ -158,18 +148,11 @@ if (dart_target_arch != "ia32" && dart_target_arch != "x86") { ], ] } else { - _platform_sdk_snapshots += [ - [ - "frontend_server", - "../utils/kernel-service:frontend_server", - "frontend_server", - ], - [ - "dds", - "../utils/dds:dds", - "dds", - ], - ] + _platform_sdk_snapshots += [ [ + "dds", + "../utils/dds:dds", + "dds", + ] ] } if (dart_snapshot_kind == "app-jit") { _platform_sdk_snapshots += [ [ @@ -190,9 +173,6 @@ if ((dart_target_arch == "x64" || dart_target_arch == "arm64") && ] ] } -# AOT snapshots are not supported on the ia32 architecture, we continue -# to use JIT snapshots on that architecture, the conditional code below -# accounts for this. if (dart_target_arch != "ia32" && dart_target_arch != "x86") { _full_sdk_snapshots = _platform_sdk_snapshots + [ @@ -221,23 +201,7 @@ if (dart_target_arch != "ia32" && dart_target_arch != "x86") { ], ] } else { - _full_sdk_snapshots = _platform_sdk_snapshots + [ - [ - "dart2js", - "../utils/compiler:dart2js", - "dart2js", - ], - [ - "dartdevc", - "../utils/ddc:dartdevc", - "dartdevc", - ], - [ - "kernel_worker", - "../utils/bazel:kernel_worker", - "kernel_worker", - ], - ] + _full_sdk_snapshots = _platform_sdk_snapshots } # Libraries that go under lib/ @@ -349,13 +313,13 @@ if (target_os != current_os && target_os == "fuchsia") { # make a link to the symlink rather than the symlink's target, and the # relative symlink interpreted from a different containing directory # will not find the actual binary. - action("copy_dart") { + action("copy_dartvm") { visibility = [ ":create_common_sdk" ] - dart_label = "../runtime/bin:dart" + dart_label = "../runtime/bin:dartvm" deps = [ dart_label ] dart_out = get_label_info(dart_label, "root_out_dir") - sources = [ "$dart_out/$dart_stripped_binary" ] - outputs = [ "$root_out_dir/$dart_sdk_output/bin/$dart_stripped_binary" ] + sources = [ "$dart_out/$dartvm_stripped_binary" ] + outputs = [ "$root_out_dir/$dart_sdk_output/bin/$dartvm_stripped_binary" ] script = "/bin/ln" args = [ "-snf", @@ -364,18 +328,36 @@ if (target_os != current_os && target_os == "fuchsia") { ] } } else { + copy("copy_dartvm") { + visibility = [ ":create_common_sdk" ] + deps = [ "../runtime/bin:dartvm" ] + dart_out = get_label_info("../runtime/bin:dartvm", "root_out_dir") + sources = [ "$dart_out/${dartvm_stripped_binary}${executable_suffix}" ] + if (is_win && dart_lib_export_symbols) { + sources += [ "$dart_out/dartvm.lib" ] + } + if (_has_dot_sym) { + sources += [ "$dart_out/dartvm.sym" ] + } + outputs = [ "$root_out_dir/$dart_sdk_output/bin/{{source_file_part}}" ] + } +} + +if (dart_target_arch != "ia32" && dart_target_arch != "x86") { copy("copy_dart") { visibility = [ ":create_common_sdk" ] deps = [ "../runtime/bin:dart" ] - dart_out = get_label_info("../runtime/bin:dart", "root_out_dir") - sources = [ "$dart_out/${dart_stripped_binary}${executable_suffix}" ] - if (is_win && dart_lib_export_symbols) { - sources += [ "$dart_out/dart.lib" ] - } - if (_has_dot_sym) { - sources += [ "$dart_out/dart.sym" ] - } - outputs = [ "$root_out_dir/$dart_sdk_output/bin/{{source_file_part}}" ] + src_dir = get_label_info("../runtime/bin:dart", "root_out_dir") + sources = [ "$src_dir/${dart_stripped_binary}${executable_suffix}" ] + outputs = [ "$root_out_dir/$dart_sdk_output/bin/dart${executable_suffix}" ] + } +} else { + copy("copy_dart") { + visibility = [ ":create_common_sdk" ] + deps = [ "../runtime/bin:dartvm" ] + src_dir = get_label_info("../runtime/bin:dartvm", "root_out_dir") + sources = [ "$src_dir/${dartvm_stripped_binary}${executable_suffix}" ] + outputs = [ "$root_out_dir/$dart_sdk_output/bin/dart${executable_suffix}" ] } } @@ -499,9 +481,9 @@ foreach(snapshot, _full_sdk_snapshots) { root = root_out_dir } - # The dds and dtd snapshots are output to root_out_dir in order to be - # compatible with the way the dart sdk is distributed internally. - if (snapshot[0] == "dds_aot_product" || + # The dartdev, dds and dtd snapshots are output to root_out_dir in order to + # be compatible with the way the dart sdk is distributed internally. + if (snapshot[0] == "dartdev_aot" || snapshot[0] == "dds_aot_product" || snapshot[0] == "dart_tooling_daemon_aot_product") { root = root_out_dir } @@ -818,6 +800,7 @@ group("create_common_sdk") { ":copy_api_readme", ":copy_dart", ":copy_dartdoc_files", + ":copy_dartvm", ":copy_headers", ":copy_libraries_specification", ":copy_license", diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart index 9ea25e45027..b905a3466c7 100644 --- a/sdk/lib/_internal/vm/bin/vmservice_server.dart +++ b/sdk/lib/_internal/vm/bin/vmservice_server.dart @@ -167,8 +167,14 @@ class _DebuggingSession { if (FileSystemEntity.typeSync(script) == FileSystemEntityType.notFound) { script = [dartDir, 'dds_aot.dart.snapshot'].join(Platform.pathSeparator); if (FileSystemEntity.typeSync(script) == FileSystemEntityType.notFound) { + // We could be running on IA32 architecture so check if the JIT + // snapshot is available. executable = [dartDir, dart].join(Platform.pathSeparator); - script = 'development-service'; + script = [dartDir, 'dds.dart.snapshot'].join(Platform.pathSeparator); + if (FileSystemEntity.typeSync(script) == + FileSystemEntityType.notFound) { + script = 'development-service'; + } } } diff --git a/tests/ffi/native_assets/helpers.dart b/tests/ffi/native_assets/helpers.dart index 40e95e65ea0..18a82b059ca 100644 --- a/tests/ffi/native_assets/helpers.dart +++ b/tests/ffi/native_assets/helpers.dart @@ -29,7 +29,10 @@ final ffiTestFunctionsFileName = dylibName('ffi_test_functions'); final cwdUri = Directory.current.uri; final platformExecutableUriAbsolute = cwdUri.resolveUri( - Uri.file(Platform.resolvedExecutable), + Uri.file( + Platform.resolvedExecutable.replaceAll(r'\', '/'), + windows: Platform.isWindows, + ), ); /// The build folder on desktop platforms. @@ -58,7 +61,7 @@ final genSnapshotUri = buildUriAbsolute.resolve( 'gen_snapshot$standaloneExtensionExe', ); -final dartUri = buildUriAbsolute.resolve('dart$standaloneExtensionExe'); +final dartUri = buildUriAbsolute.resolve('dartvm$standaloneExtensionExe'); final dartPrecompiledRuntimeUri = buildUriAbsolute.resolve( 'dartaotruntime$standaloneExtensionExe', diff --git a/tests/standalone/regress_41329_absolute_test.dart b/tests/standalone/regress_41329_absolute_test.dart index e0ae5055c41..97c333eb7a6 100644 --- a/tests/standalone/regress_41329_absolute_test.dart +++ b/tests/standalone/regress_41329_absolute_test.dart @@ -28,6 +28,6 @@ Future main() async { final link2 = Link('dart')..createSync(linkLocation, recursive: true); final path = Uri.parse(link2.absolute.path).path; Directory.current = origDir; - final result = await Process.run('$path', ['help']); + final result = await Process.run('$path', ['--help']); Expect.equals(result.exitCode, 0); } diff --git a/tests/standalone/regress_41329_relative_test.dart b/tests/standalone/regress_41329_relative_test.dart index e35e126cb1c..2c29d9e8efd 100644 --- a/tests/standalone/regress_41329_relative_test.dart +++ b/tests/standalone/regress_41329_relative_test.dart @@ -27,6 +27,6 @@ Future main() async { final link2 = Link('dart')..createSync(linkLocation, recursive: true); final path = Uri.parse(link2.absolute.path).path; Directory.current = origDir; - final result = await Process.run('${path}', ['help']); + final result = await Process.run('${path}', ['--help']); Expect.equals(result.exitCode, 0); } diff --git a/tools/bots/try_benchmarks.sh b/tools/bots/try_benchmarks.sh index 31839262c21..756a980d014 100755 --- a/tools/bots/try_benchmarks.sh +++ b/tools/bots/try_benchmarks.sh @@ -150,6 +150,7 @@ for command; do out/ReleaseX64/gen/kernel_service.dill \ out/ReleaseX64/dart-sdk \ out/ReleaseX64/dart \ + out/ReleaseX64/dartvm \ out/ReleaseX64/gen_snapshot \ out/ReleaseX64/kernel-service.dart.snapshot \ out/ReleaseX64/dart2wasm.snapshot \ diff --git a/tools/gn.py b/tools/gn.py index 752c6aa59d2..ae9deb86f8f 100755 --- a/tools/gn.py +++ b/tools/gn.py @@ -280,6 +280,7 @@ def ToGnArgs(args, mode, arch, target_os, sanitizer, verify_sdk_hash, # We don't support stripping on Windows if host_os != 'win': gn_args['dart_stripped_binary'] = 'exe.stripped/dart' + gn_args['dartvm_stripped_binary'] = 'exe.stripped/dartvm' gn_args['dart_aotruntime_stripped_binary'] = ( 'exe.stripped/dartaotruntime_product') gn_args['gen_snapshot_stripped_binary'] = ( diff --git a/tools/task_kill.py b/tools/task_kill.py index 46a5bf57828..7c54c5766cd 100755 --- a/tools/task_kill.py +++ b/tools/task_kill.py @@ -25,7 +25,8 @@ EXECUTABLE_NAMES = { 'win32': { 'chrome': 'chrome.exe', 'crashpad_handler': 'crashpad_handler.exe', - 'dart': 'dart.exe', + 'dart': 'dart.exe',\ + 'dart_product': 'dart_product.exe', 'dartaotruntime': 'dartaotruntime.exe', 'dartaotruntime_product': 'dartaotruntime_product.exe', 'firefox': 'firefox.exe', @@ -38,6 +39,7 @@ EXECUTABLE_NAMES = { 'linux': { 'chrome': 'chrome', 'dart': 'dart', + 'dart_product': 'dart_product', 'dartaotruntime': 'dartaotruntime', 'dartaotruntime_product': 'dartaotruntime_product', 'firefox': 'firefox', @@ -49,6 +51,7 @@ EXECUTABLE_NAMES = { 'chrome': 'Chrome', 'chrome_helper': 'Chrome Helper', 'dart': 'dart', + 'dart_product': 'dart_product', 'dartaotruntime': 'dartaotruntime', 'dartaotruntime_product': 'dartaotruntime_product', 'firefox': 'firefox', diff --git a/utils/application_snapshot.gni b/utils/application_snapshot.gni index 37037cf6548..414d25dbad3 100644 --- a/utils/application_snapshot.gni +++ b/utils/application_snapshot.gni @@ -186,7 +186,7 @@ template("application_snapshot") { assert(training_deps != "", "Ignoring unused argument") } } else { - dart_action(target_name) { + dartvm_action(target_name) { if (defined(invoker.pool)) { pool = invoker.pool } diff --git a/utils/compiler/BUILD.gn b/utils/compiler/BUILD.gn index e39348e45fa..c8dfe53a3ca 100644 --- a/utils/compiler/BUILD.gn +++ b/utils/compiler/BUILD.gn @@ -54,9 +54,7 @@ sdk_root = "../../sdk" application_snapshot("dart2js") { deps = [ ":dart2js_create_snapshot_entry" ] - training_deps = [ - ":compile_dart2js_platform", - ] + training_deps = [ ":compile_dart2js_platform" ] training_inputs = [ "$root_out_dir/dart2js_platform.dill", "$root_out_dir/dart2js_outline.dill", @@ -77,13 +75,6 @@ application_snapshot("dart2js") { ] } -aot_snapshot("dart2js_aot") { - deps = [ ":dart2js_create_snapshot_entry" ] - - main_dart = "$target_gen_dir/dart2js.dart" - name = "dart2js_aot" -} - aot_snapshot("dart2js_sdk_aot") { deps = [ ":dart2js_create_snapshot_entry" ] diff --git a/utils/dartdev/BUILD.gn b/utils/dartdev/BUILD.gn index 89d4379d73d..9a58e7ab8e8 100644 --- a/utils/dartdev/BUILD.gn +++ b/utils/dartdev/BUILD.gn @@ -3,8 +3,34 @@ # BSD-style license that can be found in the LICENSE file. import("../../build/dart/copy_tree.gni") +import("../aot_snapshot.gni") import("../application_snapshot.gni") +group("dartdev_aot") { + public_deps = [ + ":copy_prebuilt_devtools", + ":dartdev_aot_snapshot", + ] +} + +aot_snapshot("dartdev_aot_profile_snapshot") { + main_dart = "../../pkg/dartdev/bin/dartdev.dart" + output = "$root_out_dir/dartdev_aot_profile.dart.snapshot" +} + +aot_snapshot("dartdev_aot_snapshot") { + main_dart = "../../pkg/dartdev/bin/dartdev.dart" + output = "$root_out_dir/dartdev_aot.dart.snapshot" + + # dart has dart_product_config applied to it, + # so it is built in product mode in both release and + # product builds, and is only built in debug mode in debug + # builds. The following line ensures that the dart + # and dartdev_aot snapshot in an SDK build are + # always compatible with each other. + force_product_mode = !dart_debug +} + group("dartdev") { public_deps = [ ":copy_dartdev_snapshot", @@ -12,6 +38,12 @@ group("dartdev") { ] } +application_snapshot("generate_dartdev_snapshot") { + main_dart = "../../pkg/dartdev/bin/dartdev.dart" + training_args = [ "--help" ] + output = "$root_gen_dir/dartdev.dart.snapshot" +} + copy("copy_dartdev_snapshot") { visibility = [ ":dartdev" ] public_deps = [ ":generate_dartdev_snapshot" ] @@ -19,14 +51,11 @@ copy("copy_dartdev_snapshot") { outputs = [ "$root_out_dir/dartdev.dart.snapshot" ] } -application_snapshot("generate_dartdev_snapshot") { - main_dart = "../../pkg/dartdev/bin/dartdev.dart" - training_args = [ "--help" ] - output = "$root_gen_dir/dartdev.dart.snapshot" -} - copy_tree("copy_prebuilt_devtools") { - visibility = [ ":dartdev" ] + visibility = [ + ":dartdev", + ":dartdev_aot", + ] source = "../../third_party/devtools/web" dest = "$root_out_dir/devtools" exclude = "{}"