// 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* app_snapshot_data = nullptr; static const uint8_t* app_snapshot_text = 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::SetupCoreLibraries( /*is_service_isolate=*/false, /*trace_loading=*/false, /*profile_microtasks=*/false, DartIoSettings{ .namespace_root = Options::namespc() != nullptr ? DartUtils::NewString(Options::namespc()) : nullptr, .script_uri = script_uri, .disable_exit = Options::exit_disabled()}); 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. VmService::SetNativeResolver(); 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* snapshot_data = nullptr; const uint8_t* snapshot_text = nullptr; if (is_dartdev_isolate) { snapshot_data = app_snapshot_data; snapshot_text = app_snapshot_text; } 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(&snapshot_data, &snapshot_text); } 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, snapshot_data, snapshot_text, 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_SetEnvironmentVariable = 4, } 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(); if (dartvm_path.get() == nullptr || !CheckForInvalidPath(dartvm_path.get())) { // 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 + 3; char** exec_argv = new char*[num_args]; #if defined(DART_HOST_OS_WINDOWS) char* exec_name = StringUtilsWin::ArgumentEscape(dartvm_path.get()); #else char* exec_name = Utils::StrDup(dartvm_path.get()); #endif exec_argv[idx] = exec_name; const size_t kPathBufSize = PATH_MAX + 1; char dart_path[kPathBufSize]; Platform::ResolveExecutablePathInto(dart_path, kPathBufSize); idx += 1; #if defined(DART_HOST_OS_WINDOWS) char* dart_name = Utils::SCreate("--resolved_executable_name=%s", dart_path); exec_argv[idx] = StringUtilsWin::ArgumentEscape(dart_name); free(dart_name); idx += 1; dart_name = Utils::SCreate("--executable_name=%s", Platform::GetExecutableName()); exec_argv[idx] = StringUtilsWin::ArgumentEscape(dart_name); free(dart_name); #else exec_argv[idx] = Utils::SCreate("--resolved_executable_name=%s", dart_path); idx += 1; exec_argv[idx] = Utils::SCreate("--executable_name=%s", Platform::GetExecutableName()); #endif 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(); if (dartvm_path.get() == nullptr || !CheckForInvalidPath(dartvm_path.get())) { Syslog::PrintErr("Unable to locate the Dart VM executable"); Platform::Exit(kErrorExitCode); } ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kString); // scriptUriOverride. auto item2 = GetArrayItem(message, 2); ASSERT(item2->type == Dart_CObject_kString || item2->type == Dart_CObject_kNull); // packageConfigOverride. auto item3 = GetArrayItem(message, 3); ASSERT(item3->type == Dart_CObject_kString || item3->type == Dart_CObject_kNull); package_config_override_ = nullptr; if (item3->type == Dart_CObject_kString) { package_config_override_ = Utils::StrDup(item3->value.as_string); } // markMainIsolateAsSystemIsolate auto item4 = GetArrayItem(message, 4); ASSERT(item4->type == Dart_CObject_kBool); const bool mark_main_isolate_as_system_isolate = item4->value.as_bool; // argsList ASSERT(GetArrayItem(message, 5)->type == Dart_CObject_kArray); Dart_CObject* args = GetArrayItem(message, 5); intptr_t 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; }; intptr_t num_vm_options = dart_vm_options_->count(); const char** vm_options = dart_vm_options_->arguments(); // Total count of arguments to be passed to the script being execed. if (mark_main_isolate_as_system_isolate) { argc_ = argc + num_vm_options + 5; } else { argc_ = argc + num_vm_options + 4; } if (package_config_override_ != nullptr) { argc_++; } if (item2->type == Dart_CObject_kString) { argc_++; } // 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). #if defined(DART_HOST_OS_WINDOWS) script_name_ = StringUtilsWin::ArgumentEscape(dartvm_path.get()); #else script_name_ = Utils::StrDup(dartvm_path.get()); #endif 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) { #if defined(DART_HOST_OS_WINDOWS) argv_[i + idx] = StringUtilsWin::ArgumentEscape(vm_options[i]); #else argv_[i + idx] = Utils::StrDup(vm_options[i]); #endif } idx += num_vm_options; { const size_t kPathBufSize = PATH_MAX + 1; char dart_path[kPathBufSize]; Platform::ResolveExecutablePathInto(dart_path, kPathBufSize); #if defined(DART_HOST_OS_WINDOWS) char* dart_name = Utils::SCreate("--resolved_executable_name=%s", dart_path); argv_[idx++] = StringUtilsWin::ArgumentEscape(dart_name); free(dart_name); dart_name = Utils::SCreate("--executable_name=%s", Platform::GetExecutableName()); argv_[idx++] = StringUtilsWin::ArgumentEscape(dart_name); free(dart_name); #else argv_[idx++] = Utils::SCreate("--resolved_executable_name=%s", dart_path); argv_[idx++] = Utils::SCreate("--executable_name=%s", Platform::GetExecutableName()); #endif } if (mark_main_isolate_as_system_isolate) { argv_[idx++] = Utils::StrDup("--mark_main_isolate_as_system_isolate"); } if (package_config_override_ != nullptr) { #if defined(DART_HOST_OS_WINDOWS) char* packages_arg = Utils::SCreate("--packages=%s", package_config_override_); argv_[idx++] = StringUtilsWin::ArgumentEscape(packages_arg); free(packages_arg); #else argv_[idx++] = Utils::SCreate("--packages=%s", package_config_override_); #endif } if (item2->value.as_string != nullptr) { #if defined(DART_HOST_OS_WINDOWS) char* script_uri_override = Utils::SCreate("--script_uri_override=%s", item2->value.as_string); argv_[idx++] = StringUtilsWin::ArgumentEscape(script_uri_override); free(script_uri_override); #else argv_[idx++] = Utils::SCreate("--script_uri_override=%s", item2->value.as_string); #endif } // 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_); exited_ = true; locker.Notify(); } } static void SetEnvironmentVariableCallback(Dart_CObject* message) { ASSERT(GetArrayItem(message, 1)->type == Dart_CObject_kSendPort); Dart_Port reply_port = GetArrayItem(message, 1)->value.as_send_port.id; ASSERT(GetArrayItem(message, 2)->type == Dart_CObject_kString); const char* name = GetArrayItem(message, 2)->value.as_string; const char* value = nullptr; if (GetArrayItem(message, 3)->type == Dart_CObject_kString) { value = GetArrayItem(message, 3)->value.as_string; } Platform::SetEnvironmentVariable(name, value); Dart_CObject reply; reply.type = Dart_CObject_kNull; Dart_PostCObject(reply_port, &reply); } // 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; } case DartDev_Result_SetEnvironmentVariable: { SetEnvironmentVariableCallback(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 (!exited_) { 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 bool exited_; 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(); bool DartDev::exited_ = false; 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(); auto [app_snapshot, script_name] = Snapshot::TryReadSDKSnapshot("dartdev_aot.dart.snapshot"); if (app_snapshot == nullptr) { FreeConvertedArgs(argc, argv, argv_converted); Platform::Exit(kErrorExitCode); } app_snapshot->SetBuffers(&app_snapshot_data, &app_snapshot_text); 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.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; 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.get(), &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(); }