| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "flutter/runtime/dart_service_isolate.h" | |
| #include <algorithm> | |
| #include <cstring> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/posix_wrappers.h" | |
| #include "flutter/runtime/embedder_resources.h" | |
| #include "third_party/dart/runtime/include/dart_api.h" | |
| #include "third_party/tonic/converter/dart_converter.h" | |
| #include "third_party/tonic/dart_library_natives.h" | |
| #include "third_party/tonic/logging/dart_error.h" | |
| #define RETURN_ERROR_HANDLE(handle) \ | |
| if (Dart_IsError(handle)) { \ | |
| return handle; \ | |
| } | |
| #define SHUTDOWN_ON_ERROR(handle) \ | |
| if (Dart_IsError(handle)) { \ | |
| *error = fml::strdup(Dart_GetError(handle)); \ | |
| Dart_ExitScope(); \ | |
| Dart_ShutdownIsolate(); \ | |
| return false; \ | |
| } | |
| namespace flutter { | |
| namespace { | |
| static Dart_LibraryTagHandler g_embedder_tag_handler; | |
| static tonic::DartLibraryNatives* g_natives; | |
| static std::string g_observatory_uri; | |
| Dart_NativeFunction GetNativeFunction(Dart_Handle name, | |
| int argument_count, | |
| bool* auto_setup_scope) { | |
| FML_CHECK(g_natives); | |
| return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope); | |
| } | |
| const uint8_t* GetSymbol(Dart_NativeFunction native_function) { | |
| FML_CHECK(g_natives); | |
| return g_natives->GetSymbol(native_function); | |
| } | |
| } // namespace | |
| std::mutex DartServiceIsolate::callbacks_mutex_; | |
| std::set<std::unique_ptr<DartServiceIsolate::ObservatoryServerStateCallback>> | |
| DartServiceIsolate::callbacks_; | |
| void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) { | |
| Dart_Handle exception = nullptr; | |
| std::string uri = | |
| tonic::DartConverter<std::string>::FromArguments(args, 0, exception); | |
| if (exception) { | |
| return; | |
| } | |
| g_observatory_uri = uri; | |
| // Collect callbacks to fire in a separate collection and invoke them outside | |
| // the lock. | |
| std::vector<DartServiceIsolate::ObservatoryServerStateCallback> | |
| callbacks_to_fire; | |
| { | |
| std::scoped_lock lock(callbacks_mutex_); | |
| for (auto& callback : callbacks_) { | |
| callbacks_to_fire.push_back(*callback.get()); | |
| } | |
| } | |
| for (const auto& callback_to_fire : callbacks_to_fire) { | |
| callback_to_fire(uri); | |
| } | |
| } | |
| DartServiceIsolate::CallbackHandle DartServiceIsolate::AddServerStatusCallback( | |
| const DartServiceIsolate::ObservatoryServerStateCallback& callback) { | |
| if (!callback) { | |
| return 0; | |
| } | |
| auto callback_pointer = | |
| std::make_unique<DartServiceIsolate::ObservatoryServerStateCallback>( | |
| callback); | |
| auto handle = reinterpret_cast<CallbackHandle>(callback_pointer.get()); | |
| { | |
| std::scoped_lock lock(callbacks_mutex_); | |
| callbacks_.insert(std::move(callback_pointer)); | |
| } | |
| if (!g_observatory_uri.empty()) { | |
| callback(g_observatory_uri); | |
| } | |
| return handle; | |
| } | |
| bool DartServiceIsolate::RemoveServerStatusCallback( | |
| CallbackHandle callback_handle) { | |
| std::scoped_lock lock(callbacks_mutex_); | |
| auto found = std::find_if( | |
| callbacks_.begin(), callbacks_.end(), | |
| [callback_handle](const auto& item) { | |
| return reinterpret_cast<CallbackHandle>(item.get()) == callback_handle; | |
| }); | |
| if (found == callbacks_.end()) { | |
| return false; | |
| } | |
| callbacks_.erase(found); | |
| return true; | |
| } | |
| void DartServiceIsolate::Shutdown(Dart_NativeArguments args) { | |
| // NO-OP. | |
| } | |
| bool DartServiceIsolate::Startup(std::string server_ip, | |
| intptr_t server_port, | |
| Dart_LibraryTagHandler embedder_tag_handler, | |
| bool disable_origin_check, | |
| bool disable_service_auth_codes, | |
| bool enable_service_port_fallback, | |
| char** error) { | |
| Dart_Isolate isolate = Dart_CurrentIsolate(); | |
| FML_CHECK(isolate); | |
| // Remember the embedder's library tag handler. | |
| g_embedder_tag_handler = embedder_tag_handler; | |
| FML_CHECK(g_embedder_tag_handler); | |
| // Setup native entries. | |
| if (!g_natives) { | |
| g_natives = new tonic::DartLibraryNatives(); | |
| g_natives->Register({ | |
| {"VMServiceIO_NotifyServerState", NotifyServerState, 1, true}, | |
| {"VMServiceIO_Shutdown", Shutdown, 0, true}, | |
| }); | |
| } | |
| Dart_Handle uri = Dart_NewStringFromCString("dart:vmservice_io"); | |
| Dart_Handle library = Dart_LookupLibrary(uri); | |
| SHUTDOWN_ON_ERROR(library); | |
| Dart_Handle result = Dart_SetRootLibrary(library); | |
| SHUTDOWN_ON_ERROR(result); | |
| result = Dart_SetNativeResolver(library, GetNativeFunction, GetSymbol); | |
| SHUTDOWN_ON_ERROR(result); | |
| // Make runnable. | |
| Dart_ExitScope(); | |
| Dart_ExitIsolate(); | |
| *error = Dart_IsolateMakeRunnable(isolate); | |
| if (*error) { | |
| Dart_EnterIsolate(isolate); | |
| Dart_ShutdownIsolate(); | |
| return false; | |
| } | |
| Dart_EnterIsolate(isolate); | |
| Dart_EnterScope(); | |
| library = Dart_RootLibrary(); | |
| SHUTDOWN_ON_ERROR(library); | |
| // Set the HTTP server's ip. | |
| result = Dart_SetField(library, Dart_NewStringFromCString("_ip"), | |
| Dart_NewStringFromCString(server_ip.c_str())); | |
| SHUTDOWN_ON_ERROR(result); | |
| // If we have a port specified, start the server immediately. | |
| bool auto_start = server_port >= 0; | |
| if (server_port < 0) { | |
| // Adjust server_port to port 0 which will result in the first available | |
| // port when the HTTP server is started. | |
| server_port = 0; | |
| } | |
| // Set the HTTP's servers port. | |
| result = Dart_SetField(library, Dart_NewStringFromCString("_port"), | |
| Dart_NewInteger(server_port)); | |
| SHUTDOWN_ON_ERROR(result); | |
| result = Dart_SetField(library, Dart_NewStringFromCString("_autoStart"), | |
| Dart_NewBoolean(auto_start)); | |
| SHUTDOWN_ON_ERROR(result); | |
| result = | |
| Dart_SetField(library, Dart_NewStringFromCString("_originCheckDisabled"), | |
| Dart_NewBoolean(disable_origin_check)); | |
| SHUTDOWN_ON_ERROR(result); | |
| result = | |
| Dart_SetField(library, Dart_NewStringFromCString("_authCodesDisabled"), | |
| Dart_NewBoolean(disable_service_auth_codes)); | |
| SHUTDOWN_ON_ERROR(result); | |
| result = Dart_SetField( | |
| library, Dart_NewStringFromCString("_enableServicePortFallback"), | |
| Dart_NewBoolean(enable_service_port_fallback)); | |
| SHUTDOWN_ON_ERROR(result); | |
| return true; | |
| } | |
| } // namespace flutter |