1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/runtime/dart_service_isolate.h"
6
7#include <string.h>
8#include <algorithm>
9
10#include "flutter/fml/logging.h"
11#include "flutter/fml/posix_wrappers.h"
12#include "flutter/runtime/embedder_resources.h"
13#include "third_party/dart/runtime/include/dart_api.h"
14#include "third_party/tonic/converter/dart_converter.h"
15#include "third_party/tonic/dart_library_natives.h"
16#include "third_party/tonic/logging/dart_error.h"
17
18#define RETURN_ERROR_HANDLE(handle) \
19 if (Dart_IsError(handle)) { \
20 return handle; \
21 }
22
23#define SHUTDOWN_ON_ERROR(handle) \
24 if (Dart_IsError(handle)) { \
25 *error = fml::strdup(Dart_GetError(handle)); \
26 Dart_ExitScope(); \
27 Dart_ShutdownIsolate(); \
28 return false; \
29 }
30
31namespace flutter {
32namespace {
33
34static Dart_LibraryTagHandler g_embedder_tag_handler;
35static tonic::DartLibraryNatives* g_natives;
36static std::string g_observatory_uri;
37
38Dart_NativeFunction GetNativeFunction(Dart_Handle name,
39 int argument_count,
40 bool* auto_setup_scope) {
41 FML_CHECK(g_natives);
42 return g_natives->GetNativeFunction(name, argument_count, auto_setup_scope);
43}
44
45const uint8_t* GetSymbol(Dart_NativeFunction native_function) {
46 FML_CHECK(g_natives);
47 return g_natives->GetSymbol(native_function);
48}
49
50} // namespace
51
52std::mutex DartServiceIsolate::callbacks_mutex_;
53
54std::set<std::unique_ptr<DartServiceIsolate::ObservatoryServerStateCallback>>
55 DartServiceIsolate::callbacks_;
56
57void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) {
58 Dart_Handle exception = nullptr;
59 std::string uri =
60 tonic::DartConverter<std::string>::FromArguments(args, 0, exception);
61
62 if (exception) {
63 return;
64 }
65
66 g_observatory_uri = uri;
67
68 // Collect callbacks to fire in a separate collection and invoke them outside
69 // the lock.
70 std::vector<DartServiceIsolate::ObservatoryServerStateCallback>
71 callbacks_to_fire;
72 {
73 std::scoped_lock lock(callbacks_mutex_);
74 for (auto& callback : callbacks_) {
75 callbacks_to_fire.push_back(*callback.get());
76 }
77 }
78
79 for (const auto& callback_to_fire : callbacks_to_fire) {
80 callback_to_fire(uri);
81 }
82}
83
84DartServiceIsolate::CallbackHandle DartServiceIsolate::AddServerStatusCallback(
85 const DartServiceIsolate::ObservatoryServerStateCallback& callback) {
86 if (!callback) {
87 return 0;
88 }
89
90 auto callback_pointer =
91 std::make_unique<DartServiceIsolate::ObservatoryServerStateCallback>(
92 callback);
93
94 auto handle = reinterpret_cast<CallbackHandle>(callback_pointer.get());
95
96 {
97 std::scoped_lock lock(callbacks_mutex_);
98 callbacks_.insert(std::move(callback_pointer));
99 }
100
101 if (!g_observatory_uri.empty()) {
102 callback(g_observatory_uri);
103 }
104
105 return handle;
106}
107
108bool DartServiceIsolate::RemoveServerStatusCallback(
109 CallbackHandle callback_handle) {
110 std::scoped_lock lock(callbacks_mutex_);
111 auto found = std::find_if(
112 callbacks_.begin(), callbacks_.end(),
113 [callback_handle](const auto& item) {
114 return reinterpret_cast<CallbackHandle>(item.get()) == callback_handle;
115 });
116
117 if (found == callbacks_.end()) {
118 return false;
119 }
120
121 callbacks_.erase(found);
122 return true;
123}
124
125void DartServiceIsolate::Shutdown(Dart_NativeArguments args) {
126 // NO-OP.
127}
128
129bool DartServiceIsolate::Startup(std::string server_ip,
130 intptr_t server_port,
131 Dart_LibraryTagHandler embedder_tag_handler,
132 bool disable_origin_check,
133 bool disable_service_auth_codes,
134 bool enable_service_port_fallback,
135 char** error) {
136 Dart_Isolate isolate = Dart_CurrentIsolate();
137 FML_CHECK(isolate);
138
139 // Remember the embedder's library tag handler.
140 g_embedder_tag_handler = embedder_tag_handler;
141 FML_CHECK(g_embedder_tag_handler);
142
143 // Setup native entries.
144 if (!g_natives) {
145 g_natives = new tonic::DartLibraryNatives();
146 g_natives->Register({
147 {"VMServiceIO_NotifyServerState", NotifyServerState, 1, true},
148 {"VMServiceIO_Shutdown", Shutdown, 0, true},
149 });
150 }
151
152 Dart_Handle uri = Dart_NewStringFromCString("dart:vmservice_io");
153 Dart_Handle library = Dart_LookupLibrary(uri);
154 SHUTDOWN_ON_ERROR(library);
155 Dart_Handle result = Dart_SetRootLibrary(library);
156 SHUTDOWN_ON_ERROR(result);
157 result = Dart_SetNativeResolver(library, GetNativeFunction, GetSymbol);
158 SHUTDOWN_ON_ERROR(result);
159
160 // Make runnable.
161 Dart_ExitScope();
162 Dart_ExitIsolate();
163 *error = Dart_IsolateMakeRunnable(isolate);
164 if (*error) {
165 Dart_EnterIsolate(isolate);
166 Dart_ShutdownIsolate();
167 return false;
168 }
169 Dart_EnterIsolate(isolate);
170 Dart_EnterScope();
171
172 library = Dart_RootLibrary();
173 SHUTDOWN_ON_ERROR(library);
174
175 // Set the HTTP server's ip.
176 result = Dart_SetField(library, Dart_NewStringFromCString("_ip"),
177 Dart_NewStringFromCString(server_ip.c_str()));
178 SHUTDOWN_ON_ERROR(result);
179 // If we have a port specified, start the server immediately.
180 bool auto_start = server_port >= 0;
181 if (server_port < 0) {
182 // Adjust server_port to port 0 which will result in the first available
183 // port when the HTTP server is started.
184 server_port = 0;
185 }
186 // Set the HTTP's servers port.
187 result = Dart_SetField(library, Dart_NewStringFromCString("_port"),
188 Dart_NewInteger(server_port));
189 SHUTDOWN_ON_ERROR(result);
190 result = Dart_SetField(library, Dart_NewStringFromCString("_autoStart"),
191 Dart_NewBoolean(auto_start));
192 SHUTDOWN_ON_ERROR(result);
193 result =
194 Dart_SetField(library, Dart_NewStringFromCString("_originCheckDisabled"),
195 Dart_NewBoolean(disable_origin_check));
196 SHUTDOWN_ON_ERROR(result);
197 result =
198 Dart_SetField(library, Dart_NewStringFromCString("_authCodesDisabled"),
199 Dart_NewBoolean(disable_service_auth_codes));
200 SHUTDOWN_ON_ERROR(result);
201 result = Dart_SetField(
202 library, Dart_NewStringFromCString("_enableServicePortFallback"),
203 Dart_NewBoolean(enable_service_port_fallback));
204 SHUTDOWN_ON_ERROR(result);
205 return true;
206}
207
208} // namespace flutter
209