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) |
34 | extern "C" { |
35 | // Cannot import the syslog.h header directly because of macro collision. |
36 | extern void syslog(int, const char*, ...); |
37 | } |
38 | #endif |
39 | |
40 | using tonic::DartConverter; |
41 | using tonic::LogIfError; |
42 | using tonic::ToDart; |
43 | |
44 | namespace 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 | |
58 | BUILTIN_NATIVE_LIST(DECLARE_FUNCTION); |
59 | |
60 | void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) { |
61 | natives->Register({BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)}); |
62 | } |
63 | |
64 | static void PropagateIfError(Dart_Handle result) { |
65 | if (Dart_IsError(result)) { |
66 | Dart_PropagateError(result); |
67 | } |
68 | } |
69 | |
70 | static 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 | |
75 | static 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 | |
102 | static 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 | |
112 | static 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 | |
130 | static 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 | |
155 | void 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 | |
164 | void 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. |
172 | void 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 | |
230 | void 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 | |
259 | void ScheduleMicrotask(Dart_NativeArguments args) { |
260 | Dart_Handle closure = Dart_GetNativeArgument(args, 0); |
261 | UIDartState::Current()->ScheduleMicrotask(closure); |
262 | } |
263 | |
264 | static 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 | |
286 | static 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 | |
314 | static 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 | |
337 | void 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 | |
356 | void 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 | |