|  | // 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/runtime/dart_vm.h" | 
|  |  | 
|  | #include <sys/stat.h> | 
|  |  | 
|  | #include <sstream> | 
|  | #include <vector> | 
|  |  | 
|  | #include "flutter/common/settings.h" | 
|  | #include "flutter/fml/compiler_specific.h" | 
|  | #include "flutter/fml/cpu_affinity.h" | 
|  | #include "flutter/fml/logging.h" | 
|  | #include "flutter/fml/mapping.h" | 
|  | #include "flutter/fml/size.h" | 
|  | #include "flutter/fml/time/time_delta.h" | 
|  | #include "flutter/fml/trace_event.h" | 
|  | #include "flutter/lib/ui/dart_ui.h" | 
|  | #include "flutter/runtime/dart_isolate.h" | 
|  | #include "flutter/runtime/dart_vm_initializer.h" | 
|  | #include "flutter/runtime/ptrace_check.h" | 
|  | #include "third_party/dart/runtime/include/bin/dart_io_api.h" | 
|  | #include "third_party/skia/include/core/SkExecutor.h" | 
|  | #include "third_party/tonic/converter/dart_converter.h" | 
|  | #include "third_party/tonic/dart_class_library.h" | 
|  | #include "third_party/tonic/dart_class_provider.h" | 
|  | #include "third_party/tonic/file_loader/file_loader.h" | 
|  | #include "third_party/tonic/logging/dart_error.h" | 
|  | #include "third_party/tonic/typed_data/typed_list.h" | 
|  |  | 
|  | namespace dart { | 
|  | namespace observatory { | 
|  |  | 
|  | #if !OS_FUCHSIA && !FLUTTER_RELEASE | 
|  |  | 
|  | // These two symbols are defined in |observatory_archive.cc| which is generated | 
|  | // by the |//third_party/dart/runtime/observatory:archive_observatory| rule. | 
|  | // Both of these symbols will be part of the data segment and therefore are read | 
|  | // only. | 
|  | extern unsigned int observatory_assets_archive_len; | 
|  | extern const uint8_t* observatory_assets_archive; | 
|  |  | 
|  | #endif  // !OS_FUCHSIA && !FLUTTER_RELEASE | 
|  |  | 
|  | }  // namespace observatory | 
|  | }  // namespace dart | 
|  |  | 
|  | namespace flutter { | 
|  |  | 
|  | // Arguments passed to the Dart VM in all configurations. | 
|  | static const char* kDartAllConfigsArgs[] = { | 
|  | // clang-format off | 
|  | "--enable_mirrors=false", | 
|  | "--background_compilation", | 
|  | // 'mark_when_idle' appears to cause a regression, turning off for now. | 
|  | // "--mark_when_idle", | 
|  | // clang-format on | 
|  | }; | 
|  |  | 
|  | static const char* kDartPrecompilationArgs[] = {"--precompilation"}; | 
|  |  | 
|  | static const char* kSerialGCArgs[] = { | 
|  | // clang-format off | 
|  | "--concurrent_mark=false", | 
|  | "--concurrent_sweep=false", | 
|  | "--compactor_tasks=1", | 
|  | "--scavenger_tasks=0", | 
|  | "--marker_tasks=0", | 
|  | // clang-format on | 
|  | }; | 
|  |  | 
|  | FML_ALLOW_UNUSED_TYPE | 
|  | static const char* kDartWriteProtectCodeArgs[] = { | 
|  | "--no_write_protect_code", | 
|  | }; | 
|  |  | 
|  | FML_ALLOW_UNUSED_TYPE | 
|  | static const char* kDartDisableIntegerDivisionArgs[] = { | 
|  | "--no_use_integer_division", | 
|  | }; | 
|  |  | 
|  | static const char* kDartAssertArgs[] = { | 
|  | // clang-format off | 
|  | "--enable_asserts", | 
|  | // clang-format on | 
|  | }; | 
|  |  | 
|  | static const char* kDartStartPausedArgs[]{ | 
|  | "--pause_isolates_on_start", | 
|  | }; | 
|  |  | 
|  | static const char* kDartEndlessTraceBufferArgs[]{ | 
|  | "--timeline_recorder=endless", | 
|  | }; | 
|  |  | 
|  | static const char* kDartSystraceTraceBufferArgs[] = { | 
|  | "--timeline_recorder=systrace", | 
|  | }; | 
|  |  | 
|  | static std::string DartFileRecorderArgs(const std::string& path) { | 
|  | std::ostringstream oss; | 
|  | oss << "--timeline_recorder=perfettofile:" << path; | 
|  | return oss.str(); | 
|  | } | 
|  |  | 
|  | FML_ALLOW_UNUSED_TYPE | 
|  | static const char* kDartDefaultTraceStreamsArgs[]{ | 
|  | "--timeline_streams=Dart,Embedder,GC", | 
|  | }; | 
|  |  | 
|  | static const char* kDartStartupTraceStreamsArgs[]{ | 
|  | "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API", | 
|  | }; | 
|  |  | 
|  | static const char* kDartSystraceTraceStreamsArgs[] = { | 
|  | "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM,API", | 
|  | }; | 
|  |  | 
|  | static std::string DartOldGenHeapSizeArgs(uint64_t heap_size) { | 
|  | std::ostringstream oss; | 
|  | oss << "--old_gen_heap_size=" << heap_size; | 
|  | return oss.str(); | 
|  | } | 
|  |  | 
|  | constexpr char kFileUriPrefix[] = "file://"; | 
|  | constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1; | 
|  |  | 
|  | bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) { | 
|  | if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) { | 
|  | // Assume modified. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const char* path = source_url + kFileUriPrefixLength; | 
|  | struct stat info; | 
|  | if (stat(path, &info) < 0) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // If st_mtime is zero, it's more likely that the file system doesn't support | 
|  | // mtime than that the file was actually modified in the 1970s. | 
|  | if (!info.st_mtime) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // It's very unclear what time bases we're with here. The Dart API doesn't | 
|  | // document the time base for since_ms. Reading the code, the value varies by | 
|  | // platform, with a typical source being something like gettimeofday. | 
|  | // | 
|  | // We add one to st_mtime because st_mtime has less precision than since_ms | 
|  | // and we want to treat the file as modified if the since time is between | 
|  | // ticks of the mtime. | 
|  | fml::TimeDelta mtime = fml::TimeDelta::FromSeconds(info.st_mtime + 1); | 
|  | fml::TimeDelta since = fml::TimeDelta::FromMilliseconds(since_ms); | 
|  |  | 
|  | return mtime > since; | 
|  | } | 
|  |  | 
|  | void ThreadExitCallback() {} | 
|  |  | 
|  | Dart_Handle GetVMServiceAssetsArchiveCallback() { | 
|  | #if FLUTTER_RELEASE | 
|  | return nullptr; | 
|  | #elif OS_FUCHSIA | 
|  | fml::UniqueFD fd = fml::OpenFile("pkg/data/observatory.tar", false, | 
|  | fml::FilePermission::kRead); | 
|  | fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kRead}); | 
|  | if (mapping.GetSize() == 0 || mapping.GetMapping() == nullptr) { | 
|  | FML_LOG(ERROR) << "Fail to load Observatory archive"; | 
|  | return nullptr; | 
|  | } | 
|  | return tonic::DartConverter<tonic::Uint8List>::ToDart(mapping.GetMapping(), | 
|  | mapping.GetSize()); | 
|  | #else | 
|  | return tonic::DartConverter<tonic::Uint8List>::ToDart( | 
|  | ::dart::observatory::observatory_assets_archive, | 
|  | ::dart::observatory::observatory_assets_archive_len); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static const char kStdoutStreamId[] = "Stdout"; | 
|  | static const char kStderrStreamId[] = "Stderr"; | 
|  |  | 
|  | static bool ServiceStreamListenCallback(const char* stream_id) { | 
|  | if (strcmp(stream_id, kStdoutStreamId) == 0) { | 
|  | dart::bin::SetCaptureStdout(true); | 
|  | return true; | 
|  | } else if (strcmp(stream_id, kStderrStreamId) == 0) { | 
|  | dart::bin::SetCaptureStderr(true); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static void ServiceStreamCancelCallback(const char* stream_id) { | 
|  | if (strcmp(stream_id, kStdoutStreamId) == 0) { | 
|  | dart::bin::SetCaptureStdout(false); | 
|  | } else if (strcmp(stream_id, kStderrStreamId) == 0) { | 
|  | dart::bin::SetCaptureStderr(false); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool DartVM::IsRunningPrecompiledCode() { | 
|  | return Dart_IsPrecompiledRuntime(); | 
|  | } | 
|  |  | 
|  | static std::vector<const char*> ProfilingFlags(bool enable_profiling) { | 
|  | // Disable Dart's built in profiler when building a debug build. This | 
|  | // works around a race condition that would sometimes stop a crash's | 
|  | // stack trace from being printed on Android. | 
|  | #ifndef NDEBUG | 
|  | enable_profiling = false; | 
|  | #endif | 
|  |  | 
|  | // We want to disable profiling by default because it overwhelms LLDB. But | 
|  | // the VM enables the same by default. In either case, we have some profiling | 
|  | // flags. | 
|  | if (enable_profiling) { | 
|  | return { | 
|  | // This is the default. But just be explicit. | 
|  | "--profiler", | 
|  | // This instructs the profiler to walk C++ frames, and to include | 
|  | // them in the profile. | 
|  | "--profile-vm", | 
|  | #if FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL | 
|  | // Set the profiler interrupt period to 500Hz instead of the | 
|  | // default 1000Hz on 32-bit iOS devices to reduce average and worst | 
|  | // case frame build times. | 
|  | // | 
|  | // Note: profile_period is time in microseconds between sampling | 
|  | // events, not frequency. Frequency is calculated 1/period (or | 
|  | // 1,000,000 / 2,000 -> 500Hz in this case). | 
|  | "--profile_period=2000", | 
|  | #else | 
|  | "--profile_period=1000", | 
|  | #endif  // FML_OS_IOS && FML_ARCH_CPU_ARM_FAMILY && FML_ARCH_CPU_ARMEL | 
|  | }; | 
|  | } else { | 
|  | return {"--no-profiler"}; | 
|  | } | 
|  | } | 
|  |  | 
|  | void PushBackAll(std::vector<const char*>* args, | 
|  | const char** argv, | 
|  | size_t argc) { | 
|  | for (size_t i = 0; i < argc; ++i) { | 
|  | args->push_back(argv[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void EmbedderInformationCallback(Dart_EmbedderInformation* info) { | 
|  | info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION; | 
|  | dart::bin::GetIOEmbedderInformation(info); | 
|  | info->name = "Flutter"; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<DartVM> DartVM::Create( | 
|  | const Settings& settings, | 
|  | fml::RefPtr<const DartSnapshot> vm_snapshot, | 
|  | fml::RefPtr<const DartSnapshot> isolate_snapshot, | 
|  | std::shared_ptr<IsolateNameServer> isolate_name_server) { | 
|  | auto vm_data = DartVMData::Create(settings,                    // | 
|  | std::move(vm_snapshot),      // | 
|  | std::move(isolate_snapshot)  // | 
|  | ); | 
|  |  | 
|  | if (!vm_data) { | 
|  | FML_LOG(ERROR) << "Could not set up VM data to bootstrap the VM from."; | 
|  | return {}; | 
|  | } | 
|  |  | 
|  | // Note: std::make_shared unviable due to hidden constructor. | 
|  | return std::shared_ptr<DartVM>( | 
|  | new DartVM(vm_data, std::move(isolate_name_server))); | 
|  | } | 
|  |  | 
|  | static std::atomic_size_t gVMLaunchCount; | 
|  |  | 
|  | size_t DartVM::GetVMLaunchCount() { | 
|  | return gVMLaunchCount; | 
|  | } | 
|  |  | 
|  | DartVM::DartVM(const std::shared_ptr<const DartVMData>& vm_data, | 
|  | std::shared_ptr<IsolateNameServer> isolate_name_server) | 
|  | : settings_(vm_data->GetSettings()), | 
|  | concurrent_message_loop_(fml::ConcurrentMessageLoop::Create( | 
|  | fml::EfficiencyCoreCount().value_or( | 
|  | std::thread::hardware_concurrency()))), | 
|  | skia_concurrent_executor_( | 
|  | [runner = concurrent_message_loop_->GetTaskRunner()]( | 
|  | const fml::closure& work) { runner->PostTask(work); }), | 
|  | vm_data_(vm_data), | 
|  | isolate_name_server_(std::move(isolate_name_server)), | 
|  | service_protocol_(std::make_shared<ServiceProtocol>()) { | 
|  | TRACE_EVENT0("flutter", "DartVMInitializer"); | 
|  |  | 
|  | gVMLaunchCount++; | 
|  |  | 
|  | // Setting the executor is not thread safe but Dart VM initialization is. So | 
|  | // this call is thread-safe. | 
|  | SkExecutor::SetDefault(&skia_concurrent_executor_); | 
|  |  | 
|  | FML_DCHECK(vm_data_); | 
|  | FML_DCHECK(isolate_name_server_); | 
|  | FML_DCHECK(service_protocol_); | 
|  |  | 
|  | { | 
|  | TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo"); | 
|  | dart::bin::BootstrapDartIo(); | 
|  |  | 
|  | if (!settings_.temp_directory_path.empty()) { | 
|  | dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<const char*> args; | 
|  |  | 
|  | // Instruct the VM to ignore unrecognized flags. | 
|  | // There is a lot of diversity in a lot of combinations when it | 
|  | // comes to the arguments the VM supports. And, if the VM comes across a flag | 
|  | // it does not recognize, it exits immediately. | 
|  | args.push_back("--ignore-unrecognized-flags"); | 
|  |  | 
|  | for (auto* const profiler_flag : | 
|  | ProfilingFlags(settings_.enable_dart_profiling)) { | 
|  | args.push_back(profiler_flag); | 
|  | } | 
|  |  | 
|  | PushBackAll(&args, kDartAllConfigsArgs, fml::size(kDartAllConfigsArgs)); | 
|  |  | 
|  | if (IsRunningPrecompiledCode()) { | 
|  | PushBackAll(&args, kDartPrecompilationArgs, | 
|  | fml::size(kDartPrecompilationArgs)); | 
|  | } | 
|  |  | 
|  | // Enable Dart assertions if we are not running precompiled code. We run non- | 
|  | // precompiled code only in the debug product mode. | 
|  | bool enable_asserts = !settings_.disable_dart_asserts; | 
|  |  | 
|  | #if !OS_FUCHSIA | 
|  | if (IsRunningPrecompiledCode()) { | 
|  | enable_asserts = false; | 
|  | } | 
|  | #endif  // !OS_FUCHSIA | 
|  |  | 
|  | #if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) | 
|  | #if !FML_OS_IOS && !FML_OS_MACOSX | 
|  | // Debug mode uses the JIT, disable code page write protection to avoid | 
|  | // memory page protection changes before and after every compilation. | 
|  | PushBackAll(&args, kDartWriteProtectCodeArgs, | 
|  | fml::size(kDartWriteProtectCodeArgs)); | 
|  | #else | 
|  | const bool tracing_result = EnableTracingIfNecessary(settings_); | 
|  | // This check should only trip if the embedding made no attempts to enable | 
|  | // tracing. At this point, it is too late display user visible messages. Just | 
|  | // log and die. | 
|  | FML_CHECK(tracing_result) | 
|  | << "Tracing not enabled before attempting to run JIT mode VM."; | 
|  | #if TARGET_CPU_ARM | 
|  | // Tell Dart in JIT mode to not use integer division on armv7 | 
|  | // Ideally, this would be detected at runtime by Dart. | 
|  | // TODO(dnfield): Remove this code | 
|  | // https://github.com/dart-lang/sdk/issues/24743 | 
|  | PushBackAll(&args, kDartDisableIntegerDivisionArgs, | 
|  | fml::size(kDartDisableIntegerDivisionArgs)); | 
|  | #endif  // TARGET_CPU_ARM | 
|  | #endif  // !FML_OS_IOS && !FML_OS_MACOSX | 
|  | #endif  // (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) | 
|  |  | 
|  | if (enable_asserts) { | 
|  | PushBackAll(&args, kDartAssertArgs, fml::size(kDartAssertArgs)); | 
|  | } | 
|  |  | 
|  | // On low power devices with lesser number of cores, using concurrent | 
|  | // marking or sweeping causes contention for the UI thread leading to | 
|  | // Jank, this option can be used to turn off all concurrent GC activities. | 
|  | if (settings_.enable_serial_gc) { | 
|  | PushBackAll(&args, kSerialGCArgs, fml::size(kSerialGCArgs)); | 
|  | } | 
|  |  | 
|  | if (settings_.start_paused) { | 
|  | PushBackAll(&args, kDartStartPausedArgs, fml::size(kDartStartPausedArgs)); | 
|  | } | 
|  |  | 
|  | if (settings_.endless_trace_buffer || settings_.trace_startup) { | 
|  | // If we are tracing startup, make sure the trace buffer is endless so we | 
|  | // don't lose early traces. | 
|  | PushBackAll(&args, kDartEndlessTraceBufferArgs, | 
|  | fml::size(kDartEndlessTraceBufferArgs)); | 
|  | } | 
|  |  | 
|  | if (settings_.trace_systrace) { | 
|  | PushBackAll(&args, kDartSystraceTraceBufferArgs, | 
|  | fml::size(kDartSystraceTraceBufferArgs)); | 
|  | PushBackAll(&args, kDartSystraceTraceStreamsArgs, | 
|  | fml::size(kDartSystraceTraceStreamsArgs)); | 
|  | } | 
|  |  | 
|  | std::string file_recorder_args; | 
|  | if (!settings_.trace_to_file.empty()) { | 
|  | file_recorder_args = DartFileRecorderArgs(settings_.trace_to_file); | 
|  | args.push_back(file_recorder_args.c_str()); | 
|  | PushBackAll(&args, kDartSystraceTraceStreamsArgs, | 
|  | fml::size(kDartSystraceTraceStreamsArgs)); | 
|  | } | 
|  |  | 
|  | if (settings_.trace_startup) { | 
|  | PushBackAll(&args, kDartStartupTraceStreamsArgs, | 
|  | fml::size(kDartStartupTraceStreamsArgs)); | 
|  | } | 
|  |  | 
|  | #if defined(OS_FUCHSIA) | 
|  | PushBackAll(&args, kDartSystraceTraceBufferArgs, | 
|  | fml::size(kDartSystraceTraceBufferArgs)); | 
|  | PushBackAll(&args, kDartSystraceTraceStreamsArgs, | 
|  | fml::size(kDartSystraceTraceStreamsArgs)); | 
|  | #else | 
|  | if (!settings_.trace_systrace && !settings_.trace_startup) { | 
|  | PushBackAll(&args, kDartDefaultTraceStreamsArgs, | 
|  | fml::size(kDartDefaultTraceStreamsArgs)); | 
|  | } | 
|  | #endif  // defined(OS_FUCHSIA) | 
|  |  | 
|  | std::string old_gen_heap_size_args; | 
|  | if (settings_.old_gen_heap_size >= 0) { | 
|  | old_gen_heap_size_args = | 
|  | DartOldGenHeapSizeArgs(settings_.old_gen_heap_size); | 
|  | args.push_back(old_gen_heap_size_args.c_str()); | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < settings_.dart_flags.size(); i++) { | 
|  | args.push_back(settings_.dart_flags[i].c_str()); | 
|  | } | 
|  |  | 
|  | char* flags_error = Dart_SetVMFlags(args.size(), args.data()); | 
|  | if (flags_error) { | 
|  | FML_LOG(FATAL) << "Error while setting Dart VM flags: " << flags_error; | 
|  | ::free(flags_error); | 
|  | } | 
|  |  | 
|  | dart::bin::SetExecutableName(settings_.executable_name.c_str()); | 
|  |  | 
|  | { | 
|  | TRACE_EVENT0("flutter", "Dart_Initialize"); | 
|  | Dart_InitializeParams params = {}; | 
|  | params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; | 
|  | params.vm_snapshot_data = vm_data_->GetVMSnapshot().GetDataMapping(); | 
|  | params.vm_snapshot_instructions = | 
|  | vm_data_->GetVMSnapshot().GetInstructionsMapping(); | 
|  | params.create_group = reinterpret_cast<decltype(params.create_group)>( | 
|  | DartIsolate::DartIsolateGroupCreateCallback); | 
|  | params.initialize_isolate = | 
|  | reinterpret_cast<decltype(params.initialize_isolate)>( | 
|  | DartIsolate::DartIsolateInitializeCallback); | 
|  | params.shutdown_isolate = | 
|  | reinterpret_cast<decltype(params.shutdown_isolate)>( | 
|  | DartIsolate::DartIsolateShutdownCallback); | 
|  | params.cleanup_isolate = reinterpret_cast<decltype(params.cleanup_isolate)>( | 
|  | DartIsolate::DartIsolateCleanupCallback); | 
|  | params.cleanup_group = reinterpret_cast<decltype(params.cleanup_group)>( | 
|  | DartIsolate::DartIsolateGroupCleanupCallback); | 
|  | params.thread_exit = ThreadExitCallback; | 
|  | params.file_open = dart::bin::OpenFile; | 
|  | params.file_read = dart::bin::ReadFile; | 
|  | params.file_write = dart::bin::WriteFile; | 
|  | params.file_close = dart::bin::CloseFile; | 
|  | params.entropy_source = dart::bin::GetEntropy; | 
|  | params.get_service_assets = GetVMServiceAssetsArchiveCallback; | 
|  | DartVMInitializer::Initialize(¶ms, | 
|  | settings_.enable_timeline_event_handler, | 
|  | settings_.trace_systrace); | 
|  | // Send the earliest available timestamp in the application lifecycle to | 
|  | // timeline. The difference between this timestamp and the time we render | 
|  | // the very first frame gives us a good idea about Flutter's startup time. | 
|  | // Use an instant event because the call to Dart_TimelineGetMicros | 
|  | // may behave differently before and after the Dart VM is initialized. | 
|  | // As this call is immediately after initialization of the Dart VM, | 
|  | // we are interested in only one timestamp. | 
|  | int64_t micros = Dart_TimelineGetMicros(); | 
|  | Dart_RecordTimelineEvent("FlutterEngineMainEnter",  // label | 
|  | micros,                    // timestamp0 | 
|  | micros,   // timestamp1_or_async_id | 
|  | 0,        // flow_id_count | 
|  | nullptr,  // flow_ids | 
|  | Dart_Timeline_Event_Instant,  // event type | 
|  | 0,                            // argument_count | 
|  | nullptr,                      // argument_names | 
|  | nullptr                       // argument_values | 
|  | ); | 
|  | } | 
|  |  | 
|  | Dart_SetFileModifiedCallback(&DartFileModifiedCallback); | 
|  |  | 
|  | // Allow streaming of stdout and stderr by the Dart vm. | 
|  | Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, | 
|  | &ServiceStreamCancelCallback); | 
|  |  | 
|  | Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); | 
|  |  | 
|  | if (settings_.dart_library_sources_kernel != nullptr) { | 
|  | std::unique_ptr<fml::Mapping> dart_library_sources = | 
|  | settings_.dart_library_sources_kernel(); | 
|  | // Set sources for dart:* libraries for debugging. | 
|  | Dart_SetDartLibrarySourcesKernel(dart_library_sources->GetMapping(), | 
|  | dart_library_sources->GetSize()); | 
|  | } | 
|  |  | 
|  | // Update thread names now that the Dart VM is initialized. | 
|  | concurrent_message_loop_->PostTaskToAllWorkers( | 
|  | [] { Dart_SetThreadName("FlutterConcurrentMessageLoopWorker"); }); | 
|  | } | 
|  |  | 
|  | DartVM::~DartVM() { | 
|  | // Setting the executor is not thread safe but Dart VM shutdown is. So | 
|  | // this call is thread-safe. | 
|  | SkExecutor::SetDefault(nullptr); | 
|  |  | 
|  | if (Dart_CurrentIsolate() != nullptr) { | 
|  | Dart_ExitIsolate(); | 
|  | } | 
|  |  | 
|  | DartVMInitializer::Cleanup(); | 
|  |  | 
|  | dart::bin::CleanupDartIo(); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<const DartVMData> DartVM::GetVMData() const { | 
|  | return vm_data_; | 
|  | } | 
|  |  | 
|  | const Settings& DartVM::GetSettings() const { | 
|  | return settings_; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<ServiceProtocol> DartVM::GetServiceProtocol() const { | 
|  | return service_protocol_; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<IsolateNameServer> DartVM::GetIsolateNameServer() const { | 
|  | return isolate_name_server_; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<fml::ConcurrentTaskRunner> | 
|  | DartVM::GetConcurrentWorkerTaskRunner() const { | 
|  | return concurrent_message_loop_->GetTaskRunner(); | 
|  | } | 
|  |  | 
|  | std::shared_ptr<fml::ConcurrentMessageLoop> DartVM::GetConcurrentMessageLoop() { | 
|  | return concurrent_message_loop_; | 
|  | } | 
|  |  | 
|  | }  // namespace flutter |