blob: 18b51a32a6f1a33fa538a14bd39c74b7aa80ea71 [file] [log] [blame]
// 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(const 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);
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);
// Make runnable.
Dart_ExitScope();
Dart_ExitIsolate();
*error = Dart_IsolateMakeRunnable(isolate);
if (*error) {
Dart_EnterIsolate(isolate);
Dart_ShutdownIsolate();
return false;
}
Dart_EnterIsolate(isolate);
Dart_EnterScope();
return true;
}
} // namespace flutter