blob: c7aab45c11532062a134cf06d94da89143cc0055 [file] [log] [blame] [edit]
// 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 <functional>
#include <memory>
#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;
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<char*[], void (*)(char*[])>
DartDevIsolate::DartDevRunner::argv_ =
std::unique_ptr<char*[], void (*)(char**)>(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, "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)));
}
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<uword>(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<const char**>(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<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);
}
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<DartDevRunner*>(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<char**>(&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* dart_options) {
runner_.Run(create_isolate, packages_file, script, dart_options);
return runner_.result();
}
} // namespace bin
} // namespace dart
#endif // if !defined(DART_PRECOMPILED_RUNTIME)