blob: 1b3dde7b448861fe86a1d98df356d1d78de99c55 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/dart_runtime_hooks.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>
#include "flutter/common/settings.h"
#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/runtime/dart_plugin_registrant.h"
#include "third_party/dart/runtime/include/bin/dart_io_api.h"
#include "third_party/dart/runtime/include/dart_api.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
#include "third_party/tonic/converter/dart_converter.h"
#include "third_party/tonic/dart_library_natives.h"
#include "third_party/tonic/dart_microtask_queue.h"
#include "third_party/tonic/dart_state.h"
#include "third_party/tonic/logging/dart_error.h"
#include "third_party/tonic/logging/dart_invoke.h"
#include "third_party/tonic/scopes/dart_api_scope.h"
#include "third_party/tonic/scopes/dart_isolate_scope.h"
using tonic::DartConverter;
using tonic::ToDart;
namespace flutter {
static void PropagateIfError(Dart_Handle result) {
if (Dart_IsError(result)) {
FML_LOG(ERROR) << "Dart Error: " << ::Dart_GetError(result);
Dart_PropagateError(result);
}
}
static Dart_Handle InvokeFunction(Dart_Handle builtin_library,
const char* name) {
Dart_Handle getter_name = ToDart(name);
return Dart_Invoke(builtin_library, getter_name, 0, nullptr);
}
static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) {
Dart_Handle print = InvokeFunction(builtin_library, "_getPrintClosure");
Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal"));
Dart_Handle result =
Dart_SetField(internal_library, ToDart("_printClosure"), print);
PropagateIfError(result);
if (is_ui_isolate) {
// Call |_setupHooks| to configure |VMLibraryHooks|.
Dart_Handle method_name = Dart_NewStringFromCString("_setupHooks");
result = Dart_Invoke(builtin_library, method_name, 0, NULL);
PropagateIfError(result);
}
Dart_Handle setup_hooks = Dart_NewStringFromCString("_setupHooks");
Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
result = Dart_Invoke(io_lib, setup_hooks, 0, NULL);
PropagateIfError(result);
Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
result = Dart_Invoke(isolate_lib, setup_hooks, 0, NULL);
PropagateIfError(result);
}
static void InitDartCore(Dart_Handle builtin, const std::string& script_uri) {
Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
Dart_Handle get_base_url =
Dart_Invoke(io_lib, ToDart("_getUriBaseClosure"), 0, NULL);
Dart_Handle core_library = Dart_LookupLibrary(ToDart("dart:core"));
Dart_Handle result =
Dart_SetField(core_library, ToDart("_uriBaseClosure"), get_base_url);
PropagateIfError(result);
}
static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) {
Dart_Handle schedule_microtask;
if (is_ui_isolate) {
schedule_microtask =
InvokeFunction(builtin_library, "_getScheduleMicrotaskClosure");
} else {
Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
Dart_Handle method_name =
Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
}
Dart_Handle async_library = Dart_LookupLibrary(ToDart("dart:async"));
Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
Dart_Handle result = Dart_Invoke(async_library, set_schedule_microtask, 1,
&schedule_microtask);
PropagateIfError(result);
}
static void InitDartIO(Dart_Handle builtin_library,
const std::string& script_uri) {
Dart_Handle io_lib = Dart_LookupLibrary(ToDart("dart:io"));
Dart_Handle platform_type =
Dart_GetNonNullableType(io_lib, ToDart("_Platform"), 0, nullptr);
if (!script_uri.empty()) {
Dart_Handle result = Dart_SetField(platform_type, ToDart("_nativeScript"),
ToDart(script_uri));
PropagateIfError(result);
}
// typedef _LocaleClosure = String Function();
Dart_Handle /* _LocaleClosure? */ locale_closure =
InvokeFunction(builtin_library, "_getLocaleClosure");
PropagateIfError(locale_closure);
// static String Function()? _localeClosure;
Dart_Handle result =
Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure);
PropagateIfError(result);
// Register dart:io service extensions used for network profiling.
Dart_Handle network_profiling_type =
Dart_GetNonNullableType(io_lib, ToDart("_NetworkProfiling"), 0, nullptr);
PropagateIfError(network_profiling_type);
result = Dart_Invoke(network_profiling_type,
ToDart("_registerServiceExtension"), 0, nullptr);
PropagateIfError(result);
}
void DartRuntimeHooks::Install(bool is_ui_isolate,
const std::string& script_uri) {
Dart_Handle builtin = Dart_LookupLibrary(ToDart("dart:ui"));
InitDartInternal(builtin, is_ui_isolate);
InitDartCore(builtin, script_uri);
InitDartAsync(builtin, is_ui_isolate);
InitDartIO(builtin, script_uri);
}
void DartRuntimeHooks::Logger_PrintDebugString(const std::string& message) {
#ifndef NDEBUG
DartRuntimeHooks::Logger_PrintString(message);
#endif
}
void DartRuntimeHooks::Logger_PrintString(const std::string& message) {
const auto& tag = UIDartState::Current()->logger_prefix();
UIDartState::Current()->LogMessage(tag, message);
if (dart::bin::ShouldCaptureStdout()) {
std::stringstream stream;
if (!tag.empty()) {
stream << tag << ": ";
}
stream << message;
std::string log = stream.str();
// For now we report print output on the Stdout stream.
uint8_t newline[] = {'\n'};
Dart_ServiceSendDataEvent("Stdout", "WriteEvent",
reinterpret_cast<const uint8_t*>(log.c_str()),
log.size());
Dart_ServiceSendDataEvent("Stdout", "WriteEvent", newline, sizeof(newline));
}
}
void DartRuntimeHooks::ScheduleMicrotask(Dart_Handle closure) {
UIDartState::Current()->ScheduleMicrotask(closure);
}
static std::string GetFunctionLibraryUrl(Dart_Handle closure) {
if (Dart_IsClosure(closure)) {
closure = Dart_ClosureFunction(closure);
PropagateIfError(closure);
}
if (!Dart_IsFunction(closure)) {
return "";
}
Dart_Handle url = Dart_Null();
Dart_Handle owner = Dart_FunctionOwner(closure);
if (Dart_IsInstance(owner)) {
owner = Dart_ClassLibrary(owner);
}
if (Dart_IsLibrary(owner)) {
url = Dart_LibraryUrl(owner);
PropagateIfError(url);
}
return DartConverter<std::string>::FromDart(url);
}
static std::string GetFunctionClassName(Dart_Handle closure) {
Dart_Handle result;
if (Dart_IsClosure(closure)) {
closure = Dart_ClosureFunction(closure);
PropagateIfError(closure);
}
if (!Dart_IsFunction(closure)) {
return "";
}
bool is_static = false;
result = Dart_FunctionIsStatic(closure, &is_static);
PropagateIfError(result);
if (!is_static) {
return "";
}
result = Dart_FunctionOwner(closure);
PropagateIfError(result);
if (Dart_IsLibrary(result) || !Dart_IsInstance(result)) {
return "";
}
return DartConverter<std::string>::FromDart(Dart_ClassName(result));
}
static std::string GetFunctionName(Dart_Handle func) {
if (Dart_IsClosure(func)) {
func = Dart_ClosureFunction(func);
PropagateIfError(func);
}
if (!Dart_IsFunction(func)) {
return "";
}
bool is_static = false;
Dart_Handle result = Dart_FunctionIsStatic(func, &is_static);
PropagateIfError(result);
if (!is_static) {
return "";
}
result = Dart_FunctionName(func);
PropagateIfError(result);
return DartConverter<std::string>::FromDart(result);
}
Dart_Handle DartRuntimeHooks::GetCallbackHandle(Dart_Handle func) {
std::string name = GetFunctionName(func);
std::string class_name = GetFunctionClassName(func);
std::string library_path = GetFunctionLibraryUrl(func);
// `name` is empty if `func` can't be used as a callback. This is the case
// when `func` is not a function object or is not a static function. Anonymous
// closures (e.g. `(int a, int b) => a + b;`) also cannot be used as
// callbacks, so `func` must be a tear-off of a named static function.
if (!Dart_IsTearOff(func) || name.empty()) {
return Dart_Null();
}
return DartConverter<int64_t>::ToDart(
DartCallbackCache::GetCallbackHandle(name, class_name, library_path));
}
Dart_Handle DartRuntimeHooks::GetCallbackFromHandle(int64_t handle) {
Dart_Handle result = DartCallbackCache::GetCallback(handle);
PropagateIfError(result);
return result;
}
void DartPluginRegistrant_EnsureInitialized() {
tonic::DartApiScope api_scope;
FindAndInvokeDartPluginRegistrant();
}
} // namespace flutter