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/fml/make_copyable.h"
6 #include "flutter/fml/mapping.h"
7 #include "flutter/fml/paths.h"
8 #include "flutter/fml/synchronization/count_down_latch.h"
9 #include "flutter/fml/synchronization/waitable_event.h"
10 #include "flutter/fml/thread.h"
11 #include "flutter/runtime/dart_isolate.h"
12 #include "flutter/runtime/dart_vm.h"
13 #include "flutter/runtime/dart_vm_lifecycle.h"
14 #include "flutter/runtime/runtime_test.h"
15 #include "flutter/testing/testing.h"
16 namespace flutter {
17 namespace testing {
18
19 using DartIsolateTest = RuntimeTest;
20
TEST_F(DartIsolateTest,RootIsolateCreationAndShutdown)21 TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) {
22 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
23 auto settings = CreateSettingsForFixture();
24 auto vm_ref = DartVMRef::Create(settings);
25 ASSERT_TRUE(vm_ref);
26 auto vm_data = vm_ref.GetVMData();
27 ASSERT_TRUE(vm_data);
28 TaskRunners task_runners(GetCurrentTestName(), //
29 GetCurrentTaskRunner(), //
30 GetCurrentTaskRunner(), //
31 GetCurrentTaskRunner(), //
32 GetCurrentTaskRunner() //
33 );
34 auto weak_isolate = DartIsolate::CreateRootIsolate(
35 vm_data->GetSettings(), // settings
36 vm_data->GetIsolateSnapshot(), // isolate snapshot
37 vm_data->GetSharedSnapshot(), // shared snapshot
38 std::move(task_runners), // task runners
39 nullptr, // window
40 {}, // io manager
41 {}, // image decoder
42 "main.dart", // advisory uri
43 "main", // advisory entrypoint,
44 nullptr, // flags
45 settings.isolate_create_callback, // isolate create callback
46 settings.isolate_shutdown_callback // isolate shutdown callback
47 );
48 auto root_isolate = weak_isolate.lock();
49 ASSERT_TRUE(root_isolate);
50 ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup);
51 ASSERT_TRUE(root_isolate->Shutdown());
52 }
53
TEST_F(DartIsolateTest,IsolateShutdownCallbackIsInIsolateScope)54 TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) {
55 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
56 auto settings = CreateSettingsForFixture();
57 auto vm_ref = DartVMRef::Create(settings);
58 ASSERT_TRUE(vm_ref);
59 auto vm_data = vm_ref.GetVMData();
60 ASSERT_TRUE(vm_data);
61 TaskRunners task_runners(GetCurrentTestName(), //
62 GetCurrentTaskRunner(), //
63 GetCurrentTaskRunner(), //
64 GetCurrentTaskRunner(), //
65 GetCurrentTaskRunner() //
66 );
67 auto weak_isolate = DartIsolate::CreateRootIsolate(
68 vm_data->GetSettings(), // settings
69 vm_data->GetIsolateSnapshot(), // isolate snapshot
70 vm_data->GetSharedSnapshot(), // shared snapshot
71 std::move(task_runners), // task runners
72 nullptr, // window
73 {}, // io manager
74 {}, // image decoder
75 "main.dart", // advisory uri
76 "main", // advisory entrypoint
77 nullptr, // flags
78 settings.isolate_create_callback, // isolate create callback
79 settings.isolate_shutdown_callback // isolate shutdown callback
80 );
81 auto root_isolate = weak_isolate.lock();
82 ASSERT_TRUE(root_isolate);
83 ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup);
84 size_t destruction_callback_count = 0;
85 root_isolate->AddIsolateShutdownCallback([&destruction_callback_count]() {
86 ASSERT_NE(Dart_CurrentIsolate(), nullptr);
87 destruction_callback_count++;
88 });
89 ASSERT_TRUE(root_isolate->Shutdown());
90 ASSERT_EQ(destruction_callback_count, 1u);
91 }
92
93 class AutoIsolateShutdown {
94 public:
95 AutoIsolateShutdown() = default;
96
AutoIsolateShutdown(std::shared_ptr<DartIsolate> isolate,fml::RefPtr<fml::TaskRunner> runner)97 AutoIsolateShutdown(std::shared_ptr<DartIsolate> isolate,
98 fml::RefPtr<fml::TaskRunner> runner)
99 : isolate_(std::move(isolate)), runner_(std::move(runner)) {}
100
~AutoIsolateShutdown()101 ~AutoIsolateShutdown() {
102 if (!IsValid()) {
103 return;
104 }
105 fml::AutoResetWaitableEvent latch;
106 fml::TaskRunner::RunNowOrPostTask(runner_, [isolate = isolate_, &latch]() {
107 FML_LOG(INFO) << "Shutting down isolate.";
108 if (!isolate->Shutdown()) {
109 FML_LOG(ERROR) << "Could not shutdown isolate.";
110 FML_CHECK(false);
111 }
112 latch.Signal();
113 });
114 latch.Wait();
115 }
116
IsValid() const117 bool IsValid() const { return isolate_ != nullptr && runner_; }
118
119 FML_WARN_UNUSED_RESULT
RunInIsolateScope(std::function<bool (void)> closure)120 bool RunInIsolateScope(std::function<bool(void)> closure) {
121 if (!IsValid()) {
122 return false;
123 }
124
125 bool result = false;
126 fml::AutoResetWaitableEvent latch;
127 fml::TaskRunner::RunNowOrPostTask(
128 runner_, [this, &result, &latch, closure]() {
129 tonic::DartIsolateScope scope(isolate_->isolate());
130 tonic::DartApiScope api_scope;
131 if (closure) {
132 result = closure();
133 }
134 latch.Signal();
135 });
136 latch.Wait();
137 return true;
138 }
139
get()140 DartIsolate* get() {
141 FML_CHECK(isolate_);
142 return isolate_.get();
143 }
144
145 private:
146 std::shared_ptr<DartIsolate> isolate_;
147 fml::RefPtr<fml::TaskRunner> runner_;
148
149 FML_DISALLOW_COPY_AND_ASSIGN(AutoIsolateShutdown);
150 };
151
RunDartCodeInIsolate(DartVMRef & vm_ref,std::unique_ptr<AutoIsolateShutdown> & result,const Settings & settings,fml::RefPtr<fml::TaskRunner> task_runner,std::string entrypoint,const std::vector<std::string> & args)152 static void RunDartCodeInIsolate(DartVMRef& vm_ref,
153 std::unique_ptr<AutoIsolateShutdown>& result,
154 const Settings& settings,
155 fml::RefPtr<fml::TaskRunner> task_runner,
156 std::string entrypoint,
157 const std::vector<std::string>& args) {
158 FML_CHECK(task_runner->RunsTasksOnCurrentThread());
159
160 if (!vm_ref) {
161 return;
162 }
163
164 TaskRunners task_runners(GetCurrentTestName(), //
165 task_runner, //
166 task_runner, //
167 task_runner, //
168 task_runner //
169 );
170
171 auto vm_data = vm_ref.GetVMData();
172
173 if (!vm_data) {
174 return;
175 }
176
177 auto weak_isolate = DartIsolate::CreateRootIsolate(
178 vm_data->GetSettings(), // settings
179 vm_data->GetIsolateSnapshot(), // isolate snapshot
180 vm_data->GetSharedSnapshot(), // shared snapshot
181 std::move(task_runners), // task runners
182 nullptr, // window
183 {}, // io manager
184 {}, // image decoder
185 "main.dart", // advisory uri
186 "main", // advisory entrypoint
187 nullptr, // flags
188 settings.isolate_create_callback, // isolate create callback
189 settings.isolate_shutdown_callback // isolate shutdown callback
190 );
191
192 auto root_isolate =
193 std::make_unique<AutoIsolateShutdown>(weak_isolate.lock(), task_runner);
194
195 if (!root_isolate->IsValid()) {
196 FML_LOG(ERROR) << "Could not create isolate.";
197 return;
198 }
199
200 if (root_isolate->get()->GetPhase() != DartIsolate::Phase::LibrariesSetup) {
201 FML_LOG(ERROR) << "Created isolate is in unexpected phase.";
202 return;
203 }
204
205 if (!DartVM::IsRunningPrecompiledCode()) {
206 auto kernel_file_path =
207 fml::paths::JoinPaths({GetFixturesPath(), "kernel_blob.bin"});
208
209 if (!fml::IsFile(kernel_file_path)) {
210 FML_LOG(ERROR) << "Could not locate kernel file.";
211 return;
212 }
213
214 auto kernel_file = fml::OpenFile(kernel_file_path.c_str(), false,
215 fml::FilePermission::kRead);
216
217 if (!kernel_file.is_valid()) {
218 FML_LOG(ERROR) << "Kernel file descriptor was invalid.";
219 return;
220 }
221
222 auto kernel_mapping = std::make_unique<fml::FileMapping>(kernel_file);
223
224 if (kernel_mapping->GetMapping() == nullptr) {
225 FML_LOG(ERROR) << "Could not setup kernel mapping.";
226 return;
227 }
228
229 if (!root_isolate->get()->PrepareForRunningFromKernel(
230 std::move(kernel_mapping))) {
231 FML_LOG(ERROR)
232 << "Could not prepare to run the isolate from the kernel file.";
233 return;
234 }
235 } else {
236 if (!root_isolate->get()->PrepareForRunningFromPrecompiledCode()) {
237 FML_LOG(ERROR)
238 << "Could not prepare to run the isolate from precompiled code.";
239 return;
240 }
241 }
242
243 if (root_isolate->get()->GetPhase() != DartIsolate::Phase::Ready) {
244 FML_LOG(ERROR) << "Isolate is in unexpected phase.";
245 return;
246 }
247
248 if (!root_isolate->get()->Run(entrypoint, args,
249 settings.root_isolate_create_callback)) {
250 FML_LOG(ERROR) << "Could not run the method \"" << entrypoint
251 << "\" in the isolate.";
252 return;
253 }
254
255 root_isolate->get()->AddIsolateShutdownCallback(
256 settings.root_isolate_shutdown_callback);
257
258 result = std::move(root_isolate);
259 }
260
RunDartCodeInIsolate(DartVMRef & vm_ref,const Settings & settings,fml::RefPtr<fml::TaskRunner> task_runner,std::string entrypoint,const std::vector<std::string> & args)261 static std::unique_ptr<AutoIsolateShutdown> RunDartCodeInIsolate(
262 DartVMRef& vm_ref,
263 const Settings& settings,
264 fml::RefPtr<fml::TaskRunner> task_runner,
265 std::string entrypoint,
266 const std::vector<std::string>& args) {
267 std::unique_ptr<AutoIsolateShutdown> result;
268 fml::AutoResetWaitableEvent latch;
269 fml::TaskRunner::RunNowOrPostTask(
270 task_runner, fml::MakeCopyable([&]() mutable {
271 RunDartCodeInIsolate(vm_ref, result, settings, task_runner, entrypoint,
272 args);
273 latch.Signal();
274 }));
275 latch.Wait();
276 return result;
277 }
278
TEST_F(DartIsolateTest,IsolateCanLoadAndRunDartCode)279 TEST_F(DartIsolateTest, IsolateCanLoadAndRunDartCode) {
280 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
281 const auto settings = CreateSettingsForFixture();
282 auto vm_ref = DartVMRef::Create(settings);
283 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(),
284 "main", {});
285 ASSERT_TRUE(isolate);
286 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
287 }
288
TEST_F(DartIsolateTest,IsolateCannotLoadAndRunUnknownDartEntrypoint)289 TEST_F(DartIsolateTest, IsolateCannotLoadAndRunUnknownDartEntrypoint) {
290 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
291 const auto settings = CreateSettingsForFixture();
292 auto vm_ref = DartVMRef::Create(settings);
293 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(),
294 "thisShouldNotExist", {});
295 ASSERT_FALSE(isolate);
296 }
297
TEST_F(DartIsolateTest,CanRunDartCodeCodeSynchronously)298 TEST_F(DartIsolateTest, CanRunDartCodeCodeSynchronously) {
299 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
300 const auto settings = CreateSettingsForFixture();
301 auto vm_ref = DartVMRef::Create(settings);
302 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetCurrentTaskRunner(),
303 "main", {});
304
305 ASSERT_TRUE(isolate);
306 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
307 ASSERT_TRUE(isolate->RunInIsolateScope([]() -> bool {
308 if (tonic::LogIfError(::Dart_Invoke(Dart_RootLibrary(),
309 tonic::ToDart("sayHi"), 0, nullptr))) {
310 return false;
311 }
312 return true;
313 }));
314 }
315
TEST_F(DartIsolateTest,CanRegisterNativeCallback)316 TEST_F(DartIsolateTest, CanRegisterNativeCallback) {
317 ASSERT_FALSE(DartVMRef::IsInstanceRunning());
318 fml::AutoResetWaitableEvent latch;
319 AddNativeCallback("NotifyNative",
320 CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
321 FML_LOG(ERROR) << "Hello from Dart!";
322 latch.Signal();
323 })));
324 const auto settings = CreateSettingsForFixture();
325 auto vm_ref = DartVMRef::Create(settings);
326 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(),
327 "canRegisterNativeCallback", {});
328 ASSERT_TRUE(isolate);
329 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
330 latch.Wait();
331 }
332
TEST_F(DartIsolateTest,CanSaveCompilationTrace)333 TEST_F(DartIsolateTest, CanSaveCompilationTrace) {
334 if (DartVM::IsRunningPrecompiledCode()) {
335 // Can only save compilation traces in JIT modes.
336 GTEST_SKIP();
337 return;
338 }
339 fml::AutoResetWaitableEvent latch;
340 AddNativeCallback("NotifyNative",
341 CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
342 ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(
343 Dart_GetNativeArgument(args, 0)));
344 latch.Signal();
345 })));
346
347 const auto settings = CreateSettingsForFixture();
348 auto vm_ref = DartVMRef::Create(settings);
349 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(),
350 "testCanSaveCompilationTrace", {});
351 ASSERT_TRUE(isolate);
352 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
353
354 latch.Wait();
355 }
356
TEST_F(DartIsolateTest,CanLaunchSecondaryIsolates)357 TEST_F(DartIsolateTest, CanLaunchSecondaryIsolates) {
358 fml::CountDownLatch latch(3);
359 AddNativeCallback("NotifyNative",
360 CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
361 latch.CountDown();
362 })));
363 AddNativeCallback(
364 "PassMessage", CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
365 auto message = tonic::DartConverter<std::string>::FromDart(
366 Dart_GetNativeArgument(args, 0));
367 ASSERT_EQ("Hello from code is secondary isolate.", message);
368 latch.CountDown();
369 })));
370 const auto settings = CreateSettingsForFixture();
371 auto vm_ref = DartVMRef::Create(settings);
372 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(),
373 "testCanLaunchSecondaryIsolate", {});
374 ASSERT_TRUE(isolate);
375 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
376
377 latch.Wait();
378 }
379
TEST_F(DartIsolateTest,CanRecieveArguments)380 TEST_F(DartIsolateTest, CanRecieveArguments) {
381 fml::AutoResetWaitableEvent latch;
382 AddNativeCallback("NotifyNative",
383 CREATE_NATIVE_ENTRY(([&latch](Dart_NativeArguments args) {
384 ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(
385 Dart_GetNativeArgument(args, 0)));
386 latch.Signal();
387 })));
388
389 const auto settings = CreateSettingsForFixture();
390 auto vm_ref = DartVMRef::Create(settings);
391 auto isolate = RunDartCodeInIsolate(vm_ref, settings, GetThreadTaskRunner(),
392 "testCanRecieveArguments", {"arg1"});
393 ASSERT_TRUE(isolate);
394 ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
395
396 latch.Wait();
397 }
398
399 } // namespace testing
400 } // namespace flutter
401