| // Copyright 2015 The Chromium 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_init.h" |
| |
| #include <dlfcn.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "dart/runtime/bin/embedded_dart_io.h" |
| #include "dart/runtime/include/dart_mirrors_api.h" |
| #include "flutter/assets/unzipper_provider.h" |
| #include "flutter/assets/zip_asset_store.h" |
| #include "flutter/common/settings.h" |
| #include "flutter/glue/trace_event.h" |
| #include "flutter/lib/io/dart_io.h" |
| #include "flutter/lib/mojo/dart_mojo_internal.h" |
| #include "flutter/lib/ui/dart_runtime_hooks.h" |
| #include "flutter/lib/ui/dart_ui.h" |
| #include "flutter/lib/ui/ui_dart_state.h" |
| #include "flutter/runtime/dart_service_isolate.h" |
| #include "flutter/runtime/start_up.h" |
| #include "lib/ftl/arraysize.h" |
| #include "lib/ftl/build_config.h" |
| #include "lib/ftl/files/eintr_wrapper.h" |
| #include "lib/ftl/files/unique_fd.h" |
| #include "lib/ftl/logging.h" |
| #include "lib/ftl/time/time_delta.h" |
| #include "lib/tonic/dart_class_library.h" |
| #include "lib/tonic/dart_state.h" |
| #include "lib/tonic/dart_wrappable.h" |
| #include "lib/tonic/debugger/dart_debugger.h" |
| #include "lib/tonic/logging/dart_error.h" |
| #include "lib/tonic/logging/dart_invoke.h" |
| #include "lib/tonic/scopes/dart_api_scope.h" |
| #include "lib/tonic/scopes/dart_isolate_scope.h" |
| #include "lib/tonic/typed_data/uint8_list.h" |
| #include "mojo/public/platform/dart/dart_handle_watcher.h" |
| |
| #if defined(OS_ANDROID) |
| #include "flutter/lib/jni/dart_jni.h" |
| #endif |
| |
| using tonic::DartClassProvider; |
| using tonic::LogIfError; |
| using tonic::ToDart; |
| |
| namespace dart { |
| namespace observatory { |
| |
| #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE |
| |
| // These two symbols are defined in |observatory_archive.cc| which is generated |
| // by the |//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 // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE |
| |
| } // namespace observatory |
| } // namespace dart |
| |
| namespace blink { |
| |
| const char kSnapshotAssetKey[] = "snapshot_blob.bin"; |
| |
| namespace { |
| |
| static const char* kDartProfilingArgs[] = { |
| // Dart assumes ARM devices are insufficiently powerful and sets the |
| // default profile period to 100Hz. This number is suitable for older |
| // Raspberry Pi devices but quite low for current smartphones. |
| "--profile_period=1000", |
| #if (WTF_OS_IOS || WTF_OS_MACOSX) |
| // On platforms where LLDB is the primary debugger, SIGPROF signals |
| // overwhelm LLDB. |
| "--no-profiler", |
| #endif |
| }; |
| |
| static const char* kDartMirrorsArgs[] = { |
| "--enable_mirrors=false", |
| }; |
| |
| static const char* kDartPrecompilationArgs[] = { |
| "--precompilation", |
| }; |
| |
| static const char* kDartBackgroundCompilationArgs[] = { |
| "--background_compilation", |
| }; |
| |
| static const char* kDartCheckedModeArgs[] = { |
| // clang-format off |
| "--enable_asserts", |
| "--enable_type_checks", |
| "--error_on_bad_type", |
| "--error_on_bad_override", |
| // clang-format on |
| }; |
| |
| static const char* kDartStartPausedArgs[]{ |
| "--pause_isolates_on_start", |
| }; |
| |
| static const char* kDartTraceStartupArgs[]{ |
| "--timeline_streams=Compiler,Dart,Embedder,GC", |
| }; |
| |
| static const char* kDartEndlessTraceBufferArgs[]{ |
| "--timeline_recorder=endless", |
| }; |
| |
| constexpr char kFileUriPrefix[] = "file://"; |
| constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1; |
| |
| bool g_service_isolate_initialized = false; |
| ServiceIsolateHook g_service_isolate_hook = nullptr; |
| RegisterNativeServiceProtocolExtensionHook |
| g_register_native_service_protocol_extensions_hook = nullptr; |
| |
| void IsolateShutdownCallback(void* callback_data) { |
| tonic::DartState* dart_state = static_cast<tonic::DartState*>(callback_data); |
| delete dart_state; |
| } |
| |
| 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. |
| ftl::TimeDelta mtime = ftl::TimeDelta::FromSeconds(info.st_mtime + 1); |
| ftl::TimeDelta since = ftl::TimeDelta::FromMilliseconds(since_ms); |
| |
| return mtime > since; |
| } |
| |
| void ThreadExitCallback() { |
| #if defined(OS_ANDROID) |
| DartJni::OnThreadExit(); |
| #endif |
| } |
| |
| bool IsServiceIsolateURL(const char* url_name) { |
| return url_name != nullptr && |
| std::string(url_name) == DART_VM_SERVICE_ISOLATE_NAME; |
| } |
| |
| #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE |
| |
| Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, |
| char** error) { |
| return nullptr; |
| } |
| |
| #else // FLUTTER_RUNTIME_MODE |
| |
| Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, |
| char** error) { |
| tonic::DartState* dart_state = new tonic::DartState(); |
| Dart_Isolate isolate = Dart_CreateIsolate( |
| script_uri, "main", |
| reinterpret_cast<const uint8_t*>(DART_SYMBOL(kDartIsolateSnapshotBuffer)), |
| nullptr, dart_state, error); |
| FTL_CHECK(isolate) << error; |
| dart_state->SetIsolate(isolate); |
| FTL_CHECK(Dart_IsServiceIsolate(isolate)); |
| FTL_CHECK(!LogIfError( |
| Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); |
| { |
| tonic::DartApiScope dart_api_scope; |
| DartIO::InitForIsolate(); |
| DartUI::InitForIsolate(); |
| DartMojoInternal::InitForIsolate(); |
| DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); |
| const Settings& settings = Settings::Get(); |
| if (settings.enable_observatory) { |
| std::string ip = "127.0.0.1"; |
| const intptr_t port = settings.observatory_port; |
| const bool disable_websocket_origin_check = false; |
| const bool service_isolate_booted = DartServiceIsolate::Startup( |
| ip, port, tonic::DartState::HandleLibraryTag, |
| IsRunningPrecompiledCode(), disable_websocket_origin_check, error); |
| FTL_CHECK(service_isolate_booted) << error; |
| } |
| |
| if (g_service_isolate_hook) |
| g_service_isolate_hook(IsRunningPrecompiledCode()); |
| } |
| Dart_ExitIsolate(); |
| |
| g_service_isolate_initialized = true; |
| // Register any native service protocol extensions. |
| if (g_register_native_service_protocol_extensions_hook) { |
| g_register_native_service_protocol_extensions_hook( |
| IsRunningPrecompiledCode()); |
| } |
| return isolate; |
| } |
| |
| #endif // FLUTTER_RUNTIME_MODE |
| |
| Dart_Isolate IsolateCreateCallback(const char* script_uri, |
| const char* main, |
| const char* package_root, |
| const char* package_config, |
| Dart_IsolateFlags* flags, |
| void* callback_data, |
| char** error) { |
| TRACE_EVENT0("flutter", __func__); |
| |
| if (IsServiceIsolateURL(script_uri)) { |
| return ServiceIsolateCreateCallback(script_uri, error); |
| } |
| |
| std::vector<uint8_t> snapshot_data; |
| if (!IsRunningPrecompiledCode()) { |
| std::string uri = script_uri; |
| FTL_CHECK(uri.find(kFileUriPrefix) == 0u); |
| std::string bundle_path(script_uri + strlen(kFileUriPrefix)); |
| ftl::RefPtr<ZipAssetStore> zip_asset_store = |
| ftl::MakeRefCounted<ZipAssetStore>( |
| GetUnzipperProviderForPath(std::move(bundle_path)), |
| ftl::RefPtr<ftl::TaskRunner>()); |
| FTL_CHECK(zip_asset_store->GetAsBuffer(kSnapshotAssetKey, &snapshot_data)); |
| } |
| |
| UIDartState* parent_dart_state = static_cast<UIDartState*>(callback_data); |
| UIDartState* dart_state = parent_dart_state->CreateForChildIsolate(); |
| |
| Dart_Isolate isolate = Dart_CreateIsolate( |
| script_uri, main, |
| reinterpret_cast<uint8_t*>(DART_SYMBOL(kDartIsolateSnapshotBuffer)), |
| nullptr, dart_state, error); |
| FTL_CHECK(isolate) << error; |
| dart_state->SetIsolate(isolate); |
| FTL_CHECK(!LogIfError( |
| Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); |
| |
| { |
| tonic::DartApiScope dart_api_scope; |
| DartIO::InitForIsolate(); |
| DartUI::InitForIsolate(); |
| DartMojoInternal::InitForIsolate(); |
| DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); |
| |
| std::unique_ptr<DartClassProvider> ui_class_provider( |
| new DartClassProvider(dart_state, "dart:ui")); |
| dart_state->class_library().add_provider("ui", |
| std::move(ui_class_provider)); |
| |
| #if defined(OS_ANDROID) |
| DartJni::InitForIsolate(); |
| std::unique_ptr<DartClassProvider> jni_class_provider( |
| new DartClassProvider(dart_state, "dart:jni")); |
| dart_state->class_library().add_provider("jni", |
| std::move(jni_class_provider)); |
| #endif |
| |
| if (!snapshot_data.empty()) { |
| FTL_CHECK(!LogIfError(Dart_LoadScriptFromSnapshot(snapshot_data.data(), |
| snapshot_data.size()))); |
| } |
| |
| dart_state->isolate_client()->DidCreateSecondaryIsolate(isolate); |
| } |
| |
| Dart_ExitIsolate(); |
| |
| FTL_CHECK(Dart_IsolateMakeRunnable(isolate)); |
| return isolate; |
| } |
| |
| Dart_Handle GetVMServiceAssetsArchiveCallback() { |
| #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE || defined(OS_FUCHSIA) |
| return nullptr; |
| #else // FLUTTER_RUNTIME_MODE |
| return tonic::DartConverter<tonic::Uint8List>::ToDart( |
| ::dart::observatory::observatory_assets_archive, |
| ::dart::observatory::observatory_assets_archive_len); |
| #endif // FLUTTER_RUNTIME_MODE |
| } |
| |
| 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); |
| } |
| } |
| |
| #if defined(OS_ANDROID) |
| |
| DartJniIsolateData* GetDartJniDataForCurrentIsolate() { |
| return UIDartState::Current()->jni_data(); |
| } |
| |
| #endif |
| |
| } // namespace |
| |
| #if DART_ALLOW_DYNAMIC_RESOLUTION |
| |
| constexpr char kDartVmIsolateSnapshotBufferName[] = |
| "kDartVmIsolateSnapshotBuffer"; |
| constexpr char kDartIsolateSnapshotBufferName[] = "kDartIsolateSnapshotBuffer"; |
| constexpr char kInstructionsSnapshotName[] = "kInstructionsSnapshot"; |
| constexpr char kDataSnapshotName[] = "kDataSnapshot"; |
| |
| #if OS(IOS) |
| |
| const char* kDartApplicationLibraryPath = "app.dylib"; |
| |
| static void* DartLookupSymbolInLibrary(const char* symbol_name, |
| const char* library) { |
| TRACE_EVENT0("flutter", __func__); |
| if (symbol_name == nullptr) { |
| return nullptr; |
| } |
| dlerror(); // clear previous errors on thread |
| void* library_handle = dlopen(library, RTLD_NOW); |
| if (dlerror() != nullptr) { |
| return nullptr; |
| } |
| void* sym = dlsym(library_handle, symbol_name); |
| return dlerror() != nullptr ? nullptr : sym; |
| } |
| |
| void* _DartSymbolLookup(const char* symbol_name) { |
| TRACE_EVENT0("flutter", __func__); |
| if (symbol_name == nullptr) { |
| return nullptr; |
| } |
| |
| // First the application library is checked for the valid symbols. This |
| // library may not necessarily exist. If it does exist, it is loaded and the |
| // symbols resolved. Once the application library is loaded, there is |
| // currently no provision to unload the same. |
| void* symbol = |
| DartLookupSymbolInLibrary(symbol_name, kDartApplicationLibraryPath); |
| if (symbol != nullptr) { |
| return symbol; |
| } |
| |
| // Check inside the default library |
| return DartLookupSymbolInLibrary(symbol_name, nullptr); |
| } |
| |
| #elif OS(ANDROID) |
| |
| // Describes an asset file that holds a part of the precompiled snapshot. |
| struct SymbolAsset { |
| const char* symbol_name; |
| const char* file_name; |
| bool is_executable; |
| size_t settings_offset; |
| void* mapping; |
| }; |
| |
| static SymbolAsset g_symbol_assets[] = { |
| {kDartVmIsolateSnapshotBufferName, "snapshot_aot_vmisolate", false, |
| offsetof(Settings, aot_vm_isolate_snapshot_file_name)}, |
| {kDartIsolateSnapshotBufferName, "snapshot_aot_isolate", false, |
| offsetof(Settings, aot_isolate_snapshot_file_name)}, |
| {kInstructionsSnapshotName, "snapshot_aot_instr", true, |
| offsetof(Settings, aot_instructions_blob_file_name)}, |
| {kDataSnapshotName, "snapshot_aot_rodata", false, |
| offsetof(Settings, aot_rodata_blob_file_name)}, |
| }; |
| |
| // Resolve a precompiled snapshot symbol by mapping the corresponding asset |
| // file into memory. |
| void* _DartSymbolLookup(const char* symbol_name) { |
| for (SymbolAsset& symbol_asset : g_symbol_assets) { |
| if (strcmp(symbol_name, symbol_asset.symbol_name)) |
| continue; |
| |
| if (symbol_asset.mapping) { |
| return symbol_asset.mapping; |
| } |
| |
| const Settings& settings = Settings::Get(); |
| const std::string& aot_snapshot_path = settings.aot_snapshot_path; |
| FTL_CHECK(!aot_snapshot_path.empty()); |
| |
| const char* file_name = symbol_asset.file_name; |
| const std::string* settings_override = reinterpret_cast<const std::string*>( |
| reinterpret_cast<const uint8_t*>(&settings) + |
| symbol_asset.settings_offset); |
| if (!settings_override->empty()) |
| file_name = settings_override->c_str(); |
| |
| std::string asset_path = aot_snapshot_path + "/" + file_name; |
| struct stat info; |
| if (stat(asset_path.c_str(), &info) < 0) |
| return nullptr; |
| int64_t asset_size = info.st_size; |
| |
| ftl::UniqueFD fd(HANDLE_EINTR(open(asset_path.c_str(), O_RDONLY))); |
| if (fd.get() == -1) |
| return nullptr; |
| |
| int mmap_flags = PROT_READ; |
| if (symbol_asset.is_executable) |
| mmap_flags |= PROT_EXEC; |
| |
| void* symbol = mmap(NULL, asset_size, mmap_flags, MAP_PRIVATE, fd.get(), 0); |
| symbol_asset.mapping = symbol == MAP_FAILED ? nullptr : symbol; |
| |
| return symbol_asset.mapping; |
| } |
| |
| return nullptr; |
| } |
| |
| #else |
| |
| #error "AOT mode is not supported on this platform" |
| |
| #endif |
| |
| static const uint8_t* PrecompiledInstructionsSymbolIfPresent() { |
| return reinterpret_cast<uint8_t*>(DART_SYMBOL(kInstructionsSnapshot)); |
| } |
| |
| static const uint8_t* PrecompiledDataSnapshotSymbolIfPresent() { |
| return reinterpret_cast<uint8_t*>(DART_SYMBOL(kDataSnapshot)); |
| } |
| |
| bool IsRunningPrecompiledCode() { |
| TRACE_EVENT0("flutter", __func__); |
| return PrecompiledInstructionsSymbolIfPresent() != nullptr; |
| } |
| |
| #else // DART_ALLOW_DYNAMIC_RESOLUTION |
| |
| static const uint8_t* PrecompiledInstructionsSymbolIfPresent() { |
| return nullptr; |
| } |
| |
| static const uint8_t* PrecompiledDataSnapshotSymbolIfPresent() { |
| return nullptr; |
| } |
| |
| bool IsRunningPrecompiledCode() { |
| return false; |
| } |
| |
| #endif // DART_ALLOW_DYNAMIC_RESOLUTION |
| |
| EmbedderTracingCallbacks* g_tracing_callbacks = nullptr; |
| |
| EmbedderTracingCallbacks::EmbedderTracingCallbacks( |
| EmbedderTracingCallback start, |
| EmbedderTracingCallback stop) |
| : start_tracing_callback(start), stop_tracing_callback(stop) {} |
| |
| void SetEmbedderTracingCallbacks( |
| std::unique_ptr<EmbedderTracingCallbacks> callbacks) { |
| g_tracing_callbacks = callbacks.release(); |
| } |
| |
| static void EmbedderTimelineStartRecording() { |
| if (g_tracing_callbacks) |
| g_tracing_callbacks->start_tracing_callback(); |
| } |
| |
| static void EmbedderTimelineStopRecording() { |
| if (g_tracing_callbacks) |
| g_tracing_callbacks->stop_tracing_callback(); |
| } |
| |
| void SetServiceIsolateHook(ServiceIsolateHook hook) { |
| FTL_CHECK(!g_service_isolate_initialized); |
| g_service_isolate_hook = hook; |
| } |
| |
| void SetRegisterNativeServiceProtocolExtensionHook( |
| RegisterNativeServiceProtocolExtensionHook hook) { |
| FTL_CHECK(!g_service_isolate_initialized); |
| g_register_native_service_protocol_extensions_hook = hook; |
| } |
| |
| 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]); |
| } |
| } |
| |
| void InitDartVM() { |
| TRACE_EVENT0("flutter", __func__); |
| |
| const Settings& settings = Settings::Get(); |
| |
| { |
| TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo"); |
| dart::bin::BootstrapDartIo(); |
| |
| if (!settings.temp_directory_path.empty()) { |
| dart::bin::SetSystemTempDirectory(settings.temp_directory_path.c_str()); |
| } |
| } |
| |
| DartMojoInternal::SetHandleWatcherProducerHandle( |
| mojo::dart::HandleWatcher::Start()); |
| |
| 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"); |
| |
| PushBackAll(&args, kDartProfilingArgs, arraysize(kDartProfilingArgs)); |
| PushBackAll(&args, kDartMirrorsArgs, arraysize(kDartMirrorsArgs)); |
| PushBackAll(&args, kDartBackgroundCompilationArgs, |
| arraysize(kDartBackgroundCompilationArgs)); |
| |
| if (IsRunningPrecompiledCode()) { |
| PushBackAll(&args, kDartPrecompilationArgs, |
| arraysize(kDartPrecompilationArgs)); |
| } |
| |
| if (!IsRunningPrecompiledCode()) { |
| // Enable checked mode if we are not running precompiled code. We run non- |
| // precompiled code only in the debug product mode. |
| PushBackAll(&args, kDartCheckedModeArgs, arraysize(kDartCheckedModeArgs)); |
| } |
| |
| if (settings.start_paused) |
| PushBackAll(&args, kDartStartPausedArgs, arraysize(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, |
| arraysize(kDartEndlessTraceBufferArgs)); |
| } |
| |
| if (settings.trace_startup) { |
| PushBackAll(&args, kDartTraceStartupArgs, arraysize(kDartTraceStartupArgs)); |
| } |
| |
| for (size_t i = 0; i < settings.dart_flags.size(); i++) |
| args.push_back(settings.dart_flags[i].c_str()); |
| |
| FTL_CHECK(Dart_SetVMFlags(args.size(), args.data())); |
| |
| #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE |
| { |
| TRACE_EVENT0("flutter", "DartDebugger::InitDebugger"); |
| // This should be called before calling Dart_Initialize. |
| tonic::DartDebugger::InitDebugger(); |
| } |
| #endif |
| |
| DartUI::InitForGlobal(); |
| #if defined(OS_ANDROID) |
| DartJni::InitForGlobal(GetDartJniDataForCurrentIsolate); |
| #endif |
| |
| // Setup embedder tracing hooks. To avoid data races, it is recommended that |
| // these hooks be installed before the DartInitialize, so do that setup now. |
| Dart_SetEmbedderTimelineCallbacks(&EmbedderTimelineStartRecording, |
| &EmbedderTimelineStopRecording); |
| |
| Dart_SetFileModifiedCallback(&DartFileModifiedCallback); |
| |
| { |
| TRACE_EVENT0("flutter", "Dart_Initialize"); |
| Dart_InitializeParams params = {}; |
| params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; |
| params.vm_isolate_snapshot = |
| reinterpret_cast<uint8_t*>(DART_SYMBOL(kDartVmIsolateSnapshotBuffer)); |
| params.instructions_snapshot = PrecompiledInstructionsSymbolIfPresent(); |
| params.data_snapshot = PrecompiledDataSnapshotSymbolIfPresent(); |
| params.create = IsolateCreateCallback; |
| params.shutdown = IsolateShutdownCallback; |
| params.thread_exit = ThreadExitCallback; |
| params.get_service_assets = GetVMServiceAssetsArchiveCallback; |
| char* init_error = Dart_Initialize(¶ms); |
| if (init_error != nullptr) |
| FTL_LOG(FATAL) << "Error while initializing the Dart VM: " << init_error; |
| free(init_error); |
| |
| // 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 a duration event so about:tracing will consider this event when |
| // deciding the earliest event to use as time 0. |
| if (blink::engine_main_enter_ts != 0) { |
| Dart_TimelineEvent("FlutterEngineMainEnter", // label |
| blink::engine_main_enter_ts, // timestamp0 |
| blink::engine_main_enter_ts, // timestamp1_or_async_id |
| Dart_Timeline_Event_Duration, // event type |
| 0, // argument_count |
| nullptr, // argument_names |
| nullptr // argument_values |
| ); |
| } |
| } |
| |
| // Allow streaming of stdout and stderr by the Dart vm. |
| Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, |
| &ServiceStreamCancelCallback); |
| } |
| |
| } // namespace blink |