| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "dart_runner.h" | |
| #include <lib/async-loop/loop.h> | |
| #include <lib/async/default.h> | |
| #include <lib/syslog/global.h> | |
| #include <sys/stat.h> | |
| #include <zircon/status.h> | |
| #include <zircon/syscalls.h> | |
| #include <cerrno> | |
| #include <memory> | |
| #include <thread> | |
| #include <utility> | |
| #include "dart_component_controller.h" | |
| #include "flutter/fml/command_line.h" | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/trace_event.h" | |
| #include "logging.h" | |
| #include "runtime/dart/utils/inlines.h" | |
| #include "runtime/dart/utils/vmservice_object.h" | |
| #include "service_isolate.h" | |
| #include "third_party/dart/runtime/include/bin/dart_io_api.h" | |
| #include "third_party/tonic/dart_microtask_queue.h" | |
| #include "third_party/tonic/dart_state.h" | |
| #if defined(AOT_RUNTIME) | |
| extern "C" uint8_t _kDartVmSnapshotData[]; | |
| extern "C" uint8_t _kDartVmSnapshotInstructions[]; | |
| #endif | |
| namespace dart_runner { | |
| namespace { | |
| const char* kDartVMArgs[] = { | |
| // clang-format off | |
| "--systrace_timeline", | |
| "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", | |
| #if defined(AOT_RUNTIME) | |
| "--precompilation", | |
| #else | |
| "--enable_mirrors=false", | |
| #endif | |
| // No asserts in debug or release product. | |
| // No asserts in release with flutter_profile=true (non-product) | |
| // Yes asserts in non-product debug. | |
| #if !defined(DART_PRODUCT) && (!defined(FLUTTER_PROFILE) || !defined(NDEBUG)) | |
| "--enable_asserts", | |
| #endif | |
| // clang-format on | |
| }; | |
| Dart_Isolate IsolateGroupCreateCallback(const char* uri, | |
| const char* name, | |
| const char* package_root, | |
| const char* package_config, | |
| Dart_IsolateFlags* flags, | |
| void* callback_data, | |
| char** error) { | |
| if (std::string(uri) == DART_VM_SERVICE_ISOLATE_NAME) { | |
| #if defined(DART_PRODUCT) | |
| *error = strdup("The service isolate is not implemented in product mode"); | |
| return NULL; | |
| #else | |
| return CreateServiceIsolate(uri, flags, error); | |
| #endif | |
| } | |
| *error = strdup("Isolate spawning is not implemented in dart_runner"); | |
| return NULL; | |
| } | |
| void IsolateShutdownCallback(void* isolate_group_data, void* isolate_data) { | |
| // The service isolate (and maybe later the kernel isolate) doesn't have an | |
| // async loop. | |
| auto dispatcher = async_get_default_dispatcher(); | |
| auto loop = async_loop_from_dispatcher(dispatcher); | |
| if (loop) { | |
| tonic::DartMicrotaskQueue* queue = | |
| tonic::DartMicrotaskQueue::GetForCurrentThread(); | |
| if (queue) { | |
| queue->Destroy(); | |
| } | |
| async_loop_quit(loop); | |
| } | |
| auto state = | |
| static_cast<std::shared_ptr<tonic::DartState>*>(isolate_group_data); | |
| state->get()->SetIsShuttingDown(); | |
| } | |
| void IsolateGroupCleanupCallback(void* isolate_group_data) { | |
| delete static_cast<std::shared_ptr<tonic::DartState>*>(isolate_group_data); | |
| } | |
| // Runs the application for a Dart component. | |
| void RunApplication( | |
| DartRunner* runner, | |
| fuchsia::component::runner::ComponentStartInfo start_info, | |
| std::shared_ptr<sys::ServiceDirectory> runner_incoming_services, | |
| fidl::InterfaceRequest<fuchsia::component::runner::ComponentController> | |
| controller) { | |
| const int64_t start = Dart_TimelineGetMicros(); | |
| DartComponentController app(std::move(start_info), runner_incoming_services, | |
| std::move(controller)); | |
| const bool success = app.SetUp(); | |
| const int64_t end = Dart_TimelineGetMicros(); | |
| Dart_TimelineEvent("DartComponentController::SetUp", start, end, | |
| Dart_Timeline_Event_Duration, 0, NULL, NULL); | |
| if (success) { | |
| app.Run(); | |
| } | |
| if (Dart_CurrentIsolate()) { | |
| Dart_ShutdownIsolate(); | |
| } | |
| } | |
| void RunTestApplication( | |
| DartRunner* runner, | |
| fuchsia::component::runner::ComponentStartInfo start_info, | |
| std::shared_ptr<sys::ServiceDirectory> runner_incoming_services, | |
| fidl::InterfaceRequest<fuchsia::component::runner::ComponentController> | |
| controller, | |
| fit::function<void(std::shared_ptr<DartTestComponentController>)> | |
| component_created_callback, | |
| fit::function<void(DartTestComponentController*)> done_callback) { | |
| const int64_t start = Dart_TimelineGetMicros(); | |
| auto test_component = std::make_shared<DartTestComponentController>( | |
| std::move(start_info), runner_incoming_services, std::move(controller), | |
| std::move(done_callback)); | |
| component_created_callback(test_component); | |
| // Start up the dart isolate and serve the suite protocol. | |
| test_component->SetUp(); | |
| const int64_t end = Dart_TimelineGetMicros(); | |
| Dart_TimelineEvent("DartTestComponentController::SetUp", start, end, | |
| Dart_Timeline_Event_Duration, 0, NULL, NULL); | |
| } | |
| bool EntropySource(uint8_t* buffer, intptr_t count) { | |
| zx_cprng_draw(buffer, count); | |
| return true; | |
| } | |
| } // namespace | |
| // "args" are how the component specifies arguments to the runner. | |
| constexpr char kArgsKey[] = "args"; | |
| /// Parses the |args| field from the "program" field to determine | |
| /// if a test component is being executed. | |
| bool IsTestProgram(const fuchsia::data::Dictionary& program_metadata) { | |
| for (const auto& entry : program_metadata.entries()) { | |
| if (entry.key.compare(kArgsKey) != 0 || entry.value == nullptr) { | |
| continue; | |
| } | |
| auto args = entry.value->str_vec(); | |
| // fml::CommandLine expects the first argument to be the name of the | |
| // program, so we prepend a dummy argument so we can use fml::CommandLine to | |
| // parse the arguments for us. | |
| std::vector<std::string> command_line_args = {""}; | |
| command_line_args.insert(command_line_args.end(), args.begin(), args.end()); | |
| fml::CommandLine parsed_args = fml::CommandLineFromIterators( | |
| command_line_args.begin(), command_line_args.end()); | |
| std::string is_test_str; | |
| return parsed_args.GetOptionValue("is_test", &is_test_str) && | |
| is_test_str == "true"; | |
| } | |
| return false; | |
| } | |
| DartRunner::DartRunner(sys::ComponentContext* context) : context_(context) { | |
| context_->outgoing() | |
| ->AddPublicService<fuchsia::component::runner::ComponentRunner>( | |
| [this](fidl::InterfaceRequest< | |
| fuchsia::component::runner::ComponentRunner> request) { | |
| component_runner_bindings_.AddBinding(this, std::move(request)); | |
| }); | |
| #if !defined(DART_PRODUCT) | |
| // The VM service isolate uses the process-wide namespace. It writes the | |
| // vm service protocol port under /tmp. The VMServiceObject exposes that | |
| // port number to The Hub. | |
| context_->outgoing()->debug_dir()->AddEntry( | |
| dart_utils::VMServiceObject::kPortDirName, | |
| std::make_unique<dart_utils::VMServiceObject>()); | |
| #endif // !defined(DART_PRODUCT) | |
| dart::bin::BootstrapDartIo(); | |
| char* error = | |
| Dart_SetVMFlags(dart_utils::ArraySize(kDartVMArgs), kDartVMArgs); | |
| if (error) { | |
| FX_LOGF(FATAL, LOG_TAG, "Dart_SetVMFlags failed: %s", error); | |
| } | |
| Dart_InitializeParams params = {}; | |
| params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; | |
| #if defined(AOT_RUNTIME) | |
| params.vm_snapshot_data = ::_kDartVmSnapshotData; | |
| params.vm_snapshot_instructions = ::_kDartVmSnapshotInstructions; | |
| #else | |
| if (!dart_utils::MappedResource::LoadFromNamespace( | |
| nullptr, "/pkg/data/vm_snapshot_data.bin", vm_snapshot_data_)) { | |
| FX_LOG(FATAL, LOG_TAG, "Failed to load vm snapshot data"); | |
| } | |
| if (!dart_utils::MappedResource::LoadFromNamespace( | |
| nullptr, "/pkg/data/vm_snapshot_instructions.bin", | |
| vm_snapshot_instructions_, true /* executable */)) { | |
| FX_LOG(FATAL, LOG_TAG, "Failed to load vm snapshot instructions"); | |
| } | |
| params.vm_snapshot_data = vm_snapshot_data_.address(); | |
| params.vm_snapshot_instructions = vm_snapshot_instructions_.address(); | |
| #endif | |
| params.create_group = IsolateGroupCreateCallback; | |
| params.shutdown_isolate = IsolateShutdownCallback; | |
| params.cleanup_group = IsolateGroupCleanupCallback; | |
| params.entropy_source = EntropySource; | |
| #if !defined(DART_PRODUCT) | |
| params.get_service_assets = GetVMServiceAssetsArchiveCallback; | |
| #endif | |
| error = Dart_Initialize(¶ms); | |
| if (error) | |
| FX_LOGF(FATAL, LOG_TAG, "Dart_Initialize failed: %s", error); | |
| } | |
| DartRunner::~DartRunner() { | |
| char* error = Dart_Cleanup(); | |
| if (error) | |
| FX_LOGF(FATAL, LOG_TAG, "Dart_Cleanup failed: %s", error); | |
| } | |
| void DartRunner::Start( | |
| fuchsia::component::runner::ComponentStartInfo start_info, | |
| fidl::InterfaceRequest<fuchsia::component::runner::ComponentController> | |
| controller) { | |
| // Parse the program field of the component's cml and check if it is a test | |
| // component. If so, serve the |fuchsia.test.Suite| protocol from the | |
| // component's outgoing directory, via DartTestComponentController. | |
| if (IsTestProgram(start_info.program())) { | |
| std::string url_copy = start_info.resolved_url(); | |
| TRACE_EVENT1("dart", "Start", "url", url_copy.c_str()); | |
| std::thread thread( | |
| RunTestApplication, this, std::move(start_info), context_->svc(), | |
| std::move(controller), | |
| // component_created_callback | |
| [this](std::shared_ptr<DartTestComponentController> ptr) { | |
| test_components_.emplace(ptr.get(), std::move(ptr)); | |
| }, | |
| // done_callback | |
| [this](DartTestComponentController* ptr) { | |
| auto it = test_components_.find(ptr); | |
| if (it != test_components_.end()) { | |
| test_components_.erase(it); | |
| } | |
| }); | |
| thread.detach(); | |
| } else { | |
| std::string url_copy = start_info.resolved_url(); | |
| TRACE_EVENT1("dart", "Start", "url", url_copy.c_str()); | |
| std::thread thread(RunApplication, this, std::move(start_info), | |
| context_->svc(), std::move(controller)); | |
| thread.detach(); | |
| } | |
| } | |
| } // namespace dart_runner |