8e00ebc689
google3 makes heavy use of symlinks, with no guarantee that the relative location of files with respect to each other will be maintained after the symlinks are resolved. This causes issues when trying to locate the DartDev snapshot using the VM executables resolved path as a base. This change updates the DartDev snapshot detection logic to first try to find the snapshot using the resolved executable path before falling back to using the unresolved path used to launch the VM (e.g., the path specified by argv[0]). TEST=pkg/dartdev/test/ Change-Id: I19a41c440ac82cbc671dafb3bda23a31fb4cdc0c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/370000 Reviewed-by: Derek Xu <derekx@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
329 lines
12 KiB
C++
329 lines
12 KiB
C++
// 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_);
|
|
int result = Thread::Start("DartDev Runner", RunCallback,
|
|
reinterpret_cast<uword>(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<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;
|
|
|
|
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)
|