• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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