// 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 #include "dart/runtime/include/dart_tools_api.h" #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/ftl/files/directory.h" #include "lib/ftl/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/debugger/dart_debugger.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" using tonic::LogIfError; using tonic::ToDart; namespace blink { namespace { // TODO(abarth): Consider adding this to //lib/ftl. std::string ResolvePath(std::string path) { if (!path.empty() && path[0] == '/') return path; return files::SimplifyPath(files::GetCurrentDirectory() + "/" + path); } } // namespace DartController::DartController() : ui_dart_state_(nullptr) {} DartController::~DartController() { if (ui_dart_state_) { // 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(); // deletes ui_dart_state_ ui_dart_state_ = nullptr; } } bool DartController::SendStartMessage(Dart_Handle root_library) { if (LogIfError(root_library)) return true; { // Temporarily exit the isolate while we make it runnable. Dart_Isolate isolate = dart_state()->isolate(); FTL_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. // Grab the 'dart:ui' library. Dart_Handle ui_library = Dart_LookupLibrary(ToDart("dart:ui")); DART_CHECK_VALID(ui_library); // Grab the 'dart:isolate' library. Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); DART_CHECK_VALID(isolate_lib); // Import the root library into the 'dart:ui' library so that we can // reach main. Dart_LibraryImportLibrary(ui_library, root_library, Dart_Null()); // Get the closure of main(). Dart_Handle main_closure = Dart_Invoke(ui_library, ToDart("_getMainClosure"), 0, NULL); if (LogIfError(main_closure)) return true; // 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 uint8_t* buffer, size_t size) { tonic::DartState::Scope scope(dart_state()); Dart_Handle result = Dart_LoadKernel(Dart_ReadKernelBinary(buffer, size)); LogIfError(result); tonic::DartErrorHandleType error = tonic::GetErrorHandleType(result); if (SendStartMessage(Dart_RootLibrary())) { return tonic::kUnknownErrorType; } return error; } tonic::DartErrorHandleType DartController::RunFromPrecompiledSnapshot() { TRACE_EVENT0("flutter", "DartController::RunFromPrecompiledSnapshot"); FTL_DCHECK(Dart_CurrentIsolate() == nullptr); tonic::DartState::Scope scope(dart_state()); if (SendStartMessage(Dart_RootLibrary())) { return tonic::kUnknownErrorType; } return tonic::kNoError; } tonic::DartErrorHandleType DartController::RunFromScriptSnapshot( const uint8_t* buffer, size_t size) { tonic::DartState::Scope scope(dart_state()); Dart_Handle result = Dart_LoadScriptFromSnapshot(buffer, size); LogIfError(result); tonic::DartErrorHandleType error = tonic::GetErrorHandleType(result); if (SendStartMessage(Dart_RootLibrary())) { return tonic::kUnknownErrorType; } return error; } tonic::DartErrorHandleType DartController::RunFromSource( const std::string& main, const std::string& packages) { tonic::DartState::Scope scope(dart_state()); tonic::FileLoader& loader = dart_state()->file_loader(); if (!packages.empty() && !loader.LoadPackagesMap(ResolvePath(packages))) FTL_LOG(WARNING) << "Failed to load package map: " << packages; Dart_Handle result = loader.LoadScript(main); LogIfError(result); tonic::DartErrorHandleType 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; Dart_Isolate isolate = Dart_CreateIsolate( script_uri.c_str(), "main", isolate_snapshot_data, isolate_snapshot_instr, nullptr, static_cast(state.get()), &error); FTL_CHECK(isolate) << error; ui_dart_state_ = state.release(); dart_state()->message_handler().Initialize(blink::Threads::UI()); Dart_SetShouldPauseOnStart(Settings::Get().start_paused); ui_dart_state_->SetIsolate(isolate); FTL_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