mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
* Roll src/third_party/dart 06c3d7ad3a...09fc76bc51 dart-lang/sdk@09fc76bc51 [vm, compiler] Remove dead _classRangeCheckNegative. dart-lang/sdk@b472d7a9be Unnecessary null aware spread hint dart-lang/sdk@f939ad3964 library scope extensions dart-lang/sdk@9503969664 [vm/bytecode] Add DebugCheck bytecode instruction dart-lang/sdk@fc542be6b4 Issue 36682. Check that selection offset/length is valid in Extract Method refactoring. dart-lang/sdk@803658a6d8 [corelib_2] fix bigint_from_test for web platforms dart-lang/sdk@ad6b1ebbd6 Support for 'double' fields in protocol. dart-lang/sdk@5479175012 Remove incorrect test. dart-lang/sdk@058092b5f6 [vm] Fix simarm_x64 build on mac dart-lang/sdk@307ca3f3b6 [vm/bytecode] Improve single stepping and breakpoint setting in bytecode. dart-lang/sdk@855830f324 Update LSP spec dart-lang/sdk@d992f55094 [dartfix] Bump pedantic dep to v1.8.0 and cleanup lint violations dart-lang/sdk@41330f3e34 gitignore .clangd dart-lang/sdk@8d07009931 [ VM / Gardening ] Mark disassemble_determinism_test as slow on Windows dart-lang/sdk@1b82367ed6 Update CHANGELOG to reflect breaking change #36765 dart-lang/sdk@a86db84e0a [fasta] Preserve information about const constructors in outline. dart-lang/sdk@25319ef7a6 Fix bug in codegen/string_escapes_test.dart. dart-lang/sdk@7acecda2cc [vm/ffi] Fix FFI Utf8 example. dart-lang/sdk@e3b3c6fa28 [vm] Rename 3head flutter patch after re-land of concurrency change dart-lang/sdk@2cb5303782 [infra] Update checked in SDKs to 2.4.0 dart-lang/sdk@fce43ebc4e [ddk] Pass environmentDefines to CFE through CompilerOptions dart-lang/sdk@5450d08ca1 Add support for analyzer static error tests. dart-lang/sdk@0425997b31 Second attempt to reland "[vm/concurrency] Introduce concept of Isolate Groups" dart-lang/sdk@5470159054 [vm, gc] Produce a proper error message when crashing due to lack of memory at isolate startup. dart-lang/sdk@bfc7d21da6 Remove useless dart2js_extra/class_test dart-lang/sdk@572619b639 [vm, bytecode] Fix vm/cc/CompileFunctionOnHelperThread. dart-lang/sdk@4de495fccc [vm, compiler] Remove dead stub ICCallThroughFunction. dart-lang/sdk@8a1dcdae68 Graduate 'flutter' domain from experimental, remove unused methods and fields. dart-lang/sdk@a945888627 Discard constructors and fields temporarily to get valid code compiling * Fix analyzer warning.
462 lines
15 KiB
C++
462 lines
15 KiB
C++
// 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/runtime/dart_vm.h"
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
#include "flutter/common/settings.h"
|
|
#include "flutter/fml/compiler_specific.h"
|
|
#include "flutter/fml/file.h"
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/fml/mapping.h"
|
|
#include "flutter/fml/size.h"
|
|
#include "flutter/fml/synchronization/count_down_latch.h"
|
|
#include "flutter/fml/synchronization/thread_annotations.h"
|
|
#include "flutter/fml/time/time_delta.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
#include "flutter/lib/io/dart_io.h"
|
|
#include "flutter/lib/ui/dart_runtime_hooks.h"
|
|
#include "flutter/lib/ui/dart_ui.h"
|
|
#include "flutter/runtime/dart_isolate.h"
|
|
#include "flutter/runtime/dart_service_isolate.h"
|
|
#include "flutter/runtime/start_up.h"
|
|
#include "third_party/dart/runtime/include/bin/dart_io_api.h"
|
|
#include "third_party/tonic/converter/dart_converter.h"
|
|
#include "third_party/tonic/dart_class_library.h"
|
|
#include "third_party/tonic/dart_class_provider.h"
|
|
#include "third_party/tonic/file_loader/file_loader.h"
|
|
#include "third_party/tonic/logging/dart_error.h"
|
|
#include "third_party/tonic/scopes/dart_api_scope.h"
|
|
#include "third_party/tonic/typed_data/typed_list.h"
|
|
|
|
namespace dart {
|
|
namespace observatory {
|
|
|
|
#if !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE)
|
|
|
|
// These two symbols are defined in |observatory_archive.cc| which is generated
|
|
// by the |//third_party/dart/runtime/observatory:archive_observatory| rule.
|
|
// Both of these symbols will be part of the data segment and therefore are read
|
|
// only.
|
|
extern unsigned int observatory_assets_archive_len;
|
|
extern const uint8_t* observatory_assets_archive;
|
|
|
|
#endif // !OS_FUCHSIA && (FLUTTER_RUNTIME_MODE !=
|
|
// FLUTTER_RUNTIME_MODE_RELEASE)
|
|
|
|
} // namespace observatory
|
|
} // namespace dart
|
|
|
|
namespace flutter {
|
|
|
|
// Arguments passed to the Dart VM in all configurations.
|
|
static const char* kDartLanguageArgs[] = {
|
|
// clang-format off
|
|
"--enable_mirrors=false",
|
|
"--background_compilation",
|
|
"--causal_async_stacks",
|
|
// clang-format on
|
|
};
|
|
|
|
static const char* kDartPrecompilationArgs[] = {
|
|
"--precompilation",
|
|
};
|
|
|
|
FML_ALLOW_UNUSED_TYPE
|
|
static const char* kDartWriteProtectCodeArgs[] = {
|
|
"--no_write_protect_code",
|
|
};
|
|
|
|
static const char* kDartAssertArgs[] = {
|
|
// clang-format off
|
|
"--enable_asserts",
|
|
// clang-format on
|
|
};
|
|
|
|
static const char* kDartStartPausedArgs[]{
|
|
"--pause_isolates_on_start",
|
|
};
|
|
|
|
static const char* kDartDisableServiceAuthCodesArgs[]{
|
|
"--disable-service-auth-codes",
|
|
};
|
|
|
|
static const char* kDartTraceStartupArgs[]{
|
|
"--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM",
|
|
};
|
|
|
|
static const char* kDartEndlessTraceBufferArgs[]{
|
|
"--timeline_recorder=endless",
|
|
};
|
|
|
|
static const char* kDartSystraceTraceBufferArgs[]{
|
|
"--timeline_recorder=systrace",
|
|
};
|
|
|
|
static const char* kDartFuchsiaTraceArgs[] FML_ALLOW_UNUSED_TYPE = {
|
|
"--systrace_timeline",
|
|
};
|
|
|
|
static const char* kDartTraceStreamsArgs[] = {
|
|
"--timeline_streams=Compiler,Dart,Debugger,Embedder,GC,Isolate,VM",
|
|
};
|
|
|
|
constexpr char kFileUriPrefix[] = "file://";
|
|
constexpr size_t kFileUriPrefixLength = sizeof(kFileUriPrefix) - 1;
|
|
|
|
bool DartFileModifiedCallback(const char* source_url, int64_t since_ms) {
|
|
if (strncmp(source_url, kFileUriPrefix, kFileUriPrefixLength) != 0u) {
|
|
// Assume modified.
|
|
return true;
|
|
}
|
|
|
|
const char* path = source_url + kFileUriPrefixLength;
|
|
struct stat info;
|
|
if (stat(path, &info) < 0)
|
|
return true;
|
|
|
|
// If st_mtime is zero, it's more likely that the file system doesn't support
|
|
// mtime than that the file was actually modified in the 1970s.
|
|
if (!info.st_mtime)
|
|
return true;
|
|
|
|
// It's very unclear what time bases we're with here. The Dart API doesn't
|
|
// document the time base for since_ms. Reading the code, the value varies by
|
|
// platform, with a typical source being something like gettimeofday.
|
|
//
|
|
// We add one to st_mtime because st_mtime has less precision than since_ms
|
|
// and we want to treat the file as modified if the since time is between
|
|
// ticks of the mtime.
|
|
fml::TimeDelta mtime = fml::TimeDelta::FromSeconds(info.st_mtime + 1);
|
|
fml::TimeDelta since = fml::TimeDelta::FromMilliseconds(since_ms);
|
|
|
|
return mtime > since;
|
|
}
|
|
|
|
void ThreadExitCallback() {}
|
|
|
|
Dart_Handle GetVMServiceAssetsArchiveCallback() {
|
|
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE)
|
|
return nullptr;
|
|
#elif OS_FUCHSIA
|
|
fml::UniqueFD fd = fml::OpenFile("pkg/data/observatory.tar", false,
|
|
fml::FilePermission::kRead);
|
|
fml::FileMapping mapping(fd, {fml::FileMapping::Protection::kRead});
|
|
if (mapping.GetSize() == 0 || mapping.GetMapping() == nullptr) {
|
|
FML_LOG(ERROR) << "Fail to load Observatory archive";
|
|
return nullptr;
|
|
}
|
|
return tonic::DartConverter<tonic::Uint8List>::ToDart(mapping.GetMapping(),
|
|
mapping.GetSize());
|
|
#else
|
|
return tonic::DartConverter<tonic::Uint8List>::ToDart(
|
|
::dart::observatory::observatory_assets_archive,
|
|
::dart::observatory::observatory_assets_archive_len);
|
|
#endif
|
|
}
|
|
|
|
static const char kStdoutStreamId[] = "Stdout";
|
|
static const char kStderrStreamId[] = "Stderr";
|
|
|
|
static bool ServiceStreamListenCallback(const char* stream_id) {
|
|
if (strcmp(stream_id, kStdoutStreamId) == 0) {
|
|
dart::bin::SetCaptureStdout(true);
|
|
return true;
|
|
} else if (strcmp(stream_id, kStderrStreamId) == 0) {
|
|
dart::bin::SetCaptureStderr(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void ServiceStreamCancelCallback(const char* stream_id) {
|
|
if (strcmp(stream_id, kStdoutStreamId) == 0) {
|
|
dart::bin::SetCaptureStdout(false);
|
|
} else if (strcmp(stream_id, kStderrStreamId) == 0) {
|
|
dart::bin::SetCaptureStderr(false);
|
|
}
|
|
}
|
|
|
|
bool DartVM::IsRunningPrecompiledCode() {
|
|
return Dart_IsPrecompiledRuntime();
|
|
}
|
|
|
|
static std::vector<const char*> ProfilingFlags(bool enable_profiling) {
|
|
// Disable Dart's built in profiler when building a debug build. This
|
|
// works around a race condition that would sometimes stop a crash's
|
|
// stack trace from being printed on Android.
|
|
#ifndef NDEBUG
|
|
enable_profiling = false;
|
|
#endif
|
|
|
|
// We want to disable profiling by default because it overwhelms LLDB. But
|
|
// the VM enables the same by default. In either case, we have some profiling
|
|
// flags.
|
|
if (enable_profiling) {
|
|
return {// This is the default. But just be explicit.
|
|
"--profiler",
|
|
// This instructs the profiler to walk C++ frames, and to include
|
|
// them in the profile.
|
|
"--profile-vm"};
|
|
} else {
|
|
return {"--no-profiler"};
|
|
}
|
|
}
|
|
|
|
void PushBackAll(std::vector<const char*>* args,
|
|
const char** argv,
|
|
size_t argc) {
|
|
for (size_t i = 0; i < argc; ++i) {
|
|
args->push_back(argv[i]);
|
|
}
|
|
}
|
|
|
|
static void EmbedderInformationCallback(Dart_EmbedderInformation* info) {
|
|
info->version = DART_EMBEDDER_INFORMATION_CURRENT_VERSION;
|
|
dart::bin::GetIOEmbedderInformation(info);
|
|
info->name = "Flutter";
|
|
}
|
|
|
|
std::shared_ptr<DartVM> DartVM::Create(
|
|
Settings settings,
|
|
fml::RefPtr<DartSnapshot> vm_snapshot,
|
|
fml::RefPtr<DartSnapshot> isolate_snapshot,
|
|
fml::RefPtr<DartSnapshot> shared_snapshot,
|
|
std::shared_ptr<IsolateNameServer> isolate_name_server) {
|
|
auto vm_data = DartVMData::Create(settings, //
|
|
std::move(vm_snapshot), //
|
|
std::move(isolate_snapshot), //
|
|
std::move(shared_snapshot) //
|
|
);
|
|
|
|
if (!vm_data) {
|
|
FML_LOG(ERROR) << "Could not setup VM data to bootstrap the VM from.";
|
|
return {};
|
|
}
|
|
|
|
// Note: std::make_shared unviable due to hidden constructor.
|
|
return std::shared_ptr<DartVM>(
|
|
new DartVM(std::move(vm_data), std::move(isolate_name_server)));
|
|
}
|
|
|
|
static std::atomic_size_t gVMLaunchCount;
|
|
|
|
size_t DartVM::GetVMLaunchCount() {
|
|
return gVMLaunchCount;
|
|
}
|
|
|
|
DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
|
|
std::shared_ptr<IsolateNameServer> isolate_name_server)
|
|
: settings_(vm_data->GetSettings()),
|
|
concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()),
|
|
vm_data_(vm_data),
|
|
isolate_name_server_(std::move(isolate_name_server)),
|
|
service_protocol_(std::make_shared<ServiceProtocol>()) {
|
|
TRACE_EVENT0("flutter", "DartVMInitializer");
|
|
|
|
gVMLaunchCount++;
|
|
|
|
FML_DCHECK(vm_data_);
|
|
FML_DCHECK(isolate_name_server_);
|
|
FML_DCHECK(service_protocol_);
|
|
|
|
FML_DLOG(INFO) << "Attempting Dart VM launch for mode: "
|
|
<< (IsRunningPrecompiledCode() ? "AOT" : "Interpreter");
|
|
|
|
{
|
|
TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
|
|
dart::bin::BootstrapDartIo();
|
|
|
|
if (!settings_.temp_directory_path.empty()) {
|
|
dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str());
|
|
}
|
|
}
|
|
|
|
std::vector<const char*> args;
|
|
|
|
// Instruct the VM to ignore unrecognized flags.
|
|
// There is a lot of diversity in a lot of combinations when it
|
|
// comes to the arguments the VM supports. And, if the VM comes across a flag
|
|
// it does not recognize, it exits immediately.
|
|
args.push_back("--ignore-unrecognized-flags");
|
|
|
|
for (auto* const profiler_flag :
|
|
ProfilingFlags(settings_.enable_dart_profiling)) {
|
|
args.push_back(profiler_flag);
|
|
}
|
|
|
|
PushBackAll(&args, kDartLanguageArgs, fml::size(kDartLanguageArgs));
|
|
|
|
if (IsRunningPrecompiledCode()) {
|
|
PushBackAll(&args, kDartPrecompilationArgs,
|
|
fml::size(kDartPrecompilationArgs));
|
|
}
|
|
|
|
// Enable Dart assertions if we are not running precompiled code. We run non-
|
|
// precompiled code only in the debug product mode.
|
|
bool enable_asserts = !settings_.disable_dart_asserts;
|
|
|
|
#if !OS_FUCHSIA
|
|
if (IsRunningPrecompiledCode()) {
|
|
enable_asserts = false;
|
|
}
|
|
#endif // !OS_FUCHSIA
|
|
|
|
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
|
// Debug mode uses the JIT, disable code page write protection to avoid
|
|
// memory page protection changes before and after every compilation.
|
|
PushBackAll(&args, kDartWriteProtectCodeArgs,
|
|
fml::size(kDartWriteProtectCodeArgs));
|
|
#endif
|
|
|
|
if (enable_asserts) {
|
|
PushBackAll(&args, kDartAssertArgs, fml::size(kDartAssertArgs));
|
|
}
|
|
|
|
if (settings_.start_paused) {
|
|
PushBackAll(&args, kDartStartPausedArgs, fml::size(kDartStartPausedArgs));
|
|
}
|
|
|
|
if (settings_.disable_service_auth_codes) {
|
|
PushBackAll(&args, kDartDisableServiceAuthCodesArgs,
|
|
fml::size(kDartDisableServiceAuthCodesArgs));
|
|
}
|
|
|
|
if (settings_.endless_trace_buffer || settings_.trace_startup) {
|
|
// If we are tracing startup, make sure the trace buffer is endless so we
|
|
// don't lose early traces.
|
|
PushBackAll(&args, kDartEndlessTraceBufferArgs,
|
|
fml::size(kDartEndlessTraceBufferArgs));
|
|
}
|
|
|
|
if (settings_.trace_systrace) {
|
|
PushBackAll(&args, kDartSystraceTraceBufferArgs,
|
|
fml::size(kDartSystraceTraceBufferArgs));
|
|
PushBackAll(&args, kDartTraceStreamsArgs, fml::size(kDartTraceStreamsArgs));
|
|
}
|
|
|
|
if (settings_.trace_startup) {
|
|
PushBackAll(&args, kDartTraceStartupArgs, fml::size(kDartTraceStartupArgs));
|
|
}
|
|
|
|
#if defined(OS_FUCHSIA)
|
|
PushBackAll(&args, kDartFuchsiaTraceArgs, fml::size(kDartFuchsiaTraceArgs));
|
|
PushBackAll(&args, kDartTraceStreamsArgs, fml::size(kDartTraceStreamsArgs));
|
|
#endif
|
|
|
|
for (size_t i = 0; i < settings_.dart_flags.size(); i++)
|
|
args.push_back(settings_.dart_flags[i].c_str());
|
|
|
|
char* flags_error = Dart_SetVMFlags(args.size(), args.data());
|
|
if (flags_error) {
|
|
FML_LOG(FATAL) << "Error while setting Dart VM flags: " << flags_error;
|
|
::free(flags_error);
|
|
}
|
|
|
|
DartUI::InitForGlobal();
|
|
|
|
{
|
|
TRACE_EVENT0("flutter", "Dart_Initialize");
|
|
Dart_InitializeParams params = {};
|
|
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
|
|
params.vm_snapshot_data = vm_data_->GetVMSnapshot().GetDataMapping();
|
|
params.vm_snapshot_instructions =
|
|
vm_data_->GetVMSnapshot().GetInstructionsMapping();
|
|
params.create_group = reinterpret_cast<decltype(params.create_group)>(
|
|
DartIsolate::DartIsolateGroupCreateCallback);
|
|
params.shutdown_isolate =
|
|
reinterpret_cast<decltype(params.shutdown_isolate)>(
|
|
DartIsolate::DartIsolateShutdownCallback);
|
|
params.cleanup_group = reinterpret_cast<decltype(params.cleanup_group)>(
|
|
DartIsolate::DartIsolateGroupCleanupCallback);
|
|
params.thread_exit = ThreadExitCallback;
|
|
params.get_service_assets = GetVMServiceAssetsArchiveCallback;
|
|
params.entropy_source = dart::bin::GetEntropy;
|
|
char* init_error = Dart_Initialize(¶ms);
|
|
if (init_error) {
|
|
FML_LOG(FATAL) << "Error while initializing the Dart VM: " << init_error;
|
|
::free(init_error);
|
|
}
|
|
// Send the earliest available timestamp in the application lifecycle to
|
|
// timeline. The difference between this timestamp and the time we render
|
|
// the very first frame gives us a good idea about Flutter's startup time.
|
|
// Use a duration event so about:tracing will consider this event when
|
|
// deciding the earliest event to use as time 0.
|
|
if (engine_main_enter_ts != 0) {
|
|
Dart_TimelineEvent("FlutterEngineMainEnter", // label
|
|
engine_main_enter_ts, // timestamp0
|
|
engine_main_enter_ts, // timestamp1_or_async_id
|
|
Dart_Timeline_Event_Duration, // event type
|
|
0, // argument_count
|
|
nullptr, // argument_names
|
|
nullptr // argument_values
|
|
);
|
|
}
|
|
}
|
|
|
|
Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
|
|
|
|
// Allow streaming of stdout and stderr by the Dart vm.
|
|
Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback,
|
|
&ServiceStreamCancelCallback);
|
|
|
|
Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback);
|
|
|
|
if (settings_.dart_library_sources_kernel != nullptr) {
|
|
std::unique_ptr<fml::Mapping> dart_library_sources =
|
|
settings_.dart_library_sources_kernel();
|
|
// Set sources for dart:* libraries for debugging.
|
|
Dart_SetDartLibrarySourcesKernel(dart_library_sources->GetMapping(),
|
|
dart_library_sources->GetSize());
|
|
}
|
|
|
|
FML_DLOG(INFO) << "New Dart VM instance created. Instance count: "
|
|
<< gVMLaunchCount;
|
|
}
|
|
|
|
DartVM::~DartVM() {
|
|
if (Dart_CurrentIsolate() != nullptr) {
|
|
Dart_ExitIsolate();
|
|
}
|
|
|
|
char* result = Dart_Cleanup();
|
|
|
|
dart::bin::CleanupDartIo();
|
|
|
|
FML_CHECK(result == nullptr)
|
|
<< "Could not cleanly shut down the Dart VM. Error: \"" << result
|
|
<< "\".";
|
|
free(result);
|
|
|
|
FML_DLOG(INFO) << "Dart VM instance destroyed. Instance count: "
|
|
<< gVMLaunchCount;
|
|
}
|
|
|
|
std::shared_ptr<const DartVMData> DartVM::GetVMData() const {
|
|
return vm_data_;
|
|
}
|
|
|
|
const Settings& DartVM::GetSettings() const {
|
|
return settings_;
|
|
}
|
|
|
|
std::shared_ptr<ServiceProtocol> DartVM::GetServiceProtocol() const {
|
|
return service_protocol_;
|
|
}
|
|
|
|
std::shared_ptr<IsolateNameServer> DartVM::GetIsolateNameServer() const {
|
|
return isolate_name_server_;
|
|
}
|
|
|
|
std::shared_ptr<fml::ConcurrentTaskRunner>
|
|
DartVM::GetConcurrentWorkerTaskRunner() const {
|
|
return concurrent_message_loop_->GetTaskRunner();
|
|
}
|
|
|
|
} // namespace flutter
|