1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | #include "flutter/testing/dart_isolate_runner.h" |
6 | |
7 | namespace flutter { |
8 | namespace testing { |
9 | AutoIsolateShutdown::AutoIsolateShutdown(std::shared_ptr<DartIsolate> isolate, |
10 | fml::RefPtr<fml::TaskRunner> runner) |
11 | : isolate_(std::move(isolate)), runner_(std::move(runner)) {} |
12 | |
13 | AutoIsolateShutdown::~AutoIsolateShutdown() { |
14 | if (!IsValid()) { |
15 | return; |
16 | } |
17 | fml::AutoResetWaitableEvent latch; |
18 | fml::TaskRunner::RunNowOrPostTask( |
19 | runner_, [isolate = std::move(isolate_), &latch]() { |
20 | if (!isolate->Shutdown()) { |
21 | FML_LOG(ERROR) << "Could not shutdown isolate." ; |
22 | FML_CHECK(false); |
23 | } |
24 | latch.Signal(); |
25 | }); |
26 | latch.Wait(); |
27 | } |
28 | |
29 | [[nodiscard]] bool AutoIsolateShutdown::RunInIsolateScope( |
30 | std::function<bool(void)> closure) { |
31 | if (!IsValid()) { |
32 | return false; |
33 | } |
34 | |
35 | bool result = false; |
36 | fml::AutoResetWaitableEvent latch; |
37 | fml::TaskRunner::RunNowOrPostTask( |
38 | runner_, [this, &result, &latch, closure]() { |
39 | tonic::DartIsolateScope scope(isolate_->isolate()); |
40 | tonic::DartApiScope api_scope; |
41 | if (closure) { |
42 | result = closure(); |
43 | } |
44 | latch.Signal(); |
45 | }); |
46 | latch.Wait(); |
47 | return true; |
48 | } |
49 | |
50 | void RunDartCodeInIsolate(DartVMRef& vm_ref, |
51 | std::unique_ptr<AutoIsolateShutdown>& result, |
52 | const Settings& settings, |
53 | const TaskRunners& task_runners, |
54 | std::string entrypoint, |
55 | const std::vector<std::string>& args, |
56 | const std::string& fixtures_path, |
57 | fml::WeakPtr<IOManager> io_manager) { |
58 | FML_CHECK(task_runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); |
59 | |
60 | if (!vm_ref) { |
61 | return; |
62 | } |
63 | |
64 | auto vm_data = vm_ref.GetVMData(); |
65 | |
66 | if (!vm_data) { |
67 | return; |
68 | } |
69 | |
70 | auto weak_isolate = DartIsolate::CreateRootIsolate( |
71 | vm_data->GetSettings(), // settings |
72 | vm_data->GetIsolateSnapshot(), // isolate snapshot |
73 | std::move(task_runners), // task runners |
74 | nullptr, // window |
75 | {}, // snapshot delegate |
76 | io_manager, // io manager |
77 | {}, // unref queue |
78 | {}, // image decoder |
79 | "main.dart" , // advisory uri |
80 | "main" , // advisory entrypoint |
81 | nullptr, // flags |
82 | settings.isolate_create_callback, // isolate create callback |
83 | settings.isolate_shutdown_callback // isolate shutdown callback |
84 | ); |
85 | |
86 | auto root_isolate = std::make_unique<AutoIsolateShutdown>( |
87 | weak_isolate.lock(), task_runners.GetUITaskRunner()); |
88 | |
89 | if (!root_isolate->IsValid()) { |
90 | FML_LOG(ERROR) << "Could not create isolate." ; |
91 | return; |
92 | } |
93 | |
94 | if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) { |
95 | FML_LOG(ERROR) << "Created isolate is in unexpected phase." ; |
96 | return; |
97 | } |
98 | |
99 | if (!DartVM::IsRunningPrecompiledCode()) { |
100 | auto kernel_file_path = |
101 | fml::paths::JoinPaths({fixtures_path, "kernel_blob.bin" }); |
102 | |
103 | if (!fml::IsFile(kernel_file_path)) { |
104 | FML_LOG(ERROR) << "Could not locate kernel file." ; |
105 | return; |
106 | } |
107 | |
108 | auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false, |
109 | fml::FilePermission::kRead); |
110 | |
111 | if (!kernel_file.is_valid()) { |
112 | FML_LOG(ERROR) << "Kernel file descriptor was invalid." ; |
113 | return; |
114 | } |
115 | |
116 | auto kernel_mapping = std::make_unique<fml::FileMapping>(kernel_file); |
117 | |
118 | if (kernel_mapping->GetMapping() == nullptr) { |
119 | FML_LOG(ERROR) << "Could not setup kernel mapping." ; |
120 | return; |
121 | } |
122 | |
123 | if (!root_isolate->get()->PrepareForRunningFromKernel( |
124 | std::move(kernel_mapping))) { |
125 | FML_LOG(ERROR) |
126 | << "Could not prepare to run the isolate from the kernel file." ; |
127 | return; |
128 | } |
129 | } else { |
130 | if (!root_isolate->get()->PrepareForRunningFromPrecompiledCode()) { |
131 | FML_LOG(ERROR) |
132 | << "Could not prepare to run the isolate from precompiled code." ; |
133 | return; |
134 | } |
135 | } |
136 | |
137 | if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) { |
138 | FML_LOG(ERROR) << "Isolate is in unexpected phase." ; |
139 | return; |
140 | } |
141 | |
142 | if (!root_isolate->get()->Run(entrypoint, args, |
143 | settings.root_isolate_create_callback)) { |
144 | FML_LOG(ERROR) << "Could not run the method \"" << entrypoint |
145 | << "\" in the isolate." ; |
146 | return; |
147 | } |
148 | |
149 | root_isolate->get()->AddIsolateShutdownCallback( |
150 | settings.root_isolate_shutdown_callback); |
151 | |
152 | result = std::move(root_isolate); |
153 | } |
154 | |
155 | std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate( |
156 | DartVMRef& vm_ref, |
157 | const Settings& settings, |
158 | const TaskRunners& task_runners, |
159 | std::string entrypoint, |
160 | const std::vector<std::string>& args, |
161 | const std::string& fixtures_path, |
162 | fml::WeakPtr<IOManager> io_manager) { |
163 | std::unique_ptr<AutoIsolateShutdown> result; |
164 | fml::AutoResetWaitableEvent latch; |
165 | fml::TaskRunner::RunNowOrPostTask( |
166 | task_runners.GetUITaskRunner(), fml::MakeCopyable([&]() mutable { |
167 | RunDartCodeInIsolate(vm_ref, result, settings, task_runners, entrypoint, |
168 | args, fixtures_path, io_manager); |
169 | latch.Signal(); |
170 | })); |
171 | latch.Wait(); |
172 | return result; |
173 | } |
174 | |
175 | } // namespace testing |
176 | } // namespace flutter |
177 | |