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