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 <cstdio> |
8 | #include <cstdlib> |
9 | #include <cstring> |
10 | #include <iostream> |
11 | #include <sstream> |
12 | |
13 | #include "flutter/common/settings.h" |
14 | #include "flutter/fml/build_config.h" |
15 | #include "flutter/fml/logging.h" |
16 | #include "flutter/lib/ui/plugins/callback_cache.h" |
17 | #include "flutter/lib/ui/ui_dart_state.h" |
18 | #include "flutter/runtime/dart_plugin_registrant.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 | using tonic::DartConverter; |
32 | using tonic::ToDart; |
33 | |
34 | namespace flutter { |
35 | |
36 | static void PropagateIfError(Dart_Handle result) { |
37 | if (Dart_IsError(handle: result)) { |
38 | FML_LOG(ERROR) << "Dart Error: " << ::Dart_GetError(handle: result); |
39 | Dart_PropagateError(handle: result); |
40 | } |
41 | } |
42 | |
43 | static Dart_Handle InvokeFunction(Dart_Handle builtin_library, |
44 | const char* name) { |
45 | Dart_Handle getter_name = ToDart(val: name); |
46 | return Dart_Invoke(target: builtin_library, name: getter_name, number_of_arguments: 0, arguments: nullptr); |
47 | } |
48 | |
49 | static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) { |
50 | Dart_Handle print = InvokeFunction(builtin_library, name: "_getPrintClosure" ); |
51 | |
52 | Dart_Handle internal_library = Dart_LookupLibrary(url: ToDart(val: "dart:_internal" )); |
53 | |
54 | Dart_Handle result = |
55 | Dart_SetField(container: internal_library, name: ToDart(val: "_printClosure" ), value: print); |
56 | PropagateIfError(result); |
57 | |
58 | if (is_ui_isolate) { |
59 | // Call |_setupHooks| to configure |VMLibraryHooks|. |
60 | Dart_Handle method_name = Dart_NewStringFromCString(str: "_setupHooks" ); |
61 | result = Dart_Invoke(target: builtin_library, name: method_name, number_of_arguments: 0, NULL); |
62 | PropagateIfError(result); |
63 | } |
64 | |
65 | Dart_Handle setup_hooks = Dart_NewStringFromCString(str: "_setupHooks" ); |
66 | |
67 | Dart_Handle io_lib = Dart_LookupLibrary(url: ToDart(val: "dart:io" )); |
68 | result = Dart_Invoke(target: io_lib, name: setup_hooks, number_of_arguments: 0, NULL); |
69 | PropagateIfError(result); |
70 | |
71 | Dart_Handle isolate_lib = Dart_LookupLibrary(url: ToDart(val: "dart:isolate" )); |
72 | result = Dart_Invoke(target: isolate_lib, name: setup_hooks, number_of_arguments: 0, NULL); |
73 | PropagateIfError(result); |
74 | } |
75 | |
76 | static void InitDartCore(Dart_Handle builtin, const std::string& script_uri) { |
77 | Dart_Handle io_lib = Dart_LookupLibrary(url: ToDart(val: "dart:io" )); |
78 | Dart_Handle get_base_url = |
79 | Dart_Invoke(target: io_lib, name: ToDart(val: "_getUriBaseClosure" ), number_of_arguments: 0, NULL); |
80 | Dart_Handle core_library = Dart_LookupLibrary(url: ToDart(val: "dart:core" )); |
81 | Dart_Handle result = |
82 | Dart_SetField(container: core_library, name: ToDart(val: "_uriBaseClosure" ), value: get_base_url); |
83 | PropagateIfError(result); |
84 | } |
85 | |
86 | static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) { |
87 | Dart_Handle schedule_microtask; |
88 | if (is_ui_isolate) { |
89 | schedule_microtask = |
90 | InvokeFunction(builtin_library, name: "_getScheduleMicrotaskClosure" ); |
91 | } else { |
92 | Dart_Handle isolate_lib = Dart_LookupLibrary(url: ToDart(val: "dart:isolate" )); |
93 | Dart_Handle method_name = |
94 | Dart_NewStringFromCString(str: "_getIsolateScheduleImmediateClosure" ); |
95 | schedule_microtask = Dart_Invoke(target: isolate_lib, name: method_name, number_of_arguments: 0, NULL); |
96 | } |
97 | Dart_Handle async_library = Dart_LookupLibrary(url: ToDart(val: "dart:async" )); |
98 | Dart_Handle set_schedule_microtask = ToDart(val: "_setScheduleImmediateClosure" ); |
99 | Dart_Handle result = Dart_Invoke(target: async_library, name: set_schedule_microtask, number_of_arguments: 1, |
100 | arguments: &schedule_microtask); |
101 | PropagateIfError(result); |
102 | } |
103 | |
104 | static void InitDartIO(Dart_Handle builtin_library, |
105 | const std::string& script_uri) { |
106 | Dart_Handle io_lib = Dart_LookupLibrary(url: ToDart(val: "dart:io" )); |
107 | Dart_Handle platform_type = |
108 | Dart_GetNonNullableType(library: io_lib, class_name: ToDart(val: "_Platform" ), number_of_type_arguments: 0, type_arguments: nullptr); |
109 | if (!script_uri.empty()) { |
110 | Dart_Handle result = Dart_SetField(container: platform_type, name: ToDart(val: "_nativeScript" ), |
111 | value: ToDart(object: script_uri)); |
112 | PropagateIfError(result); |
113 | } |
114 | // typedef _LocaleClosure = String Function(); |
115 | Dart_Handle /* _LocaleClosure? */ locale_closure = |
116 | InvokeFunction(builtin_library, name: "_getLocaleClosure" ); |
117 | PropagateIfError(result: locale_closure); |
118 | // static String Function()? _localeClosure; |
119 | Dart_Handle result = |
120 | Dart_SetField(container: platform_type, name: ToDart(val: "_localeClosure" ), value: locale_closure); |
121 | PropagateIfError(result); |
122 | |
123 | #if !FLUTTER_RELEASE |
124 | // Register dart:io service extensions used for network profiling. |
125 | Dart_Handle network_profiling_type = |
126 | Dart_GetNonNullableType(library: io_lib, class_name: ToDart(val: "_NetworkProfiling" ), number_of_type_arguments: 0, type_arguments: nullptr); |
127 | PropagateIfError(result: network_profiling_type); |
128 | result = Dart_Invoke(target: network_profiling_type, |
129 | name: ToDart(val: "_registerServiceExtension" ), number_of_arguments: 0, arguments: nullptr); |
130 | PropagateIfError(result); |
131 | #endif // !FLUTTER_RELEASE |
132 | } |
133 | |
134 | void DartRuntimeHooks::Install(bool is_ui_isolate, |
135 | const std::string& script_uri) { |
136 | Dart_Handle builtin = Dart_LookupLibrary(url: ToDart(val: "dart:ui" )); |
137 | InitDartInternal(builtin_library: builtin, is_ui_isolate); |
138 | InitDartCore(builtin, script_uri); |
139 | InitDartAsync(builtin_library: builtin, is_ui_isolate); |
140 | InitDartIO(builtin_library: builtin, script_uri); |
141 | } |
142 | |
143 | void DartRuntimeHooks::Logger_PrintDebugString(const std::string& message) { |
144 | #ifndef NDEBUG |
145 | DartRuntimeHooks::Logger_PrintString(message); |
146 | #endif |
147 | } |
148 | |
149 | void DartRuntimeHooks::Logger_PrintString(const std::string& message) { |
150 | const auto& tag = UIDartState::Current()->logger_prefix(); |
151 | UIDartState::Current()->LogMessage(tag, message); |
152 | |
153 | if (dart::bin::ShouldCaptureStdout()) { |
154 | std::stringstream stream; |
155 | if (!tag.empty()) { |
156 | stream << tag << ": " ; |
157 | } |
158 | stream << message; |
159 | std::string log = stream.str(); |
160 | |
161 | // For now we report print output on the Stdout stream. |
162 | uint8_t newline[] = {'\n'}; |
163 | Dart_ServiceSendDataEvent(stream_id: "Stdout" , event_kind: "WriteEvent" , |
164 | bytes: reinterpret_cast<const uint8_t*>(log.c_str()), |
165 | bytes_length: log.size()); |
166 | Dart_ServiceSendDataEvent(stream_id: "Stdout" , event_kind: "WriteEvent" , bytes: newline, bytes_length: sizeof(newline)); |
167 | } |
168 | } |
169 | |
170 | void DartRuntimeHooks::ScheduleMicrotask(Dart_Handle closure) { |
171 | UIDartState::Current()->ScheduleMicrotask(handle: closure); |
172 | } |
173 | |
174 | static std::string GetFunctionLibraryUrl(Dart_Handle closure) { |
175 | if (Dart_IsClosure(object: closure)) { |
176 | closure = Dart_ClosureFunction(closure); |
177 | PropagateIfError(result: closure); |
178 | } |
179 | |
180 | if (!Dart_IsFunction(handle: closure)) { |
181 | return "" ; |
182 | } |
183 | |
184 | Dart_Handle url = Dart_Null(); |
185 | Dart_Handle owner = Dart_FunctionOwner(function: closure); |
186 | if (Dart_IsInstance(object: owner)) { |
187 | owner = Dart_ClassLibrary(cls_type: owner); |
188 | } |
189 | if (Dart_IsLibrary(object: owner)) { |
190 | url = Dart_LibraryUrl(library: owner); |
191 | PropagateIfError(result: url); |
192 | } |
193 | return DartConverter<std::string>::FromDart(handle: url); |
194 | } |
195 | |
196 | static std::string GetFunctionClassName(Dart_Handle closure) { |
197 | Dart_Handle result; |
198 | |
199 | if (Dart_IsClosure(object: closure)) { |
200 | closure = Dart_ClosureFunction(closure); |
201 | PropagateIfError(result: closure); |
202 | } |
203 | |
204 | if (!Dart_IsFunction(handle: closure)) { |
205 | return "" ; |
206 | } |
207 | |
208 | bool is_static = false; |
209 | result = Dart_FunctionIsStatic(function: closure, is_static: &is_static); |
210 | PropagateIfError(result); |
211 | if (!is_static) { |
212 | return "" ; |
213 | } |
214 | |
215 | result = Dart_FunctionOwner(function: closure); |
216 | PropagateIfError(result); |
217 | |
218 | if (Dart_IsLibrary(object: result) || !Dart_IsInstance(object: result)) { |
219 | return "" ; |
220 | } |
221 | return DartConverter<std::string>::FromDart(handle: Dart_ClassName(cls_type: result)); |
222 | } |
223 | |
224 | static std::string GetFunctionName(Dart_Handle func) { |
225 | if (Dart_IsClosure(object: func)) { |
226 | func = Dart_ClosureFunction(closure: func); |
227 | PropagateIfError(result: func); |
228 | } |
229 | |
230 | if (!Dart_IsFunction(handle: func)) { |
231 | return "" ; |
232 | } |
233 | |
234 | bool is_static = false; |
235 | Dart_Handle result = Dart_FunctionIsStatic(function: func, is_static: &is_static); |
236 | PropagateIfError(result); |
237 | if (!is_static) { |
238 | return "" ; |
239 | } |
240 | |
241 | result = Dart_FunctionName(function: func); |
242 | PropagateIfError(result); |
243 | |
244 | return DartConverter<std::string>::FromDart(handle: result); |
245 | } |
246 | |
247 | Dart_Handle DartRuntimeHooks::GetCallbackHandle(Dart_Handle func) { |
248 | std::string name = GetFunctionName(func); |
249 | std::string class_name = GetFunctionClassName(closure: func); |
250 | std::string library_path = GetFunctionLibraryUrl(closure: func); |
251 | |
252 | // `name` is empty if `func` can't be used as a callback. This is the case |
253 | // when `func` is not a function object or is not a static function. Anonymous |
254 | // closures (e.g. `(int a, int b) => a + b;`) also cannot be used as |
255 | // callbacks, so `func` must be a tear-off of a named static function. |
256 | if (!Dart_IsTearOff(object: func) || name.empty()) { |
257 | return Dart_Null(); |
258 | } |
259 | return DartConverter<int64_t>::ToDart( |
260 | val: DartCallbackCache::GetCallbackHandle(name, class_name, library_path)); |
261 | } |
262 | |
263 | Dart_Handle DartRuntimeHooks::GetCallbackFromHandle(int64_t handle) { |
264 | Dart_Handle result = DartCallbackCache::GetCallback(handle); |
265 | PropagateIfError(result); |
266 | return result; |
267 | } |
268 | |
269 | void DartPluginRegistrant_EnsureInitialized() { |
270 | tonic::DartApiScope api_scope; |
271 | FindAndInvokeDartPluginRegistrant(); |
272 | } |
273 | |
274 | } // namespace flutter |
275 | |