8#include <lib/async-loop/loop.h>
9#include <lib/async/cpp/task.h>
10#include <lib/async/default.h>
11#include <lib/fdio/directory.h>
12#include <lib/fdio/fd.h>
13#include <lib/fdio/namespace.h>
14#include <lib/fidl/cpp/string.h>
15#include <lib/sys/cpp/service_directory.h>
16#include <lib/vfs/cpp/composed_service_dir.h>
17#include <lib/vfs/cpp/remote_dir.h>
18#include <lib/zx/clock.h>
19#include <lib/zx/thread.h>
23#include <zircon/status.h>
33#include "third_party/dart/runtime/include/dart_tools_api.h"
47constexpr char kTmpPath[] =
"/tmp";
49constexpr zx::duration kIdleWaitDuration = zx::sec(2);
50constexpr zx::duration kIdleNotifyDuration = zx::msec(500);
51constexpr zx::duration kIdleSlack = zx::sec(1);
53void AfterTask(async_loop_t*,
void*) {
59 queue->RunMicrotasks();
63constexpr async_loop_config_t kLoopConfig = {
66 .getter = async_get_default_dispatcher,
67 .setter = async_set_default_dispatcher,
69 .make_default_for_current_thread =
true,
70 .epilogue = &AfterTask,
75std::string GetLabelFromUrl(
const std::string& url) {
76 for (
size_t i = url.length() - 1;
i > 0;
i--) {
78 return url.substr(
i + 1, url.length() - 1);
86std::string GetComponentNameFromUrl(
const std::string& url) {
87 const std::string label = GetLabelFromUrl(url);
88 for (
size_t i = 0;
i < label.length(); ++
i) {
89 if (label[
i] ==
'.') {
90 return label.substr(0,
i);
99 fuchsia::component::runner::ComponentStartInfo start_info,
100 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
101 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
103 : loop_(new async::Loop(&kLoopConfig)),
104 label_(GetLabelFromUrl(start_info.resolved_url())),
105 url_(start_info.resolved_url()),
106 runner_incoming_services_(
std::move(runner_incoming_services)),
107 dart_outgoing_dir_(new vfs::PseudoDir()),
108 start_info_(
std::move(start_info)),
110 binding_.set_error_handler([
this](zx_status_t status) { Kill(); });
116 const std::string component_name = GetComponentNameFromUrl(url_);
117 data_path_ =
"pkg/data/" + component_name;
119 zx_status_t idle_timer_status =
120 zx::timer::create(ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &idle_timer_);
121 if (idle_timer_status != ZX_OK) {
122 FML_LOG(INFO) <<
"Idle timer creation failed: "
123 << zx_status_get_string(idle_timer_status);
125 idle_wait_.set_object(idle_timer_.get());
126 idle_wait_.set_trigger(ZX_TIMER_SIGNALED);
127 idle_wait_.Begin(async_get_default_dispatcher());
132 start_info_.clear_runtime_dir();
137 fdio_ns_destroy(namespace_);
138 namespace_ =
nullptr;
146 zx::thread::self()->set_property(ZX_PROP_NAME, label_.c_str(), label_.size());
147 Dart_SetThreadName(label_.c_str());
149 if (!CreateAndBindNamespace()) {
153 if (SetUpFromAppSnapshot()) {
154 FML_LOG(INFO) << url_ <<
" is running from an app snapshot";
155 }
else if (SetUpFromKernel()) {
156 FML_LOG(INFO) << url_ <<
" is running from kernel";
158 FML_LOG(ERROR) <<
"Failed to set up component controller for " << url_;
165bool DartComponentController::CreateAndBindNamespace() {
166 if (!start_info_.has_ns()) {
167 FML_LOG(ERROR) <<
"Component start info does not have a namespace.";
171 const zx_status_t ns_create_status = fdio_ns_create(&namespace_);
172 if (ns_create_status != ZX_OK) {
173 FML_LOG(ERROR) <<
"Failed to create namespace: "
174 << zx_status_get_string(ns_create_status);
181 for (
auto& ns_entry : *start_info_.mutable_ns()) {
184 if (!ns_entry.has_path() || !ns_entry.has_directory()) {
188 if (ns_entry.path() == kTmpPath) {
195 fidl::InterfaceHandle<::fuchsia::io::Directory> dir =
196 std::move(*ns_entry.mutable_directory());
197 const std::string
path = std::move(*ns_entry.mutable_path());
199 const zx_status_t ns_bind_status =
200 fdio_ns_bind(namespace_,
path.c_str(), dir.TakeChannel().release());
201 if (ns_bind_status != ZX_OK) {
202 FML_LOG(ERROR) <<
"Failed to bind " <<
path <<
" to namespace: "
203 << zx_status_get_string(ns_bind_status);
208 dart_outgoing_dir_request_ = dart_outgoing_dir_ptr_.NewRequest();
210 fuchsia::io::DirectoryHandle dart_public_dir;
212 auto request = dart_public_dir.NewRequest().TakeChannel();
213 const zx_status_t status =
214 fdio_open3_at(dart_outgoing_dir_ptr_.channel().get(),
"svc",
215 uint64_t{fuchsia::io::PERM_READABLE}, request.release());
216 if (status != ZX_OK) {
217 FML_LOG(ERROR) <<
"Failed to open /svc in outgoing directory: "
218 << zx_status_get_string(status);
223 auto composed_service_dir = std::make_unique<vfs::ComposedServiceDir>();
224 composed_service_dir->SetFallback(
225 fidl::ClientEnd<fuchsia_io::Directory>(dart_public_dir.TakeChannel()));
228 dart_outgoing_dir_ptr_->Open(
230 fuchsia::io::Flags::PROTOCOL_NODE |
231 fuchsia::io::Flags::FLAG_SEND_REPRESENTATION,
232 {}, dart_outgoing_dir_ptr_to_check_on_open_.NewRequest().TakeChannel());
235 std::vector<std::string> other_dirs = {
"debug",
"ctrl"};
237 dart_outgoing_dir_ptr_to_check_on_open_.events().OnRepresentation =
238 [
this, other_dirs](
auto unused) {
239 dart_outgoing_dir_ptr_to_check_on_open_.Unbind();
241 for (
auto& dir_str : other_dirs) {
242 fuchsia::io::DirectoryHandle dir;
243 auto request = dir.NewRequest().TakeChannel();
244 const zx_status_t status = fdio_open3_at(
245 dart_outgoing_dir_ptr_.channel().get(), dir_str.c_str(),
246 uint64_t{fuchsia::io::Flags::PROTOCOL_DIRECTORY |
247 fuchsia::io::PERM_READABLE},
249 if (status == ZX_OK) {
250 dart_outgoing_dir_->AddEntry(
252 std::make_unique<vfs::RemoteDir>(dir.TakeChannel()));
254 FML_LOG(ERROR) <<
"could not add out directory entry(" << dir_str
255 <<
") for flutter component(" << label_
256 <<
"): " << zx_status_get_string(status);
260 dart_outgoing_dir_ptr_to_check_on_open_.set_error_handler(
261 [
this](zx_status_t status) {
262 dart_outgoing_dir_ptr_to_check_on_open_.Unbind();
271 composed_service_dir->AddService(
272 dart::test::Echo::Name_,
273 std::make_unique<vfs::Service>([
this](zx::channel
channel,
274 async_dispatcher_t* dispatcher) {
275 echo_binding_.AddBinding(
276 this, fidl::InterfaceRequest<dart::test::Echo>(std::move(
channel)));
278 dart_outgoing_dir_->AddEntry(
"svc", std::move(composed_service_dir));
280 if (start_info_.has_outgoing_dir()) {
281 fidl::ServerEnd<fuchsia_io::Directory> server_end{
282 start_info_.mutable_outgoing_dir()->TakeChannel()};
283 dart_outgoing_dir_->Serve(
284 fuchsia_io::wire::kPermReadable | fuchsia_io::wire::kPermWritable,
285 std::move(server_end));
291bool DartComponentController::SetUpFromKernel() {
294 namespace_, data_path_ +
"/app.dilplist", manifest)) {
299 nullptr,
"/pkg/data/isolate_core_snapshot_data.bin",
300 isolate_snapshot_data_)) {
304 std::string str(
reinterpret_cast<const char*
>(manifest.
address()),
306 Dart_Handle library = Dart_Null();
308 for (
size_t start = 0;
start < manifest.
size();) {
309 size_t end = str.find(
"\n", start);
310 if (
end == std::string::npos) {
311 FML_LOG(ERROR) <<
"Malformed manifest";
316 std::string
path = data_path_ +
"/" + str.substr(start,
end - start);
322 FML_LOG(ERROR) <<
"Cannot load kernel from namespace: " <<
path;
326 kernel_peices_.emplace_back(std::move(kernel));
329 if (!CreateIsolate(isolate_snapshot_data_.
address(),
336 for (
const auto& kernel : kernel_peices_) {
337 library = Dart_LoadLibraryFromKernel(kernel.
address(), kernel.
size());
338 if (Dart_IsError(library)) {
339 FML_LOG(ERROR) <<
"Cannot load library from kernel: "
340 << Dart_GetError(library);
346 Dart_SetRootLibrary(library);
348 Dart_Handle result = Dart_FinalizeLoading(
false);
349 if (Dart_IsError(result)) {
350 FML_LOG(ERROR) <<
"Failed to FinalizeLoading: " << Dart_GetError(result);
358bool DartComponentController::SetUpFromAppSnapshot() {
359#if !defined(AOT_RUNTIME)
364 const uint8_t *isolate_data, *isolate_instructions;
365 if (elf_snapshot_.
Load(namespace_, data_path_ +
"/app_aot_snapshot.so")) {
368 if (isolate_data ==
nullptr || isolate_instructions ==
nullptr) {
373 namespace_, data_path_ +
"/isolate_snapshot_data.bin",
374 isolate_snapshot_data_)) {
377 isolate_data = isolate_snapshot_data_.
address();
378 isolate_instructions =
nullptr;
380 return CreateIsolate(isolate_data, isolate_instructions);
384bool DartComponentController::CreateIsolate(
385 const uint8_t* isolate_snapshot_data,
386 const uint8_t* isolate_snapshot_instructions) {
388 char*
error =
nullptr;
391 intptr_t namespace_fd = -1;
395 namespace_fd, [
this](Dart_Handle result) { MessageEpilogue(result); }));
397 Dart_IsolateFlags isolate_flags;
398 Dart_IsolateFlagsInitialize(&isolate_flags);
399 isolate_flags.null_safety =
true;
401 isolate_ = Dart_CreateIsolateGroup(
402 url_.c_str(), label_.c_str(), isolate_snapshot_data,
403 isolate_snapshot_instructions, &isolate_flags, state, state, &
error);
405 FML_LOG(ERROR) <<
"Dart_CreateIsolateGroup failed: " <<
error;
409 state->get()->SetIsolate(isolate_);
412 [loop = loop_.get()](
auto callback) {
413 async::PostTask(loop->dispatcher(), std::move(
callback));
415 state->get()->message_handler().Initialize(dispatcher);
417 state->get()->SetReturnCodeCallback(
418 [
this](uint32_t return_code) { return_code_ = return_code; });
424 async::PostTask(loop_->dispatcher(), [loop = loop_.get(), app =
this] {
425 if (!app->RunDartMain()) {
439 if (return_code_ == 0) {
442 FML_LOG(ERROR) <<
"Component exited with non-zero return code: "
444 binding_.Close(zx_status_t(fuchsia::component::Error::INTERNAL));
449bool DartComponentController::RunDartMain() {
460 stdout_fd_ = fileno(stdout);
461 stderr_fd_ = fileno(stderr);
464 dart_outgoing_dir_request_.TakeChannel(),
469 char*
error = Dart_IsolateMakeRunnable(isolate_);
470 if (
error !=
nullptr) {
471 Dart_EnterIsolate(isolate_);
472 Dart_ShutdownIsolate();
473 FML_LOG(ERROR) <<
"Unable to make isolate runnable: " <<
error;
477 Dart_EnterIsolate(isolate_);
481 Dart_Handle corelib = Dart_LookupLibrary(
ToDart(
"dart:core"));
482 Dart_Handle string_type =
483 Dart_GetNonNullableType(corelib,
ToDart(
"String"), 0, NULL);
484 Dart_Handle dart_arguments =
485 Dart_NewListOfTypeFilled(string_type, Dart_EmptyString(), 0);
487 if (Dart_IsError(dart_arguments)) {
488 FML_LOG(ERROR) <<
"Failed to allocate Dart arguments list: "
489 << Dart_GetError(dart_arguments);
494 Dart_Handle user_main = Dart_GetField(Dart_RootLibrary(),
ToDart(
"main"));
496 if (Dart_IsError(user_main)) {
497 FML_LOG(ERROR) <<
"Failed to locate user_main in the root library: "
498 << Dart_GetError(user_main);
503 Dart_Handle fuchsia_lib = Dart_LookupLibrary(
tonic::ToDart(
"dart:fuchsia"));
505 if (Dart_IsError(fuchsia_lib)) {
506 FML_LOG(ERROR) <<
"Failed to locate dart:fuchsia: "
507 << Dart_GetError(fuchsia_lib);
513 fuchsia_lib,
"_runUserMainForDartRunner", {user_main, dart_arguments});
515 if (Dart_IsError(main_result)) {
517 if (!dart_state->has_set_return_code()) {
519 FML_LOG(ERROR) << Dart_GetError(main_result);
533void DartComponentController::EchoString(fidl::StringPtr value,
537 Dart_Handle builtin_lib = Dart_LookupLibrary(
ToDart(
"dart:fuchsia.builtin"));
540 Dart_Handle receive_echo_string =
ToDart(
"_receiveEchoString");
541 Dart_Handle string_to_echo =
544 Dart_Invoke(builtin_lib, receive_echo_string, 1, &string_to_echo);
547 fidl::StringPtr echo_string;
548 if (!Dart_IsNull(result)) {
556void DartComponentController::Kill() {
557 if (Dart_CurrentIsolate()) {
569 Dart_SetMessageNotifyCallback(
nullptr);
571 Dart_ShutdownIsolate();
575void DartComponentController::Stop() {
579void DartComponentController::MessageEpilogue(Dart_Handle result) {
584 if (dart_state->has_set_return_code()) {
585 Dart_ShutdownIsolate();
593 if (return_code_ != 0) {
594 Dart_ShutdownIsolate();
598 idle_start_ = zx::clock::get_monotonic();
600 idle_timer_.set(idle_start_ + kIdleWaitDuration, kIdleSlack);
601 if (status != ZX_OK) {
602 FML_LOG(INFO) <<
"Idle timer set failed: " << zx_status_get_string(status);
606void DartComponentController::OnIdleTimer(async_dispatcher_t* dispatcher,
607 async::WaitBase* wait,
609 const zx_packet_signal* signal) {
610 if ((status != ZX_OK) || !(signal->observed & ZX_TIMER_SIGNALED) ||
611 !Dart_CurrentIsolate()) {
616 zx::time deadline = idle_start_ + kIdleWaitDuration;
617 zx::time now = zx::clock::get_monotonic();
618 if (now >= deadline) {
621 Dart_NotifyIdle((now + kIdleNotifyDuration).get());
622 idle_start_ = zx::time(0);
623 idle_timer_.cancel();
626 zx_status_t status = idle_timer_.set(deadline, kIdleSlack);
627 if (status != ZX_OK) {
628 FML_LOG(INFO) <<
"Idle timer set failed: "
629 << zx_status_get_string(status);
632 wait->Begin(dispatcher);
635void DartComponentController::handle_unknown_method(uint64_t ordinal,
636 bool method_has_response) {
637 FML_LOG(ERROR) <<
"Unknown method called on DartComponentController. "
DartComponentController(fuchsia::component::runner::ComponentStartInfo start_info, std::shared_ptr< sys::ServiceDirectory > runner_incoming_services, fidl::InterfaceRequest< fuchsia::component::runner::ComponentController > controller)
~DartComponentController() override
bool Load(fdio_ns_t *namespc, const std::string &path)
const uint8_t * IsolateData() const
const uint8_t * IsolateInstrs() const
static bool LoadFromNamespace(fdio_ns_t *namespc, const std::string &path, MappedResource &resource, bool executable=false)
const uint8_t * address() const
std::function< void(std::function< void(void)>)> TaskDispatcher
static DartMicrotaskQueue * GetForCurrentThread()
static void StartForCurrentThread()
static DartState * Current()
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
#define FML_CHECK(condition)
void InitBuiltinLibrariesForIsolate(const std::string &script_uri, fdio_ns_t *namespc, int stdoutfd, int stderrfd, zx::channel directory_request, bool service_isolate)
void HandleIfException(std::shared_ptr<::sys::ServiceDirectory > services, const std::string &component_url, Dart_Handle result)
void BindTemp(fdio_ns_t *ns)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Dart_Handle ToDart(const T &object)
int GetErrorExitCode(Dart_Handle handle)
bool CheckAndHandleError(Dart_Handle handle)
std::string StdStringFromDart(Dart_Handle handle)
Dart_Handle DartInvokeField(Dart_Handle target, const char *name, std::initializer_list< Dart_Handle > args)