// 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/fml/make_copyable.h" #include "flutter/fml/mapping.h" #include "flutter/fml/paths.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/thread.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/runtime/runtime_test.h" #include "flutter/testing/testing.h" #include "flutter/testing/thread_test.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/scopes/dart_isolate_scope.h" namespace flutter { namespace testing { using DartIsolateTest = RuntimeTest; TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); ASSERT_TRUE(vm_ref); auto vm_data = vm_ref.GetVMData(); ASSERT_TRUE(vm_data); TaskRunners task_runners(::testing::GetCurrentTestName(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner() // ); auto weak_isolate = DartIsolate::CreateRootIsolate( vm_data->GetSettings(), // settings vm_data->GetIsolateSnapshot(), // isolate snapshot vm_data->GetSharedSnapshot(), // shared snapshot std::move(task_runners), // task runners nullptr, // window {}, // snapshot delegate {}, // io manager "main.dart", // advisory uri "main" // advisory entrypoint ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); ASSERT_TRUE(root_isolate->Shutdown()); } TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); ASSERT_TRUE(vm_ref); auto vm_data = vm_ref.GetVMData(); ASSERT_TRUE(vm_data); TaskRunners task_runners(::testing::GetCurrentTestName(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner(), // GetCurrentTaskRunner() // ); auto weak_isolate = DartIsolate::CreateRootIsolate( vm_data->GetSettings(), // settings vm_data->GetIsolateSnapshot(), // isolate snapshot vm_data->GetSharedSnapshot(), // shared snapshot std::move(task_runners), // task runners nullptr, // window {}, // snapshot delegate {}, // io manager "main.dart", // advisory uri "main" // advisory entrypoint ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); size_t destruction_callback_count = 0; root_isolate->AddIsolateShutdownCallback([&destruction_callback_count]() { ASSERT_NE(Dart_CurrentIsolate(), nullptr); destruction_callback_count++; }); ASSERT_TRUE(root_isolate->Shutdown()); ASSERT_EQ(destruction_callback_count, 1u); } class AutoIsolateShutdown { public: AutoIsolateShutdown() = default; AutoIsolateShutdown(std::shared_ptr isolate, fml::RefPtr runner) : isolate_(std::move(isolate)), runner_(std::move(runner)) {} ~AutoIsolateShutdown() { if (!IsValid()) { return; } fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask(runner_, [isolate = isolate_, &latch]() { FML_LOG(INFO) << "Shutting down isolate."; if (!isolate->Shutdown()) { FML_LOG(ERROR) << "Could not shutdown isolate."; FML_CHECK(false); } latch.Signal(); }); latch.Wait(); } bool IsValid() const { return isolate_ != nullptr && runner_; } FML_WARN_UNUSED_RESULT bool RunInIsolateScope(std::function closure) { if (!IsValid()) { return false; } bool result = false; fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( runner_, [this, &result, &latch, closure]() { tonic::DartIsolateScope scope(isolate_->isolate()); tonic::DartApiScope api_scope; if (closure) { result = closure(); } latch.Signal(); }); latch.Wait(); return true; } DartIsolate* get() { FML_CHECK(isolate_); return isolate_.get(); } private: std::shared_ptr isolate_; fml::RefPtr runner_; FML_DISALLOW_COPY_AND_ASSIGN(AutoIsolateShutdown); }; static void RunDartCodeInIsolate(DartVMRef& vm_ref, std::unique_ptr& result, const Settings& settings, fml::RefPtr task_runner, std::string entrypoint) { FML_CHECK(task_runner->RunsTasksOnCurrentThread()); if (!vm_ref) { return; } TaskRunners task_runners(::testing::GetCurrentTestName(), // task_runner, // task_runner, // task_runner, // task_runner // ); auto vm_data = vm_ref.GetVMData(); if (!vm_data) { return; } auto weak_isolate = DartIsolate::CreateRootIsolate( vm_data->GetSettings(), // settings vm_data->GetIsolateSnapshot(), // isolate snapshot vm_data->GetSharedSnapshot(), // shared snapshot std::move(task_runners), // task runners nullptr, // window {}, // snapshot delegate {}, // io manager "main.dart", // advisory uri "main" // advisory entrypoint ); auto root_isolate = std::make_unique(weak_isolate.lock(), task_runner); if (!root_isolate->IsValid()) { FML_LOG(ERROR) << "Could not create isolate."; return; } if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) { FML_LOG(ERROR) << "Created isolate is in unexpected phase."; return; } if (!DartVM::IsRunningPrecompiledCode()) { auto kernel_file_path = fml::paths::JoinPaths( {::testing::GetFixturesPath(), "kernel_blob.bin"}); if (!fml::IsFile(kernel_file_path)) { FML_LOG(ERROR) << "Could not locate kernel file."; return; } auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false, fml::FilePermission::kRead); if (!kernel_file.is_valid()) { FML_LOG(ERROR) << "Kernel file descriptor was invalid."; return; } auto kernel_mapping = std::make_unique(kernel_file); if (kernel_mapping->GetMapping() == nullptr) { FML_LOG(ERROR) << "Could not setup kernel mapping."; return; } if (!root_isolate->get()->PrepareForRunningFromKernel( std::move(kernel_mapping))) { FML_LOG(ERROR) << "Could not prepare to run the isolate from the kernel file."; return; } } else { if (!root_isolate->get()->PrepareForRunningFromPrecompiledCode()) { FML_LOG(ERROR) << "Could not prepare to run the isolate from precompiled code."; return; } } if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) { FML_LOG(ERROR) << "Isolate is in unexpected phase."; return; } if (!root_isolate->get()->Run(entrypoint, settings.root_isolate_create_callback)) { FML_LOG(ERROR) << "Could not run the method \"" << entrypoint << "\" in the isolate."; return; } root_isolate->get()->AddIsolateShutdownCallback( settings.root_isolate_shutdown_callback); result = std::move(root_isolate); } static std::unique_ptr RunDartCodeInIsolate( DartVMRef& vm_ref, const Settings& settings, fml::RefPtr task_runner, std::string entrypoint) { std::unique_ptr result; fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( task_runner, fml::MakeCopyable([&]() mutable { RunDartCodeInIsolate(vm_ref, result, settings, task_runner, entrypoint); latch.Signal(); })); latch.Wait(); return result; } TEST_F(DartIsolateTest, IsolateCanLoadAndRunDartCode) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); const auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(), "main"); ASSERT_TRUE(isolate); ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); } TEST_F(DartIsolateTest, IsolateCannotLoadAndRunUnknownDartEntrypoint) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); const auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(), "thisShouldNotExist"); ASSERT_FALSE(isolate); } TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); const auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(), "main"); ASSERT_TRUE(isolate); ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); ASSERT_TRUE(isolate->RunInIsolateScope([]() -> bool { if (tonic::LogIfError(::Dart_Invoke(Dart_RootLibrary(), tonic::ToDart("sayHi"), 0, nullptr))) { return false; } return true; })); } TEST_F(DartIsolateTest, CanRegisterNativeCallback) { ASSERT_FALSE(DartVMRef::IsInstanceRunning()); fml::AutoResetWaitableEvent latch; AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) { FML_LOG(ERROR) << "Hello from Dart!"; latch.Signal(); }))); const auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(), "canRegisterNativeCallback"); ASSERT_TRUE(isolate); ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); latch.Wait(); } TEST_F(DartIsolateTest, CanSaveCompilationTrace) { if (DartVM::IsRunningPrecompiledCode()) { // Can only save compilation traces in JIT modes. GTEST_SKIP(); return; } fml::AutoResetWaitableEvent latch; AddNativeCallback("NotifyNative", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) { ASSERT_TRUE(tonic::DartConverter::FromDart( Dart_GetNativeArgument(args, 0))); latch.Signal(); }))); const auto settings = CreateSettingsForFixture(); auto vm_ref = DartVMRef::Create(settings); auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(), "testCanSaveCompilationTrace"); ASSERT_TRUE(isolate); ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); latch.Wait(); } } // namespace testing } // namespace flutter