| // 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 "flutter/runtime/dart_isolate.h" | |
| #include <cstdlib> | |
| #include <tuple> | |
| #include <utility> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/posix_wrappers.h" | |
| #include "flutter/fml/trace_event.h" | |
| #include "flutter/lib/io/dart_io.h" | |
| #include "flutter/lib/ui/dart_runtime_hooks.h" | |
| #include "flutter/lib/ui/dart_ui.h" | |
| #include "flutter/lib/ui/window/platform_isolate.h" | |
| #include "flutter/runtime/dart_isolate_group_data.h" | |
| #include "flutter/runtime/dart_plugin_registrant.h" | |
| #include "flutter/runtime/dart_service_isolate.h" | |
| #include "flutter/runtime/dart_vm.h" | |
| #include "flutter/runtime/dart_vm_lifecycle.h" | |
| #include "flutter/runtime/isolate_configuration.h" | |
| #include "flutter/runtime/runtime_controller.h" | |
| #include "fml/message_loop_task_queues.h" | |
| #include "fml/task_source.h" | |
| #include "fml/time/time_point.h" | |
| #include "third_party/dart/runtime/include/dart_api.h" | |
| #include "third_party/dart/runtime/include/dart_tools_api.h" | |
| #include "third_party/tonic/converter/dart_converter.h" | |
| #include "third_party/tonic/dart_class_library.h" | |
| #include "third_party/tonic/dart_class_provider.h" | |
| #include "third_party/tonic/dart_message_handler.h" | |
| #include "third_party/tonic/dart_state.h" | |
| #include "third_party/tonic/file_loader/file_loader.h" | |
| #include "third_party/tonic/logging/dart_invoke.h" | |
| #include "third_party/tonic/scopes/dart_api_scope.h" | |
| #include "third_party/tonic/scopes/dart_isolate_scope.h" | |
| namespace flutter { | |
| namespace { | |
| constexpr std::string_view kFileUriPrefix = "file://"; | |
| class DartErrorString { | |
| public: | |
| DartErrorString() {} | |
| ~DartErrorString() { | |
| if (str_) { | |
| ::free(str_); | |
| } | |
| } | |
| char** error() { return &str_; } | |
| const char* str() const { return str_; } | |
| explicit operator bool() const { return str_ != nullptr; } | |
| private: | |
| FML_DISALLOW_COPY_AND_ASSIGN(DartErrorString); | |
| char* str_ = nullptr; | |
| }; | |
| } // anonymous namespace | |
| DartIsolate::Flags::Flags() : Flags(nullptr) {} | |
| DartIsolate::Flags::Flags(const Dart_IsolateFlags* flags) { | |
| if (flags) { | |
| flags_ = *flags; | |
| } else { | |
| ::Dart_IsolateFlagsInitialize(&flags_); | |
| } | |
| } | |
| DartIsolate::Flags::~Flags() = default; | |
| void DartIsolate::Flags::SetNullSafetyEnabled(bool enabled) { | |
| flags_.null_safety = enabled; | |
| } | |
| void DartIsolate::Flags::SetIsDontNeedSafe(bool value) { | |
| flags_.snapshot_is_dontneed_safe = value; | |
| } | |
| Dart_IsolateFlags DartIsolate::Flags::Get() const { | |
| return flags_; | |
| } | |
| std::weak_ptr<DartIsolate> DartIsolate::CreateRunningRootIsolate( | |
| const Settings& settings, | |
| const fml::RefPtr<const DartSnapshot>& isolate_snapshot, | |
| std::unique_ptr<PlatformConfiguration> platform_configuration, | |
| Flags isolate_flags, | |
| const fml::closure& root_isolate_create_callback, | |
| const fml::closure& isolate_create_callback, | |
| const fml::closure& isolate_shutdown_callback, | |
| std::optional<std::string> dart_entrypoint, | |
| std::optional<std::string> dart_entrypoint_library, | |
| const std::vector<std::string>& dart_entrypoint_args, | |
| std::unique_ptr<IsolateConfiguration> isolate_configuration, | |
| const UIDartState::Context& context, | |
| const DartIsolate* spawning_isolate) { | |
| if (!isolate_snapshot) { | |
| FML_LOG(ERROR) << "Invalid isolate snapshot."; | |
| return {}; | |
| } | |
| if (!isolate_configuration) { | |
| FML_LOG(ERROR) << "Invalid isolate configuration."; | |
| return {}; | |
| } | |
| isolate_flags.SetNullSafetyEnabled( | |
| isolate_configuration->IsNullSafetyEnabled(*isolate_snapshot)); | |
| isolate_flags.SetIsDontNeedSafe(isolate_snapshot->IsDontNeedSafe()); | |
| auto isolate = CreateRootIsolate(settings, // | |
| isolate_snapshot, // | |
| std::move(platform_configuration), // | |
| isolate_flags, // | |
| isolate_create_callback, // | |
| isolate_shutdown_callback, // | |
| context, // | |
| spawning_isolate // | |
| ) | |
| .lock(); | |
| if (!isolate) { | |
| FML_LOG(ERROR) << "Could not create root isolate."; | |
| return {}; | |
| } | |
| fml::ScopedCleanupClosure shutdown_on_error([isolate]() { | |
| if (!isolate->Shutdown()) { | |
| FML_DLOG(ERROR) << "Could not shutdown transient isolate."; | |
| } | |
| }); | |
| if (isolate->GetPhase() != DartIsolate::Phase::LibrariesSetup) { | |
| FML_LOG(ERROR) << "Root isolate was created in an incorrect phase: " | |
| << static_cast<int>(isolate->GetPhase()); | |
| return {}; | |
| } | |
| if (!isolate_configuration->PrepareIsolate(*isolate.get())) { | |
| FML_LOG(ERROR) << "Could not prepare isolate."; | |
| return {}; | |
| } | |
| if (isolate->GetPhase() != DartIsolate::Phase::Ready) { | |
| FML_LOG(ERROR) << "Root isolate not in the ready phase for Dart entrypoint " | |
| "invocation."; | |
| return {}; | |
| } | |
| if (settings.root_isolate_create_callback) { | |
| // Isolate callbacks always occur in isolate scope and before user code has | |
| // had a chance to run. | |
| tonic::DartState::Scope scope(isolate.get()); | |
| settings.root_isolate_create_callback(*isolate.get()); | |
| } | |
| if (root_isolate_create_callback) { | |
| root_isolate_create_callback(); | |
| } | |
| if (!isolate->RunFromLibrary(std::move(dart_entrypoint_library), // | |
| std::move(dart_entrypoint), // | |
| dart_entrypoint_args)) { | |
| FML_LOG(ERROR) << "Could not run the run main Dart entrypoint."; | |
| return {}; | |
| } | |
| if (settings.root_isolate_shutdown_callback) { | |
| isolate->AddIsolateShutdownCallback( | |
| settings.root_isolate_shutdown_callback); | |
| } | |
| shutdown_on_error.Release(); | |
| return isolate; | |
| } | |
| void DartIsolate::SpawnIsolateShutdownCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data) { | |
| DartIsolate::DartIsolateShutdownCallback(isolate_group_data, isolate_data); | |
| } | |
| std::weak_ptr<DartIsolate> DartIsolate::CreateRootIsolate( | |
| const Settings& settings, | |
| fml::RefPtr<const DartSnapshot> isolate_snapshot, | |
| std::unique_ptr<PlatformConfiguration> platform_configuration, | |
| const Flags& flags, | |
| const fml::closure& isolate_create_callback, | |
| const fml::closure& isolate_shutdown_callback, | |
| const UIDartState::Context& context, | |
| const DartIsolate* spawning_isolate) { | |
| TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); | |
| // Only needed if this is the main isolate for the group. | |
| std::unique_ptr<std::shared_ptr<DartIsolateGroupData>> isolate_group_data; | |
| auto isolate_data = std::make_unique<std::shared_ptr<DartIsolate>>( | |
| std::shared_ptr<DartIsolate>(new DartIsolate( | |
| /*settings=*/settings, | |
| /*is_root_isolate=*/true, | |
| /*context=*/context, | |
| /*is_spawning_in_group=*/!!spawning_isolate))); | |
| DartErrorString error; | |
| Dart_Isolate vm_isolate = nullptr; | |
| auto isolate_flags = flags.Get(); | |
| IsolateMaker isolate_maker; | |
| if (spawning_isolate) { | |
| isolate_maker = | |
| [spawning_isolate]( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data, | |
| Dart_IsolateFlags* flags, char** error) { | |
| return Dart_CreateIsolateInGroup( | |
| /*group_member=*/spawning_isolate->isolate(), | |
| /*name=*/ | |
| spawning_isolate->GetIsolateGroupData() | |
| .GetAdvisoryScriptEntrypoint() | |
| .c_str(), | |
| /*shutdown_callback=*/ | |
| reinterpret_cast<Dart_IsolateShutdownCallback>( | |
| DartIsolate::SpawnIsolateShutdownCallback), | |
| /*cleanup_callback=*/ | |
| reinterpret_cast<Dart_IsolateCleanupCallback>( | |
| DartIsolateCleanupCallback), | |
| /*child_isolate_data=*/isolate_data, | |
| /*error=*/error); | |
| }; | |
| } else { | |
| // The child isolate preparer is null but will be set when the isolate is | |
| // being prepared to run. | |
| isolate_group_data = | |
| std::make_unique<std::shared_ptr<DartIsolateGroupData>>( | |
| std::shared_ptr<DartIsolateGroupData>(new DartIsolateGroupData( | |
| settings, // settings | |
| std::move(isolate_snapshot), // isolate snapshot | |
| context.advisory_script_uri, // advisory URI | |
| context.advisory_script_entrypoint, // advisory entrypoint | |
| nullptr, // child isolate preparer | |
| isolate_create_callback, // isolate create callback | |
| isolate_shutdown_callback // isolate shutdown callback | |
| ))); | |
| isolate_maker = [](std::shared_ptr<DartIsolateGroupData>* | |
| isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data, | |
| Dart_IsolateFlags* flags, char** error) { | |
| return Dart_CreateIsolateGroup( | |
| (*isolate_group_data)->GetAdvisoryScriptURI().c_str(), | |
| (*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(), | |
| (*isolate_group_data)->GetIsolateSnapshot()->GetDataMapping(), | |
| (*isolate_group_data)->GetIsolateSnapshot()->GetInstructionsMapping(), | |
| flags, isolate_group_data, isolate_data, error); | |
| }; | |
| } | |
| vm_isolate = CreateDartIsolateGroup(std::move(isolate_group_data), | |
| std::move(isolate_data), &isolate_flags, | |
| error.error(), isolate_maker); | |
| if (error) { | |
| FML_LOG(ERROR) << "CreateRootIsolate failed: " << error.str(); | |
| } | |
| if (vm_isolate == nullptr) { | |
| return {}; | |
| } | |
| std::shared_ptr<DartIsolate>* root_isolate_data = | |
| static_cast<std::shared_ptr<DartIsolate>*>(Dart_IsolateData(vm_isolate)); | |
| (*root_isolate_data) | |
| ->SetPlatformConfiguration(std::move(platform_configuration)); | |
| return (*root_isolate_data)->GetWeakIsolatePtr(); | |
| } | |
| Dart_Isolate DartIsolate::CreatePlatformIsolate(Dart_Handle entry_point, | |
| char** error) { | |
| *error = nullptr; | |
| PlatformConfiguration* platform_config = platform_configuration(); | |
| FML_DCHECK(platform_config != nullptr); | |
| std::shared_ptr<PlatformIsolateManager> platform_isolate_manager = | |
| platform_config->client()->GetPlatformIsolateManager(); | |
| std::weak_ptr<PlatformIsolateManager> weak_platform_isolate_manager = | |
| platform_isolate_manager; | |
| if (platform_isolate_manager->HasShutdownMaybeFalseNegative()) { | |
| // Don't set the error string. We want to silently ignore this error, | |
| // because the engine is shutting down. | |
| FML_LOG(INFO) << "CreatePlatformIsolate called after shutdown"; | |
| return nullptr; | |
| } | |
| Dart_Isolate parent_isolate = isolate(); | |
| Dart_ExitIsolate(); // Exit parent_isolate. | |
| const TaskRunners& task_runners = GetTaskRunners(); | |
| fml::RefPtr<fml::TaskRunner> platform_task_runner = | |
| task_runners.GetPlatformTaskRunner(); | |
| FML_DCHECK(platform_task_runner); | |
| auto isolate_group_data = std::shared_ptr<DartIsolateGroupData>( | |
| *static_cast<std::shared_ptr<DartIsolateGroupData>*>( | |
| Dart_IsolateGroupData(parent_isolate))); | |
| Settings settings(isolate_group_data->GetSettings()); | |
| // PlatformIsolate.spawn should behave like Isolate.spawn when unhandled | |
| // exceptions happen (log the exception, but don't terminate the app). But the | |
| // default unhandled_exception_callback may terminate the app, because it is | |
| // only called for the root isolate (child isolates are managed by the VM and | |
| // have a different error code path). So override it to simply log the error. | |
| settings.unhandled_exception_callback = [](const std::string& error, | |
| const std::string& stack_trace) { | |
| FML_LOG(ERROR) << "Unhandled exception:\n" << error << "\n" << stack_trace; | |
| return true; | |
| }; | |
| // The platform isolate task observer must be added on the platform thread. So | |
| // schedule the add function on the platform task runner. | |
| TaskObserverAdd old_task_observer_add = settings.task_observer_add; | |
| settings.task_observer_add = [old_task_observer_add, platform_task_runner, | |
| weak_platform_isolate_manager]( | |
| intptr_t key, const fml::closure& callback) { | |
| platform_task_runner->PostTask([old_task_observer_add, | |
| weak_platform_isolate_manager, key, | |
| callback]() { | |
| std::shared_ptr<PlatformIsolateManager> platform_isolate_manager = | |
| weak_platform_isolate_manager.lock(); | |
| if (platform_isolate_manager == nullptr || | |
| platform_isolate_manager->HasShutdown()) { | |
| // Shutdown happened in between this task being posted, and it running. | |
| // platform_isolate has already been shut down. Do nothing. | |
| FML_LOG(INFO) << "Shutdown before platform isolate task observer added"; | |
| return; | |
| } | |
| old_task_observer_add(key, callback); | |
| }); | |
| }; | |
| UIDartState::Context context(task_runners); | |
| context.advisory_script_uri = isolate_group_data->GetAdvisoryScriptURI(); | |
| context.advisory_script_entrypoint = | |
| isolate_group_data->GetAdvisoryScriptEntrypoint(); | |
| auto isolate_data = std::make_unique<std::shared_ptr<DartIsolate>>( | |
| std::shared_ptr<DartIsolate>( | |
| new DartIsolate(settings, context, platform_isolate_manager))); | |
| IsolateMaker isolate_maker = | |
| [parent_isolate]( | |
| std::shared_ptr<DartIsolateGroupData>* unused_isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data, Dart_IsolateFlags* flags, | |
| char** error) { | |
| return Dart_CreateIsolateInGroup( | |
| /*group_member=*/parent_isolate, | |
| /*name=*/"PlatformIsolate", | |
| /*shutdown_callback=*/ | |
| reinterpret_cast<Dart_IsolateShutdownCallback>( | |
| DartIsolate::SpawnIsolateShutdownCallback), | |
| /*cleanup_callback=*/ | |
| reinterpret_cast<Dart_IsolateCleanupCallback>( | |
| DartIsolateCleanupCallback), | |
| /*child_isolate_data=*/isolate_data, | |
| /*error=*/error); | |
| }; | |
| Dart_Isolate platform_isolate = CreateDartIsolateGroup( | |
| nullptr, std::move(isolate_data), nullptr, error, isolate_maker); | |
| Dart_EnterIsolate(parent_isolate); | |
| if (*error) { | |
| return nullptr; | |
| } | |
| if (!platform_isolate_manager->RegisterPlatformIsolate(platform_isolate)) { | |
| // The PlatformIsolateManager was shutdown while we were creating the | |
| // isolate. This means that we're shutting down the engine. We need to | |
| // shutdown the platform isolate. | |
| FML_LOG(INFO) << "Shutdown during platform isolate creation"; | |
| tonic::DartIsolateScope isolate_scope(platform_isolate); | |
| Dart_ShutdownIsolate(); | |
| return nullptr; | |
| } | |
| tonic::DartApiScope api_scope; | |
| Dart_PersistentHandle entry_point_handle = | |
| Dart_NewPersistentHandle(entry_point); | |
| platform_task_runner->PostTask([entry_point_handle, platform_isolate, | |
| weak_platform_isolate_manager]() { | |
| std::shared_ptr<PlatformIsolateManager> platform_isolate_manager = | |
| weak_platform_isolate_manager.lock(); | |
| if (platform_isolate_manager == nullptr || | |
| platform_isolate_manager->HasShutdown()) { | |
| // Shutdown happened in between this task being posted, and it running. | |
| // platform_isolate has already been shut down. Do nothing. | |
| FML_LOG(INFO) << "Shutdown before platform isolate entry point"; | |
| return; | |
| } | |
| tonic::DartIsolateScope isolate_scope(platform_isolate); | |
| tonic::DartApiScope api_scope; | |
| Dart_Handle entry_point = Dart_HandleFromPersistent(entry_point_handle); | |
| Dart_DeletePersistentHandle(entry_point_handle); | |
| // Disable Isolate.exit(). | |
| Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); | |
| FML_CHECK(!tonic::CheckAndHandleError(isolate_lib)); | |
| Dart_Handle isolate_type = Dart_GetNonNullableType( | |
| isolate_lib, tonic::ToDart("Isolate"), 0, nullptr); | |
| FML_CHECK(!tonic::CheckAndHandleError(isolate_type)); | |
| Dart_Handle result = | |
| Dart_SetField(isolate_type, tonic::ToDart("_mayExit"), Dart_False()); | |
| FML_CHECK(!tonic::CheckAndHandleError(result)); | |
| tonic::DartInvokeVoid(entry_point); | |
| }); | |
| return platform_isolate; | |
| } | |
| DartIsolate::DartIsolate(const Settings& settings, | |
| bool is_root_isolate, | |
| const UIDartState::Context& context, | |
| bool is_spawning_in_group) | |
| : UIDartState(settings.task_observer_add, | |
| settings.task_observer_remove, | |
| settings.log_tag, | |
| settings.unhandled_exception_callback, | |
| settings.log_message_callback, | |
| DartVMRef::GetIsolateNameServer(), | |
| is_root_isolate, | |
| context), | |
| may_insecurely_connect_to_all_domains_( | |
| settings.may_insecurely_connect_to_all_domains), | |
| is_platform_isolate_(false), | |
| is_spawning_in_group_(is_spawning_in_group), | |
| domain_network_policy_(settings.domain_network_policy) { | |
| phase_ = Phase::Uninitialized; | |
| } | |
| DartIsolate::DartIsolate( | |
| const Settings& settings, | |
| const UIDartState::Context& context, | |
| std::shared_ptr<PlatformIsolateManager> platform_isolate_manager) | |
| : UIDartState(settings.task_observer_add, | |
| settings.task_observer_remove, | |
| settings.log_tag, | |
| settings.unhandled_exception_callback, | |
| settings.log_message_callback, | |
| DartVMRef::GetIsolateNameServer(), | |
| false, // is_root_isolate | |
| context), | |
| may_insecurely_connect_to_all_domains_( | |
| settings.may_insecurely_connect_to_all_domains), | |
| is_platform_isolate_(true), | |
| is_spawning_in_group_(false), | |
| domain_network_policy_(settings.domain_network_policy), | |
| platform_isolate_manager_(std::move(platform_isolate_manager)) { | |
| phase_ = Phase::Uninitialized; | |
| } | |
| DartIsolate::~DartIsolate() { | |
| if (IsRootIsolate() && GetMessageHandlingTaskRunner()) { | |
| FML_DCHECK(GetMessageHandlingTaskRunner()->RunsTasksOnCurrentThread()); | |
| } | |
| } | |
| DartIsolate::Phase DartIsolate::GetPhase() const { | |
| return phase_; | |
| } | |
| std::string DartIsolate::GetServiceId() { | |
| const char* service_id_buf = Dart_IsolateServiceId(isolate()); | |
| std::string service_id(service_id_buf); | |
| free(const_cast<char*>(service_id_buf)); | |
| return service_id; | |
| } | |
| bool DartIsolate::Initialize(Dart_Isolate dart_isolate) { | |
| TRACE_EVENT0("flutter", "DartIsolate::Initialize"); | |
| if (phase_ != Phase::Uninitialized) { | |
| return false; | |
| } | |
| FML_DCHECK(dart_isolate != nullptr); | |
| FML_DCHECK(dart_isolate == Dart_CurrentIsolate()); | |
| // After this point, isolate scopes can be safely used. | |
| SetIsolate(dart_isolate); | |
| // For the root isolate set the "AppStartUp" as soon as the root isolate | |
| // has been initialized. This is to ensure that all the timeline events | |
| // that have the set user-tag will be listed user AppStartUp. | |
| if (IsRootIsolate()) { | |
| tonic::DartApiScope api_scope; | |
| Dart_SetCurrentUserTag(Dart_NewUserTag("AppStartUp")); | |
| } | |
| if (is_platform_isolate_) { | |
| SetMessageHandlingTaskRunner(GetTaskRunners().GetPlatformTaskRunner(), | |
| true); | |
| } else { | |
| SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner(), false); | |
| } | |
| if (tonic::CheckAndHandleError( | |
| Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))) { | |
| return false; | |
| } | |
| if (tonic::CheckAndHandleError( | |
| Dart_SetDeferredLoadHandler(OnDartLoadLibrary))) { | |
| return false; | |
| } | |
| if (!UpdateThreadPoolNames()) { | |
| return false; | |
| } | |
| phase_ = Phase::Initialized; | |
| return true; | |
| } | |
| fml::RefPtr<fml::TaskRunner> DartIsolate::GetMessageHandlingTaskRunner() const { | |
| return message_handling_task_runner_; | |
| } | |
| bool DartIsolate::LoadLoadingUnit( | |
| intptr_t loading_unit_id, | |
| std::unique_ptr<const fml::Mapping> snapshot_data, | |
| std::unique_ptr<const fml::Mapping> snapshot_instructions) { | |
| tonic::DartState::Scope scope(this); | |
| fml::RefPtr<DartSnapshot> dart_snapshot = | |
| DartSnapshot::IsolateSnapshotFromMappings( | |
| std::move(snapshot_data), std::move(snapshot_instructions)); | |
| Dart_Handle result = Dart_DeferredLoadComplete( | |
| loading_unit_id, dart_snapshot->GetDataMapping(), | |
| dart_snapshot->GetInstructionsMapping()); | |
| if (tonic::CheckAndHandleError(result)) { | |
| LoadLoadingUnitError(loading_unit_id, Dart_GetError(result), | |
| /*transient*/ true); | |
| return false; | |
| } | |
| loading_unit_snapshots_.insert(dart_snapshot); | |
| return true; | |
| } | |
| void DartIsolate::LoadLoadingUnitError(intptr_t loading_unit_id, | |
| const std::string& error_message, | |
| bool transient) { | |
| tonic::DartState::Scope scope(this); | |
| Dart_Handle result = Dart_DeferredLoadCompleteError( | |
| loading_unit_id, error_message.c_str(), transient); | |
| tonic::CheckAndHandleError(result); | |
| } | |
| void DartIsolate::SetMessageHandlingTaskRunner( | |
| const fml::RefPtr<fml::TaskRunner>& runner, | |
| bool post_directly_to_runner) { | |
| if (!runner) { | |
| return; | |
| } | |
| message_handling_task_runner_ = runner; | |
| tonic::DartMessageHandler::TaskDispatcher dispatcher; | |
| #ifdef OS_FUCHSIA | |
| post_directly_to_runner = true; | |
| #endif | |
| if (post_directly_to_runner) { | |
| dispatcher = [runner](std::function<void()> task) { | |
| runner->PostTask([task = std::move(task)]() { | |
| TRACE_EVENT0("flutter", "DartIsolate::HandleMessage"); | |
| task(); | |
| }); | |
| }; | |
| } else { | |
| dispatcher = [runner](std::function<void()> task) { | |
| auto task_queues = fml::MessageLoopTaskQueues::GetInstance(); | |
| task_queues->RegisterTask( | |
| runner->GetTaskQueueId(), | |
| [task = std::move(task)]() { | |
| TRACE_EVENT0("flutter", "DartIsolate::HandleMessage"); | |
| task(); | |
| }, | |
| fml::TimePoint::Now(), fml::TaskSourceGrade::kDartEventLoop); | |
| }; | |
| } | |
| message_handler().Initialize(dispatcher); | |
| } | |
| // Updating thread names here does not change the underlying OS thread names. | |
| // Instead, this is just additional metadata for the Dart VM Service to show the | |
| // thread name of the isolate. | |
| bool DartIsolate::UpdateThreadPoolNames() const { | |
| // TODO(chinmaygarde): This implementation does not account for multiple | |
| // shells sharing the same (or subset of) threads. | |
| const auto& task_runners = GetTaskRunners(); | |
| if (auto task_runner = task_runners.GetRasterTaskRunner()) { | |
| task_runner->PostTask( | |
| [label = task_runners.GetLabel() + std::string{".raster"}]() { | |
| Dart_SetThreadName(label.c_str()); | |
| }); | |
| } | |
| if (auto task_runner = task_runners.GetUITaskRunner()) { | |
| task_runner->PostTask( | |
| [label = task_runners.GetLabel() + std::string{".ui"}]() { | |
| Dart_SetThreadName(label.c_str()); | |
| }); | |
| } | |
| if (auto task_runner = task_runners.GetIOTaskRunner()) { | |
| task_runner->PostTask( | |
| [label = task_runners.GetLabel() + std::string{".io"}]() { | |
| Dart_SetThreadName(label.c_str()); | |
| }); | |
| } | |
| if (auto task_runner = task_runners.GetPlatformTaskRunner()) { | |
| task_runner->PostTask( | |
| [label = task_runners.GetLabel() + std::string{".platform"}]() { | |
| Dart_SetThreadName(label.c_str()); | |
| }); | |
| } | |
| return true; | |
| } | |
| bool DartIsolate::LoadLibraries() { | |
| TRACE_EVENT0("flutter", "DartIsolate::LoadLibraries"); | |
| if (phase_ != Phase::Initialized) { | |
| return false; | |
| } | |
| tonic::DartState::Scope scope(this); | |
| DartIO::InitForIsolate(may_insecurely_connect_to_all_domains_, | |
| domain_network_policy_); | |
| DartUI::InitForIsolate(GetIsolateGroupData().GetSettings()); | |
| const bool is_service_isolate = Dart_IsServiceIsolate(isolate()); | |
| DartRuntimeHooks::Install(IsRootIsolate() && !is_service_isolate, | |
| GetAdvisoryScriptURI()); | |
| if (!is_service_isolate) { | |
| class_library().add_provider( | |
| "ui", std::make_unique<tonic::DartClassProvider>(this, "dart:ui")); | |
| } | |
| phase_ = Phase::LibrariesSetup; | |
| return true; | |
| } | |
| bool DartIsolate::PrepareForRunningFromPrecompiledCode() { | |
| TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromPrecompiledCode"); | |
| if (phase_ != Phase::LibrariesSetup) { | |
| return false; | |
| } | |
| tonic::DartState::Scope scope(this); | |
| if (Dart_IsNull(Dart_RootLibrary())) { | |
| return false; | |
| } | |
| if (!MarkIsolateRunnable()) { | |
| return false; | |
| } | |
| if (GetIsolateGroupData().GetChildIsolatePreparer() == nullptr) { | |
| GetIsolateGroupData().SetChildIsolatePreparer([](DartIsolate* isolate) { | |
| return isolate->PrepareForRunningFromPrecompiledCode(); | |
| }); | |
| } | |
| const fml::closure& isolate_create_callback = | |
| GetIsolateGroupData().GetIsolateCreateCallback(); | |
| if (isolate_create_callback) { | |
| isolate_create_callback(); | |
| } | |
| phase_ = Phase::Ready; | |
| return true; | |
| } | |
| bool DartIsolate::LoadKernel(const std::shared_ptr<const fml::Mapping>& mapping, | |
| bool last_piece) { | |
| if (!Dart_IsKernel(mapping->GetMapping(), mapping->GetSize())) { | |
| return false; | |
| } | |
| // Mapping must be retained until isolate group shutdown. | |
| GetIsolateGroupData().AddKernelBuffer(mapping); | |
| Dart_Handle library = | |
| Dart_LoadLibraryFromKernel(mapping->GetMapping(), mapping->GetSize()); | |
| if (tonic::CheckAndHandleError(library)) { | |
| return false; | |
| } | |
| if (!last_piece) { | |
| // More to come. | |
| return true; | |
| } | |
| Dart_SetRootLibrary(library); | |
| if (tonic::CheckAndHandleError(Dart_FinalizeLoading(false))) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| [[nodiscard]] bool DartIsolate::PrepareForRunningFromKernel( | |
| const std::shared_ptr<const fml::Mapping>& mapping, | |
| bool child_isolate, | |
| bool last_piece) { | |
| TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromKernel"); | |
| if (phase_ != Phase::LibrariesSetup) { | |
| return false; | |
| } | |
| if (DartVM::IsRunningPrecompiledCode()) { | |
| return false; | |
| } | |
| tonic::DartState::Scope scope(this); | |
| if (!child_isolate && !is_spawning_in_group_) { | |
| if (!mapping || mapping->GetSize() == 0) { | |
| return false; | |
| } | |
| // Use root library provided by kernel in favor of one provided by snapshot. | |
| Dart_SetRootLibrary(Dart_Null()); | |
| if (!LoadKernel(mapping, last_piece)) { | |
| return false; | |
| } | |
| } | |
| if (!last_piece) { | |
| // More to come. | |
| return true; | |
| } | |
| if (Dart_IsNull(Dart_RootLibrary())) { | |
| return false; | |
| } | |
| if (!MarkIsolateRunnable()) { | |
| return false; | |
| } | |
| // Child isolate shares root isolate embedder_isolate (lines 691 and 693 | |
| // below). Re-initializing child_isolate_preparer_ lambda while it is being | |
| // executed leads to crashes. | |
| if (GetIsolateGroupData().GetChildIsolatePreparer() == nullptr) { | |
| GetIsolateGroupData().SetChildIsolatePreparer( | |
| [buffers = | |
| GetIsolateGroupData().GetKernelBuffers()](DartIsolate* isolate) { | |
| for (uint64_t i = 0; i < buffers.size(); i++) { | |
| bool last_piece = i + 1 == buffers.size(); | |
| const std::shared_ptr<const fml::Mapping>& buffer = buffers.at(i); | |
| if (!isolate->PrepareForRunningFromKernel(buffer, | |
| /*child_isolate=*/true, | |
| last_piece)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| }); | |
| } | |
| const fml::closure& isolate_create_callback = | |
| GetIsolateGroupData().GetIsolateCreateCallback(); | |
| if (isolate_create_callback) { | |
| isolate_create_callback(); | |
| } | |
| phase_ = Phase::Ready; | |
| return true; | |
| } | |
| [[nodiscard]] bool DartIsolate::PrepareForRunningFromKernels( | |
| std::vector<std::shared_ptr<const fml::Mapping>> kernels) { | |
| const auto count = kernels.size(); | |
| if (count == 0) { | |
| return false; | |
| } | |
| for (size_t i = 0; i < count; ++i) { | |
| bool last = (i == (count - 1)); | |
| if (!PrepareForRunningFromKernel(kernels[i], /*child_isolate=*/false, | |
| last)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| [[nodiscard]] bool DartIsolate::PrepareForRunningFromKernels( | |
| std::vector<std::unique_ptr<const fml::Mapping>> kernels) { | |
| std::vector<std::shared_ptr<const fml::Mapping>> shared_kernels; | |
| shared_kernels.reserve(kernels.size()); | |
| for (auto& kernel : kernels) { | |
| shared_kernels.emplace_back(std::move(kernel)); | |
| } | |
| return PrepareForRunningFromKernels(shared_kernels); | |
| } | |
| bool DartIsolate::MarkIsolateRunnable() { | |
| TRACE_EVENT0("flutter", "DartIsolate::MarkIsolateRunnable"); | |
| if (phase_ != Phase::LibrariesSetup) { | |
| return false; | |
| } | |
| // This function may only be called from an active isolate scope. | |
| if (Dart_CurrentIsolate() != isolate()) { | |
| return false; | |
| } | |
| // There must be no current isolate to mark an isolate as being runnable. | |
| Dart_ExitIsolate(); | |
| char* error = Dart_IsolateMakeRunnable(isolate()); | |
| if (error) { | |
| FML_DLOG(ERROR) << error; | |
| ::free(error); | |
| // Failed. Restore the isolate. | |
| Dart_EnterIsolate(isolate()); | |
| return false; | |
| } | |
| // Success. Restore the isolate. | |
| Dart_EnterIsolate(isolate()); | |
| return true; | |
| } | |
| [[nodiscard]] static bool InvokeMainEntrypoint( | |
| Dart_Handle user_entrypoint_function, | |
| Dart_Handle args) { | |
| if (tonic::CheckAndHandleError(user_entrypoint_function)) { | |
| FML_LOG(ERROR) << "Could not resolve main entrypoint function."; | |
| return false; | |
| } | |
| Dart_Handle start_main_isolate_function = | |
| tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")), | |
| "_getStartMainIsolateFunction", {}); | |
| if (tonic::CheckAndHandleError(start_main_isolate_function)) { | |
| FML_LOG(ERROR) << "Could not resolve main entrypoint trampoline."; | |
| return false; | |
| } | |
| if (tonic::CheckAndHandleError(tonic::DartInvokeField( | |
| Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMain", | |
| {start_main_isolate_function, user_entrypoint_function, args}))) { | |
| FML_LOG(ERROR) << "Could not invoke the main entrypoint."; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool DartIsolate::RunFromLibrary(std::optional<std::string> library_name, | |
| std::optional<std::string> entrypoint, | |
| const std::vector<std::string>& args) { | |
| TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary"); | |
| if (phase_ != Phase::Ready) { | |
| return false; | |
| } | |
| tonic::DartState::Scope scope(this); | |
| auto library_handle = | |
| library_name.has_value() && !library_name.value().empty() | |
| ? ::Dart_LookupLibrary(tonic::ToDart(library_name.value().c_str())) | |
| : ::Dart_RootLibrary(); | |
| auto entrypoint_handle = entrypoint.has_value() && !entrypoint.value().empty() | |
| ? tonic::ToDart(entrypoint.value().c_str()) | |
| : tonic::ToDart("main"); | |
| if (!FindAndInvokeDartPluginRegistrant()) { | |
| // TODO(gaaclarke): Remove once the framework PR lands that uses `--source` | |
| // to compile the Dart Plugin Registrant | |
| // (https://github.com/flutter/flutter/pull/100572). | |
| InvokeDartPluginRegistrantIfAvailable(library_handle); | |
| } | |
| auto user_entrypoint_function = | |
| ::Dart_GetField(library_handle, entrypoint_handle); | |
| auto entrypoint_args = tonic::ToDart(args); | |
| if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) { | |
| return false; | |
| } | |
| phase_ = Phase::Running; | |
| return true; | |
| } | |
| bool DartIsolate::Shutdown() { | |
| TRACE_EVENT0("flutter", "DartIsolate::Shutdown"); | |
| // This call may be re-entrant since Dart_ShutdownIsolate can invoke the | |
| // cleanup callback which deletes the embedder side object of the dart isolate | |
| // (a.k.a. this). | |
| if (phase_ == Phase::Shutdown) { | |
| return false; | |
| } | |
| phase_ = Phase::Shutdown; | |
| Dart_Isolate vm_isolate = isolate(); | |
| // The isolate can be nullptr if this instance is the stub isolate data used | |
| // during root isolate creation. | |
| if (vm_isolate != nullptr) { | |
| // We need to enter the isolate because Dart_ShutdownIsolate does not take | |
| // the isolate to shutdown as a parameter. | |
| FML_DCHECK(Dart_CurrentIsolate() == nullptr); | |
| Dart_EnterIsolate(vm_isolate); | |
| Dart_ShutdownIsolate(); | |
| FML_DCHECK(Dart_CurrentIsolate() == nullptr); | |
| } | |
| return true; | |
| } | |
| Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( | |
| const char* package_root, | |
| const char* package_config, | |
| Dart_IsolateFlags* flags, | |
| char** error) { | |
| auto vm_data = DartVMRef::GetVMData(); | |
| if (!vm_data) { | |
| *error = fml::strdup( | |
| "Could not access VM data to initialize isolates. This may be because " | |
| "the VM has initialized shutdown on another thread already."); | |
| return nullptr; | |
| } | |
| const auto& settings = vm_data->GetSettings(); | |
| if (!settings.enable_vm_service) { | |
| return nullptr; | |
| } | |
| flags->load_vmservice_library = true; | |
| #if (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DEBUG) | |
| // TODO(68663): The service isolate in debug mode is always launched without | |
| // sound null safety. Fix after the isolate snapshot data is created with the | |
| // right flags. | |
| flags->null_safety = vm_data->GetServiceIsolateSnapshotNullSafety(); | |
| #endif | |
| UIDartState::Context context( | |
| TaskRunners("io.flutter." DART_VM_SERVICE_ISOLATE_NAME, nullptr, nullptr, | |
| nullptr, nullptr)); | |
| context.advisory_script_uri = DART_VM_SERVICE_ISOLATE_NAME; | |
| context.advisory_script_entrypoint = DART_VM_SERVICE_ISOLATE_NAME; | |
| std::weak_ptr<DartIsolate> weak_service_isolate = | |
| DartIsolate::CreateRootIsolate(vm_data->GetSettings(), // | |
| vm_data->GetServiceIsolateSnapshot(), // | |
| nullptr, // | |
| DartIsolate::Flags{flags}, // | |
| nullptr, // | |
| nullptr, // | |
| context); // | |
| std::shared_ptr<DartIsolate> service_isolate = weak_service_isolate.lock(); | |
| if (!service_isolate) { | |
| *error = fml::strdup("Could not create the service isolate."); | |
| FML_DLOG(ERROR) << *error; | |
| return nullptr; | |
| } | |
| tonic::DartState::Scope scope(service_isolate); | |
| if (!DartServiceIsolate::Startup( | |
| settings.vm_service_host, // server IP address | |
| settings.vm_service_port, // server VM service port | |
| tonic::DartState::HandleLibraryTag, // embedder library tag handler | |
| false, // disable websocket origin check | |
| settings.disable_service_auth_codes, // disable VM service auth codes | |
| settings.enable_service_port_fallback, // enable fallback to port 0 | |
| // when bind fails. | |
| error // error (out) | |
| )) { | |
| // Error is populated by call to startup. | |
| FML_DLOG(ERROR) << *error; | |
| return nullptr; | |
| } | |
| if (auto callback = vm_data->GetSettings().service_isolate_create_callback) { | |
| callback(); | |
| } | |
| if (auto service_protocol = DartVMRef::GetServiceProtocol()) { | |
| service_protocol->ToggleHooks(true); | |
| } else { | |
| FML_DLOG(ERROR) | |
| << "Could not acquire the service protocol handlers. This might be " | |
| "because the VM has already begun teardown on another thread."; | |
| } | |
| return service_isolate->isolate(); | |
| } | |
| DartIsolateGroupData& DartIsolate::GetIsolateGroupData() { | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data = | |
| static_cast<std::shared_ptr<DartIsolateGroupData>*>( | |
| Dart_IsolateGroupData(isolate())); | |
| return **isolate_group_data; | |
| } | |
| const DartIsolateGroupData& DartIsolate::GetIsolateGroupData() const { | |
| DartIsolate* non_const_this = const_cast<DartIsolate*>(this); | |
| return non_const_this->GetIsolateGroupData(); | |
| } | |
| // |Dart_IsolateGroupCreateCallback| | |
| Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( | |
| const char* advisory_script_uri, | |
| const char* advisory_script_entrypoint, | |
| const char* package_root, | |
| const char* package_config, | |
| Dart_IsolateFlags* flags, | |
| std::shared_ptr<DartIsolate>* parent_isolate_data, | |
| char** error) { | |
| TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCreateCallback"); | |
| if (parent_isolate_data == nullptr && | |
| strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { | |
| // The VM attempts to start the VM service for us on |Dart_Initialize|. In | |
| // such a case, the callback data will be null and the script URI will be | |
| // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service | |
| // isolate like normal but dont hold a reference to it at all. We also start | |
| // this isolate since we will never again reference it from the engine. | |
| return DartCreateAndStartServiceIsolate(package_root, // | |
| package_config, // | |
| flags, // | |
| error // | |
| ); | |
| } | |
| if (!parent_isolate_data) { | |
| return nullptr; | |
| } | |
| DartIsolateGroupData& parent_group_data = | |
| (*parent_isolate_data)->GetIsolateGroupData(); | |
| if (strncmp(advisory_script_uri, kFileUriPrefix.data(), | |
| kFileUriPrefix.size())) { | |
| std::string error_msg = | |
| std::string("Unsupported isolate URI: ") + advisory_script_uri; | |
| *error = fml::strdup(error_msg.c_str()); | |
| return nullptr; | |
| } | |
| auto isolate_group_data = | |
| std::make_unique<std::shared_ptr<DartIsolateGroupData>>( | |
| std::shared_ptr<DartIsolateGroupData>(new DartIsolateGroupData( | |
| parent_group_data.GetSettings(), | |
| parent_group_data.GetIsolateSnapshot(), advisory_script_uri, | |
| advisory_script_entrypoint, | |
| parent_group_data.GetChildIsolatePreparer(), | |
| parent_group_data.GetIsolateCreateCallback(), | |
| parent_group_data.GetIsolateShutdownCallback()))); | |
| TaskRunners null_task_runners(advisory_script_uri, | |
| /* platform= */ nullptr, | |
| /* raster= */ nullptr, | |
| /* ui= */ nullptr, | |
| /* io= */ nullptr); | |
| UIDartState::Context context(null_task_runners); | |
| context.advisory_script_uri = advisory_script_uri; | |
| context.advisory_script_entrypoint = advisory_script_entrypoint; | |
| auto isolate_data = std::make_unique<std::shared_ptr<DartIsolate>>( | |
| std::shared_ptr<DartIsolate>( | |
| new DartIsolate((*isolate_group_data)->GetSettings(), // settings | |
| false, // is_root_isolate | |
| context))); // context | |
| Dart_Isolate vm_isolate = CreateDartIsolateGroup( | |
| std::move(isolate_group_data), std::move(isolate_data), flags, error, | |
| [](std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data, Dart_IsolateFlags* flags, | |
| char** error) { | |
| return Dart_CreateIsolateGroup( | |
| (*isolate_group_data)->GetAdvisoryScriptURI().c_str(), | |
| (*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(), | |
| (*isolate_group_data)->GetIsolateSnapshot()->GetDataMapping(), | |
| (*isolate_group_data) | |
| ->GetIsolateSnapshot() | |
| ->GetInstructionsMapping(), | |
| flags, isolate_group_data, isolate_data, error); | |
| }); | |
| if (*error) { | |
| FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << *error; | |
| } | |
| return vm_isolate; | |
| } | |
| // |Dart_IsolateInitializeCallback| | |
| bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, | |
| char** error) { | |
| TRACE_EVENT0("flutter", "DartIsolate::DartIsolateInitializeCallback"); | |
| Dart_Isolate isolate = Dart_CurrentIsolate(); | |
| if (isolate == nullptr) { | |
| *error = fml::strdup("Isolate should be available in initialize callback."); | |
| FML_DLOG(ERROR) << *error; | |
| return false; | |
| } | |
| auto* isolate_group_data = | |
| static_cast<std::shared_ptr<DartIsolateGroupData>*>( | |
| Dart_CurrentIsolateGroupData()); | |
| TaskRunners null_task_runners((*isolate_group_data)->GetAdvisoryScriptURI(), | |
| /* platform= */ nullptr, | |
| /* raster= */ nullptr, | |
| /* ui= */ nullptr, | |
| /* io= */ nullptr); | |
| UIDartState::Context context(null_task_runners); | |
| context.advisory_script_uri = (*isolate_group_data)->GetAdvisoryScriptURI(); | |
| context.advisory_script_entrypoint = | |
| (*isolate_group_data)->GetAdvisoryScriptEntrypoint(); | |
| auto embedder_isolate = std::make_unique<std::shared_ptr<DartIsolate>>( | |
| std::shared_ptr<DartIsolate>( | |
| new DartIsolate((*isolate_group_data)->GetSettings(), // settings | |
| false, // is_root_isolate | |
| context))); // context | |
| // root isolate should have been created via CreateRootIsolate | |
| if (!InitializeIsolate(*embedder_isolate, isolate, error)) { | |
| return false; | |
| } | |
| // The ownership of the embedder object is controlled by the Dart VM. So the | |
| // only reference returned to the caller is weak. | |
| *child_callback_data = embedder_isolate.release(); | |
| return true; | |
| } | |
| Dart_Isolate DartIsolate::CreateDartIsolateGroup( | |
| std::unique_ptr<std::shared_ptr<DartIsolateGroupData>> isolate_group_data, | |
| std::unique_ptr<std::shared_ptr<DartIsolate>> isolate_data, | |
| Dart_IsolateFlags* flags, | |
| char** error, | |
| const DartIsolate::IsolateMaker& make_isolate) { | |
| TRACE_EVENT0("flutter", "DartIsolate::CreateDartIsolateGroup"); | |
| // Create the Dart VM isolate and give it the embedder object as the baton. | |
| Dart_Isolate isolate = | |
| make_isolate(isolate_group_data.get(), isolate_data.get(), flags, error); | |
| if (isolate == nullptr) { | |
| return nullptr; | |
| } | |
| bool success = false; | |
| { | |
| // Ownership of the isolate data objects has been transferred to the Dart | |
| // VM. | |
| // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) | |
| std::shared_ptr<DartIsolate> embedder_isolate(*isolate_data); | |
| isolate_group_data.release(); | |
| isolate_data.release(); | |
| // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) | |
| success = InitializeIsolate(embedder_isolate, isolate, error); | |
| } | |
| if (!success) { | |
| Dart_ShutdownIsolate(); | |
| return nullptr; | |
| } | |
| // Balances the implicit [Dart_EnterIsolate] by [make_isolate] above. | |
| Dart_ExitIsolate(); | |
| return isolate; | |
| } | |
| bool DartIsolate::InitializeIsolate( | |
| const std::shared_ptr<DartIsolate>& embedder_isolate, | |
| Dart_Isolate isolate, | |
| char** error) { | |
| TRACE_EVENT0("flutter", "DartIsolate::InitializeIsolate"); | |
| if (!embedder_isolate->Initialize(isolate)) { | |
| *error = fml::strdup("Embedder could not initialize the Dart isolate."); | |
| FML_DLOG(ERROR) << *error; | |
| return false; | |
| } | |
| if (!embedder_isolate->LoadLibraries()) { | |
| *error = fml::strdup( | |
| "Embedder could not load libraries in the new Dart isolate."); | |
| FML_DLOG(ERROR) << *error; | |
| return false; | |
| } | |
| // Root isolates will be set up by the engine and the service isolate | |
| // (which is also a root isolate) by the utility routines in the VM. | |
| // However, secondary isolates will be run by the VM if they are | |
| // marked as runnable. | |
| if (!embedder_isolate->IsRootIsolate()) { | |
| auto child_isolate_preparer = | |
| embedder_isolate->GetIsolateGroupData().GetChildIsolatePreparer(); | |
| FML_DCHECK(child_isolate_preparer); | |
| if (!child_isolate_preparer(embedder_isolate.get())) { | |
| *error = fml::strdup("Could not prepare the child isolate to run."); | |
| FML_DLOG(ERROR) << *error; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // |Dart_IsolateShutdownCallback| | |
| void DartIsolate::DartIsolateShutdownCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data) { | |
| TRACE_EVENT0("flutter", "DartIsolate::DartIsolateShutdownCallback"); | |
| // If the isolate initialization failed there will be nothing to do. | |
| // This can happen e.g. during a [DartIsolateInitializeCallback] invocation | |
| // that fails to initialize the VM-created isolate. | |
| if (isolate_data == nullptr) { | |
| return; | |
| } | |
| isolate_data->get()->OnShutdownCallback(); | |
| } | |
| // |Dart_IsolateGroupCleanupCallback| | |
| void DartIsolate::DartIsolateGroupCleanupCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_data) { | |
| TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCleanupCallback"); | |
| delete isolate_data; | |
| } | |
| // |Dart_IsolateCleanupCallback| | |
| void DartIsolate::DartIsolateCleanupCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data) { | |
| TRACE_EVENT0("flutter", "DartIsolate::DartIsolateCleanupCallback"); | |
| delete isolate_data; | |
| } | |
| std::weak_ptr<DartIsolate> DartIsolate::GetWeakIsolatePtr() { | |
| return std::static_pointer_cast<DartIsolate>(shared_from_this()); | |
| } | |
| void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) { | |
| shutdown_callbacks_.emplace_back(std::make_unique<AutoFireClosure>(closure)); | |
| } | |
| void DartIsolate::OnShutdownCallback() { | |
| tonic::DartState* state = tonic::DartState::Current(); | |
| if (state != nullptr) { | |
| state->SetIsShuttingDown(); | |
| } | |
| { | |
| tonic::DartApiScope api_scope; | |
| Dart_Handle sticky_error = Dart_GetStickyError(); | |
| if (!Dart_IsNull(sticky_error) && !Dart_IsFatalError(sticky_error)) { | |
| FML_LOG(ERROR) << Dart_GetError(sticky_error); | |
| } | |
| } | |
| if (is_platform_isolate_) { | |
| FML_DCHECK(platform_isolate_manager_ != nullptr); | |
| platform_isolate_manager_->RemovePlatformIsolate(isolate()); | |
| } | |
| shutdown_callbacks_.clear(); | |
| const fml::closure& isolate_shutdown_callback = | |
| GetIsolateGroupData().GetIsolateShutdownCallback(); | |
| if (isolate_shutdown_callback) { | |
| isolate_shutdown_callback(); | |
| } | |
| } | |
| Dart_Handle DartIsolate::OnDartLoadLibrary(intptr_t loading_unit_id) { | |
| if (Current()->platform_configuration()) { | |
| Current()->platform_configuration()->client()->RequestDartDeferredLibrary( | |
| loading_unit_id); | |
| return Dart_Null(); | |
| } | |
| const std::string error_message = | |
| "Platform Configuration was null. Deferred library load request " | |
| "for loading unit id " + | |
| std::to_string(loading_unit_id) + " was not sent."; | |
| FML_LOG(ERROR) << error_message; | |
| return Dart_NewApiError(error_message.c_str()); | |
| } | |
| Dart_Handle DartIsolate::LoadLibraryFromKernel( | |
| const std::shared_ptr<const fml::Mapping>& mapping) { | |
| if (DartVM::IsRunningPrecompiledCode()) { | |
| return Dart_Null(); | |
| } | |
| auto* isolate_group_data = | |
| static_cast<std::shared_ptr<DartIsolateGroupData>*>( | |
| Dart_CurrentIsolateGroupData()); | |
| // Mapping must be retained until isolate shutdown. | |
| (*isolate_group_data)->AddKernelBuffer(mapping); | |
| auto lib = | |
| Dart_LoadLibraryFromKernel(mapping->GetMapping(), mapping->GetSize()); | |
| if (tonic::CheckAndHandleError(lib)) { | |
| return Dart_Null(); | |
| } | |
| auto result = Dart_FinalizeLoading(false); | |
| if (Dart_IsError(result)) { | |
| return result; | |
| } | |
| return Dart_GetField(lib, Dart_NewStringFromCString("main")); | |
| } | |
| DartIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure) | |
| : closure_(closure) {} | |
| DartIsolate::AutoFireClosure::~AutoFireClosure() { | |
| if (closure_) { | |
| closure_(); | |
| } | |
| } | |
| } // namespace flutter |