From 5399dbf6f6ed5210ed3e6474f877cf9153362f92 Mon Sep 17 00:00:00 2001 From: asiva Date: Fri, 4 Jul 2025 13:22:13 -0700 Subject: [PATCH] [VM/dartdev] Switch dartdev to use an AOT runtime. - split the Dart CLI tool 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' to run - '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) - the Dart CLI tool is not built in the internal Dart SDK builds TEST=ci Some performance improvement numbers 'dart format pkg/dartdev' goes from 1.17 secs to 0.22 secs 'dart doc pkg/dartdev' goes from 100.2 secs to 66.6 secs 'dart fix pkg/dartdev' goes from 19.3 secs to 14.5 secs Change-Id: I66984a26cb2ab014b34dc1873f1f3d2884e13518 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364202 Commit-Queue: Ben Konyi Reviewed-by: Ben Konyi --- BUILD.gn | 6 +- CHANGELOG.md | 19 + build/dart/dart_action.gni | 8 +- build/rbe/rewrapper_dart.py | 3 + .../get_errors_non_standard_sdk_test.dart | 9 + pkg/dartdev/lib/src/commands/compile.dart | 36 +- .../lib/src/commands/dart_mcp_server.dart | 5 +- .../lib/src/commands/development_service.dart | 29 +- .../lib/src/commands/language_server.dart | 25 +- pkg/dartdev/lib/src/commands/run.dart | 296 ++--- pkg/dartdev/lib/src/commands/test.dart | 1 + .../lib/src/commands/tooling_daemon.dart | 21 +- pkg/dartdev/lib/src/dds_runner.dart | 18 +- pkg/dartdev/lib/src/generate_kernel.dart | 12 +- pkg/dartdev/lib/src/processes.dart | 3 +- pkg/dartdev/lib/src/sdk.dart | 35 +- pkg/dartdev/test/commands/flag_test.dart | 16 +- .../test/commands/info_linux_test.dart | 2 +- .../test/commands/info_macos_test.dart | 2 +- .../test/commands/info_windows_test.dart | 2 +- pkg/dartdev/test/commands/run_test.dart | 20 +- pkg/dartdev/test/sdk_test.dart | 8 +- .../test/smoke/implicit_smoke_test.dart | 3 +- pkg/dartdev/test/smoke/smoke_test.dart | 9 +- pkg/dds/lib/src/devtools/dtd.dart | 23 +- .../devtools_server/utils/server_driver.dart | 4 +- pkg/test_runner/lib/src/process_queue.dart | 2 + .../lib/src/runtime_configuration.dart | 2 +- runtime/BUILD.gn | 19 + runtime/bin/BUILD.gn | 61 +- runtime/bin/common_options.h | 173 +++ runtime/bin/dartdev.cc | 1137 +++++++++++++++++ runtime/bin/dartdev_isolate.cc | 415 ------ runtime/bin/dartdev_isolate.h | 118 -- runtime/bin/dartdev_options.cc | 378 ++++++ runtime/bin/dartdev_options.h | 140 ++ ...omponent.cml => dartvm_test_component.cml} | 2 +- runtime/bin/main_impl.cc | 212 +-- runtime/bin/main_options.cc | 692 +++------- runtime/bin/main_options.h | 37 +- runtime/bin/platform.h | 7 + runtime/bin/process_linux.cc | 8 +- runtime/bin/process_macos.cc | 3 + runtime/bin/process_win.cc | 5 +- runtime/bin/utils_win.cc | 65 + runtime/bin/utils_win.h | 2 + runtime/bin/vmservice_impl.cc | 2 +- .../tests/vm/dart/exported_symbols_test.dart | 5 +- .../tests/vm/dart/use_flag_test_helper.dart | 2 +- runtime/tools/entitlements/dart.plist | 2 +- runtime/tools/entitlements/dartvm.plist | 10 + sdk/BUILD.gn | 143 +-- .../_internal/vm/bin/vmservice_server.dart | 8 +- tests/ffi/native_assets/helpers.dart | 7 +- .../regress_41329_absolute_test.dart | 2 +- .../regress_41329_relative_test.dart | 2 +- tools/bots/try_benchmarks.sh | 1 + tools/gn.py | 1 + tools/task_kill.py | 5 +- utils/application_snapshot.gni | 2 +- utils/compiler/BUILD.gn | 11 +- utils/dartdev/BUILD.gn | 43 +- 62 files changed, 2645 insertions(+), 1694 deletions(-) create mode 100644 runtime/bin/common_options.h create mode 100644 runtime/bin/dartdev.cc delete mode 100644 runtime/bin/dartdev_isolate.cc delete mode 100644 runtime/bin/dartdev_isolate.h create mode 100644 runtime/bin/dartdev_options.cc create mode 100644 runtime/bin/dartdev_options.h rename runtime/bin/{dart_test_component.cml => dartvm_test_component.cml} (93%) create mode 100644 runtime/tools/entitlements/dartvm.plist 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 = "{}"