diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index dea8a8906d031..455d7e228168f 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -4,6 +4,7 @@ import("//third_party/dart/runtime/bin/vmservice/vmservice_sources.gni") import("$flutter_root/common/config.gni") +import("$flutter_root/testing/testing.gni") action("gen_embedded_resources_cc") { script = "//third_party/dart/runtime/tools/create_resources.py" @@ -37,9 +38,7 @@ source_set("embedded_resources_cc") { deps = [ ":gen_embedded_resources_cc", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } source_set("test_font") { @@ -50,9 +49,7 @@ source_set("test_font") { deps = [ "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] defines = [] if (flutter_runtime_mode == "debug" || current_toolchain == host_toolchain) { # Though the test font data is small, we dont want to add to the binary size @@ -63,16 +60,19 @@ source_set("test_font") { } source_set("runtime") { - sources = [ "asset_font_selector.cc", "asset_font_selector.h", - "dart_controller.cc", - "dart_controller.h", - "dart_init.cc", - "dart_init.h", + "dart_isolate.cc", + "dart_isolate.h", "dart_service_isolate.cc", "dart_service_isolate.h", + "dart_snapshot.cc", + "dart_snapshot.h", + "dart_snapshot_buffer.cc", + "dart_snapshot_buffer.h", + "dart_vm.cc", + "dart_vm.h", "embedder_resources.cc", "embedder_resources.h", "platform_impl.cc", @@ -81,8 +81,8 @@ source_set("runtime") { "runtime_controller.h", "runtime_delegate.cc", "runtime_delegate.h", - "runtime_init.cc", - "runtime_init.h", + "service_protocol.cc", + "service_protocol.h", "start_up.cc", "start_up.h", "test_font_selector.cc", @@ -92,25 +92,24 @@ source_set("runtime") { deps = [ ":embedded_resources_cc", ":test_font", - "//third_party/dart/runtime:dart_api", - "//third_party/dart/runtime/bin:embedded_dart_io", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", + "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/lib/io", "$flutter_root/lib/ui", "$flutter_root/sky/engine/platform", "$flutter_root/third_party/txt", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", + "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/rapidjson", "//third_party/skia", "//topaz/lib/tonic", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] # In AOT mode, precompiled snapshots contain the instruction buffer. # Generation of the same requires all application specific script code to be @@ -119,3 +118,28 @@ source_set("runtime") { deps += [ "$flutter_root/lib/snapshot" ] } } + +test_fixtures("runtime_fixtures") { + fixtures = [ "fixtures/simple_main.dart" ] +} + +executable("runtime_unittests") { + testonly = true + + sources = [ + "dart_isolate_unittests.cc", + "dart_vm_unittests.cc", + ] + + deps = [ + ":runtime", + ":runtime_fixtures", + "$flutter_root/fml", + "$flutter_root/lib/snapshot", + "$flutter_root/testing", + "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/skia", + "//topaz/lib/tonic", + ] +} diff --git a/runtime/asset_font_selector.cc b/runtime/asset_font_selector.cc index abf4bf9874ddd..2a43f39e3a89b 100644 --- a/runtime/asset_font_selector.cc +++ b/runtime/asset_font_selector.cc @@ -80,27 +80,15 @@ struct FontMatcher { } // namespace -void AssetFontSelector::Install( - fxl::RefPtr asset_provider) { +void AssetFontSelector::Install(fxl::RefPtr asset_manager) { RefPtr font_selector = - adoptRef(new AssetFontSelector(std::move(asset_provider))); + adoptRef(new AssetFontSelector(std::move(asset_manager))); font_selector->parseFontManifest(); UIDartState::Current()->set_font_selector(font_selector); } -void AssetFontSelector::Install(fxl::RefPtr asset_store) { - RefPtr font_selector = - adoptRef(new AssetFontSelector(std::move(asset_store))); - font_selector->parseFontManifest(); - UIDartState::Current()->set_font_selector(font_selector); -} - -AssetFontSelector::AssetFontSelector( - fxl::RefPtr asset_provider) - : asset_provider_(std::move(asset_provider)) {} - -AssetFontSelector::AssetFontSelector(fxl::RefPtr asset_store) - : asset_store_(std::move(asset_store)) {} +AssetFontSelector::AssetFontSelector(fxl::RefPtr asset_manager) + : asset_manager_(std::move(asset_manager)) {} AssetFontSelector::~AssetFontSelector() {} @@ -118,12 +106,9 @@ AssetFontSelector::FlutterFontAttributes::~FlutterFontAttributes() {} void AssetFontSelector::parseFontManifest() { std::vector font_manifest_data; - if (!asset_provider_ || - !asset_provider_->GetAsBuffer(kFontManifestAssetPath, - &font_manifest_data)) { - if (!asset_store_ || - !asset_store_->GetAsBuffer(kFontManifestAssetPath, &font_manifest_data)) - return; + if (!asset_manager_->GetAsBuffer(kFontManifestAssetPath, + &font_manifest_data)) { + return; } rapidjson::Document document; @@ -239,13 +224,8 @@ sk_sp AssetFontSelector::getTypefaceAsset( } std::unique_ptr typeface_asset(new TypefaceAsset); - if (!asset_provider_ || !asset_provider_->GetAsBuffer( - asset_path, &typeface_asset->data)) { - if (!asset_store_ || - !asset_store_->GetAsBuffer(asset_path, &typeface_asset->data)) { - typeface_cache_.insert(std::make_pair(asset_path, nullptr)); - return nullptr; - } + if (!asset_manager_->GetAsBuffer(asset_path, &typeface_asset->data)) { + return nullptr; } sk_sp font_mgr(SkFontMgr::RefDefault()); diff --git a/runtime/asset_font_selector.h b/runtime/asset_font_selector.h index 921c0472dba78..8d7e946d89e7e 100644 --- a/runtime/asset_font_selector.h +++ b/runtime/asset_font_selector.h @@ -8,7 +8,7 @@ #include #include -#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/assets/asset_manager.h" #include "flutter/assets/zip_asset_store.h" #include "flutter/sky/engine/platform/fonts/FontCacheKey.h" #include "flutter/sky/engine/platform/fonts/FontSelector.h" @@ -24,11 +24,7 @@ class AssetFontSelector : public FontSelector { ~AssetFontSelector() override; - static void Install(fxl::RefPtr asset_provider); - - // TODO(zarah): Remove this and related code using asset_store once flx is - // removed. - static void Install(fxl::RefPtr asset_store); + static void Install(fxl::RefPtr asset_manager); PassRefPtr getFontData(const FontDescription& font_description, const AtomicString& family_name) override; @@ -44,19 +40,14 @@ class AssetFontSelector : public FontSelector { private: struct TypefaceAsset; - explicit AssetFontSelector( - fxl::RefPtr asset_provider); - - explicit AssetFontSelector(fxl::RefPtr asset_store); + explicit AssetFontSelector(fxl::RefPtr asset_manager); void parseFontManifest(); sk_sp getTypefaceAsset(const FontDescription& font_description, const AtomicString& family_name); - fxl::RefPtr asset_provider_; - - fxl::RefPtr asset_store_; + fxl::RefPtr asset_manager_; HashMap> font_family_map_; diff --git a/runtime/dart_controller.cc b/runtime/dart_controller.cc deleted file mode 100644 index 84cfceeaecc10..0000000000000 --- a/runtime/dart_controller.cc +++ /dev/null @@ -1,250 +0,0 @@ -// 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_controller.h" -#include "lib/fxl/build_config.h" - -#if defined(OS_WIN) -#include -#undef GetCurrentDirectory -#endif - -#include - -#include "flutter/common/settings.h" -#include "flutter/common/threads.h" -#include "flutter/glue/trace_event.h" -#include "flutter/lib/io/dart_io.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_init.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "lib/fxl/files/directory.h" -#include "lib/fxl/files/path.h" -#include "lib/tonic/dart_class_library.h" -#include "lib/tonic/dart_message_handler.h" -#include "lib/tonic/dart_state.h" -#include "lib/tonic/dart_wrappable.h" -#include "lib/tonic/file_loader/file_loader.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 "third_party/dart/runtime/include/dart_tools_api.h" - -using tonic::LogIfError; -using tonic::ToDart; - -namespace blink { -namespace { -#if defined(OS_WIN) - -std::string FindAndReplace(const std::string& str, - const std::string& findStr, - const std::string& replaceStr) { - std::string rStr = str; - size_t pos = 0; - while ((pos = rStr.find(findStr, pos)) != std::string::npos) { - rStr.replace(pos, findStr.length(), replaceStr); - pos += replaceStr.length(); - } - return rStr; -} - -std::string SanitizePath(const std::string& path) { - return FindAndReplace(path, "\\\\", "/"); -} - -std::string ResolvePath(std::string path) { - std::string sanitized = SanitizePath(path); - if ((sanitized.length() > 2) && (sanitized[1] == ':')) { - return sanitized; - } - return files::SimplifyPath(files::GetCurrentDirectory() + "/" + sanitized); -} - -#else // defined(OS_WIN) - -std::string SanitizePath(const std::string& path) { - return path; -} - -// TODO(abarth): Consider adding this to //garnet/public/lib/fxl. -std::string ResolvePath(std::string path) { - if (!path.empty() && path[0] == '/') - return path; - return files::SimplifyPath(files::GetCurrentDirectory() + "/" + path); -} - -#endif - -} // namespace - -DartController::DartController() : ui_dart_state_(nullptr) {} - -DartController::~DartController() { - if (ui_dart_state_) { - ui_dart_state_->set_isolate_client(nullptr); - - if (!ui_dart_state_->shutting_down()) { - // Don't use a tonic::DartIsolateScope here since we never exit the - // isolate. - Dart_EnterIsolate(ui_dart_state_->isolate()); - // Clear the message notify callback. - Dart_SetMessageNotifyCallback(nullptr); - Dart_ShutdownIsolate(); - } - } -} - -const std::string DartController::main_entrypoint_ = "main"; - -bool DartController::SendStartMessage(Dart_Handle root_library, - const std::string& entrypoint) { - if (LogIfError(root_library)) - return true; - - { - // Temporarily exit the isolate while we make it runnable. - Dart_Isolate isolate = dart_state()->isolate(); - FXL_DCHECK(Dart_CurrentIsolate() == isolate); - Dart_ExitIsolate(); - Dart_IsolateMakeRunnable(isolate); - Dart_EnterIsolate(isolate); - } - - // In order to support pausing the isolate at start, we indirectly invoke - // main by sending a message to the isolate. - - // Get the closure of main(). - Dart_Handle main_closure = Dart_GetClosure( - root_library, Dart_NewStringFromCString(entrypoint.c_str())); - if (LogIfError(main_closure)) - return true; - - // Grab the 'dart:isolate' library. - Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); - DART_CHECK_VALID(isolate_lib); - - // Send the start message containing the entry point by calling - // _startMainIsolate in dart:isolate. - const intptr_t kNumIsolateArgs = 2; - Dart_Handle isolate_args[kNumIsolateArgs]; - isolate_args[0] = main_closure; - isolate_args[1] = Dart_Null(); - Dart_Handle result = Dart_Invoke(isolate_lib, ToDart("_startMainIsolate"), - kNumIsolateArgs, isolate_args); - return LogIfError(result); -} - -tonic::DartErrorHandleType DartController::RunFromKernel( - const std::vector& kernel, - const std::string& entrypoint) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - Dart_Handle result = Dart_LoadScriptFromKernel(kernel.data(), kernel.size()); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return error; -} - -tonic::DartErrorHandleType DartController::RunFromPrecompiledSnapshot( - const std::string& entrypoint) { - TRACE_EVENT0("flutter", "DartController::RunFromPrecompiledSnapshot"); - FXL_DCHECK(Dart_CurrentIsolate() == nullptr); - tonic::DartState::Scope scope(dart_state()); - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return tonic::kNoError; -} - -tonic::DartErrorHandleType DartController::RunFromScriptSnapshot( - const uint8_t* buffer, - size_t size, - const std::string& entrypoint) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - Dart_Handle result = Dart_LoadScriptFromSnapshot(buffer, size); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary(), entrypoint)) { - return tonic::kUnknownErrorType; - } - return error; -} - -tonic::DartErrorHandleType DartController::RunFromSource( - const std::string& main, - const std::string& packages) { - tonic::DartState::Scope scope(dart_state()); - tonic::DartErrorHandleType error = tonic::kNoError; - if (Dart_IsNull(Dart_RootLibrary())) { - tonic::FileLoader& loader = dart_state()->file_loader(); - if (!packages.empty() && !loader.LoadPackagesMap(ResolvePath(packages))) - FXL_LOG(WARNING) << "Failed to load package map: " << packages; - Dart_Handle result = loader.LoadScript(SanitizePath(main)); - LogIfError(result); - error = tonic::GetErrorHandleType(result); - } - if (SendStartMessage(Dart_RootLibrary())) { - return tonic::kCompilationErrorType; - } - return error; -} - -void DartController::CreateIsolateFor(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - std::unique_ptr state) { - char* error = nullptr; - - void* platform_kernel = GetKernelPlatformBinary(); - - Dart_Isolate isolate; - if (platform_kernel != nullptr) { - isolate = Dart_CreateIsolateFromKernel( - script_uri.c_str(), "main", platform_kernel, nullptr /* flags */, - static_cast(state.get()), &error); - } else { - isolate = - Dart_CreateIsolate(script_uri.c_str(), "main", isolate_snapshot_data, - isolate_snapshot_instr, nullptr, - static_cast(state.get()), &error); - } - FXL_CHECK(isolate) << error; - ui_dart_state_ = state.release(); - ui_dart_state_->set_is_controller_state(true); - dart_state()->message_handler().Initialize(blink::Threads::UI()); - - Dart_SetShouldPauseOnStart(Settings::Get().start_paused); - - ui_dart_state_->set_debug_name_prefix(script_uri); - ui_dart_state_->SetIsolate(isolate); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::MainIsolate, script_uri); - - std::unique_ptr ui_class_provider( - new tonic::DartClassProvider(dart_state(), "dart:ui")); - dart_state()->class_library().add_provider("ui", - std::move(ui_class_provider)); - } - Dart_ExitIsolate(); -} - -} // namespace blink diff --git a/runtime/dart_controller.h b/runtime/dart_controller.h deleted file mode 100644 index 4b11a8660b7e7..0000000000000 --- a/runtime/dart_controller.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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. - -#ifndef FLUTTER_RUNTIME_DART_CONTROLLER_H_ -#define FLUTTER_RUNTIME_DART_CONTROLLER_H_ - -#include -#include - -#include "lib/fxl/macros.h" -#include "lib/tonic/logging/dart_error.h" -#include "third_party/dart/runtime/include/dart_api.h" - -namespace blink { -class UIDartState; - -class DartController { - public: - DartController(); - ~DartController(); - - tonic::DartErrorHandleType RunFromKernel( - const std::vector& kernel, - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromPrecompiledSnapshot( - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromScriptSnapshot( - const uint8_t* buffer, - size_t size, - const std::string& entrypoint = main_entrypoint_); - tonic::DartErrorHandleType RunFromSource(const std::string& main, - const std::string& packages); - - void CreateIsolateFor(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - std::unique_ptr ui_dart_state); - - UIDartState* dart_state() const { return ui_dart_state_; } - - private: - bool SendStartMessage(Dart_Handle root_library, - const std::string& entrypoint = main_entrypoint_); - - static const std::string main_entrypoint_; - - // The DartState associated with the main isolate. - UIDartState* ui_dart_state_; - - FXL_DISALLOW_COPY_AND_ASSIGN(DartController); -}; -} // namespace blink - -#endif // FLUTTER_RUNTIME_DART_CONTROLLER_H_ diff --git a/runtime/dart_init.cc b/runtime/dart_init.cc deleted file mode 100644 index 5dd9e202a6398..0000000000000 --- a/runtime/dart_init.cc +++ /dev/null @@ -1,724 +0,0 @@ -// 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 "flutter/sky/engine/wtf/OperatingSystem.h" - -#include -#include -#include - -#if defined(OS_WIN) -#include -#include -#undef ERROR - -#define access _access -#define R_OK 0x4 - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - -#else -#include -#endif - -#include -#include -#include -#include - -#include "flutter/assets/directory_asset_bundle.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/ui/dart_runtime_hooks.h" -#include "flutter/lib/ui/dart_ui.h" -#include "flutter/lib/ui/ui_dart_state.h" -#include "flutter/lib/ui/window/window.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "flutter/runtime/start_up.h" -#include "lib/fxl/arraysize.h" -#include "lib/fxl/build_config.h" -#include "lib/fxl/files/path.h" -#include "lib/fxl/files/file.h" -#include "lib/fxl/logging.h" -#include "lib/fxl/time/time_delta.h" -#include "lib/tonic/converter/dart_converter.h" -#include "lib/tonic/dart_class_library.h" -#include "lib/tonic/dart_state.h" -#include "lib/tonic/dart_sticky_error.h" -#include "lib/tonic/dart_wrappable.h" -#include "lib/tonic/file_loader/file_loader.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 "third_party/dart/runtime/bin/embedded_dart_io.h" -#include "third_party/dart/runtime/include/dart_mirrors_api.h" - -using tonic::DartClassProvider; -using tonic::LogIfError; -using tonic::ToDart; - -namespace dart { -namespace observatory { - -#if !OS(FUCHSIA) && FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_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 // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - -} // namespace observatory -} // namespace dart - -namespace blink { - -const char kKernelAssetKey[] = "kernel_blob.bin"; -const char kSnapshotAssetKey[] = "snapshot_blob.bin"; -const char kPlatformKernelAssetKey[] = "platform.dill"; - -namespace { - -// Arguments passed to the Dart VM in all configurations. -static const char* kDartLanguageArgs[] = { - "--enable_mirrors=false", "--background_compilation", "--await_is_keyword", - "--causal_async_stacks", "--limit-ints-to-64-bits", -}; - -static const char* kDartPrecompilationArgs[] = { - "--precompilation", -}; - -static const char* kDartWriteProtectCodeArgs[] FXL_ALLOW_UNUSED_TYPE = { - "--no_write_protect_code", -}; - -static const char* kDartAssertArgs[] = { - // clang-format off - "--enable_asserts", - // clang-format on -}; - -static const char* kDartCheckedModeArgs[] = { - // clang-format off - "--enable_type_checks", - "--error_on_bad_type", - "--error_on_bad_override", - // clang-format on -}; - -static const char* kDartStrongModeArgs[] = { - // clang-format off - "--limit_ints_to_64_bits", - "--reify_generic_functions", - "--strong", - "--sync_async", - // 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", -}; - -static const char* kDartFuchsiaTraceArgs[] FXL_ALLOW_UNUSED_TYPE = { - "--systrace_timeline", - "--timeline_streams=VM,Isolate,Compiler,Dart,GC", -}; - -constexpr char kFileUriPrefix[] = "file://"; -constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1; - -static const uint8_t* g_default_isolate_snapshot_data = nullptr; -static const uint8_t* g_default_isolate_snapshot_instructions = nullptr; -static bool g_service_isolate_initialized = false; -static ServiceIsolateHook g_service_isolate_hook = nullptr; -static RegisterNativeServiceProtocolExtensionHook - g_register_native_service_protocol_extensions_hook = nullptr; - -// Kernel representation of core dart libraries(loaded from platform.dill). -// TODO(aam): This (and platform_data below) have to be released when engine -// gets torn down. At that point we could also call Dart_Cleanup to complete -// Dart VM cleanup. -static void* kernel_platform = nullptr; -// Bytes actually read from platform.dill that are referenced by kernel_platform -static std::vector platform_data; - -void IsolateShutdownCallback(void* callback_data) { - if (tonic::DartStickyError::IsSet()) { - tonic::DartApiScope api_scope; - FXL_LOG(ERROR) << "Isolate " << tonic::StdStringFromDart(Dart_DebugName()) - << " exited with an error"; - Dart_Handle sticky_error = Dart_GetStickyError(); - FXL_CHECK(LogIfError(sticky_error)); - } - - UIDartState* dart_state = static_cast(callback_data); - // If the isolate that's shutting down is the main one, tell the higher layers - // of the stack. - if ((dart_state != NULL) && dart_state->is_controller_state()) { - dart_state->set_shutting_down(true); - if (dart_state->isolate_client()) { - dart_state->isolate_client()->DidShutdownMainIsolate(); - } - } -} - -// The cleanup callback frees the DartState object. -void IsolateCleanupCallback(void* callback_data) { - UIDartState* dart_state = static_cast(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. - fxl::TimeDelta mtime = fxl::TimeDelta::FromSeconds(info.st_mtime + 1); - fxl::TimeDelta since = fxl::TimeDelta::FromMilliseconds(since_ms); - - return mtime > since; -} - -void ThreadExitCallback() {} - -bool IsServiceIsolateURL(const char* url_name) { - return url_name != nullptr && - std::string(url_name) == DART_VM_SERVICE_ISOLATE_NAME; -} - -static bool StringEndsWith(const std::string& string, - const std::string& ending) { - if (ending.size() > string.size()) - return false; - - return string.compare(string.size() - ending.size(), ending.size(), ending) == - 0; -} - -static void ReleaseFetchedBytes(uint8_t* buffer) { - free(buffer); -} - -Dart_Isolate ServiceIsolateCreateCallback(const char* script_uri, - Dart_IsolateFlags* flags, - char** error) { -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - // No VM-service in release mode. - return nullptr; -#else // FLUTTER_RUNTIME_MODE - UIDartState* dart_state = new UIDartState(nullptr, nullptr); - - bool is_running_from_kernel = GetKernelPlatformBinary() != nullptr; - - flags->load_vmservice_library = true; - Dart_Isolate isolate = - is_running_from_kernel - ? Dart_CreateIsolateFromKernel( - script_uri, "main", kernel_platform, flags, - static_cast(dart_state), error) - : Dart_CreateIsolate( - script_uri, "main", g_default_isolate_snapshot_data, - g_default_isolate_snapshot_instructions, flags, - static_cast(dart_state), error); - - FXL_CHECK(isolate) << error; - dart_state->set_debug_name_prefix(script_uri); - dart_state->SetIsolate(isolate); - FXL_CHECK(Dart_IsServiceIsolate(isolate)); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); - const Settings& settings = Settings::Get(); - if (settings.enable_observatory) { - std::string ip = settings.ipv6 ? "::1" : "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() && !is_running_from_kernel, - disable_websocket_origin_check, error); - FXL_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 -} - -static bool GetAssetAsBuffer( - const std::string& name, - std::vector* data, - fxl::RefPtr& directory_asset_bundle, - fxl::RefPtr& asset_store) { - return (directory_asset_bundle && - directory_asset_bundle->GetAsBuffer(name, data)) || - (asset_store && asset_store->GetAsBuffer(name, data)); -} - -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, flags, error); - } - - std::string entry_uri = script_uri; - // Are we running from a Dart source file? - const bool running_from_source = StringEndsWith(entry_uri, ".dart"); - - std::vector kernel_data; - std::vector snapshot_data; - std::string entry_path; - if (!IsRunningPrecompiledCode()) { - // Check that the entry script URI starts with file:// - if (entry_uri.find(kFileUriPrefix) != 0u) { - *error = strdup("Isolates must use file:// URIs"); - return nullptr; - } - // Entry script path (file:// is stripped). - entry_path = std::string(script_uri + strlen(kFileUriPrefix)); - if (!running_from_source) { - // Attempt to copy the snapshot from the asset bundle. - const std::string& bundle_path = entry_path; - - struct stat stat_result = {}; - if (::stat(bundle_path.c_str(), &stat_result) == 0) { - fxl::RefPtr directory_asset_bundle; - // TODO(zarah): Remove usage of zip_asset_store once app.flx is removed. - fxl::RefPtr zip_asset_store; - // bundle_path is either the path to app.flx or the flutter assets - // directory. - std::string flx_path = bundle_path; - if (S_ISDIR(stat_result.st_mode)) { - directory_asset_bundle = - fxl::MakeRefCounted(bundle_path); - flx_path = files::GetDirectoryName(bundle_path) + "/app.flx"; - } - - if (access(flx_path.c_str(), R_OK) == 0) { - zip_asset_store = fxl::MakeRefCounted( - GetUnzipperProviderForPath(flx_path)); - } - GetAssetAsBuffer(kKernelAssetKey, &kernel_data, directory_asset_bundle, - zip_asset_store); - GetAssetAsBuffer(kSnapshotAssetKey, &snapshot_data, - directory_asset_bundle, zip_asset_store); - } - } - } - - UIDartState* parent_dart_state = static_cast(callback_data); - UIDartState* dart_state = parent_dart_state->CreateForChildIsolate(); - - Dart_Isolate isolate = - kernel_platform != nullptr - ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_platform, - nullptr /* flags */, dart_state, error) - : Dart_CreateIsolate(script_uri, main, - g_default_isolate_snapshot_data, - g_default_isolate_snapshot_instructions, nullptr, - dart_state, error); - FXL_CHECK(isolate) << error; - dart_state->set_debug_name_prefix(script_uri); - dart_state->SetIsolate(isolate); - FXL_CHECK(!LogIfError( - Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))); - - { - tonic::DartApiScope dart_api_scope; - DartIO::InitForIsolate(); - DartUI::InitForIsolate(); - DartRuntimeHooks::Install(DartRuntimeHooks::SecondaryIsolate, script_uri); - - std::unique_ptr ui_class_provider( - new DartClassProvider(dart_state, "dart:ui")); - dart_state->class_library().add_provider("ui", - std::move(ui_class_provider)); - - if (!kernel_data.empty()) { - // We are running kernel code. - FXL_CHECK(!LogIfError(Dart_LoadScriptFromKernel(kernel_data.data(), - kernel_data.size()))); - } else if (!snapshot_data.empty()) { - // We are running from a script snapshot. - FXL_CHECK(!LogIfError(Dart_LoadScriptFromSnapshot(snapshot_data.data(), - snapshot_data.size()))); - } else if (running_from_source) { - // We are running from source. - // Forward the .packages configuration from the parent isolate to the - // child isolate. - tonic::FileLoader& parent_loader = parent_dart_state->file_loader(); - const std::string& packages = parent_loader.packages(); - tonic::FileLoader& loader = dart_state->file_loader(); - if (!packages.empty() && !loader.LoadPackagesMap(packages)) { - FXL_LOG(WARNING) << "Failed to load package map: " << packages; - } - // Load the script. - FXL_CHECK(!LogIfError(loader.LoadScript(entry_path))); - } - - dart_state->isolate_client()->DidCreateSecondaryIsolate(isolate); - } - - Dart_ExitIsolate(); - - FXL_CHECK(Dart_IsolateMakeRunnable(isolate)); - return isolate; -} - -Dart_Handle GetVMServiceAssetsArchiveCallback() { -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - return nullptr; -#elif OS(FUCHSIA) - std::vector observatory_assets_archive; - if (!files::ReadFileToVector("pkg/data/observatory.tar", - &observatory_assets_archive)) { - FXL_LOG(ERROR) << "Fail to load Observatory archive"; - return nullptr; - } - return tonic::DartConverter::ToDart( - observatory_assets_archive.data(), - observatory_assets_archive.size()); -#else - return tonic::DartConverter::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); - } -} - -} // namespace - -bool IsRunningPrecompiledCode() { - return Dart_IsPrecompiledRuntime(); -} - -EmbedderTracingCallbacks* g_tracing_callbacks = nullptr; - -EmbedderTracingCallbacks::EmbedderTracingCallbacks( - EmbedderTracingCallback start, - EmbedderTracingCallback stop) - : start_tracing_callback(start), stop_tracing_callback(stop) {} - -void SetEmbedderTracingCallbacks( - std::unique_ptr 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(); -} - -static std::vector 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 { - // 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", - // 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"}; - } else { - return {"--no-profiler"}; - } -} - -void SetServiceIsolateHook(ServiceIsolateHook hook) { - FXL_CHECK(!g_service_isolate_initialized); - g_service_isolate_hook = hook; -} - -void SetRegisterNativeServiceProtocolExtensionHook( - RegisterNativeServiceProtocolExtensionHook hook) { - FXL_CHECK(!g_service_isolate_initialized); - g_register_native_service_protocol_extensions_hook = hook; -} - -void PushBackAll(std::vector* 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"; -} - -void* GetKernelPlatformBinary() { - return kernel_platform; -} - -void InitDartVM(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path) { - TRACE_EVENT0("flutter", __func__); - - g_default_isolate_snapshot_data = default_isolate_snapshot_data; - g_default_isolate_snapshot_instructions = - default_isolate_snapshot_instructions; - - 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()); - } - } - - std::vector 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 (const auto& profiler_flag : - ProfilingFlags(settings.enable_dart_profiling)) { - args.push_back(profiler_flag); - } - - PushBackAll(&args, kDartLanguageArgs, arraysize(kDartLanguageArgs)); - - if (IsRunningPrecompiledCode()) { - PushBackAll(&args, kDartPrecompilationArgs, - arraysize(kDartPrecompilationArgs)); - } - -#if defined(OS_FUCHSIA) -#if defined(NDEBUG) - // Do not enable checked mode for Fuchsia release builds - // TODO(mikejurka): remove this once precompiled code is working on Fuchsia - const bool use_checked_mode = false; -#else // !defined(NDEBUG) - const bool use_checked_mode = true; -#endif // !defined(NDEBUG) -#else // !defined(OS_FUCHSIA) - // Enable checked mode if we are not running precompiled code. We run non- - // precompiled code only in the debug product mode. - const bool use_checked_mode = - !IsRunningPrecompiledCode() && !settings.dart_non_checked_mode; -#endif // !defined(OS_FUCHSIA) - -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG - // Debug mode uses the JIT, disable code page write protection to avoid - // memory page protection changes before and after every compilation. - PushBackAll(&args, kDartWriteProtectCodeArgs, - arraysize(kDartWriteProtectCodeArgs)); -#endif - - 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)); - } - -#if defined(OS_FUCHSIA) - PushBackAll(&args, kDartFuchsiaTraceArgs, arraysize(kDartFuchsiaTraceArgs)); -#endif - - if (!bundle_path.empty()) { - fxl::RefPtr directory_asset_bundle = - fxl::MakeRefCounted( - std::move(bundle_path)); - directory_asset_bundle->GetAsBuffer(kPlatformKernelAssetKey, - &platform_data); - if (!platform_data.empty()) { - uint8_t* kernel_buf = static_cast(malloc(platform_data.size())); - memcpy(kernel_buf, platform_data.data(), platform_data.size()); - kernel_platform = Dart_ReadKernelBinary(kernel_buf, platform_data.size(), - ReleaseFetchedBytes); - FXL_DCHECK(kernel_platform != nullptr); - } - } - if ((kernel_platform != nullptr) || - Dart_IsDart2Snapshot(g_default_isolate_snapshot_data)) { - // The presence of the kernel platform file or a snapshot that was generated - // for Dart2 indicates we are running in preview-dart-2 mode and in this - // mode enable strong mode options by default. - // Note: When we start using core snapshots instead of the platform file - // in the engine just sniffing the snapshot file should be sufficient. - PushBackAll(&args, kDartStrongModeArgs, arraysize(kDartStrongModeArgs)); - // In addition if we are running in debug mode we also enable asserts. - if (use_checked_mode) { - PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); - } - } else if (use_checked_mode) { - // In non preview-dart-2 mode we enable checked mode and asserts if - // we are running in debug mode. - PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); - PushBackAll(&args, kDartCheckedModeArgs, arraysize(kDartCheckedModeArgs)); - } - - for (size_t i = 0; i < settings.dart_flags.size(); i++) - args.push_back(settings.dart_flags[i].c_str()); - - FXL_CHECK(Dart_SetVMFlags(args.size(), args.data())); - - DartUI::InitForGlobal(); - - // 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_snapshot_data = vm_snapshot_data; - params.vm_snapshot_instructions = vm_snapshot_instructions; - params.create = IsolateCreateCallback; - params.shutdown = IsolateShutdownCallback; - params.cleanup = IsolateCleanupCallback; - params.thread_exit = ThreadExitCallback; - params.get_service_assets = GetVMServiceAssetsArchiveCallback; - params.entropy_source = DartIO::EntropySource; - char* init_error = Dart_Initialize(¶ms); - if (init_error != nullptr) - FXL_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); - - Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); -} - -} // namespace blink diff --git a/runtime/dart_init.h b/runtime/dart_init.h deleted file mode 100644 index 99c8fe89137e3..0000000000000 --- a/runtime/dart_init.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -#ifndef FLUTTER_RUNTIME_DART_INIT_H_ -#define FLUTTER_RUNTIME_DART_INIT_H_ - -#include "lib/fxl/build_config.h" -#include "lib/fxl/functional/closure.h" -#include "third_party/dart/runtime/include/dart_api.h" - -#include -#include -#include - -namespace blink { - -// Name of the kernel blob asset within the asset directory. -extern const char kKernelAssetKey[]; - -// Name of the snapshot blob asset within the asset directory. -extern const char kSnapshotAssetKey[]; - -// Name of the platform kernel blob asset within the asset directory. -extern const char kPlatformKernelAssetKey[]; - -bool IsRunningPrecompiledCode(); - -using EmbedderTracingCallback = fxl::Closure; - -typedef void (*ServiceIsolateHook)(bool); -typedef void (*RegisterNativeServiceProtocolExtensionHook)(bool); - -struct EmbedderTracingCallbacks { - EmbedderTracingCallback start_tracing_callback; - EmbedderTracingCallback stop_tracing_callback; - - EmbedderTracingCallbacks(EmbedderTracingCallback start, - EmbedderTracingCallback stop); -}; - -void InitDartVM(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path); - -void* GetKernelPlatformBinary(); - -void SetEmbedderTracingCallbacks( - std::unique_ptr callbacks); - -// Provide a function that will be called during initialization of the -// service isolate. -void SetServiceIsolateHook(ServiceIsolateHook hook); - -// Provide a function that will be called to register native service protocol -// extensions. -void SetRegisterNativeServiceProtocolExtensionHook( - RegisterNativeServiceProtocolExtensionHook hook); - -} // namespace blink - -#endif // FLUTTER_RUNTIME_DART_INIT_H_ diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc new file mode 100644 index 0000000000000..dafb0f805be8e --- /dev/null +++ b/runtime/dart_isolate.cc @@ -0,0 +1,711 @@ +// Copyright 2017 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_isolate.h" + +#include +#include + +#include "flutter/fml/trace_event.h" +#include "flutter/lib/io/dart_io.h" +#include "flutter/lib/ui/dart_runtime_hooks.h" +#include "flutter/lib/ui/dart_ui.h" +#include "flutter/runtime/dart_service_isolate.h" +#include "flutter/runtime/dart_vm.h" +#include "lib/fxl/files/path.h" +#include "lib/tonic/converter/dart_converter.h" +#include "lib/tonic/dart_class_library.h" +#include "lib/tonic/dart_class_provider.h" +#include "lib/tonic/dart_message_handler.h" +#include "lib/tonic/dart_state.h" +#include "lib/tonic/dart_sticky_error.h" +#include "lib/tonic/file_loader/file_loader.h" +#include "lib/tonic/scopes/dart_api_scope.h" +#include "lib/tonic/scopes/dart_isolate_scope.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" + +namespace blink { + +fml::WeakPtr DartIsolate::CreateRootIsolate( + const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr window, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint, + Dart_IsolateFlags* flags) { + TRACE_EVENT0("flutter", "DartIsolate::CreateRootIsolate"); + Dart_Isolate vm_isolate = nullptr; + fml::WeakPtr embedder_isolate; + + char* error = nullptr; + + // Since this is the root isolate, we fake a parent embedder data object. We + // cannot use unique_ptr here because the destructor is private (since the + // isolate lifecycle is entirely managed by the VM). + auto root_embedder_data = std::make_unique( + vm, // VM + std::move(isolate_snapshot), // isolate snapshot + task_runners, // task runners + std::move(resource_context), // resource context + std::move(unref_queue), // skia unref queue + advisory_script_uri, // advisory URI + advisory_script_entrypoint // advisory entrypoint + ); + + std::tie(vm_isolate, embedder_isolate) = CreateDartVMAndEmbedderObjectPair( + advisory_script_uri.c_str(), // advisory script URI + advisory_script_entrypoint.c_str(), // advisory script entrypoint + nullptr, // package root + nullptr, // package config + flags, // flags + root_embedder_data.get(), // parent embedder data + true, // is root isolate + &error // error (out) + ); + + if (error != nullptr) { + free(error); + } + + if (vm_isolate == nullptr) { + return {}; + } + + if (embedder_isolate) { + // Only root isolates can interact with windows. + embedder_isolate->SetWindow(std::move(window)); + embedder_isolate->set_use_blink(vm->GetSettings().using_blink); + } + + return embedder_isolate; +} + +DartIsolate::DartIsolate(const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint) + : UIDartState(std::move(task_runners), + vm->GetSettings().task_observer_add, + vm->GetSettings().task_observer_remove, + std::move(resource_context), + std::move(unref_queue), + advisory_script_uri, + advisory_script_entrypoint, + vm->GetSettings().log_tag), + vm_(vm), + isolate_snapshot_(std::move(isolate_snapshot)), + weak_factory_(this) { + FXL_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; + weak_prototype_ = weak_factory_.GetWeakPtr(); + + if (vm_ == nullptr) { + return; + } + + phase_ = Phase::Uninitialized; +} + +DartIsolate::~DartIsolate() = default; + +DartIsolate::Phase DartIsolate::GetPhase() const { + return phase_; +} + +const DartVM* DartIsolate::GetDartVM() const { + return vm_; +} + +bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { + TRACE_EVENT0("flutter", "DartIsolate::Initialize"); + if (phase_ != Phase::Uninitialized) { + return false; + } + + if (dart_isolate == nullptr) { + return false; + } + + if (Dart_CurrentIsolate() != dart_isolate) { + return false; + } + + if (Dart_IsolateData(dart_isolate) != this) { + return false; + } + + // After this point, isolate scopes can be safely used. + SetIsolate(dart_isolate); + + // We are entering a new scope (for the first time since initialization) and + // we want to restore the current scope to null when we exit out of this + // method. This balances the implicit Dart_EnterIsolate call made by + // Dart_CreateIsolate (which calls the Initialize). + Dart_ExitIsolate(); + + tonic::DartIsolateScope scope(isolate()); + + if (is_root_isolate) { + if (auto task_runner = GetTaskRunners().GetUITaskRunner()) { + // Isolates may not have any particular thread affinity. Only initialize + // the message handler if a task runner is explicitly specified. + message_handler().Initialize(task_runner); + } + } + + if (tonic::LogIfError( + Dart_SetLibraryTagHandler(tonic::DartState::HandleLibraryTag))) { + return false; + } + + if (!UpdateThreadPoolNames()) { + return false; + } + + phase_ = Phase::Initialized; + return true; +} + +// Updating thread names here does not change the underlying OS thread names. +// Instead, this is just additional metadata for the Observatory to show the +// thread name of the isolate. +bool DartIsolate::UpdateThreadPoolNames() const { + // TODO(chinmaygarde): This implementation does not account for multiple + // shells sharing the same (or subset of) threads. + const auto& task_runners = GetTaskRunners(); + + if (auto task_runner = task_runners.GetGPUTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".gpu"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetUITaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".ui"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetIOTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".io"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + if (auto task_runner = task_runners.GetPlatformTaskRunner()) { + task_runner->PostTask( + [label = task_runners.GetLabel() + std::string{".platform"}]() { + Dart_SetThreadName(label.c_str()); + }); + } + + return true; +} + +bool DartIsolate::LoadLibraries() { + TRACE_EVENT0("flutter", "DartIsolate::LoadLibraries"); + if (phase_ != Phase::Initialized) { + return false; + } + + tonic::DartState::Scope scope(this); + + DartIO::InitForIsolate(); + + DartUI::InitForIsolate(); + + const bool is_service_isolate = Dart_IsServiceIsolate(isolate()); + + DartRuntimeHooks::Install(is_service_isolate + ? DartRuntimeHooks::SecondaryIsolate + : DartRuntimeHooks::MainIsolate, + GetAdvisoryScriptURI()); + + if (!is_service_isolate) { + class_library().add_provider( + "ui", std::make_unique(this, "dart:ui")); + } + + phase_ = Phase::LibrariesSetup; + return true; +} + +bool DartIsolate::PrepareForRunningFromPrecompiledCode() { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromPrecompiledCode"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (!DartVM::IsRunningPrecompiledCode()) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +static bool LoadScriptSnapshot(std::unique_ptr mapping) { + if (tonic::LogIfError(Dart_LoadScriptFromSnapshot(mapping->GetMapping(), + mapping->GetSize()))) { + return false; + } + return true; +} + +static bool LoadKernelSnapshot(std::unique_ptr mapping) { + if (tonic::LogIfError(Dart_LoadScriptFromKernel(mapping->GetMapping(), + mapping->GetSize()))) { + return false; + } + + return true; +} + +static bool LoadSnapshot(std::unique_ptr mapping, + bool is_kernel) { + if (is_kernel) { + return LoadKernelSnapshot(std::move(mapping)); + } else { + return LoadScriptSnapshot(std::move(mapping)); + } + return false; +} + +FXL_WARN_UNUSED_RESULT +bool DartIsolate::PrepareForRunningFromSnapshot( + std::unique_ptr mapping) { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromSnapshot"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (DartVM::IsRunningPrecompiledCode()) { + return false; + } + + if (!mapping || mapping->GetSize() == 0) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (!Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!LoadSnapshot(std::move(mapping), vm_->GetPlatformKernel() != nullptr)) { + return false; + } + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +static bool FileNameIsDill(const std::string& name) { + const std::string suffix = ".dill"; + + if (name.size() < suffix.size()) { + return false; + } + + if (name.rfind(suffix, name.size()) == name.size() - suffix.size()) { + return true; + } + return false; +} + +bool DartIsolate::PrepareForRunningFromSource( + const std::string& main_source_file, + const std::string& packages) { + TRACE_EVENT0("flutter", "DartIsolate::PrepareForRunningFromSource"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + if (DartVM::IsRunningPrecompiledCode()) { + return false; + } + + if (main_source_file.empty()) { + return false; + } + + tonic::DartState::Scope scope(this); + + if (!Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + auto& loader = file_loader(); + + if (!packages.empty()) { + auto packages_absolute_path = files::AbsolutePath(packages); + FXL_DLOG(INFO) << "Loading from packages: " << packages_absolute_path; + if (!loader.LoadPackagesMap(packages_absolute_path)) { + return false; + } + } + + auto main_source_absolute_path = files::AbsolutePath(main_source_file); + FXL_DLOG(INFO) << "Loading from source: " << main_source_absolute_path; + + // The "source" file may be a ".dill" file. + if (FileNameIsDill(main_source_absolute_path)) { + auto mapping = + std::make_unique(main_source_absolute_path, false); + if (mapping == nullptr) { + return false; + } + if (!LoadKernelSnapshot(std::move(mapping))) { + return false; + } + } else { + if (tonic::LogIfError(loader.LoadScript(main_source_absolute_path))) { + return false; + } + } + + if (Dart_IsNull(Dart_RootLibrary())) { + return false; + } + + if (!MarkIsolateRunnable()) { + return false; + } + + phase_ = Phase::Ready; + return true; +} + +bool DartIsolate::MarkIsolateRunnable() { + TRACE_EVENT0("flutter", "DartIsolate::MarkIsolateRunnable"); + if (phase_ != Phase::LibrariesSetup) { + return false; + } + + // This function may only be called from an active isolate scope. + if (Dart_CurrentIsolate() != isolate()) { + return false; + } + + // There must be no current isolate to mark an isolate as being runnable. + Dart_ExitIsolate(); + + if (!Dart_IsolateMakeRunnable(isolate())) { + // Failed. Restore the isolate. + Dart_EnterIsolate(isolate()); + return false; + } + // Success. Restore the isolate. + Dart_EnterIsolate(isolate()); + return true; +} + +FXL_WARN_UNUSED_RESULT +bool DartIsolate::Run(const std::string& entrypoint_name) { + TRACE_EVENT0("flutter", "DartIsolate::Run"); + if (phase_ != Phase::Ready) { + return false; + } + + tonic::DartState::Scope scope(this); + + Dart_Handle entrypoint = Dart_GetClosure( + Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str())); + if (tonic::LogIfError(entrypoint)) { + return false; + } + + Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); + if (tonic::LogIfError(isolate_lib)) { + return false; + } + + Dart_Handle isolate_args[] = { + entrypoint, + Dart_Null(), + }; + + if (tonic::LogIfError(Dart_Invoke( + isolate_lib, tonic::ToDart("_startMainIsolate"), + sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) { + return false; + } + + phase_ = Phase::Running; + return true; +} + +bool DartIsolate::Shutdown() { + TRACE_EVENT0("flutter", "DartIsolate::Shutdown"); + // This call may be re-entrant since Dart_ShutdownIsolate can invoke the + // cleanup callback which deletes the embedder side object of the dart isolate + // (a.k.a. this). + if (phase_ == Phase::Shutdown) { + return false; + } + phase_ = Phase::Shutdown; + Dart_Isolate vm_isolate = isolate(); + // The isolate can be nullptr if this instance is the stub isolate data used + // during root isolate creation. + if (vm_isolate != nullptr) { + // We need to enter the isolate because Dart_ShutdownIsolate does not take + // the isolate to shutdown as a parameter. + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + Dart_EnterIsolate(vm_isolate); + Dart_ShutdownIsolate(); + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + } + return true; +} + +static Dart_Isolate DartCreateAndStartServiceIsolate( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + char** error) { + auto vm = DartVM::ForProcessIfInitialized(); + + if (!vm) { + *error = strdup( + "Could not resolve the VM when attempting to create the service " + "isolate."); + return nullptr; + } + + const auto& settings = vm->GetSettings(); + + if (!settings.enable_observatory) { + FXL_DLOG(INFO) << "Observatory is disabled."; + return nullptr; + } + + blink::TaskRunners null_task_runners( + "io.flutter." DART_VM_SERVICE_ISOLATE_NAME, nullptr, nullptr, nullptr, + nullptr); + + flags->load_vmservice_library = true; + + auto service_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + null_task_runners, // task runners + nullptr, // window + {}, // resource context + {}, // unref queue + advisory_script_uri == nullptr ? "" : advisory_script_uri, // script uri + advisory_script_entrypoint == nullptr + ? "" + : advisory_script_entrypoint, // script entrypoint + flags // flags + ); + + if (!service_isolate) { + *error = strdup("Could not create the service isolate."); + FXL_DLOG(ERROR) << *error; + return nullptr; + } + + // The engine never holds a strong reference to the VM service isolate. Since + // we are about to lose our last weak reference to it, start the VM service + // while we have this reference. + + const bool running_from_sources = + !DartVM::IsRunningPrecompiledCode() && vm->GetPlatformKernel() == nullptr; + + tonic::DartState::Scope scope(service_isolate.get()); + if (!DartServiceIsolate::Startup( + settings.ipv6 ? "::1" : "127.0.0.1", // server IP address + settings.observatory_port, // server observatory port + tonic::DartState::HandleLibraryTag, // embedder library tag handler + running_from_sources, // running from source code + false, // disable websocket origin check + error // error (out) + )) { + // Error is populated by call to startup. + FXL_DLOG(ERROR) << *error; + return nullptr; + } + + vm->GetServiceProtocol().ToggleHooks(true); + + return service_isolate->isolate(); +} + +// |Dart_IsolateCreateCallback| +Dart_Isolate DartIsolate::DartIsolateCreateCallback( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + char** error) { + if (parent_embedder_isolate == nullptr && + strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) { + // The VM attempts to start the VM service for us on |Dart_Initialize|. In + // such a case, the callback data will be null and the script URI will be + // DART_VM_SERVICE_ISOLATE_NAME. In such cases, we just create the service + // isolate like normal but dont hold a reference to it at all. We also start + // this isolate since we will never again reference it from the engine. + return DartCreateAndStartServiceIsolate(advisory_script_uri, // + advisory_script_entrypoint, // + package_root, // + package_config, // + flags, // + error // + ); + } + + return CreateDartVMAndEmbedderObjectPair( + advisory_script_uri, // URI + advisory_script_entrypoint, // entrypoint + package_root, // package root + package_config, // package config + flags, // isolate flags + parent_embedder_isolate, // embedder data + false, // is root isolate + error // error + ) + .first; +} + +std::pair> +DartIsolate::CreateDartVMAndEmbedderObjectPair( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + bool is_root_isolate, + char** error) { + TRACE_EVENT0("flutter", "DartIsolate::CreateDartVMAndEmbedderObjectPair"); + if (parent_embedder_isolate == nullptr || + parent_embedder_isolate->GetDartVM() == nullptr) { + *error = + strdup("Parent isolate did not have embedder specific callback data."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + const DartVM* vm = parent_embedder_isolate->GetDartVM(); + + // Create the native object on the embedder side. This object is deleted in + // the cleanup callback. + auto embedder_isolate = std::make_unique( + vm, // + parent_embedder_isolate->GetIsolateSnapshot(), // + parent_embedder_isolate->GetTaskRunners(), // + parent_embedder_isolate->GetResourceContext(), // + parent_embedder_isolate->GetSkiaUnrefQueue(), // + advisory_script_uri, // + advisory_script_entrypoint // + ); + + // Create the Dart VM isolate and give it the embedder object as the baton. + Dart_Isolate isolate = + vm->GetPlatformKernel() != nullptr + ? Dart_CreateIsolateFromKernel(advisory_script_uri, // + advisory_script_entrypoint, // + vm->GetPlatformKernel(), // + flags, // + embedder_isolate.get(), // + error // + ) + : Dart_CreateIsolate(advisory_script_uri, // + advisory_script_entrypoint, // + embedder_isolate->GetIsolateSnapshot() + ->GetData() + ->GetSnapshotPointer(), // + embedder_isolate->GetIsolateSnapshot() + ->GetInstructionsIfPresent(), // + flags, // + embedder_isolate.get(), // + error // + ); + + if (isolate == nullptr) { + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + if (!embedder_isolate->Initialize(isolate, is_root_isolate)) { + *error = strdup("Embedder could not initialize the Dart isolate."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + if (!embedder_isolate->LoadLibraries()) { + *error = + strdup("Embedder could not load libraries in the new Dart isolate."); + FXL_DLOG(ERROR) << *error; + return {nullptr, {}}; + } + + // The ownership of the embedder object is controlled by the Dart VM. So the + // only reference returned to the caller is weak. + return {isolate, embedder_isolate.release()->GetWeakIsolatePtr()}; +} + +// |Dart_IsolateShutdownCallback| +void DartIsolate::DartIsolateShutdownCallback(DartIsolate* embedder_isolate) { + if (!tonic::DartStickyError::IsSet()) { + return; + } + + tonic::DartApiScope api_scope; + FXL_LOG(ERROR) << "Isolate " << tonic::StdStringFromDart(Dart_DebugName()) + << " exited with an error"; + Dart_Handle sticky_error = Dart_GetStickyError(); + FXL_CHECK(tonic::LogIfError(sticky_error)); +} + +// |Dart_IsolateCleanupCallback| +void DartIsolate::DartIsolateCleanupCallback(DartIsolate* embedder_isolate) { + delete embedder_isolate; +} + +fxl::RefPtr DartIsolate::GetIsolateSnapshot() const { + return isolate_snapshot_; +} + +fml::WeakPtr DartIsolate::GetWeakIsolatePtr() const { + return weak_prototype_; +} + +void DartIsolate::AddIsolateShutdownCallback(fxl::Closure closure) { + shutdown_callbacks_.emplace_back( + std::make_unique(std::move(closure))); +} + +} // namespace blink diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h new file mode 100644 index 0000000000000..13d573ebe333f --- /dev/null +++ b/runtime/dart_isolate.h @@ -0,0 +1,152 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_ISOLATE_H_ +#define FLUTTER_RUNTIME_DART_ISOLATE_H_ + +#include +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/mapping.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/dart_snapshot.h" +#include "lib/fxl/compiler_specific.h" +#include "lib/fxl/macros.h" +#include "lib/tonic/dart_state.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace blink { +class DartVM; + +class DartIsolate : public UIDartState { + public: + enum class Phase { + Unknown, + Uninitialized, + Initialized, + LibrariesSetup, + Ready, + Running, + Shutdown, + }; + + // The root isolate of a Flutter application is special because it gets Window + // bindings. From the VM's perspective, this isolate is not special in any + // way. + static fml::WeakPtr CreateRootIsolate( + const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + std::unique_ptr window, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri = "main.dart", + std::string advisory_script_entrypoint = "main", + Dart_IsolateFlags* flags = nullptr); + + DartIsolate(const DartVM* vm, + fxl::RefPtr isolate_snapshot, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + std::string advisory_script_uri, + std::string advisory_script_entrypoint); + + ~DartIsolate() override; + + Phase GetPhase() const; + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromPrecompiledCode(); + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromSnapshot(std::unique_ptr snapshot); + + FXL_WARN_UNUSED_RESULT + bool PrepareForRunningFromSource(const std::string& main_source_file, + const std::string& packages); + + FXL_WARN_UNUSED_RESULT + bool Run(const std::string& entrypoint); + + FXL_WARN_UNUSED_RESULT + bool Shutdown(); + + void AddIsolateShutdownCallback(fxl::Closure closure); + + const DartVM* GetDartVM() const; + + fxl::RefPtr GetIsolateSnapshot() const; + + fml::WeakPtr GetWeakIsolatePtr() const; + + private: + class AutoFireClosure { + public: + AutoFireClosure(fxl::Closure closure) : closure_(std::move(closure)) {} + ~AutoFireClosure() { + if (closure_) { + closure_(); + } + } + + private: + fxl::Closure closure_; + FXL_DISALLOW_COPY_AND_ASSIGN(AutoFireClosure); + }; + friend class DartVM; + + const DartVM* vm_ = nullptr; + Phase phase_ = Phase::Unknown; + const fxl::RefPtr isolate_snapshot_; + std::vector> shutdown_callbacks_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + FXL_WARN_UNUSED_RESULT + bool Initialize(Dart_Isolate isolate, bool is_root_isolate); + + FXL_WARN_UNUSED_RESULT + bool LoadLibraries(); + + bool UpdateThreadPoolNames() const; + + FXL_WARN_UNUSED_RESULT + bool MarkIsolateRunnable(); + + // |Dart_IsolateCreateCallback| + static Dart_Isolate DartIsolateCreateCallback( + const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* embedder_isolate, + char** error); + + static std::pair /* embedder */> + CreateDartVMAndEmbedderObjectPair(const char* advisory_script_uri, + const char* advisory_script_entrypoint, + const char* package_root, + const char* package_config, + Dart_IsolateFlags* flags, + DartIsolate* parent_embedder_isolate, + bool is_root_isolate, + char** error); + + // |Dart_IsolateShutdownCallback| + static void DartIsolateShutdownCallback(DartIsolate* embedder_isolate); + + // |Dart_IsolateCleanupCallback| + static void DartIsolateCleanupCallback(DartIsolate* embedder_isolate); + + FXL_DISALLOW_COPY_AND_ASSIGN(DartIsolate); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_ISOLATE_H_ diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc new file mode 100644 index 0000000000000..d8f2e02382da1 --- /dev/null +++ b/runtime/dart_isolate_unittests.cc @@ -0,0 +1,103 @@ +// Copyright 2017 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/fml/thread.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/testing/testing.h" +#include "flutter/testing/thread_test.h" + +#define CURRENT_TEST_NAME \ + std::string { \ + ::testing::UnitTest::GetInstance()->current_test_info()->name() \ + } + +namespace blink { + +using DartIsolateTest = ::testing::ThreadTest; + +TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +TEST_F(DartIsolateTest, IsolateCanAssociateSnapshot) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->PrepareForRunningFromSource( + testing::GetFixturesPath() + std::string{"/simple_main.dart"}, "")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Ready); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +TEST_F(DartIsolateTest, CanResolveAndInvokeMethod) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + TaskRunners task_runners(CURRENT_TEST_NAME, // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // + ); + auto root_isolate = DartIsolate::CreateRootIsolate( + vm.get(), // vm + vm->GetIsolateSnapshot(), // isolate snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // resource context + nullptr // unref qeueue + ); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->PrepareForRunningFromSource( + testing::GetFixturesPath() + std::string{"/simple_main.dart"}, "")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Ready); + ASSERT_TRUE(root_isolate->Run("simple_main")); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running); + ASSERT_TRUE(root_isolate->Shutdown()); +} + +} // namespace blink diff --git a/runtime/dart_service_isolate.h b/runtime/dart_service_isolate.h index 9edd663feb6bd..59a02e2e9d495 100644 --- a/runtime/dart_service_isolate.h +++ b/runtime/dart_service_isolate.h @@ -13,8 +13,6 @@ namespace blink { class DartServiceIsolate { public: - static bool Bootstrap(); - static bool Startup(std::string server_ip, intptr_t server_port, Dart_LibraryTagHandler embedder_tag_handler, diff --git a/runtime/dart_snapshot.cc b/runtime/dart_snapshot.cc new file mode 100644 index 0000000000000..a41c2e4b83cc6 --- /dev/null +++ b/runtime/dart_snapshot.cc @@ -0,0 +1,159 @@ +// Copyright 2017 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_snapshot.h" + +#include + +#include "flutter/fml/native_library.h" +#include "flutter/fml/paths.h" +#include "flutter/fml/trace_event.h" +#include "flutter/runtime/dart_snapshot_buffer.h" +#include "flutter/runtime/dart_vm.h" + +namespace blink { + +static const char* kVMDataSymbol = "kDartVmSnapshotData"; +static const char* kVMInstructionsSymbol = "kDartVmSnapshotInstructions"; +static const char* kIsolateDataSymbol = "kDartIsolateSnapshotData"; +static const char* kIsolateInstructionsSymbol = + "kDartIsolateSnapshotInstructions"; + +static std::unique_ptr ResolveVMData( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = fml::paths::JoinPaths( + {settings.aot_snapshot_path, settings.aot_vm_snapshot_data_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), false /* executable */)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary(loaded_process, + kVMDataSymbol); +} + +static std::unique_ptr ResolveVMInstructions( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = fml::paths::JoinPaths( + {settings.aot_snapshot_path, settings.aot_vm_snapshot_instr_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), true /* executable */)) { + return source; + } + } + + if (settings.application_library_path.size() > 0) { + auto library = + fml::NativeLibrary::Create(settings.application_library_path.c_str()); + if (auto source = DartSnapshotBuffer::CreateWithSymbolInLibrary( + library, kVMInstructionsSymbol)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary(loaded_process, + kVMInstructionsSymbol); +} + +static std::unique_ptr ResolveIsolateData( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = + fml::paths::JoinPaths({settings.aot_snapshot_path, + settings.aot_isolate_snapshot_data_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), false /* executable */)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary(loaded_process, + kIsolateDataSymbol); +} + +static std::unique_ptr ResolveIsolateInstructions( + const Settings& settings) { + if (settings.aot_snapshot_path.size() > 0) { + auto path = + fml::paths::JoinPaths({settings.aot_snapshot_path, + settings.aot_isolate_snapshot_instr_filename}); + if (auto source = DartSnapshotBuffer::CreateWithContentsOfFile( + path.c_str(), true /* executable */)) { + return source; + } + } + + if (settings.application_library_path.size() > 0) { + auto library = + fml::NativeLibrary::Create(settings.application_library_path.c_str()); + if (auto source = DartSnapshotBuffer::CreateWithSymbolInLibrary( + library, kIsolateInstructionsSymbol)) { + return source; + } + } + + auto loaded_process = fml::NativeLibrary::CreateForCurrentProcess(); + return DartSnapshotBuffer::CreateWithSymbolInLibrary( + loaded_process, kIsolateInstructionsSymbol); +} + +fxl::RefPtr DartSnapshot::VMSnapshotFromSettings( + const Settings& settings) { + TRACE_EVENT0("flutter", "DartSnapshot::VMSnapshotFromSettings"); + auto snapshot = + fxl::MakeRefCounted(ResolveVMData(settings), // + ResolveVMInstructions(settings) // + ); + if (snapshot->IsValid()) { + return snapshot; + } + return nullptr; +} + +fxl::RefPtr DartSnapshot::IsolateSnapshotFromSettings( + const Settings& settings) { + TRACE_EVENT0("flutter", "DartSnapshot::IsolateSnapshotFromSettings"); + auto snapshot = + fxl::MakeRefCounted(ResolveIsolateData(settings), // + ResolveIsolateInstructions(settings) // + ); + if (snapshot->IsValid()) { + return snapshot; + } + return nullptr; +} + +DartSnapshot::DartSnapshot(std::unique_ptr data, + std::unique_ptr instructions) + : data_(std::move(data)), instructions_(std::move(instructions)) {} + +DartSnapshot::~DartSnapshot() = default; + +bool DartSnapshot::IsValid() const { + return static_cast(data_); +} + +bool DartSnapshot::IsValidForAOT() const { + return data_ && instructions_; +} + +const DartSnapshotBuffer* DartSnapshot::GetData() const { + return data_.get(); +} + +const DartSnapshotBuffer* DartSnapshot::GetInstructions() const { + return instructions_.get(); +} + +const uint8_t* DartSnapshot::GetInstructionsIfPresent() const { + return instructions_ ? instructions_->GetSnapshotPointer() : nullptr; +} + +} // namespace blink diff --git a/runtime/dart_snapshot.h b/runtime/dart_snapshot.h new file mode 100644 index 0000000000000..dd97e25722ff1 --- /dev/null +++ b/runtime/dart_snapshot.h @@ -0,0 +1,52 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_H_ +#define FLUTTER_RUNTIME_DART_SNAPSHOT_H_ + +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/runtime/dart_snapshot_buffer.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" + +namespace blink { + +class DartSnapshot : public fxl::RefCountedThreadSafe { + public: + static fxl::RefPtr VMSnapshotFromSettings( + const Settings& settings); + + static fxl::RefPtr IsolateSnapshotFromSettings( + const Settings& settings); + + bool IsValid() const; + + bool IsValidForAOT() const; + + const DartSnapshotBuffer* GetData() const; + + const DartSnapshotBuffer* GetInstructions() const; + + const uint8_t* GetInstructionsIfPresent() const; + + private: + std::unique_ptr data_; + std::unique_ptr instructions_; + + DartSnapshot(std::unique_ptr data, + std::unique_ptr instructions); + + ~DartSnapshot(); + + FRIEND_REF_COUNTED_THREAD_SAFE(DartSnapshot); + FRIEND_MAKE_REF_COUNTED(DartSnapshot); + FXL_DISALLOW_COPY_AND_ASSIGN(DartSnapshot); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_SNAPSHOT_H_ diff --git a/runtime/dart_snapshot_buffer.cc b/runtime/dart_snapshot_buffer.cc new file mode 100644 index 0000000000000..c39233ac8334a --- /dev/null +++ b/runtime/dart_snapshot_buffer.cc @@ -0,0 +1,72 @@ +// Copyright 2017 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_snapshot_buffer.h" + +#include + +#include "flutter/fml/mapping.h" + +namespace blink { + +class NativeLibrarySnapshotBuffer final : public DartSnapshotBuffer { + public: + NativeLibrarySnapshotBuffer(fxl::RefPtr library, + const char* symbol_name) + : library_(std::move(library)) { + if (library_) { + symbol_ = library_->ResolveSymbol(symbol_name); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return 0; } + + private: + fxl::RefPtr library_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(NativeLibrarySnapshotBuffer); +}; + +class FileSnapshotBuffer final : public DartSnapshotBuffer { + public: + FileSnapshotBuffer(const char* path, bool executable) + : mapping_(path, executable) { + if (mapping_.GetSize() > 0) { + symbol_ = mapping_.GetMapping(); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return mapping_.GetSize(); } + + private: + fml::FileMapping mapping_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(FileSnapshotBuffer); +}; + +std::unique_ptr +DartSnapshotBuffer::CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name) { + auto source = std::make_unique( + std::move(library), symbol_name); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +std::unique_ptr +DartSnapshotBuffer::CreateWithContentsOfFile(const char* file_path, + bool executable) { + auto source = std::make_unique(file_path, executable); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +DartSnapshotBuffer::~DartSnapshotBuffer() = default; + +} // namespace blink diff --git a/runtime/dart_snapshot_buffer.h b/runtime/dart_snapshot_buffer.h new file mode 100644 index 0000000000000..675946c0ca63f --- /dev/null +++ b/runtime/dart_snapshot_buffer.h @@ -0,0 +1,34 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ +#define FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ + +#include + +#include "flutter/fml/native_library.h" +#include "lib/fxl/macros.h" + +namespace blink { + +class DartSnapshotBuffer { + public: + static std::unique_ptr CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name); + + static std::unique_ptr CreateWithContentsOfFile( + const char* file_path, + bool executable); + + virtual ~DartSnapshotBuffer(); + + virtual const uint8_t* GetSnapshotPointer() const = 0; + + virtual size_t GetSnapshotSize() const = 0; +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_SNAPSHOT_BUFFER_H_ diff --git a/runtime/dart_snapshot_source.cc b/runtime/dart_snapshot_source.cc new file mode 100644 index 0000000000000..9c3fb7d65cf0f --- /dev/null +++ b/runtime/dart_snapshot_source.cc @@ -0,0 +1,72 @@ +// Copyright 2017 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_snapshot_source.h" + +#include + +#include "flutter/fml/mapping.h" + +namespace blink { + +class NativeLibrarySnapshotSource final : public DartSnapshotSource { + public: + NativeLibrarySnapshotSource(fxl::RefPtr library, + const char* symbol_name) + : library_(std::move(library)) { + if (library_) { + symbol_ = library_->ResolveSymbol(symbol_name); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return 0; } + + private: + fxl::RefPtr library_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(NativeLibrarySnapshotSource); +}; + +class FileSnapshotSource final : public DartSnapshotSource { + public: + FileSnapshotSource(const char* path, bool executable) + : mapping_(path, executable) { + if (mapping_.GetSize() > 0) { + symbol_ = mapping_.GetMapping(); + } + } + + const uint8_t* GetSnapshotPointer() const override { return symbol_; } + + size_t GetSnapshotSize() const override { return mapping_.GetSize(); } + + private: + fml::FileMapping mapping_; + const uint8_t* symbol_ = nullptr; + + FXL_DISALLOW_COPY_AND_ASSIGN(FileSnapshotSource); +}; + +std::unique_ptr +DartSnapshotSource::CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name) { + auto source = std::make_unique( + std::move(library), symbol_name); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +std::unique_ptr +DartSnapshotSource::CreateWithContentsOfFile(const char* file_path, + bool executable) { + auto source = std::make_unique(file_path, executable); + return source->GetSnapshotPointer() == nullptr ? nullptr : std::move(source); +} + +DartSnapshotSource::~DartSnapshotSource() = default; + +} // namespace blink diff --git a/runtime/dart_snapshot_source.h b/runtime/dart_snapshot_source.h new file mode 100644 index 0000000000000..8a2cb5d3f4f57 --- /dev/null +++ b/runtime/dart_snapshot_source.h @@ -0,0 +1,34 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_SNAPSHOT_SOURCE_H_ +#define FLUTTER_RUNTIME_DART_SNAPSHOT_SOURCE_H_ + +#include + +#include "flutter/fml/native_library.h" +#include "lib/fxl/macros.h" + +namespace blink { + +class DartSnapshotSource { + public: + static std::unique_ptr CreateWithSymbolInLibrary( + fxl::RefPtr library, + const char* symbol_name); + + static std::unique_ptr CreateWithContentsOfFile( + const char* file_path, + bool executable); + + virtual ~DartSnapshotSource(); + + virtual const uint8_t* GetSnapshotPointer() const = 0; + + virtual size_t GetSnapshotSize() const = 0; +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_SNAPSHOT_SOURCE_H_ diff --git a/runtime/dart_vm.cc b/runtime/dart_vm.cc new file mode 100644 index 0000000000000..33308860187c4 --- /dev/null +++ b/runtime/dart_vm.cc @@ -0,0 +1,433 @@ +// Copyright 2017 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 + +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/trace_event.h" +#include "flutter/lib/io/dart_io.h" +#include "flutter/lib/ui/dart_runtime_hooks.h" +#include "flutter/lib/ui/dart_ui.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_service_isolate.h" +#include "flutter/runtime/start_up.h" +#include "lib/fxl/arraysize.h" +#include "lib/fxl/compiler_specific.h" +#include "lib/fxl/logging.h" +#include "lib/fxl/time/time_delta.h" +#include "lib/tonic/converter/dart_converter.h" +#include "lib/tonic/dart_class_library.h" +#include "lib/tonic/dart_class_provider.h" +#include "lib/tonic/dart_sticky_error.h" +#include "lib/tonic/file_loader/file_loader.h" +#include "lib/tonic/logging/dart_error.h" +#include "lib/tonic/scopes/dart_api_scope.h" +#include "lib/tonic/typed_data/uint8_list.h" +#include "third_party/dart/runtime/bin/embedded_dart_io.h" + +namespace dart { +namespace observatory { + +#if !OS(FUCHSIA) && (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_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 // FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + +} // namespace observatory +} // namespace dart + +namespace blink { + +// Arguments passed to the Dart VM in all configurations. +static const char* kDartLanguageArgs[] = { + "--enable_mirrors=false", + "--background_compilation", + "--await_is_keyword", + "--causal_async_stacks", +}; + +static const char* kDartPrecompilationArgs[] = { + "--precompilation", +}; + +FXL_ALLOW_UNUSED_TYPE +static const char* kDartWriteProtectCodeArgs[] = { + "--no_write_protect_code", +}; + +static const char* kDartAssertArgs[] = { + // clang-format off + "--enable_asserts", + // clang-format on +}; + +static const char* kDartCheckedModeArgs[] = { + // clang-format off + "--enable_type_checks", + "--error_on_bad_type", + "--error_on_bad_override", + // clang-format on +}; + +static const char* kDartStrongModeArgs[] = { + // clang-format off + "--strong", + "--reify_generic_functions", + "--limit_ints_to_64_bits", + // clang-format on +}; + +static const char* kDartStartPausedArgs[]{ + "--pause_isolates_on_start", +}; + +static const char* kDartTraceStartupArgs[]{ + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", +}; + +static const char* kDartEndlessTraceBufferArgs[]{ + "--timeline_recorder=endless", +}; + +static const char* kDartFuchsiaTraceArgs[] FXL_ALLOW_UNUSED_TYPE = { + "--systrace_timeline", + "--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM", +}; + +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. + fxl::TimeDelta mtime = fxl::TimeDelta::FromSeconds(info.st_mtime + 1); + fxl::TimeDelta since = fxl::TimeDelta::FromMilliseconds(since_ms); + + return mtime > since; +} + +void ThreadExitCallback() {} + +Dart_Handle GetVMServiceAssetsArchiveCallback() { +#if OS(FUCHSIA) || (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE) + return nullptr; +#else // FLUTTER_RUNTIME_MODE + return tonic::DartConverter::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); + } +} + +bool DartVM::IsRunningPrecompiledCode() { + return Dart_IsPrecompiledRuntime(); +} + +static std::vector 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"}; + } else { + return {"--no-profiler"}; + } +} + +void PushBackAll(std::vector* 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::once_flag gVMInitialization; +static fxl::RefPtr gVM; + +fxl::RefPtr DartVM::ForProcess(Settings settings) { + std::call_once(gVMInitialization, + [settings]() { gVM = fxl::MakeRefCounted(settings); }); + return gVM; +} + +fxl::RefPtr DartVM::ForProcessIfInitialized() { + return gVM; +} + +DartVM::DartVM(const Settings& settings) + : settings_(settings), + vm_snapshot_(DartSnapshot::VMSnapshotFromSettings(settings)), + isolate_snapshot_(DartSnapshot::IsolateSnapshotFromSettings(settings)), + platform_kernel_mapping_( + std::make_unique(settings.kernel_snapshot_path)), + weak_factory_(this) { + TRACE_EVENT0("flutter", "DartVMInitializer"); + + FXL_DCHECK(vm_snapshot_ && vm_snapshot_->IsValid()) + << "VM snapshot must be valid."; + + FXL_DCHECK(isolate_snapshot_ && isolate_snapshot_->IsValid()) + << "Isolate snapshot must be valid."; + + if (platform_kernel_mapping_->GetSize() > 0) { + // The platform kernel mapping lifetime is managed by this instance of the + // DartVM and hence will exceed that of the PlatformKernel. So provide an + // empty release callback. + Dart_ReleaseBufferCallback empty = [](auto arg) {}; + platform_kernel_ = reinterpret_cast(Dart_ReadKernelBinary( + platform_kernel_mapping_->GetMapping(), // buffer + platform_kernel_mapping_->GetSize(), // buffer size + empty // buffer deleter + )); + } + + { + 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 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 (const auto& profiler_flag : + ProfilingFlags(settings.enable_dart_profiling)) { + args.push_back(profiler_flag); + } + + PushBackAll(&args, kDartLanguageArgs, arraysize(kDartLanguageArgs)); + + if (IsRunningPrecompiledCode()) { + PushBackAll(&args, kDartPrecompilationArgs, + arraysize(kDartPrecompilationArgs)); + } + +#if defined(OS_FUCHSIA) && defined(NDEBUG) + // Do not enable checked mode for Fuchsia release builds + // TODO(mikejurka): remove this once precompiled code is working on Fuchsia + const bool use_checked_mode = false; +#else + // Enable checked mode if we are not running precompiled code. We run non- + // precompiled code only in the debug product mode. + const bool use_checked_mode = + !IsRunningPrecompiledCode() && !settings.dart_non_checked_mode; +#endif + +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG + // Debug mode uses the JIT, disable code page write protection to avoid + // memory page protection changes before and after every compilation. + PushBackAll(&args, kDartWriteProtectCodeArgs, + arraysize(kDartWriteProtectCodeArgs)); +#endif + + const bool isolate_snapshot_is_dart_2 = + Dart_IsDart2Snapshot(isolate_snapshot_->GetData()->GetSnapshotPointer()); + + const bool is_preview_dart2 = + platform_kernel_ != nullptr || isolate_snapshot_is_dart_2; + + if (is_preview_dart2) { + FXL_DLOG(INFO) << "Dart 2 is enabled."; + } else { + FXL_DLOG(INFO) << "Dart 2 is NOT enabled. Platform kernel: " + << static_cast(platform_kernel_) + << " Isolate Snapshot is Dart 2: " + << isolate_snapshot_is_dart_2; + } + + if (is_preview_dart2) { + PushBackAll(&args, kDartStrongModeArgs, arraysize(kDartStrongModeArgs)); + if (use_checked_mode) { + PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); + } + } else if (use_checked_mode) { + PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); + 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)); + } + +#if defined(OS_FUCHSIA) + PushBackAll(&args, kDartFuchsiaTraceArgs, arraysize(kDartFuchsiaTraceArgs)); +#endif + + for (size_t i = 0; i < settings.dart_flags.size(); i++) + args.push_back(settings.dart_flags[i].c_str()); + + FXL_CHECK(Dart_SetVMFlags(args.size(), args.data())); + + DartUI::InitForGlobal(); + + { + TRACE_EVENT0("flutter", "Dart_Initialize"); + Dart_InitializeParams params = {}; + params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; + params.vm_snapshot_data = vm_snapshot_->GetData()->GetSnapshotPointer(); + params.vm_snapshot_instructions = vm_snapshot_->GetInstructionsIfPresent(); + params.create = reinterpret_cast( + DartIsolate::DartIsolateCreateCallback); + params.shutdown = reinterpret_cast( + DartIsolate::DartIsolateShutdownCallback); + params.cleanup = reinterpret_cast( + DartIsolate::DartIsolateCleanupCallback); + params.thread_exit = ThreadExitCallback; + params.get_service_assets = GetVMServiceAssetsArchiveCallback; + params.entropy_source = DartIO::EntropySource; + char* init_error = Dart_Initialize(¶ms); + if (init_error) { + FXL_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); + + Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); +} + +DartVM::~DartVM() { + if (Dart_CurrentIsolate() != nullptr) { + Dart_ExitIsolate(); + } + char* result = Dart_Cleanup(); + if (result != nullptr) { + FXL_LOG(ERROR) << "Could not cleanly shut down the Dart VM. Message: \"" + << result << "\"."; + free(result); + } +} + +const Settings& DartVM::GetSettings() const { + return settings_; +} + +DartVM::PlatformKernel* DartVM::GetPlatformKernel() const { + return platform_kernel_; +} + +const DartSnapshot& DartVM::GetVMSnapshot() const { + return *vm_snapshot_.get(); +} + +fxl::RefPtr DartVM::GetIsolateSnapshot() const { + return isolate_snapshot_; +} + +ServiceProtocol& DartVM::GetServiceProtocol() { + return service_protocol_; +} + +fxl::WeakPtr DartVM::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +} // namespace blink diff --git a/runtime/dart_vm.h b/runtime/dart_vm.h new file mode 100644 index 0000000000000..163a1756a840b --- /dev/null +++ b/runtime/dart_vm.h @@ -0,0 +1,69 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_DART_VM_H_ +#define FLUTTER_RUNTIME_DART_VM_H_ + +#include +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/runtime/dart_isolate.h" +#include "flutter/runtime/dart_snapshot.h" +#include "flutter/runtime/service_protocol.h" +#include "lib/fxl/build_config.h" +#include "lib/fxl/functional/closure.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/ref_counted.h" +#include "lib/fxl/memory/ref_ptr.h" +#include "lib/fxl/memory/weak_ptr.h" +#include "third_party/dart/runtime/include/dart_api.h" + +namespace blink { + +class DartVM : public fxl::RefCountedThreadSafe { + public: + class PlatformKernel; + + FXL_WARN_UNUSED_RESULT + static fxl::RefPtr ForProcess(Settings settings); + + static fxl::RefPtr ForProcessIfInitialized(); + + static bool IsRunningPrecompiledCode(); + + const Settings& GetSettings() const; + + PlatformKernel* GetPlatformKernel() const; + + const DartSnapshot& GetVMSnapshot() const; + + fxl::RefPtr GetIsolateSnapshot() const; + + fxl::WeakPtr GetWeakPtr(); + + ServiceProtocol& GetServiceProtocol(); + + private: + const Settings settings_; + const fxl::RefPtr vm_snapshot_; + const fxl::RefPtr isolate_snapshot_; + std::unique_ptr platform_kernel_mapping_; + PlatformKernel* platform_kernel_ = nullptr; + ServiceProtocol service_protocol_; + fxl::WeakPtrFactory weak_factory_; + + DartVM(const Settings& settings); + + ~DartVM(); + + FRIEND_REF_COUNTED_THREAD_SAFE(DartVM); + FRIEND_MAKE_REF_COUNTED(DartVM); + FXL_DISALLOW_COPY_AND_ASSIGN(DartVM); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_VM_H_ diff --git a/runtime/dart_vm_unittests.cc b/runtime/dart_vm_unittests.cc new file mode 100644 index 0000000000000..5b2f5e6ee8299 --- /dev/null +++ b/runtime/dart_vm_unittests.cc @@ -0,0 +1,21 @@ +// Copyright 2017 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 "gtest/gtest.h" + +namespace blink { + +TEST(DartVM, SimpleInitialization) { + Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + auto vm = DartVM::ForProcess(settings); + ASSERT_TRUE(vm); + ASSERT_EQ(vm, DartVM::ForProcess(settings)); + ASSERT_FALSE(DartVM::IsRunningPrecompiledCode()); + ASSERT_EQ(vm->GetPlatformKernel(), nullptr); +} + +} // namespace blink diff --git a/runtime/fixtures/simple_main.dart b/runtime/fixtures/simple_main.dart new file mode 100644 index 0000000000000..552dfbe344902 --- /dev/null +++ b/runtime/fixtures/simple_main.dart @@ -0,0 +1,7 @@ +// Copyright 2016 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. + +void simple_main() { + print("Hello"); +} diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index 75796cd68697e..d3e138c937f60 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -4,192 +4,244 @@ #include "flutter/runtime/runtime_controller.h" +#include "flutter/fml/message_loop.h" #include "flutter/glue/trace_event.h" #include "flutter/lib/ui/compositing/scene.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/window.h" -#include "flutter/runtime/dart_controller.h" #include "flutter/runtime/runtime_delegate.h" #include "lib/tonic/dart_message_handler.h" -using tonic::DartState; - namespace blink { -std::unique_ptr RuntimeController::Create( - RuntimeDelegate* client) { - return std::unique_ptr(new RuntimeController(client)); +RuntimeController::RuntimeController( + RuntimeDelegate& p_client, + const DartVM* p_vm, + TaskRunners p_task_runners, + fml::WeakPtr p_resource_context, + fxl::RefPtr p_unref_queue) + : RuntimeController(p_client, + p_vm, + std::move(p_task_runners), + std::move(p_resource_context), + std::move(p_unref_queue), + WindowData{/* default window data */}) {} + +RuntimeController::RuntimeController( + RuntimeDelegate& p_client, + const DartVM* p_vm, + TaskRunners p_task_runners, + fml::WeakPtr p_resource_context, + fxl::RefPtr p_unref_queue, + WindowData p_window_data) + : client_(p_client), + vm_(p_vm), + task_runners_(p_task_runners), + resource_context_(p_resource_context), + unref_queue_(p_unref_queue), + window_data_(std::move(p_window_data)), + root_isolate_( + DartIsolate::CreateRootIsolate(vm_, + vm_->GetIsolateSnapshot(), + task_runners_, + std::make_unique(this), + resource_context_, + unref_queue_)) { + root_isolate_->SetReturnCodeCallback([this](uint32_t code) { + root_isolate_return_code_ = {true, code}; + }); + if (auto window = GetWindowIfAvailable()) { + tonic::DartState::Scope scope(root_isolate_.get()); + window->DidCreateIsolate(); + if (!FlushRuntimeStateToIsolate()) { + FXL_DLOG(ERROR) << "Could not setup intial isolate state."; + } + } else { + FXL_DCHECK(false) << "RuntimeController created without window binding."; + } + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); +} + +RuntimeController::~RuntimeController() { + FXL_DCHECK(Dart_CurrentIsolate() == nullptr); + if (root_isolate_) { + root_isolate_->SetReturnCodeCallback(nullptr); + auto result = root_isolate_->Shutdown(); + if (!result) { + FXL_DLOG(ERROR) << "Could not shutdown the root isolate."; + } + root_isolate_ = {}; + } } -RuntimeController::RuntimeController(RuntimeDelegate* client) - : client_(client) {} - -RuntimeController::~RuntimeController() {} +std::unique_ptr RuntimeController::Clone() const { + return std::unique_ptr(new RuntimeController( + client_, // + vm_, // + task_runners_, // + resource_context_, // + unref_queue_, // + window_data_ // + )); +} -void RuntimeController::CreateDartController( - const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - int dirfd) { - FXL_DCHECK(!dart_controller_); +bool RuntimeController::FlushRuntimeStateToIsolate() { + return SetViewportMetrics(window_data_.viewport_metrics) && + SetLocale(window_data_.language_code, window_data_.country_code) && + SetSemanticsEnabled(window_data_.semantics_enabled); +} - dart_controller_.reset(new DartController()); - dart_controller_->CreateIsolateFor( - script_uri, isolate_snapshot_data, isolate_snapshot_instr, - std::make_unique(this, std::make_unique(this), - dirfd)); +bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { + window_data_.viewport_metrics = metrics; - UIDartState* dart_state = dart_controller_->dart_state(); - DartState::Scope scope(dart_state); - dart_state->window()->DidCreateIsolate(); - client_->DidCreateMainIsolate(dart_state->isolate()); + if (auto window = GetWindowIfAvailable()) { + window->UpdateWindowMetrics(metrics); + return true; + } + return false; +} - Window* window = GetWindow(); +bool RuntimeController::SetLocale(const std::string& language_code, + const std::string& country_code) { + window_data_.language_code = language_code; + window_data_.country_code = country_code; - window->UpdateLocale(language_code_, country_code_); + if (auto window = GetWindowIfAvailable()) { + window->UpdateLocale(window_data_.language_code, window_data_.country_code); + return true; + } - if (semantics_enabled_) - window->UpdateSemanticsEnabled(semantics_enabled_); + return false; } -void RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { - GetWindow()->UpdateWindowMetrics(metrics); -} +bool RuntimeController::SetUserSettingsData(const std::string& data) { + window_data_.user_settings_data = data; -void RuntimeController::SetLocale(const std::string& language_code, - const std::string& country_code) { - if (language_code_ == language_code && country_code_ == country_code) - return; + if (auto window = GetWindowIfAvailable()) { + window->UpdateUserSettingsData(window_data_.user_settings_data); + return true; + } - language_code_ = language_code; - country_code_ = country_code; - GetWindow()->UpdateLocale(language_code_, country_code_); + return false; } -void RuntimeController::SetUserSettingsData(const std::string& data) { - if (user_settings_data_ == data) - return; - user_settings_data_ = data; - GetWindow()->UpdateUserSettingsData(user_settings_data_); -} +bool RuntimeController::SetSemanticsEnabled(bool enabled) { + window_data_.semantics_enabled = enabled; + + if (auto window = GetWindowIfAvailable()) { + window->UpdateSemanticsEnabled(window_data_.semantics_enabled); + return true; + } -void RuntimeController::SetSemanticsEnabled(bool enabled) { - if (semantics_enabled_ == enabled) - return; - semantics_enabled_ = enabled; - GetWindow()->UpdateSemanticsEnabled(semantics_enabled_); + return false; } -void RuntimeController::BeginFrame(fxl::TimePoint frame_time) { - GetWindow()->BeginFrame(frame_time); +bool RuntimeController::BeginFrame(fxl::TimePoint frame_time) { + if (auto window = GetWindowIfAvailable()) { + window->BeginFrame(frame_time); + return true; + } + return false; } -void RuntimeController::NotifyIdle(int64_t deadline) { - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return; +bool RuntimeController::NotifyIdle(int64_t deadline) { + if (!root_isolate_) { + return false; } - DartState::Scope scope(dart_state); + + tonic::DartState::Scope scope(root_isolate_.get()); Dart_NotifyIdle(deadline); + return true; } -void RuntimeController::DispatchPlatformMessage( +bool RuntimeController::DispatchPlatformMessage( fxl::RefPtr message) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", "mode", - "basic"); - GetWindow()->DispatchPlatformMessage(std::move(message)); + if (auto window = GetWindowIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage", + "mode", "basic"); + window->DispatchPlatformMessage(std::move(message)); + return true; + } + return false; } -void RuntimeController::DispatchPointerDataPacket( +bool RuntimeController::DispatchPointerDataPacket( const PointerDataPacket& packet) { - TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", - "mode", "basic"); - GetWindow()->DispatchPointerDataPacket(packet); + if (auto window = GetWindowIfAvailable()) { + TRACE_EVENT1("flutter", "RuntimeController::DispatchPointerDataPacket", + "mode", "basic"); + window->DispatchPointerDataPacket(packet); + return true; + } + return false; } -void RuntimeController::DispatchSemanticsAction(int32_t id, +bool RuntimeController::DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args) { TRACE_EVENT1("flutter", "RuntimeController::DispatchSemanticsAction", "mode", "basic"); - GetWindow()->DispatchSemanticsAction(id, action, std::move(args)); + if (auto window = GetWindowIfAvailable()) { + window->DispatchSemanticsAction(id, action, std::move(args)); + return true; + } + return false; } -Window* RuntimeController::GetWindow() { - return dart_controller_->dart_state()->window(); +Window* RuntimeController::GetWindowIfAvailable() { + return root_isolate_ ? root_isolate_->window() : nullptr; } std::string RuntimeController::DefaultRouteName() { - return client_->DefaultRouteName(); + return client_.DefaultRouteName(); } void RuntimeController::ScheduleFrame() { - client_->ScheduleFrame(); + client_.ScheduleFrame(); } void RuntimeController::Render(Scene* scene) { - client_->Render(scene->takeLayerTree()); + client_.Render(scene->takeLayerTree()); } void RuntimeController::UpdateSemantics(SemanticsUpdate* update) { - if (semantics_enabled_) - client_->UpdateSemantics(update->takeNodes()); + if (window_data_.semantics_enabled) { + client_.UpdateSemantics(update->takeNodes()); + } } void RuntimeController::HandlePlatformMessage( fxl::RefPtr message) { - client_->HandlePlatformMessage(std::move(message)); -} - -void RuntimeController::DidCreateSecondaryIsolate(Dart_Isolate isolate) { - client_->DidCreateSecondaryIsolate(isolate); -} - -void RuntimeController::DidShutdownMainIsolate() { - client_->DidShutdownMainIsolate(); + client_.HandlePlatformMessage(std::move(message)); } Dart_Port RuntimeController::GetMainPort() { - if (!dart_controller_) { - return ILLEGAL_PORT; - } - if (!dart_controller_->dart_state()) { - return ILLEGAL_PORT; - } - return dart_controller_->dart_state()->main_port(); + return root_isolate_ ? root_isolate_->main_port() : ILLEGAL_PORT; } std::string RuntimeController::GetIsolateName() { - if (!dart_controller_) { - return ""; - } - if (!dart_controller_->dart_state()) { - return ""; - } - return dart_controller_->dart_state()->debug_name(); + return root_isolate_ ? root_isolate_->debug_name() : ""; } bool RuntimeController::HasLivePorts() { - if (!dart_controller_) { + if (!root_isolate_) { return false; } - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return false; - } - DartState::Scope scope(dart_state); + tonic::DartState::Scope scope(root_isolate_.get()); return Dart_HasLivePorts(); } tonic::DartErrorHandleType RuntimeController::GetLastError() { - if (!dart_controller_) { - return tonic::kNoError; - } - UIDartState* dart_state = dart_controller_->dart_state(); - if (!dart_state) { - return tonic::kNoError; - } - return dart_state->message_handler().isolate_last_error(); + return root_isolate_ ? root_isolate_->message_handler().isolate_last_error() + : tonic::kNoError; +} + +fml::WeakPtr RuntimeController::GetRootIsolate() { + return root_isolate_; +} + +std::pair RuntimeController::GetRootIsolateReturnCode() { + return root_isolate_return_code_; } } // namespace blink diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 628bc699395c0..326c517f31db2 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -7,71 +7,108 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/lib/ui/ui_dart_state.h" #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/runtime/dart_vm.h" #include "lib/fxl/macros.h" namespace blink { -class DartController; -class DartLibraryProvider; class Scene; class RuntimeDelegate; class View; class Window; -class RuntimeController : public WindowClient, public IsolateClient { +class RuntimeController final : public WindowClient { public: - static std::unique_ptr Create(RuntimeDelegate* client); + RuntimeController(RuntimeDelegate& client, + const DartVM* vm, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue); + ~RuntimeController(); - void CreateDartController(const std::string& script_uri, - const uint8_t* isolate_snapshot_data, - const uint8_t* isolate_snapshot_instr, - int dirfd = -1); - DartController* dart_controller() const { return dart_controller_.get(); } + std::unique_ptr Clone() const; + + bool SetViewportMetrics(const ViewportMetrics& metrics); - void SetViewportMetrics(const ViewportMetrics& metrics); - void SetLocale(const std::string& language_code, + bool SetLocale(const std::string& language_code, const std::string& country_code); - void SetUserSettingsData(const std::string& data); - void SetSemanticsEnabled(bool enabled); - void BeginFrame(fxl::TimePoint frame_time); - void NotifyIdle(int64_t deadline); + bool SetUserSettingsData(const std::string& data); + + bool SetSemanticsEnabled(bool enabled); + + bool BeginFrame(fxl::TimePoint frame_time); + + bool NotifyIdle(int64_t deadline); - void DispatchPlatformMessage(fxl::RefPtr message); - void DispatchPointerDataPacket(const PointerDataPacket& packet); - void DispatchSemanticsAction(int32_t id, + bool DispatchPlatformMessage(fxl::RefPtr message); + + bool DispatchPointerDataPacket(const PointerDataPacket& packet); + + bool DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args); Dart_Port GetMainPort(); + std::string GetIsolateName(); + bool HasLivePorts(); + tonic::DartErrorHandleType GetLastError(); - private: - explicit RuntimeController(RuntimeDelegate* client); + fml::WeakPtr GetRootIsolate(); - Window* GetWindow(); + std::pair GetRootIsolateReturnCode(); + private: + struct WindowData { + ViewportMetrics viewport_metrics; + std::string language_code; + std::string country_code; + std::string user_settings_data = "{}"; + bool semantics_enabled = false; + }; + + RuntimeDelegate& client_; + const DartVM* vm_; + TaskRunners task_runners_; + fml::WeakPtr resource_context_; + fxl::RefPtr unref_queue_; + WindowData window_data_; + fml::WeakPtr root_isolate_; + std::pair root_isolate_return_code_ = {false, 0}; + + RuntimeController(RuntimeDelegate& client, + const DartVM* vm, + TaskRunners task_runners, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue, + WindowData data); + + Window* GetWindowIfAvailable(); + + bool FlushRuntimeStateToIsolate(); + + // |blink::WindowClient| std::string DefaultRouteName() override; + + // |blink::WindowClient| void ScheduleFrame() override; + + // |blink::WindowClient| void Render(Scene* scene) override; - void UpdateSemantics(SemanticsUpdate* update) override; - void HandlePlatformMessage(fxl::RefPtr message) override; - void DidCreateSecondaryIsolate(Dart_Isolate isolate) override; - void DidShutdownMainIsolate() override; + // |blink::WindowClient| + void UpdateSemantics(SemanticsUpdate* update) override; - RuntimeDelegate* client_; - std::string language_code_; - std::string country_code_; - std::string user_settings_data_ = "{}"; - bool semantics_enabled_ = false; - std::unique_ptr dart_controller_; + // |blink::WindowClient| + void HandlePlatformMessage(fxl::RefPtr message) override; FXL_DISALLOW_COPY_AND_ASSIGN(RuntimeController); }; diff --git a/runtime/runtime_delegate.cc b/runtime/runtime_delegate.cc index 6ec55c4c2e6a0..902672be06d8f 100644 --- a/runtime/runtime_delegate.cc +++ b/runtime/runtime_delegate.cc @@ -6,12 +6,6 @@ namespace blink { -RuntimeDelegate::~RuntimeDelegate() {} - -void RuntimeDelegate::DidCreateMainIsolate(Dart_Isolate isolate) {} - -void RuntimeDelegate::DidCreateSecondaryIsolate(Dart_Isolate isolate) {} - -void RuntimeDelegate::DidShutdownMainIsolate() {} +RuntimeDelegate::~RuntimeDelegate() = default; } // namespace blink diff --git a/runtime/runtime_delegate.h b/runtime/runtime_delegate.h index 36650fe7fb70b..c6d6c0a92b2b4 100644 --- a/runtime/runtime_delegate.h +++ b/runtime/runtime_delegate.h @@ -18,14 +18,14 @@ namespace blink { class RuntimeDelegate { public: virtual std::string DefaultRouteName() = 0; + virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0; + virtual void Render(std::unique_ptr layer_tree) = 0; + virtual void UpdateSemantics(blink::SemanticsNodeUpdates update) = 0; - virtual void HandlePlatformMessage(fxl::RefPtr message) = 0; - virtual void DidCreateMainIsolate(Dart_Isolate isolate); - virtual void DidCreateSecondaryIsolate(Dart_Isolate isolate); - virtual void DidShutdownMainIsolate(); + virtual void HandlePlatformMessage(fxl::RefPtr message) = 0; protected: virtual ~RuntimeDelegate(); diff --git a/runtime/runtime_init.cc b/runtime/runtime_init.cc deleted file mode 100644 index eda66e5495aed..0000000000000 --- a/runtime/runtime_init.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2016 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/runtime_init.h" - -#include "flutter/glue/trace_event.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/runtime/platform_impl.h" -#include "flutter/sky/engine/public/web/Sky.h" -#include "lib/fxl/logging.h" - -namespace blink { -namespace { - -PlatformImpl* g_platform_impl = nullptr; - -} // namespace - -void InitRuntime(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path) { - TRACE_EVENT0("flutter", "InitRuntime"); - - FXL_CHECK(!g_platform_impl); - g_platform_impl = new PlatformImpl(); - InitEngine(g_platform_impl); - InitDartVM(vm_snapshot_data, vm_snapshot_instructions, - default_isolate_snapshot_data, - default_isolate_snapshot_instructions, bundle_path); -} - -} // namespace blink diff --git a/runtime/runtime_init.h b/runtime/runtime_init.h deleted file mode 100644 index 515ae284e3460..0000000000000 --- a/runtime/runtime_init.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 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. - -#ifndef FLUTTER_RUNTIME_RUNTIME_INIT_H_ -#define FLUTTER_RUNTIME_RUNTIME_INIT_H_ - -#include -#include - -namespace blink { - -void InitRuntime(const uint8_t* vm_snapshot_data, - const uint8_t* vm_snapshot_instructions, - const uint8_t* default_isolate_snapshot_data, - const uint8_t* default_isolate_snapshot_instructions, - const std::string& bundle_path); - -} // namespace blink - -#endif // FLUTTER_RUNTIME_RUNTIME_INIT_H_ diff --git a/runtime/service_protocol.cc b/runtime/service_protocol.cc new file mode 100644 index 0000000000000..90f07d745f070 --- /dev/null +++ b/runtime/service_protocol.cc @@ -0,0 +1,277 @@ +// Copyright 2017 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. + +#define RAPIDJSON_HAS_STDSTRING 1 + +#include "flutter/runtime/service_protocol.h" + +#include +#include +#include +#include + +#include "lib/fxl/synchronization/waitable_event.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" +#include "third_party/dart/runtime/include/dart_tools_api.h" + +namespace blink { + +const fxl::StringView ServiceProtocol::kScreenshotExtensionName = + "_flutter.screenshot"; +const fxl::StringView ServiceProtocol::kScreenshotSkpExtensionName = + "_flutter.screenshotSkp"; +const fxl::StringView ServiceProtocol::kRunInViewExtensionName = + "_flutter.runInView"; +const fxl::StringView ServiceProtocol::kFlushUIThreadTasksExtensionName = + "_flutter.flushUIThreadTasks"; +const fxl::StringView ServiceProtocol::kSetAssetBundlePathExtensionName = + "_flutter.setAssetBundlePath"; + +static constexpr fxl::StringView kViewIdPrefx = "_flutterView/"; +static constexpr fxl::StringView kListViewsExtensionName = "_flutter.listViews"; + +ServiceProtocol::ServiceProtocol() + : endpoints_({ + // Private + kListViewsExtensionName, + + // Public + kScreenshotExtensionName, + kScreenshotSkpExtensionName, + kRunInViewExtensionName, + kFlushUIThreadTasksExtensionName, + kSetAssetBundlePathExtensionName, + }) {} + +ServiceProtocol::~ServiceProtocol() { + ToggleHooks(false); +} + +void ServiceProtocol::AddHandler(Handler* handler) { + std::lock_guard lock(handlers_mutex_); + handlers_.emplace(handler); +} + +void ServiceProtocol::RemoveHandler(Handler* handler) { + std::lock_guard lock(handlers_mutex_); + handlers_.erase(handler); +} + +void ServiceProtocol::ToggleHooks(bool set) { + for (const auto& endpoint : endpoints_) { + Dart_RegisterRootServiceRequestCallback( + endpoint.data(), // method + &ServiceProtocol::HandleMessage, // callback + set ? this : nullptr // user data + ); + } +} + +static void WriteServerErrorResponse(rapidjson::Document& document, + const char* message) { + document.SetObject(); + document.AddMember("code", -32000, document.GetAllocator()); + rapidjson::Value message_value; + message_value.SetString(message, document.GetAllocator()); + document.AddMember("message", message_value, document.GetAllocator()); +} + +bool ServiceProtocol::HandleMessage(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object) { + Handler::ServiceProtocolMap params; + for (intptr_t i = 0; i < num_params; i++) { + params[fxl::StringView{param_keys[i]}] = fxl::StringView{param_values[i]}; + } + +#ifndef NDEBUG + FXL_DLOG(INFO) << "Service protcol method: " << method; + FXL_DLOG(INFO) << "Arguments: " << params.size(); + for (intptr_t i = 0; i < num_params; i++) { + FXL_DLOG(INFO) << " " << i + 1 << ": " << param_keys[i] << " = " + << param_values[i]; + } +#endif // NDEBUG + + rapidjson::Document document; + bool result = HandleMessage(fxl::StringView{method}, // + params, // + static_cast(user_data), // + document // + ); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + *json_object = strndup(buffer.GetString(), buffer.GetSize()); + +#ifndef NDEBUG + FXL_DLOG(INFO) << "Response: " << *json_object; + FXL_DLOG(INFO) << "RPC Result: " << result; +#endif // NDEBUG + + return result; +} + +bool ServiceProtocol::HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + ServiceProtocol* service_protocol, + rapidjson::Document& response) { + if (service_protocol == nullptr) { + WriteServerErrorResponse(response, "Service protocol unavailable."); + return false; + } + + return service_protocol->HandleMessage(method, params, response); +} + +FXL_WARN_UNUSED_RESULT +static bool HandleMessageOnHandler( + ServiceProtocol::Handler* handler, + fxl::StringView method, + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& document) { + FXL_DCHECK(handler); + fxl::AutoResetWaitableEvent latch; + bool result = false; + fml::TaskRunner::RunNowOrPostTask( + handler->GetServiceProtocolHandlerTaskRunner(method), + [&latch, // + &result, // + &handler, // + &method, // + ¶ms, // + &document // + ]() { + result = + handler->HandleServiceProtocolMessage(method, params, document); + latch.Signal(); + }); + latch.Wait(); + return result; +} + +bool ServiceProtocol::HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + rapidjson::Document& response) const { + if (method == kListViewsExtensionName) { + // So far, this is the only built-in method that does not forward to the + // dynamic set of handlers. + return HandleListViewsMethod(response); + } + + std::lock_guard lock(handlers_mutex_); + + if (handlers_.size() == 0) { + WriteServerErrorResponse(response, + "There are no running service protocol handlers."); + return false; + } + + // Find the handler by its "viewId" in the params. + auto view_id_param_found = params.find(fxl::StringView{"viewId"}); + if (view_id_param_found != params.end()) { + auto handler = reinterpret_cast(std::stoull( + view_id_param_found->second.data() + kViewIdPrefx.size(), nullptr, 16)); + auto handler_found = handlers_.find(handler); + if (handler_found != handlers_.end()) { + return HandleMessageOnHandler(handler, method, params, response); + } + } + + // Handle legacy calls that do not specify a handler in their args. + // TODO(chinmaygarde): Deprecate these calls in the tools and remove these + // fallbacks. + if (method == kScreenshotExtensionName || + method == kScreenshotSkpExtensionName) { + return HandleMessageOnHandler(*handlers_.begin(), method, params, response); + } + + WriteServerErrorResponse( + response, + "Service protocol could not handle or find a handler for the " + "requested method."); + return false; +} + +static std::string CreateFlutterViewID(intptr_t handler) { + std::stringstream stream; + stream << kViewIdPrefx << "0x" << std::hex << handler; + return stream.str(); +} + +static std::string CreateIsolateID(int64_t isolate) { + std::stringstream stream; + stream << "isolates/" << isolate; + return stream.str(); +} + +void ServiceProtocol::Handler::Description::Write( + Handler* handler, + rapidjson::Value& view, + rapidjson::MemoryPoolAllocator<>& allocator) const { + view.SetObject(); + view.AddMember("type", "FlutterView", allocator); + view.AddMember("id", CreateFlutterViewID(reinterpret_cast(handler)), + allocator); + if (isolate_port != 0) { + rapidjson::Value isolate(rapidjson::Type::kObjectType); + { + isolate.AddMember("type", "@Isolate", allocator); + isolate.AddMember("fixedId", true, allocator); + isolate.AddMember("id", CreateIsolateID(isolate_port), allocator); + isolate.AddMember("name", isolate_name, allocator); + isolate.AddMember("number", isolate_port, allocator); + } + view.AddMember("isolate", isolate, allocator); + } +} + +bool ServiceProtocol::HandleListViewsMethod( + rapidjson::Document& response) const { + // Collect handler descriptions on their respective task runners. + std::lock_guard lock(handlers_mutex_); + std::vector> descriptions; + for (const auto& handler : handlers_) { + fxl::AutoResetWaitableEvent latch; + Handler::Description description; + + fml::TaskRunner::RunNowOrPostTask( + handler->GetServiceProtocolHandlerTaskRunner( + kListViewsExtensionName), // task runner + [&latch, // + &description, // + &handler // + ]() { + description = handler->GetServiceProtocolDescription(); + latch.Signal(); + }); + latch.Wait(); + descriptions.emplace_back(std::make_pair( + reinterpret_cast(handler), std::move(description))); + } + + auto& allocator = response.GetAllocator(); + + // Construct the response objects. + response.SetObject(); + response.AddMember("type", "FlutterViewList", allocator); + + rapidjson::Value viewsList(rapidjson::Type::kArrayType); + for (const auto& description : descriptions) { + rapidjson::Value view(rapidjson::Type::kObjectType); + description.second.Write(reinterpret_cast(description.first), + view, allocator); + viewsList.PushBack(view, allocator); + } + + response.AddMember("views", viewsList, allocator); + + return true; +} + +} // namespace blink diff --git a/runtime/service_protocol.h b/runtime/service_protocol.h new file mode 100644 index 0000000000000..60427a2a01008 --- /dev/null +++ b/runtime/service_protocol.h @@ -0,0 +1,93 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ +#define FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_ + +#include +#include +#include +#include + +#include "flutter/fml/task_runner.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/strings/string_view.h" +#include "lib/fxl/synchronization/thread_annotations.h" +#include "third_party/rapidjson/rapidjson/document.h" + +namespace blink { + +class ServiceProtocol { + public: + static const fxl::StringView kScreenshotExtensionName; + static const fxl::StringView kScreenshotSkpExtensionName; + static const fxl::StringView kRunInViewExtensionName; + static const fxl::StringView kFlushUIThreadTasksExtensionName; + static const fxl::StringView kSetAssetBundlePathExtensionName; + + class Handler { + public: + struct Description { + int64_t isolate_port = 0 /* illegal port by default. */; + std::string isolate_name; + + void Write(Handler* handler, + rapidjson::Value& value, + rapidjson::MemoryPoolAllocator<>& allocator) const; + }; + + using ServiceProtocolMap = std::map; + + virtual fxl::RefPtr GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const = 0; + + virtual Description GetServiceProtocolDescription() const = 0; + + virtual bool HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) = 0; + }; + + ServiceProtocol(); + + ~ServiceProtocol(); + + void ToggleHooks(bool set); + + void AddHandler(Handler* handler); + + void RemoveHandler(Handler* handler); + + private: + const std::set endpoints_; + mutable std::mutex handlers_mutex_; + std::set handlers_; + + FXL_WARN_UNUSED_RESULT + static bool HandleMessage(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object); + FXL_WARN_UNUSED_RESULT + static bool HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + ServiceProtocol* service_protocol, + rapidjson::Document& response); + FXL_WARN_UNUSED_RESULT + bool HandleMessage(fxl::StringView method, + const Handler::ServiceProtocolMap& params, + rapidjson::Document& response) const; + + FXL_WARN_UNUSED_RESULT + bool HandleListViewsMethod(rapidjson::Document& response) const; + + FXL_DISALLOW_COPY_AND_ASSIGN(ServiceProtocol); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_SERVICE_PROTOCOL_H_