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/lib/ui/dart_runtime_hooks.h"
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include <iostream>
12 #include <sstream>
13
14 #include "flutter/common/settings.h"
15 #include "flutter/fml/build_config.h"
16 #include "flutter/fml/logging.h"
17 #include "flutter/lib/ui/plugins/callback_cache.h"
18 #include "flutter/lib/ui/ui_dart_state.h"
19 #include "third_party/dart/runtime/include/bin/dart_io_api.h"
20 #include "third_party/dart/runtime/include/dart_api.h"
21
22 #if defined(OS_ANDROID)
23 #include <android/log.h>
24 #elif defined(OS_IOS)
25 extern "C" {
26 // Cannot import the syslog.h header directly because of macro collision.
27 extern void syslog(int, const char*, ...);
28 }
29 #endif
30
31 using tonic::DartConverter;
32 using tonic::LogIfError;
33 using tonic::ToDart;
34
35 namespace flutter {
36
37 #define REGISTER_FUNCTION(name, count) {"" #name, name, count, true},
38 #define DECLARE_FUNCTION(name, count) \
39 extern void name(Dart_NativeArguments args);
40
41 #define BUILTIN_NATIVE_LIST(V) \
42 V(Logger_PrintString, 1) \
43 V(SaveCompilationTrace, 0) \
44 V(ScheduleMicrotask, 1) \
45 V(GetCallbackHandle, 1) \
46 V(GetCallbackFromHandle, 1)
47
48 BUILTIN_NATIVE_LIST(DECLARE_FUNCTION);
49
RegisterNatives(tonic::DartLibraryNatives * natives)50 void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) {
51 natives->Register({BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)});
52 }
53
PropagateIfError(Dart_Handle result)54 static void PropagateIfError(Dart_Handle result) {
55 if (Dart_IsError(result)) {
56 Dart_PropagateError(result);
57 }
58 }
59
GetFunction(Dart_Handle builtin_library,const char * name)60 static Dart_Handle GetFunction(Dart_Handle builtin_library, const char* name) {
61 Dart_Handle getter_name = ToDart(name);
62 return Dart_Invoke(builtin_library, getter_name, 0, nullptr);
63 }
64
InitDartInternal(Dart_Handle builtin_library,bool is_ui_isolate)65 static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) {
66 Dart_Handle print = GetFunction(builtin_library, "_getPrintClosure");
67
68 Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal"));
69
70 Dart_Handle result =
71 Dart_SetField(internal_library, ToDart("_printClosure"), print);
72 PropagateIfError(result);
73
74 if (is_ui_isolate) {
75 // Call |_setupHooks| to configure |VMLibraryHooks|.
76 Dart_Handle method_name = Dart_NewStringFromCString("_setupHooks");
77 result = Dart_Invoke(builtin_library, method_name, 0, NULL);
78 PropagateIfError(result);
79 }
80
81 Dart_Handle setup_hooks = Dart_NewStringFromCString("_setupHooks");
82
83 Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
84 result = Dart_Invoke(io_lib, setup_hooks, 0, NULL);
85 PropagateIfError(result);
86
87 Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
88 result = Dart_Invoke(isolate_lib, setup_hooks, 0, NULL);
89 PropagateIfError(result);
90 }
91
InitDartCore(Dart_Handle builtin,const std::string & script_uri)92 static void InitDartCore(Dart_Handle builtin, const std::string& script_uri) {
93 Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
94 Dart_Handle get_base_url =
95 Dart_Invoke(io_lib, ToDart("_getUriBaseClosure"), 0, NULL);
96 Dart_Handle core_library = Dart_LookupLibrary(ToDart("dart:core"));
97 Dart_Handle result =
98 Dart_SetField(core_library, ToDart("_uriBaseClosure"), get_base_url);
99 PropagateIfError(result);
100 }
101
InitDartAsync(Dart_Handle builtin_library,bool is_ui_isolate)102 static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) {
103 Dart_Handle schedule_microtask;
104 if (is_ui_isolate) {
105 schedule_microtask =
106 GetFunction(builtin_library, "_getScheduleMicrotaskClosure");
107 } else {
108 Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
109 Dart_Handle method_name =
110 Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
111 schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
112 }
113 Dart_Handle async_library = Dart_LookupLibrary(ToDart("dart:async"));
114 Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
115 Dart_Handle result = Dart_Invoke(async_library, set_schedule_microtask, 1,
116 &schedule_microtask);
117 PropagateIfError(result);
118 }
119
InitDartIO(Dart_Handle builtin_library,const std::string & script_uri)120 static void InitDartIO(Dart_Handle builtin_library,
121 const std::string& script_uri) {
122 Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
123 Dart_Handle platform_type =
124 Dart_GetType(io_lib, ToDart("_Platform"), 0, nullptr);
125 if (!script_uri.empty()) {
126 Dart_Handle result = Dart_SetField(platform_type, ToDart("_nativeScript"),
127 ToDart(script_uri));
128 PropagateIfError(result);
129 }
130 Dart_Handle locale_closure =
131 GetFunction(builtin_library, "_getLocaleClosure");
132 Dart_Handle result =
133 Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure);
134 PropagateIfError(result);
135 }
136
Install(bool is_ui_isolate,const std::string & script_uri)137 void DartRuntimeHooks::Install(bool is_ui_isolate,
138 const std::string& script_uri) {
139 Dart_Handle builtin = Dart_LookupLibrary(ToDart("dart:ui"));
140 InitDartInternal(builtin, is_ui_isolate);
141 InitDartCore(builtin, script_uri);
142 InitDartAsync(builtin, is_ui_isolate);
143 InitDartIO(builtin, script_uri);
144 }
145
146 // Implementation of native functions which are used for some
147 // test/debug functionality in standalone dart mode.
Logger_PrintString(Dart_NativeArguments args)148 void Logger_PrintString(Dart_NativeArguments args) {
149 std::stringstream stream;
150 const auto& logger_prefix = UIDartState::Current()->logger_prefix();
151
152 #if !OS_ANDROID
153 // Prepend all logs with the isolate debug name except on Android where that
154 // prefix is specified in the log tag.
155 if (logger_prefix.size() > 0) {
156 stream << logger_prefix << ": ";
157 }
158 #endif // !OS_ANDROID
159
160 // Append the log buffer obtained from Dart code.
161 {
162 Dart_Handle str = Dart_GetNativeArgument(args, 0);
163 uint8_t* chars = nullptr;
164 intptr_t length = 0;
165 Dart_Handle result = Dart_StringToUTF8(str, &chars, &length);
166 if (Dart_IsError(result)) {
167 Dart_PropagateError(result);
168 return;
169 }
170 if (length > 0) {
171 stream << std::string{reinterpret_cast<const char*>(chars),
172 static_cast<size_t>(length)};
173 }
174 }
175
176 const auto log_string = stream.str();
177 const char* chars = log_string.c_str();
178 const size_t length = log_string.size();
179
180 // Log using platform specific mechanisms
181 {
182 #if defined(OS_ANDROID)
183 // Write to the logcat on Android.
184 __android_log_print(ANDROID_LOG_INFO, logger_prefix.c_str(), "%.*s",
185 (int)length, chars);
186 #elif defined(OS_IOS)
187 // Write to syslog on iOS.
188 //
189 // TODO(cbracken): replace with dedicated communication channel and bypass
190 // iOS logging APIs altogether.
191 syslog(1 /* LOG_ALERT */, "%.*s", (int)length, chars);
192 #else
193 std::cout << log_string << std::endl;
194 #endif
195 }
196
197 if (dart::bin::ShouldCaptureStdout()) {
198 // For now we report print output on the Stdout stream.
199 uint8_t newline[] = {'\n'};
200 Dart_ServiceSendDataEvent("Stdout", "WriteEvent",
201 reinterpret_cast<const uint8_t*>(chars), length);
202 Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline));
203 }
204 }
205
SaveCompilationTrace(Dart_NativeArguments args)206 void SaveCompilationTrace(Dart_NativeArguments args) {
207 uint8_t* buffer = nullptr;
208 intptr_t length = 0;
209 Dart_Handle result = Dart_SaveCompilationTrace(&buffer, &length);
210 if (Dart_IsError(result)) {
211 Dart_SetReturnValue(args, result);
212 return;
213 }
214
215 result = Dart_NewTypedData(Dart_TypedData_kUint8, length);
216 if (Dart_IsError(result)) {
217 Dart_SetReturnValue(args, result);
218 return;
219 }
220
221 Dart_TypedData_Type type;
222 void* data = nullptr;
223 intptr_t size = 0;
224 Dart_Handle status = Dart_TypedDataAcquireData(result, &type, &data, &size);
225 if (Dart_IsError(status)) {
226 Dart_SetReturnValue(args, status);
227 return;
228 }
229
230 memcpy(data, buffer, length);
231 Dart_TypedDataReleaseData(result);
232 Dart_SetReturnValue(args, result);
233 }
234
ScheduleMicrotask(Dart_NativeArguments args)235 void ScheduleMicrotask(Dart_NativeArguments args) {
236 Dart_Handle closure = Dart_GetNativeArgument(args, 0);
237 UIDartState::Current()->ScheduleMicrotask(closure);
238 }
239
GetFunctionLibraryUrl(Dart_Handle closure)240 static std::string GetFunctionLibraryUrl(Dart_Handle closure) {
241 if (Dart_IsClosure(closure)) {
242 closure = Dart_ClosureFunction(closure);
243 PropagateIfError(closure);
244 }
245
246 if (!Dart_IsFunction(closure)) {
247 return "";
248 }
249
250 Dart_Handle url = Dart_Null();
251 Dart_Handle owner = Dart_FunctionOwner(closure);
252 if (Dart_IsInstance(owner)) {
253 owner = Dart_ClassLibrary(owner);
254 }
255 if (Dart_IsLibrary(owner)) {
256 url = Dart_LibraryUrl(owner);
257 PropagateIfError(url);
258 }
259 return DartConverter<std::string>::FromDart(url);
260 }
261
GetFunctionClassName(Dart_Handle closure)262 static std::string GetFunctionClassName(Dart_Handle closure) {
263 Dart_Handle result;
264
265 if (Dart_IsClosure(closure)) {
266 closure = Dart_ClosureFunction(closure);
267 PropagateIfError(closure);
268 }
269
270 if (!Dart_IsFunction(closure)) {
271 return "";
272 }
273
274 bool is_static = false;
275 result = Dart_FunctionIsStatic(closure, &is_static);
276 PropagateIfError(result);
277 if (!is_static) {
278 return "";
279 }
280
281 result = Dart_FunctionOwner(closure);
282 PropagateIfError(result);
283
284 if (Dart_IsLibrary(result) || !Dart_IsInstance(result)) {
285 return "";
286 }
287 return DartConverter<std::string>::FromDart(Dart_ClassName(result));
288 }
289
GetFunctionName(Dart_Handle func)290 static std::string GetFunctionName(Dart_Handle func) {
291 if (Dart_IsClosure(func)) {
292 func = Dart_ClosureFunction(func);
293 PropagateIfError(func);
294 }
295
296 if (!Dart_IsFunction(func)) {
297 return "";
298 }
299
300 bool is_static = false;
301 Dart_Handle result = Dart_FunctionIsStatic(func, &is_static);
302 PropagateIfError(result);
303 if (!is_static) {
304 return "";
305 }
306
307 result = Dart_FunctionName(func);
308 PropagateIfError(result);
309
310 return DartConverter<std::string>::FromDart(result);
311 }
312
GetCallbackHandle(Dart_NativeArguments args)313 void GetCallbackHandle(Dart_NativeArguments args) {
314 Dart_Handle func = Dart_GetNativeArgument(args, 0);
315 std::string name = GetFunctionName(func);
316 std::string class_name = GetFunctionClassName(func);
317 std::string library_path = GetFunctionLibraryUrl(func);
318
319 // `name` is empty if `func` can't be used as a callback. This is the case
320 // when `func` is not a function object or is not a static function. Anonymous
321 // closures (e.g. `(int a, int b) => a + b;`) also cannot be used as
322 // callbacks, so `func` must be a tear-off of a named static function.
323 if (!Dart_IsTearOff(func) || name.empty()) {
324 Dart_SetReturnValue(args, Dart_Null());
325 return;
326 }
327 Dart_SetReturnValue(
328 args, DartConverter<int64_t>::ToDart(DartCallbackCache::GetCallbackHandle(
329 name, class_name, library_path)));
330 }
331
GetCallbackFromHandle(Dart_NativeArguments args)332 void GetCallbackFromHandle(Dart_NativeArguments args) {
333 Dart_Handle h = Dart_GetNativeArgument(args, 0);
334 int64_t handle = DartConverter<int64_t>::FromDart(h);
335 Dart_SetReturnValue(args, DartCallbackCache::GetCallback(handle));
336 }
337
338 } // namespace flutter
339