// 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/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; 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; bool* DartDevIsolate::DartDevRunner::force_no_sound_null_safety_ = 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 not a file path or of a known URI scheme, we can assume // that this is a DartDev command. return (!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)); } Utils::CStringUniquePtr DartDevIsolate::TryResolveArtifactPath( const char* filename) { // |dir_prefix| includes the last path separator. auto dir_prefix = EXEUtils::GetDirectoryPrefixFromExeName(); // 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 Utils::CreateCStringUniquePtr(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 Utils::CreateCStringUniquePtr(snapshot_path); } free(snapshot_path); return Utils::CreateCStringUniquePtr(nullptr); } Utils::CStringUniquePtr DartDevIsolate::TryResolveDartDevSnapshotPath() { return TryResolveArtifactPath("dartdev.dart.snapshot"); } Utils::CStringUniquePtr DartDevIsolate::TryResolveDartDevKernelPath() { return TryResolveArtifactPath("dartdev.dill"); } void DartDevIsolate::DartDevRunner::Run( Dart_IsolateGroupCreateCallback create_isolate, char** packages_file, char** script, bool* force_no_sound_null_safety, CommandLineOptions* dart_options) { create_isolate_ = create_isolate; dart_options_ = dart_options; package_config_override_ = packages_file; script_ = script; force_no_sound_null_safety_ = force_no_sound_null_safety; // 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_); int result = Thread::Start("DartDev Runner", RunCallback, reinterpret_cast(this)); if (result != 0) { FATAL("Failed to start DartDev thread: %d", result); } 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); 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); } *force_no_sound_null_safety_ = item3->value.as_bool; 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_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.null_safety = true; flags.use_field_guards = true; flags.use_osr = true; flags.is_system_isolate = true; flags.branch_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, bool* force_no_sound_null_safety, CommandLineOptions* dart_options) { runner_.Run(create_isolate, packages_file, script, force_no_sound_null_safety, dart_options); return runner_.result(); } } // namespace bin } // namespace dart #endif // if !defined(DART_PRECOMPILED_RUNTIME)