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"
49constexpr char kTmpPath[] =
"/tmp";
51constexpr zx::duration kIdleWaitDuration = zx::sec(2);
52constexpr zx::duration kIdleNotifyDuration = zx::msec(500);
53constexpr zx::duration kIdleSlack = zx::sec(1);
55void AfterTask(async_loop_t*,
void*) {
61 queue->RunMicrotasks();
65constexpr async_loop_config_t kLoopConfig = {
68 .getter = async_get_default_dispatcher,
69 .setter = async_set_default_dispatcher,
71 .make_default_for_current_thread =
true,
72 .epilogue = &AfterTask,
77std::string GetLabelFromUrl(
const std::string& url) {
78 for (
size_t i = url.length() - 1;
i > 0;
i--) {
80 return url.substr(
i + 1, url.length() - 1);
88std::string GetComponentNameFromUrl(
const std::string& url) {
89 const std::string label = GetLabelFromUrl(url);
90 for (
size_t i = 0;
i < label.length(); ++
i) {
91 if (label[
i] ==
'.') {
92 return label.substr(0,
i);
101 fuchsia::component::runner::ComponentStartInfo start_info,
102 std::shared_ptr<sys::ServiceDirectory> runner_incoming_services,
103 fidl::InterfaceRequest<fuchsia::component::runner::ComponentController>
105 : loop_(new async::Loop(&kLoopConfig)),
106 label_(GetLabelFromUrl(start_info.resolved_url())),
107 url_(start_info.resolved_url()),
108 runner_incoming_services_(
std::move(runner_incoming_services)),
109 dart_outgoing_dir_(new vfs::PseudoDir()),
110 start_info_(
std::move(start_info)),
112 binding_.set_error_handler([
this](zx_status_t status) { Kill(); });
118 const std::string component_name = GetComponentNameFromUrl(url_);
119 data_path_ =
"pkg/data/" + component_name;
121 zx_status_t idle_timer_status =
122 zx::timer::create(ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &idle_timer_);
123 if (idle_timer_status != ZX_OK) {
124 FML_LOG(INFO) <<
"Idle timer creation failed: "
125 << zx_status_get_string(idle_timer_status);
127 idle_wait_.set_object(idle_timer_.get());
128 idle_wait_.set_trigger(ZX_TIMER_SIGNALED);
129 idle_wait_.Begin(async_get_default_dispatcher());
134 start_info_.clear_runtime_dir();
139 fdio_ns_destroy(namespace_);
140 namespace_ =
nullptr;
148 zx::thread::self()->set_property(ZX_PROP_NAME, label_.c_str(), label_.size());
149 Dart_SetThreadName(label_.c_str());
151 if (!CreateAndBindNamespace()) {
155 if (SetUpFromAppSnapshot()) {
156 FML_LOG(INFO) << url_ <<
" is running from an app snapshot";
157 }
else if (SetUpFromKernel()) {
158 FML_LOG(INFO) << url_ <<
" is running from kernel";
160 FML_LOG(ERROR) <<
"Failed to set up component controller for " << url_;
167bool DartComponentController::CreateAndBindNamespace() {
168 if (!start_info_.has_ns()) {
169 FML_LOG(ERROR) <<
"Component start info does not have a namespace.";
173 const zx_status_t ns_create_status = fdio_ns_create(&namespace_);
174 if (ns_create_status != ZX_OK) {
175 FML_LOG(ERROR) <<
"Failed to create namespace: "
176 << zx_status_get_string(ns_create_status);
183 for (
auto& ns_entry : *start_info_.mutable_ns()) {
186 if (!ns_entry.has_path() || !ns_entry.has_directory()) {
190 if (ns_entry.path() == kTmpPath) {
197 fidl::InterfaceHandle<::fuchsia::io::Directory> dir =
198 std::move(*ns_entry.mutable_directory());
199 const std::string
path = std::move(*ns_entry.mutable_path());
201 const zx_status_t ns_bind_status =
202 fdio_ns_bind(namespace_,
path.c_str(), dir.TakeChannel().release());
203 if (ns_bind_status != ZX_OK) {
204 FML_LOG(ERROR) <<
"Failed to bind " <<
path <<
" to namespace: "
205 << zx_status_get_string(ns_bind_status);
210 dart_outgoing_dir_request_ = dart_outgoing_dir_ptr_.NewRequest();
212 fuchsia::io::DirectoryHandle dart_public_dir;
214 auto request = dart_public_dir.NewRequest().TakeChannel();
215 const zx_status_t status =
216 fdio_open3_at(dart_outgoing_dir_ptr_.channel().get(),
"svc",
217 uint64_t{fuchsia::io::PERM_READABLE}, request.release());
218 if (status != ZX_OK) {
219 FML_LOG(ERROR) <<
"Failed to open /svc in outgoing directory: "
220 << zx_status_get_string(status);
225 auto composed_service_dir = std::make_unique<vfs::ComposedServiceDir>();
226 composed_service_dir->SetFallback(
227 fidl::ClientEnd<fuchsia_io::Directory>(dart_public_dir.TakeChannel()));
230 dart_outgoing_dir_ptr_->Open(
232 fuchsia::io::Flags::PROTOCOL_NODE |
233 fuchsia::io::Flags::FLAG_SEND_REPRESENTATION,
234 {}, dart_outgoing_dir_ptr_to_check_on_open_.NewRequest().TakeChannel());
237 std::vector<std::string> other_dirs = {
"debug",
"ctrl"};
239 dart_outgoing_dir_ptr_to_check_on_open_.events().OnRepresentation =
240 [
this, other_dirs](
auto unused) {
241 dart_outgoing_dir_ptr_to_check_on_open_.Unbind();
243 for (
auto& dir_str : other_dirs) {
244 fuchsia::io::DirectoryHandle dir;
245 auto request = dir.NewRequest().TakeChannel();
246 const zx_status_t status = fdio_open3_at(
247 dart_outgoing_dir_ptr_.channel().get(), dir_str.c_str(),
248 uint64_t{fuchsia::io::Flags::PROTOCOL_DIRECTORY |
249 fuchsia::io::PERM_READABLE},
251 if (status == ZX_OK) {
252 dart_outgoing_dir_->AddEntry(
254 std::make_unique<vfs::RemoteDir>(dir.TakeChannel()));
256 FML_LOG(ERROR) <<
"could not add out directory entry(" << dir_str
257 <<
") for flutter component(" << label_
258 <<
"): " << zx_status_get_string(status);
262 dart_outgoing_dir_ptr_to_check_on_open_.set_error_handler(
263 [
this](zx_status_t status) {
264 dart_outgoing_dir_ptr_to_check_on_open_.Unbind();
273 composed_service_dir->AddService(
274 dart::test::Echo::Name_,
275 std::make_unique<vfs::Service>([
this](zx::channel
channel,
276 async_dispatcher_t* dispatcher) {
277 echo_binding_.AddBinding(
278 this, fidl::InterfaceRequest<dart::test::Echo>(std::move(
channel)));
280 dart_outgoing_dir_->AddEntry(
"svc", std::move(composed_service_dir));
282 if (start_info_.has_outgoing_dir()) {
283 fidl::ServerEnd<fuchsia_io::Directory> server_end{
284 start_info_.mutable_outgoing_dir()->TakeChannel()};
285 dart_outgoing_dir_->Serve(
286 fuchsia_io::wire::kPermReadable | fuchsia_io::wire::kPermWritable,
287 std::move(server_end));
293bool DartComponentController::SetUpFromKernel() {
296 namespace_, data_path_ +
"/app.dilplist", manifest)) {
301 nullptr,
"/pkg/data/isolate_core_snapshot_data.bin",
302 isolate_snapshot_data_)) {
306 std::string str(
reinterpret_cast<const char*
>(manifest.
address()),
308 Dart_Handle library = Dart_Null();
310 for (
size_t start = 0;
start < manifest.
size();) {
311 size_t end = str.find(
"\n", start);
312 if (
end == std::string::npos) {
313 FML_LOG(ERROR) <<
"Malformed manifest";
318 std::string
path = data_path_ +
"/" + str.substr(start,
end - start);
324 FML_LOG(ERROR) <<
"Cannot load kernel from namespace: " <<
path;
328 kernel_peices_.emplace_back(std::move(kernel));
331 if (!CreateIsolate(isolate_snapshot_data_.
address(),
338 for (
const auto& kernel : kernel_peices_) {
339 library = Dart_LoadLibraryFromKernel(kernel.
address(), kernel.
size());
340 if (Dart_IsError(library)) {
341 FML_LOG(ERROR) <<
"Cannot load library from kernel: "
342 << Dart_GetError(library);
348 Dart_SetRootLibrary(library);
350 Dart_Handle result = Dart_FinalizeLoading(
false);
351 if (Dart_IsError(result)) {
352 FML_LOG(ERROR) <<
"Failed to FinalizeLoading: " << Dart_GetError(result);
360bool DartComponentController::SetUpFromAppSnapshot() {
361#if !defined(AOT_RUNTIME)
366 const uint8_t *isolate_data, *isolate_instructions;
367 if (elf_snapshot_.
Load(namespace_, data_path_ +
"/app_aot_snapshot.so")) {
370 if (isolate_data ==
nullptr || isolate_instructions ==
nullptr) {
375 namespace_, data_path_ +
"/isolate_snapshot_data.bin",
376 isolate_snapshot_data_)) {
379 isolate_data = isolate_snapshot_data_.
address();
380 isolate_instructions =
nullptr;
382 return CreateIsolate(isolate_data, isolate_instructions);
386bool DartComponentController::CreateIsolate(
387 const uint8_t* isolate_snapshot_data,
388 const uint8_t* isolate_snapshot_instructions) {
390 char*
error =
nullptr;
393 intptr_t namespace_fd = -1;
397 namespace_fd, [
this](Dart_Handle result) { MessageEpilogue(result); }));
399 Dart_IsolateFlags isolate_flags;
400 Dart_IsolateFlagsInitialize(&isolate_flags);
401 isolate_flags.null_safety =
true;
403 isolate_ = Dart_CreateIsolateGroup(
404 url_.c_str(), label_.c_str(), isolate_snapshot_data,
405 isolate_snapshot_instructions, &isolate_flags, state, state, &
error);
407 FML_LOG(ERROR) <<
"Dart_CreateIsolateGroup failed: " <<
error;
411 state->get()->SetIsolate(isolate_);
414 [loop = loop_.get()](
auto callback) {
415 async::PostTask(loop->dispatcher(), std::move(
callback));
417 state->get()->message_handler().Initialize(dispatcher);
419 state->get()->SetReturnCodeCallback(
420 [
this](uint32_t return_code) { return_code_ = return_code; });
426 async::PostTask(loop_->dispatcher(), [loop = loop_.get(), app =
this] {
427 if (!app->RunDartMain()) {
441 if (return_code_ == 0) {
444 FML_LOG(ERROR) <<
"Component exited with non-zero return code: "
446 binding_.Close(zx_status_t(fuchsia::component::Error::INTERNAL));
451bool DartComponentController::RunDartMain() {
462 stdout_fd_ = fileno(stdout);
463 stderr_fd_ = fileno(stderr);
466 dart_outgoing_dir_request_.TakeChannel(),
471 char*
error = Dart_IsolateMakeRunnable(isolate_);
472 if (
error !=
nullptr) {
473 Dart_EnterIsolate(isolate_);
474 Dart_ShutdownIsolate();
475 FML_LOG(ERROR) <<
"Unable to make isolate runnable: " <<
error;
479 Dart_EnterIsolate(isolate_);
483 Dart_Handle corelib = Dart_LookupLibrary(
ToDart(
"dart:core"));
484 Dart_Handle string_type =
485 Dart_GetNonNullableType(corelib,
ToDart(
"String"), 0, NULL);
486 Dart_Handle dart_arguments =
487 Dart_NewListOfTypeFilled(string_type, Dart_EmptyString(), 0);
489 if (Dart_IsError(dart_arguments)) {
490 FML_LOG(ERROR) <<
"Failed to allocate Dart arguments list: "
491 << Dart_GetError(dart_arguments);
496 Dart_Handle user_main = Dart_GetField(Dart_RootLibrary(),
ToDart(
"main"));
498 if (Dart_IsError(user_main)) {
499 FML_LOG(ERROR) <<
"Failed to locate user_main in the root library: "
500 << Dart_GetError(user_main);
505 Dart_Handle fuchsia_lib = Dart_LookupLibrary(
tonic::ToDart(
"dart:fuchsia"));
507 if (Dart_IsError(fuchsia_lib)) {
508 FML_LOG(ERROR) <<
"Failed to locate dart:fuchsia: "
509 << Dart_GetError(fuchsia_lib);
515 fuchsia_lib,
"_runUserMainForDartRunner", {user_main, dart_arguments});
517 if (Dart_IsError(main_result)) {
519 if (!dart_state->has_set_return_code()) {
521 FML_LOG(ERROR) << Dart_GetError(main_result);
535void DartComponentController::EchoString(fidl::StringPtr value,
539 Dart_Handle builtin_lib = Dart_LookupLibrary(
ToDart(
"dart:fuchsia.builtin"));
542 Dart_Handle receive_echo_string =
ToDart(
"_receiveEchoString");
543 Dart_Handle string_to_echo =
546 Dart_Invoke(builtin_lib, receive_echo_string, 1, &string_to_echo);
549 fidl::StringPtr echo_string;
550 if (!Dart_IsNull(result)) {
558void DartComponentController::Kill() {
559 if (Dart_CurrentIsolate()) {
571 Dart_SetMessageNotifyCallback(
nullptr);
573 Dart_ShutdownIsolate();
577void DartComponentController::Stop() {
581void DartComponentController::MessageEpilogue(Dart_Handle result) {
586 if (dart_state->has_set_return_code()) {
587 Dart_ShutdownIsolate();
595 if (return_code_ != 0) {
596 Dart_ShutdownIsolate();
600 idle_start_ = zx::clock::get_monotonic();
602 idle_timer_.set(idle_start_ + kIdleWaitDuration, kIdleSlack);
603 if (status != ZX_OK) {
604 FML_LOG(INFO) <<
"Idle timer set failed: " << zx_status_get_string(status);
608void DartComponentController::OnIdleTimer(async_dispatcher_t* dispatcher,
609 async::WaitBase* wait,
611 const zx_packet_signal* signal) {
612 if ((status != ZX_OK) || !(signal->observed & ZX_TIMER_SIGNALED) ||
613 !Dart_CurrentIsolate()) {
618 zx::time deadline = idle_start_ + kIdleWaitDuration;
619 zx::time now = zx::clock::get_monotonic();
620 if (now >= deadline) {
623 Dart_NotifyIdle((now + kIdleNotifyDuration).get());
624 idle_start_ = zx::time(0);
625 idle_timer_.cancel();
628 zx_status_t status = idle_timer_.set(deadline, kIdleSlack);
629 if (status != ZX_OK) {
630 FML_LOG(INFO) <<
"Idle timer set failed: "
631 << zx_status_get_string(status);
634 wait->Begin(dispatcher);
637void DartComponentController::handle_unknown_method(uint64_t ordinal,
638 bool method_has_response) {
639 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)