| // 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 <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <memory> | |
| #include <utility> | |
| #include "bin/builtin.h" | |
| #include "bin/console.h" | |
| #include "bin/crashpad.h" | |
| #include "bin/dartdev_options.h" | |
| #include "bin/dartutils.h" | |
| #include "bin/directory.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<IsolateGroupData*>(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<IsolateData*>(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<IsolateData*>(callback_data); | |
| delete isolate_data; | |
| } | |
| static void DeleteIsolateGroupData(void* callback_data) { | |
| auto isolate_group_data = reinterpret_cast<IsolateGroupData*>(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<const char**>(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<char*[], void (*)(char**)>(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; | |
| const char* delete_temp_dir_on_shutdown = nullptr; | |
| if (message->value.as_array.length > 6) { | |
| auto item6 = GetArrayItem(message, 6); | |
| if (item6->type == Dart_CObject_kString) { | |
| delete_temp_dir_on_shutdown = item6->value.as_string; | |
| } | |
| } | |
| 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_++; | |
| } | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| if (delete_temp_dir_on_shutdown != nullptr) { | |
| delete_temp_dir_on_shutdown_ = Utils::StrDup(delete_temp_dir_on_shutdown); | |
| } | |
| #else | |
| if (delete_temp_dir_on_shutdown != nullptr) { | |
| argc_++; | |
| } | |
| #endif | |
| // Array of arguments to be passed to the script being execed. | |
| argv_ = std::unique_ptr<char*[], void (*)(char**)>(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 | |
| } | |
| #if !defined(DART_HOST_OS_WINDOWS) | |
| if (delete_temp_dir_on_shutdown != nullptr) { | |
| argv_[idx++] = Utils::SCreate("--delete_temp_dir_on_shutdown=%s", | |
| delete_temp_dir_on_shutdown); | |
| } | |
| #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_); | |
| ASSERT(delete_temp_dir_on_shutdown_ == nullptr); | |
| break; | |
| } | |
| case DartDev_Result_RunExec: { | |
| RunExec(script_name_); | |
| break; | |
| } | |
| case DartDev_Result_Exit: { | |
| ASSERT(delete_temp_dir_on_shutdown_ == nullptr); | |
| // 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<const char**>(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<const char**>(argv_.get()), argc_, | |
| nullptr, err_msg, sizeof(err_msg)); | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| if (delete_temp_dir_on_shutdown_ != nullptr) { | |
| dart::bin::Directory::Delete(nullptr, delete_temp_dir_on_shutdown_, | |
| /*recursive=*/true); | |
| free(delete_temp_dir_on_shutdown_); | |
| delete_temp_dir_on_shutdown_ = nullptr; | |
| } | |
| #endif | |
| 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 char* delete_temp_dir_on_shutdown_; | |
| static CommandLineOptions* dart_vm_options_; | |
| static std::unique_ptr<char*[], void (*)(char**)> 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; | |
| char* DartDev::delete_temp_dir_on_shutdown_ = nullptr; | |
| CommandLineOptions* DartDev::dart_vm_options_ = nullptr; | |
| std::unique_ptr<char*[], void (*)(char**)> DartDev::argv_ = | |
| std::unique_ptr<char*[], void (*)(char**)>(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> <cmd-name> <vm-opts-to-cmd> <script_name> <dart-options> | |
| // 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(); | |
| } |