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/runtime/dart_isolate.h"
6
7#include <cstdlib>
8#include <tuple>
9#include <utility>
10
11#include "flutter/fml/logging.h"
12#include "flutter/fml/posix_wrappers.h"
13#include "flutter/fml/trace_event.h"
14#include "flutter/lib/io/dart_io.h"
15#include "flutter/lib/ui/dart_runtime_hooks.h"
16#include "flutter/lib/ui/dart_ui.h"
17#include "flutter/runtime/dart_isolate_group_data.h"
18#include "flutter/runtime/dart_plugin_registrant.h"
19#include "flutter/runtime/dart_service_isolate.h"
20#include "flutter/runtime/dart_vm.h"
21#include "flutter/runtime/dart_vm_lifecycle.h"
22#include "flutter/runtime/isolate_configuration.h"
23#include "fml/message_loop_task_queues.h"
24#include "fml/task_source.h"
25#include "fml/time/time_point.h"
26#include "third_party/dart/runtime/include/dart_api.h"
27#include "third_party/dart/runtime/include/dart_tools_api.h"
28#include "third_party/tonic/converter/dart_converter.h"
29#include "third_party/tonic/dart_class_library.h"
30#include "third_party/tonic/dart_class_provider.h"
31#include "third_party/tonic/dart_message_handler.h"
32#include "third_party/tonic/dart_state.h"
33#include "third_party/tonic/file_loader/file_loader.h"
34#include "third_party/tonic/logging/dart_invoke.h"
35#include "third_party/tonic/scopes/dart_api_scope.h"
36#include "third_party/tonic/scopes/dart_isolate_scope.h"
37
38namespace flutter {
39
40namespace {
41
42constexpr std::string_view kFileUriPrefix = "file://";
43
44class DartErrorString {
45 public:
46 DartErrorString() {}
47 ~DartErrorString() {
48 if (str_) {
49 ::free(ptr: str_);
50 }
51 }
52 char** error() { return &str_; }
53 const char* str() const { return str_; }
54 explicit operator bool() const { return str_ != nullptr; }
55
56 private:
57 FML_DISALLOW_COPY_AND_ASSIGN(DartErrorString);
58 char* str_ = nullptr;
59};
60
61} // anonymous namespace
62
63DartIsolate::Flags::Flags() : Flags(nullptr) {}
64
65DartIsolate::Flags::Flags(const Dart_IsolateFlags* flags) {
66 if (flags) {
67 flags_ = *flags;
68 } else {
69 ::Dart_IsolateFlagsInitialize(flags: &flags_);
70 }
71}
72
73DartIsolate::Flags::~Flags() = default;
74
75void DartIsolate::Flags::SetNullSafetyEnabled(bool enabled) {
76 flags_.null_safety = enabled;
77}
78
79void DartIsolate::Flags::SetIsDontNeedSafe(bool value) {
80 flags_.snapshot_is_dontneed_safe = value;
81}
82
83Dart_IsolateFlags DartIsolate::Flags::Get() const {
84 return flags_;
85}
86
87std::weak_ptr<DartIsolate> DartIsolate::CreateRunningRootIsolate(
88 const Settings& settings,
89 const fml::RefPtr<const DartSnapshot>& isolate_snapshot,
90 std::unique_ptr<PlatformConfiguration> platform_configuration,
91 Flags isolate_flags,
92 const fml::closure& root_isolate_create_callback,
93 const fml::closure& isolate_create_callback,
94 const fml::closure& isolate_shutdown_callback,
95 std::optional<std::string> dart_entrypoint,
96 std::optional<std::string> dart_entrypoint_library,
97 const std::vector<std::string>& dart_entrypoint_args,
98 std::unique_ptr<IsolateConfiguration> isolate_configuration,
99 const UIDartState::Context& context,
100 const DartIsolate* spawning_isolate) {
101 if (!isolate_snapshot) {
102 FML_LOG(ERROR) << "Invalid isolate snapshot.";
103 return {};
104 }
105
106 if (!isolate_configuration) {
107 FML_LOG(ERROR) << "Invalid isolate configuration.";
108 return {};
109 }
110
111 isolate_flags.SetNullSafetyEnabled(
112 isolate_configuration->IsNullSafetyEnabled(snapshot: *isolate_snapshot));
113 isolate_flags.SetIsDontNeedSafe(isolate_snapshot->IsDontNeedSafe());
114
115 auto isolate = CreateRootIsolate(settings, //
116 isolate_snapshot, //
117 platform_configuration: std::move(platform_configuration), //
118 flags: isolate_flags, //
119 isolate_create_callback, //
120 isolate_shutdown_callback, //
121 context, //
122 spawning_isolate //
123 )
124 .lock();
125
126 if (!isolate) {
127 FML_LOG(ERROR) << "Could not create root isolate.";
128 return {};
129 }
130
131 fml::ScopedCleanupClosure shutdown_on_error([isolate]() {
132 if (!isolate->Shutdown()) {
133 FML_DLOG(ERROR) << "Could not shutdown transient isolate.";
134 }
135 });
136
137 if (isolate->GetPhase() != DartIsolate::Phase::LibrariesSetup) {
138 FML_LOG(ERROR) << "Root isolate was created in an incorrect phase: "
139 << static_cast<int>(isolate->GetPhase());
140 return {};
141 }
142
143 if (!isolate_configuration->PrepareIsolate(isolate&: *isolate.get())) {
144 FML_LOG(ERROR) << "Could not prepare isolate.";
145 return {};
146 }
147
148 if (isolate->GetPhase() != DartIsolate::Phase::Ready) {
149 FML_LOG(ERROR) << "Root isolate not in the ready phase for Dart entrypoint "
150 "invocation.";
151 return {};
152 }
153
154 if (settings.root_isolate_create_callback) {
155 // Isolate callbacks always occur in isolate scope and before user code has
156 // had a chance to run.
157 tonic::DartState::Scope scope(isolate.get());
158 settings.root_isolate_create_callback(*isolate.get());
159 }
160
161 if (root_isolate_create_callback) {
162 root_isolate_create_callback();
163 }
164
165 if (!isolate->RunFromLibrary(library_name: std::move(dart_entrypoint_library), //
166 entrypoint: std::move(dart_entrypoint), //
167 args: dart_entrypoint_args)) {
168 FML_LOG(ERROR) << "Could not run the run main Dart entrypoint.";
169 return {};
170 }
171
172 if (settings.root_isolate_shutdown_callback) {
173 isolate->AddIsolateShutdownCallback(
174 closure: settings.root_isolate_shutdown_callback);
175 }
176
177 shutdown_on_error.Release();
178
179 return isolate;
180}
181
182void DartIsolate::SpawnIsolateShutdownCallback(
183 std::shared_ptr<DartIsolateGroupData>* isolate_group_data,
184 std::shared_ptr<DartIsolate>* isolate_data) {
185 DartIsolate::DartIsolateShutdownCallback(isolate_group_data, isolate_data);
186}
187
188std::weak_ptr<DartIsolate> DartIsolate::CreateRootIsolate(
189 const Settings& settings,
190 fml::RefPtr<const DartSnapshot> isolate_snapshot,
191 std::unique_ptr<PlatformConfiguration> platform_configuration,
192 const Flags& flags,
193 const fml::closure& isolate_create_callback,
194 const fml::closure& isolate_shutdown_callback,
195 const UIDartState::Context& context,
196 const DartIsolate* spawning_isolate) {
197 TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate");
198
199 // The child isolate preparer is null but will be set when the isolate is
200 // being prepared to run.
201 auto isolate_group_data =
202 std::make_unique<std::shared_ptr<DartIsolateGroupData>>(
203 args: std::shared_ptr<DartIsolateGroupData>(new DartIsolateGroupData(
204 settings, // settings
205 std::move(isolate_snapshot), // isolate snapshot
206 context.advisory_script_uri, // advisory URI
207 context.advisory_script_entrypoint, // advisory entrypoint
208 nullptr, // child isolate preparer
209 isolate_create_callback, // isolate create callback
210 isolate_shutdown_callback // isolate shutdown callback
211 )));
212
213 auto isolate_data = std::make_unique<std::shared_ptr<DartIsolate>>(
214 args: std::shared_ptr<DartIsolate>(new DartIsolate(settings, // settings
215 true, // is_root_isolate
216 context // context
217 )));
218
219 DartErrorString error;
220 Dart_Isolate vm_isolate = nullptr;
221 auto isolate_flags = flags.Get();
222
223 IsolateMaker isolate_maker;
224 if (spawning_isolate) {
225 isolate_maker = [spawning_isolate](
226 std::shared_ptr<DartIsolateGroupData>*
227 isolate_group_data,
228 std::shared_ptr<DartIsolate>* isolate_data,
229 Dart_IsolateFlags* flags, char** error) {
230 return Dart_CreateIsolateInGroup(
231 /*group_member=*/spawning_isolate->isolate(),
232 /*name=*/(*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(),
233 /*shutdown_callback=*/
234 reinterpret_cast<Dart_IsolateShutdownCallback>(
235 DartIsolate::SpawnIsolateShutdownCallback),
236 /*cleanup_callback=*/
237 reinterpret_cast<Dart_IsolateCleanupCallback>(
238 DartIsolateCleanupCallback),
239 /*child_isolate_data=*/isolate_data,
240 /*error=*/error);
241 };
242 } else {
243 isolate_maker = [](std::shared_ptr<DartIsolateGroupData>*
244 isolate_group_data,
245 std::shared_ptr<DartIsolate>* isolate_data,
246 Dart_IsolateFlags* flags, char** error) {
247 return Dart_CreateIsolateGroup(
248 script_uri: (*isolate_group_data)->GetAdvisoryScriptURI().c_str(),
249 name: (*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(),
250 isolate_snapshot_data: (*isolate_group_data)->GetIsolateSnapshot()->GetDataMapping(),
251 isolate_snapshot_instructions: (*isolate_group_data)->GetIsolateSnapshot()->GetInstructionsMapping(),
252 flags, isolate_group_data, isolate_data, error);
253 };
254 }
255
256 vm_isolate = CreateDartIsolateGroup(isolate_group_data: std::move(isolate_group_data),
257 isolate_data: std::move(isolate_data), flags: &isolate_flags,
258 error: error.error(), make_isolate: isolate_maker);
259
260 if (error) {
261 FML_LOG(ERROR) << "CreateRootIsolate failed: " << error.str();
262 }
263
264 if (vm_isolate == nullptr) {
265 return {};
266 }
267
268 std::shared_ptr<DartIsolate>* root_isolate_data =
269 static_cast<std::shared_ptr<DartIsolate>*>(Dart_IsolateData(isolate: vm_isolate));
270
271 (*root_isolate_data)
272 ->SetPlatformConfiguration(std::move(platform_configuration));
273
274 return (*root_isolate_data)->GetWeakIsolatePtr();
275}
276
277DartIsolate::DartIsolate(const Settings& settings,
278 bool is_root_isolate,
279 const UIDartState::Context& context)
280 : UIDartState(settings.task_observer_add,
281 settings.task_observer_remove,
282 settings.log_tag,
283 settings.unhandled_exception_callback,
284 settings.log_message_callback,
285 DartVMRef::GetIsolateNameServer(),
286 is_root_isolate,
287 context),
288 may_insecurely_connect_to_all_domains_(
289 settings.may_insecurely_connect_to_all_domains),
290 domain_network_policy_(settings.domain_network_policy) {
291 phase_ = Phase::Uninitialized;
292}
293
294DartIsolate::~DartIsolate() {
295 if (IsRootIsolate() && GetMessageHandlingTaskRunner()) {
296 FML_DCHECK(GetMessageHandlingTaskRunner()->RunsTasksOnCurrentThread());
297 }
298}
299
300DartIsolate::Phase DartIsolate::GetPhase() const {
301 return phase_;
302}
303
304std::string DartIsolate::GetServiceId() {
305 const char* service_id_buf = Dart_IsolateServiceId(isolate: isolate());
306 std::string service_id(service_id_buf);
307 free(ptr: const_cast<char*>(service_id_buf));
308 return service_id;
309}
310
311bool DartIsolate::Initialize(Dart_Isolate dart_isolate) {
312 TRACE_EVENT0("flutter", "DartIsolate::Initialize");
313 if (phase_ != Phase::Uninitialized) {
314 return false;
315 }
316
317 FML_DCHECK(dart_isolate != nullptr);
318 FML_DCHECK(dart_isolate == Dart_CurrentIsolate());
319
320 // After this point, isolate scopes can be safely used.
321 SetIsolate(dart_isolate);
322
323 // For the root isolate set the "AppStartUp" as soon as the root isolate
324 // has been initialized. This is to ensure that all the timeline events
325 // that have the set user-tag will be listed user AppStartUp.
326 if (IsRootIsolate()) {
327 tonic::DartApiScope api_scope;
328 Dart_SetCurrentUserTag(user_tag: Dart_NewUserTag(label: "AppStartUp"));
329 }
330
331 SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner());
332
333 if (tonic::CheckAndHandleError(
334 handle: Dart_SetLibraryTagHandler(handler: tonic::DartState::HandleLibraryTag))) {
335 return false;
336 }
337
338 if (tonic::CheckAndHandleError(
339 handle: Dart_SetDeferredLoadHandler(handler: OnDartLoadLibrary))) {
340 return false;
341 }
342
343 if (!UpdateThreadPoolNames()) {
344 return false;
345 }
346
347 phase_ = Phase::Initialized;
348 return true;
349}
350
351fml::RefPtr<fml::TaskRunner> DartIsolate::GetMessageHandlingTaskRunner() const {
352 return message_handling_task_runner_;
353}
354
355bool DartIsolate::LoadLoadingUnit(
356 intptr_t loading_unit_id,
357 std::unique_ptr<const fml::Mapping> snapshot_data,
358 std::unique_ptr<const fml::Mapping> snapshot_instructions) {
359 tonic::DartState::Scope scope(this);
360
361 fml::RefPtr<DartSnapshot> dart_snapshot =
362 DartSnapshot::IsolateSnapshotFromMappings(
363 snapshot_data: std::move(snapshot_data), snapshot_instructions: std::move(snapshot_instructions));
364
365 Dart_Handle result = Dart_DeferredLoadComplete(
366 loading_unit_id, snapshot_data: dart_snapshot->GetDataMapping(),
367 snapshot_instructions: dart_snapshot->GetInstructionsMapping());
368 if (tonic::CheckAndHandleError(handle: result)) {
369 LoadLoadingUnitError(loading_unit_id, error_message: Dart_GetError(handle: result),
370 /*transient*/ true);
371 return false;
372 }
373 loading_unit_snapshots_.insert(x: dart_snapshot);
374 return true;
375}
376
377void DartIsolate::LoadLoadingUnitError(intptr_t loading_unit_id,
378 const std::string& error_message,
379 bool transient) {
380 tonic::DartState::Scope scope(this);
381 Dart_Handle result = Dart_DeferredLoadCompleteError(
382 loading_unit_id, error_message: error_message.c_str(), transient);
383 tonic::CheckAndHandleError(handle: result);
384}
385
386void DartIsolate::SetMessageHandlingTaskRunner(
387 const fml::RefPtr<fml::TaskRunner>& runner) {
388 if (!IsRootIsolate() || !runner) {
389 return;
390 }
391
392 message_handling_task_runner_ = runner;
393
394 message_handler().Initialize(dispatcher: [runner](std::function<void()> task) {
395#ifdef OS_FUCHSIA
396 runner->PostTask([task = std::move(task)]() {
397 TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
398 task();
399 });
400#else
401 auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
402 task_queues->RegisterTask(
403 queue_id: runner->GetTaskQueueId(),
404 task: [task = std::move(task)]() {
405 TRACE_EVENT0("flutter", "DartIsolate::HandleMessage");
406 task();
407 },
408 target_time: fml::TimePoint::Now(), task_source_grade: fml::TaskSourceGrade::kDartMicroTasks);
409#endif
410 });
411}
412
413// Updating thread names here does not change the underlying OS thread names.
414// Instead, this is just additional metadata for the Dart VM Service to show the
415// thread name of the isolate.
416bool DartIsolate::UpdateThreadPoolNames() const {
417 // TODO(chinmaygarde): This implementation does not account for multiple
418 // shells sharing the same (or subset of) threads.
419 const auto& task_runners = GetTaskRunners();
420
421 if (auto task_runner = task_runners.GetRasterTaskRunner()) {
422 task_runner->PostTask(
423 task: [label = task_runners.GetLabel() + std::string{".raster"}]() {
424 Dart_SetThreadName(name: label.c_str());
425 });
426 }
427
428 if (auto task_runner = task_runners.GetUITaskRunner()) {
429 task_runner->PostTask(
430 task: [label = task_runners.GetLabel() + std::string{".ui"}]() {
431 Dart_SetThreadName(name: label.c_str());
432 });
433 }
434
435 if (auto task_runner = task_runners.GetIOTaskRunner()) {
436 task_runner->PostTask(
437 task: [label = task_runners.GetLabel() + std::string{".io"}]() {
438 Dart_SetThreadName(name: label.c_str());
439 });
440 }
441
442 if (auto task_runner = task_runners.GetPlatformTaskRunner()) {
443 task_runner->PostTask(
444 task: [label = task_runners.GetLabel() + std::string{".platform"}]() {
445 Dart_SetThreadName(name: label.c_str());
446 });
447 }
448
449 return true;
450}
451
452bool DartIsolate::LoadLibraries() {
453 TRACE_EVENT0("flutter", "DartIsolate::LoadLibraries");
454 if (phase_ != Phase::Initialized) {
455 return false;
456 }
457
458 tonic::DartState::Scope scope(this);
459
460 DartIO::InitForIsolate(may_insecurely_connect_to_all_domains: may_insecurely_connect_to_all_domains_,
461 domain_network_policy: domain_network_policy_);
462
463 DartUI::InitForIsolate(settings: GetIsolateGroupData().GetSettings());
464
465 const bool is_service_isolate = Dart_IsServiceIsolate(isolate: isolate());
466
467 DartRuntimeHooks::Install(is_ui_isolate: IsRootIsolate() && !is_service_isolate,
468 script_uri: GetAdvisoryScriptURI());
469
470 if (!is_service_isolate) {
471 class_library().add_provider(
472 library_name: "ui", provider: std::make_unique<tonic::DartClassProvider>(args: this, args: "dart:ui"));
473 }
474
475 phase_ = Phase::LibrariesSetup;
476 return true;
477}
478
479bool DartIsolate::PrepareForRunningFromPrecompiledCode() {
480 TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromPrecompiledCode");
481 if (phase_ != Phase::LibrariesSetup) {
482 return false;
483 }
484
485 tonic::DartState::Scope scope(this);
486
487 if (Dart_IsNull(object: Dart_RootLibrary())) {
488 return false;
489 }
490
491 if (!MarkIsolateRunnable()) {
492 return false;
493 }
494
495 if (GetIsolateGroupData().GetChildIsolatePreparer() == nullptr) {
496 GetIsolateGroupData().SetChildIsolatePreparer([](DartIsolate* isolate) {
497 return isolate->PrepareForRunningFromPrecompiledCode();
498 });
499 }
500
501 const fml::closure& isolate_create_callback =
502 GetIsolateGroupData().GetIsolateCreateCallback();
503 if (isolate_create_callback) {
504 isolate_create_callback();
505 }
506
507 phase_ = Phase::Ready;
508 return true;
509}
510
511bool DartIsolate::LoadKernel(const std::shared_ptr<const fml::Mapping>& mapping,
512 bool last_piece) {
513 if (!Dart_IsKernel(buffer: mapping->GetMapping(), buffer_size: mapping->GetSize())) {
514 return false;
515 }
516
517 // Mapping must be retained until isolate shutdown.
518 kernel_buffers_.push_back(x: mapping);
519
520 Dart_Handle library =
521 Dart_LoadLibraryFromKernel(kernel_buffer: mapping->GetMapping(), kernel_buffer_size: mapping->GetSize());
522 if (tonic::CheckAndHandleError(handle: library)) {
523 return false;
524 }
525
526 if (!last_piece) {
527 // More to come.
528 return true;
529 }
530
531 Dart_SetRootLibrary(library);
532 if (tonic::CheckAndHandleError(handle: Dart_FinalizeLoading(complete_futures: false))) {
533 return false;
534 }
535 return true;
536}
537
538[[nodiscard]] bool DartIsolate::PrepareForRunningFromKernel(
539 const std::shared_ptr<const fml::Mapping>& mapping,
540 bool child_isolate,
541 bool last_piece) {
542 TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromKernel");
543 if (phase_ != Phase::LibrariesSetup) {
544 return false;
545 }
546
547 if (DartVM::IsRunningPrecompiledCode()) {
548 return false;
549 }
550
551 if (!mapping || mapping->GetSize() == 0) {
552 return false;
553 }
554
555 tonic::DartState::Scope scope(this);
556
557 if (!child_isolate) {
558 // Use root library provided by kernel in favor of one provided by snapshot.
559 Dart_SetRootLibrary(library: Dart_Null());
560
561 if (!LoadKernel(mapping, last_piece)) {
562 return false;
563 }
564 }
565
566 if (!last_piece) {
567 // More to come.
568 return true;
569 }
570
571 if (Dart_IsNull(object: Dart_RootLibrary())) {
572 return false;
573 }
574
575 if (!MarkIsolateRunnable()) {
576 return false;
577 }
578
579 // Child isolate shares root isolate embedder_isolate (lines 691 and 693
580 // below). Re-initializing child_isolate_preparer_ lambda while it is being
581 // executed leads to crashes.
582 if (GetIsolateGroupData().GetChildIsolatePreparer() == nullptr) {
583 GetIsolateGroupData().SetChildIsolatePreparer(
584 [buffers = kernel_buffers_](DartIsolate* isolate) {
585 for (uint64_t i = 0; i < buffers.size(); i++) {
586 bool last_piece = i + 1 == buffers.size();
587 const std::shared_ptr<const fml::Mapping>& buffer = buffers.at(n: i);
588 if (!isolate->PrepareForRunningFromKernel(mapping: buffer,
589 /*child_isolate=*/true,
590 last_piece)) {
591 return false;
592 }
593 }
594 return true;
595 });
596 }
597
598 const fml::closure& isolate_create_callback =
599 GetIsolateGroupData().GetIsolateCreateCallback();
600 if (isolate_create_callback) {
601 isolate_create_callback();
602 }
603
604 phase_ = Phase::Ready;
605
606 return true;
607}
608
609[[nodiscard]] bool DartIsolate::PrepareForRunningFromKernels(
610 std::vector<std::shared_ptr<const fml::Mapping>> kernels) {
611 const auto count = kernels.size();
612 if (count == 0) {
613 return false;
614 }
615
616 for (size_t i = 0; i < count; ++i) {
617 bool last = (i == (count - 1));
618 if (!PrepareForRunningFromKernel(mapping: kernels[i], /*child_isolate=*/false,
619 last_piece: last)) {
620 return false;
621 }
622 }
623
624 return true;
625}
626
627[[nodiscard]] bool DartIsolate::PrepareForRunningFromKernels(
628 std::vector<std::unique_ptr<const fml::Mapping>> kernels) {
629 std::vector<std::shared_ptr<const fml::Mapping>> shared_kernels;
630 for (auto& kernel : kernels) {
631 shared_kernels.emplace_back(args: std::move(kernel));
632 }
633 return PrepareForRunningFromKernels(kernels: shared_kernels);
634}
635
636bool DartIsolate::MarkIsolateRunnable() {
637 TRACE_EVENT0("flutter", "DartIsolate::MarkIsolateRunnable");
638 if (phase_ != Phase::LibrariesSetup) {
639 return false;
640 }
641
642 // This function may only be called from an active isolate scope.
643 if (Dart_CurrentIsolate() != isolate()) {
644 return false;
645 }
646
647 // There must be no current isolate to mark an isolate as being runnable.
648 Dart_ExitIsolate();
649
650 char* error = Dart_IsolateMakeRunnable(isolate: isolate());
651 if (error) {
652 FML_DLOG(ERROR) << error;
653 ::free(ptr: error);
654 // Failed. Restore the isolate.
655 Dart_EnterIsolate(isolate: isolate());
656 return false;
657 }
658 // Success. Restore the isolate.
659 Dart_EnterIsolate(isolate: isolate());
660 return true;
661}
662
663[[nodiscard]] static bool InvokeMainEntrypoint(
664 Dart_Handle user_entrypoint_function,
665 Dart_Handle args) {
666 if (tonic::CheckAndHandleError(handle: user_entrypoint_function)) {
667 FML_LOG(ERROR) << "Could not resolve main entrypoint function.";
668 return false;
669 }
670
671 Dart_Handle start_main_isolate_function =
672 tonic::DartInvokeField(target: Dart_LookupLibrary(url: tonic::ToDart(val: "dart:isolate")),
673 name: "_getStartMainIsolateFunction", args: {});
674
675 if (tonic::CheckAndHandleError(handle: start_main_isolate_function)) {
676 FML_LOG(ERROR) << "Could not resolve main entrypoint trampoline.";
677 return false;
678 }
679
680 if (tonic::CheckAndHandleError(handle: tonic::DartInvokeField(
681 target: Dart_LookupLibrary(url: tonic::ToDart(val: "dart:ui")), name: "_runMain",
682 args: {start_main_isolate_function, user_entrypoint_function, args}))) {
683 FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
684 return false;
685 }
686
687 return true;
688}
689
690bool DartIsolate::RunFromLibrary(std::optional<std::string> library_name,
691 std::optional<std::string> entrypoint,
692 const std::vector<std::string>& args) {
693 TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary");
694 if (phase_ != Phase::Ready) {
695 return false;
696 }
697
698 tonic::DartState::Scope scope(this);
699
700 auto library_handle =
701 library_name.has_value() && !library_name.value().empty()
702 ? ::Dart_LookupLibrary(url: tonic::ToDart(val: library_name.value().c_str()))
703 : ::Dart_RootLibrary();
704 auto entrypoint_handle = entrypoint.has_value() && !entrypoint.value().empty()
705 ? tonic::ToDart(val: entrypoint.value().c_str())
706 : tonic::ToDart(val: "main");
707
708 if (!FindAndInvokeDartPluginRegistrant()) {
709 // TODO(gaaclarke): Remove once the framework PR lands that uses `--source`
710 // to compile the Dart Plugin Registrant
711 // (https://github.com/flutter/flutter/pull/100572).
712 InvokeDartPluginRegistrantIfAvailable(library_handle);
713 }
714
715 auto user_entrypoint_function =
716 ::Dart_GetField(container: library_handle, name: entrypoint_handle);
717
718 auto entrypoint_args = tonic::ToDart(object: args);
719
720 if (!InvokeMainEntrypoint(user_entrypoint_function, args: entrypoint_args)) {
721 return false;
722 }
723
724 phase_ = Phase::Running;
725
726 return true;
727}
728
729bool DartIsolate::Shutdown() {
730 TRACE_EVENT0("flutter", "DartIsolate::Shutdown");
731 // This call may be re-entrant since Dart_ShutdownIsolate can invoke the
732 // cleanup callback which deletes the embedder side object of the dart isolate
733 // (a.k.a. this).
734 if (phase_ == Phase::Shutdown) {
735 return false;
736 }
737 phase_ = Phase::Shutdown;
738 Dart_Isolate vm_isolate = isolate();
739 // The isolate can be nullptr if this instance is the stub isolate data used
740 // during root isolate creation.
741 if (vm_isolate != nullptr) {
742 // We need to enter the isolate because Dart_ShutdownIsolate does not take
743 // the isolate to shutdown as a parameter.
744 FML_DCHECK(Dart_CurrentIsolate() == nullptr);
745 Dart_EnterIsolate(isolate: vm_isolate);
746 Dart_ShutdownIsolate();
747 FML_DCHECK(Dart_CurrentIsolate() == nullptr);
748 }
749 return true;
750}
751
752Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate(
753 const char* package_root,
754 const char* package_config,
755 Dart_IsolateFlags* flags,
756 char** error) {
757 auto vm_data = DartVMRef::GetVMData();
758
759 if (!vm_data) {
760 *error = fml::strdup(
761 str1: "Could not access VM data to initialize isolates. This may be because "
762 "the VM has initialized shutdown on another thread already.");
763 return nullptr;
764 }
765
766 const auto& settings = vm_data->GetSettings();
767
768 if (!settings.enable_vm_service) {
769 return nullptr;
770 }
771
772 flags->load_vmservice_library = true;
773
774#if (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_DEBUG)
775 // TODO(68663): The service isolate in debug mode is always launched without
776 // sound null safety. Fix after the isolate snapshot data is created with the
777 // right flags.
778 flags->null_safety = vm_data->GetServiceIsolateSnapshotNullSafety();
779#endif
780
781 UIDartState::Context context(
782 TaskRunners("io.flutter." DART_VM_SERVICE_ISOLATE_NAME, nullptr, nullptr,
783 nullptr, nullptr));
784 context.advisory_script_uri = DART_VM_SERVICE_ISOLATE_NAME;
785 context.advisory_script_entrypoint = DART_VM_SERVICE_ISOLATE_NAME;
786 std::weak_ptr<DartIsolate> weak_service_isolate =
787 DartIsolate::CreateRootIsolate(settings: vm_data->GetSettings(), //
788 isolate_snapshot: vm_data->GetServiceIsolateSnapshot(), //
789 platform_configuration: nullptr, //
790 flags: DartIsolate::Flags{flags}, //
791 isolate_create_callback: nullptr, //
792 isolate_shutdown_callback: nullptr, //
793 context); //
794
795 std::shared_ptr<DartIsolate> service_isolate = weak_service_isolate.lock();
796 if (!service_isolate) {
797 *error = fml::strdup(str1: "Could not create the service isolate.");
798 FML_DLOG(ERROR) << *error;
799 return nullptr;
800 }
801
802 tonic::DartState::Scope scope(service_isolate);
803 if (!DartServiceIsolate::Startup(
804 server_ip: settings.vm_service_host, // server IP address
805 server_port: settings.vm_service_port, // server VM service port
806 embedder_tag_handler: tonic::DartState::HandleLibraryTag, // embedder library tag handler
807 disable_origin_check: false, // disable websocket origin check
808 disable_service_auth_codes: settings.disable_service_auth_codes, // disable VM service auth codes
809 enable_service_port_fallback: settings.enable_service_port_fallback, // enable fallback to port 0
810 // when bind fails.
811 error // error (out)
812 )) {
813 // Error is populated by call to startup.
814 FML_DLOG(ERROR) << *error;
815 return nullptr;
816 }
817
818 if (auto callback = vm_data->GetSettings().service_isolate_create_callback) {
819 callback();
820 }
821
822 if (auto service_protocol = DartVMRef::GetServiceProtocol()) {
823 service_protocol->ToggleHooks(set: true);
824 } else {
825 FML_DLOG(ERROR)
826 << "Could not acquire the service protocol handlers. This might be "
827 "because the VM has already begun teardown on another thread.";
828 }
829
830 return service_isolate->isolate();
831}
832
833DartIsolateGroupData& DartIsolate::GetIsolateGroupData() {
834 std::shared_ptr<DartIsolateGroupData>* isolate_group_data =
835 static_cast<std::shared_ptr<DartIsolateGroupData>*>(
836 Dart_IsolateGroupData(isolate: isolate()));
837 return **isolate_group_data;
838}
839
840const DartIsolateGroupData& DartIsolate::GetIsolateGroupData() const {
841 DartIsolate* non_const_this = const_cast<DartIsolate*>(this);
842 return non_const_this->GetIsolateGroupData();
843}
844
845// |Dart_IsolateGroupCreateCallback|
846Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback(
847 const char* advisory_script_uri,
848 const char* advisory_script_entrypoint,
849 const char* package_root,
850 const char* package_config,
851 Dart_IsolateFlags* flags,
852 std::shared_ptr<DartIsolate>* parent_isolate_data,
853 char** error) {
854 TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCreateCallback");
855 if (parent_isolate_data == nullptr &&
856 strcmp(s1: advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) {
857 // The VM attempts to start the VM service for us on |Dart_Initialize|. In
858 // such a case, the callback data will be null and the script URI will be
859 // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service
860 // isolate like normal but dont hold a reference to it at all. We also start
861 // this isolate since we will never again reference it from the engine.
862 return DartCreateAndStartServiceIsolate(package_root, //
863 package_config, //
864 flags, //
865 error //
866 );
867 }
868
869 if (!parent_isolate_data) {
870 return nullptr;
871 }
872
873 DartIsolateGroupData& parent_group_data =
874 (*parent_isolate_data)->GetIsolateGroupData();
875
876 if (strncmp(s1: advisory_script_uri, s2: kFileUriPrefix.data(),
877 n: kFileUriPrefix.size())) {
878 std::string error_msg =
879 std::string("Unsupported isolate URI: ") + advisory_script_uri;
880 *error = fml::strdup(str1: error_msg.c_str());
881 return nullptr;
882 }
883
884 auto isolate_group_data =
885 std::make_unique<std::shared_ptr<DartIsolateGroupData>>(
886 args: std::shared_ptr<DartIsolateGroupData>(new DartIsolateGroupData(
887 parent_group_data.GetSettings(),
888 parent_group_data.GetIsolateSnapshot(), advisory_script_uri,
889 advisory_script_entrypoint,
890 parent_group_data.GetChildIsolatePreparer(),
891 parent_group_data.GetIsolateCreateCallback(),
892 parent_group_data.GetIsolateShutdownCallback())));
893
894 TaskRunners null_task_runners(advisory_script_uri,
895 /* platform= */ nullptr,
896 /* raster= */ nullptr,
897 /* ui= */ nullptr,
898 /* io= */ nullptr);
899
900 UIDartState::Context context(null_task_runners);
901 context.advisory_script_uri = advisory_script_uri;
902 context.advisory_script_entrypoint = advisory_script_entrypoint;
903 auto isolate_data = std::make_unique<std::shared_ptr<DartIsolate>>(
904 args: std::shared_ptr<DartIsolate>(
905 new DartIsolate((*isolate_group_data)->GetSettings(), // settings
906 false, // is_root_isolate
907 context))); // context
908
909 Dart_Isolate vm_isolate = CreateDartIsolateGroup(
910 isolate_group_data: std::move(isolate_group_data), isolate_data: std::move(isolate_data), flags, error,
911 make_isolate: [](std::shared_ptr<DartIsolateGroupData>* isolate_group_data,
912 std::shared_ptr<DartIsolate>* isolate_data, Dart_IsolateFlags* flags,
913 char** error) {
914 return Dart_CreateIsolateGroup(
915 script_uri: (*isolate_group_data)->GetAdvisoryScriptURI().c_str(),
916 name: (*isolate_group_data)->GetAdvisoryScriptEntrypoint().c_str(),
917 isolate_snapshot_data: (*isolate_group_data)->GetIsolateSnapshot()->GetDataMapping(),
918 isolate_snapshot_instructions: (*isolate_group_data)
919 ->GetIsolateSnapshot()
920 ->GetInstructionsMapping(),
921 flags, isolate_group_data, isolate_data, error);
922 });
923
924 if (*error) {
925 FML_LOG(ERROR) << "CreateDartIsolateGroup failed: " << error;
926 }
927
928 return vm_isolate;
929}
930
931// |Dart_IsolateInitializeCallback|
932bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data,
933 char** error) {
934 TRACE_EVENT0("flutter", "DartIsolate::DartIsolateInitializeCallback");
935 Dart_Isolate isolate = Dart_CurrentIsolate();
936 if (isolate == nullptr) {
937 *error = fml::strdup(str1: "Isolate should be available in initialize callback.");
938 FML_DLOG(ERROR) << *error;
939 return false;
940 }
941
942 auto* isolate_group_data =
943 static_cast<std::shared_ptr<DartIsolateGroupData>*>(
944 Dart_CurrentIsolateGroupData());
945
946 TaskRunners null_task_runners((*isolate_group_data)->GetAdvisoryScriptURI(),
947 /* platform= */ nullptr,
948 /* raster= */ nullptr,
949 /* ui= */ nullptr,
950 /* io= */ nullptr);
951
952 UIDartState::Context context(null_task_runners);
953 context.advisory_script_uri = (*isolate_group_data)->GetAdvisoryScriptURI();
954 context.advisory_script_entrypoint =
955 (*isolate_group_data)->GetAdvisoryScriptEntrypoint();
956 auto embedder_isolate = std::make_unique<std::shared_ptr<DartIsolate>>(
957 args: std::shared_ptr<DartIsolate>(
958 new DartIsolate((*isolate_group_data)->GetSettings(), // settings
959 false, // is_root_isolate
960 context))); // context
961
962 // root isolate should have been created via CreateRootIsolate
963 if (!InitializeIsolate(embedder_isolate: *embedder_isolate, isolate, error)) {
964 return false;
965 }
966
967 // The ownership of the embedder object is controlled by the Dart VM. So the
968 // only reference returned to the caller is weak.
969 *child_callback_data = embedder_isolate.release();
970
971 return true;
972}
973
974Dart_Isolate DartIsolate::CreateDartIsolateGroup(
975 std::unique_ptr<std::shared_ptr<DartIsolateGroupData>> isolate_group_data,
976 std::unique_ptr<std::shared_ptr<DartIsolate>> isolate_data,
977 Dart_IsolateFlags* flags,
978 char** error,
979 const DartIsolate::IsolateMaker& make_isolate) {
980 TRACE_EVENT0("flutter", "DartIsolate::CreateDartIsolateGroup");
981
982 // Create the Dart VM isolate and give it the embedder object as the baton.
983 Dart_Isolate isolate =
984 make_isolate(isolate_group_data.get(), isolate_data.get(), flags, error);
985
986 if (isolate == nullptr) {
987 return nullptr;
988 }
989
990 bool success = false;
991 {
992 // Ownership of the isolate data objects has been transferred to the Dart
993 // VM.
994 // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks)
995 std::shared_ptr<DartIsolate> embedder_isolate(*isolate_data);
996 isolate_group_data.release();
997 isolate_data.release();
998 // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
999
1000 success = InitializeIsolate(embedder_isolate, isolate, error);
1001 }
1002 if (!success) {
1003 Dart_ShutdownIsolate();
1004 return nullptr;
1005 }
1006
1007 // Balances the implicit [Dart_EnterIsolate] by [make_isolate] above.
1008 Dart_ExitIsolate();
1009 return isolate;
1010}
1011
1012bool DartIsolate::InitializeIsolate(
1013 const std::shared_ptr<DartIsolate>& embedder_isolate,
1014 Dart_Isolate isolate,
1015 char** error) {
1016 TRACE_EVENT0("flutter", "DartIsolate::InitializeIsolate");
1017 if (!embedder_isolate->Initialize(dart_isolate: isolate)) {
1018 *error = fml::strdup(str1: "Embedder could not initialize the Dart isolate.");
1019 FML_DLOG(ERROR) << *error;
1020 return false;
1021 }
1022
1023 if (!embedder_isolate->LoadLibraries()) {
1024 *error = fml::strdup(
1025 str1: "Embedder could not load libraries in the new Dart isolate.");
1026 FML_DLOG(ERROR) << *error;
1027 return false;
1028 }
1029
1030 // Root isolates will be set up by the engine and the service isolate
1031 // (which is also a root isolate) by the utility routines in the VM.
1032 // However, secondary isolates will be run by the VM if they are
1033 // marked as runnable.
1034 if (!embedder_isolate->IsRootIsolate()) {
1035 auto child_isolate_preparer =
1036 embedder_isolate->GetIsolateGroupData().GetChildIsolatePreparer();
1037 FML_DCHECK(child_isolate_preparer);
1038 if (!child_isolate_preparer(embedder_isolate.get())) {
1039 *error = fml::strdup(str1: "Could not prepare the child isolate to run.");
1040 FML_DLOG(ERROR) << *error;
1041 return false;
1042 }
1043 }
1044
1045 return true;
1046}
1047
1048// |Dart_IsolateShutdownCallback|
1049void DartIsolate::DartIsolateShutdownCallback(
1050 std::shared_ptr<DartIsolateGroupData>* isolate_group_data,
1051 std::shared_ptr<DartIsolate>* isolate_data) {
1052 TRACE_EVENT0("flutter", "DartIsolate::DartIsolateShutdownCallback");
1053
1054 // If the isolate initialization failed there will be nothing to do.
1055 // This can happen e.g. during a [DartIsolateInitializeCallback] invocation
1056 // that fails to initialize the VM-created isolate.
1057 if (isolate_data == nullptr) {
1058 return;
1059 }
1060
1061 isolate_data->get()->OnShutdownCallback();
1062}
1063
1064// |Dart_IsolateGroupCleanupCallback|
1065void DartIsolate::DartIsolateGroupCleanupCallback(
1066 std::shared_ptr<DartIsolateGroupData>* isolate_data) {
1067 TRACE_EVENT0("flutter", "DartIsolate::DartIsolateGroupCleanupCallback");
1068 delete isolate_data;
1069}
1070
1071// |Dart_IsolateCleanupCallback|
1072void DartIsolate::DartIsolateCleanupCallback(
1073 std::shared_ptr<DartIsolateGroupData>* isolate_group_data,
1074 std::shared_ptr<DartIsolate>* isolate_data) {
1075 TRACE_EVENT0("flutter", "DartIsolate::DartIsolateCleanupCallback");
1076 delete isolate_data;
1077}
1078
1079std::weak_ptr<DartIsolate> DartIsolate::GetWeakIsolatePtr() {
1080 return std::static_pointer_cast<DartIsolate>(r: shared_from_this());
1081}
1082
1083void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) {
1084 shutdown_callbacks_.emplace_back(args: std::make_unique<AutoFireClosure>(args: closure));
1085}
1086
1087void DartIsolate::OnShutdownCallback() {
1088 tonic::DartState* state = tonic::DartState::Current();
1089 if (state != nullptr) {
1090 state->SetIsShuttingDown();
1091 }
1092
1093 {
1094 tonic::DartApiScope api_scope;
1095 Dart_Handle sticky_error = Dart_GetStickyError();
1096 if (!Dart_IsNull(object: sticky_error) && !Dart_IsFatalError(handle: sticky_error)) {
1097 FML_LOG(ERROR) << Dart_GetError(handle: sticky_error);
1098 }
1099 }
1100
1101 shutdown_callbacks_.clear();
1102
1103 const fml::closure& isolate_shutdown_callback =
1104 GetIsolateGroupData().GetIsolateShutdownCallback();
1105 if (isolate_shutdown_callback) {
1106 isolate_shutdown_callback();
1107 }
1108}
1109
1110Dart_Handle DartIsolate::OnDartLoadLibrary(intptr_t loading_unit_id) {
1111 if (Current()->platform_configuration()) {
1112 Current()->platform_configuration()->client()->RequestDartDeferredLibrary(
1113 loading_unit_id);
1114 return Dart_Null();
1115 }
1116 const std::string error_message =
1117 "Platform Configuration was null. Deferred library load request"
1118 "for loading unit id " +
1119 std::to_string(val: loading_unit_id) + " was not sent.";
1120 FML_LOG(ERROR) << error_message;
1121 return Dart_NewApiError(error: error_message.c_str());
1122}
1123
1124DartIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure)
1125 : closure_(closure) {}
1126
1127DartIsolate::AutoFireClosure::~AutoFireClosure() {
1128 if (closure_) {
1129 closure_();
1130 }
1131}
1132
1133} // namespace flutter
1134

source code of flutter_engine/flutter/runtime/dart_isolate.cc