Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Split AOT Engine Runtime #22624

Merged
merged 15 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ FILE: ../../../flutter/runtime/dart_vm_unittests.cc
FILE: ../../../flutter/runtime/embedder_resources.cc
FILE: ../../../flutter/runtime/embedder_resources.h
FILE: ../../../flutter/runtime/fixtures/runtime_test.dart
FILE: ../../../flutter/runtime/fixtures/split_lib_test.dart
FILE: ../../../flutter/runtime/isolate_configuration.cc
FILE: ../../../flutter/runtime/isolate_configuration.h
FILE: ../../../flutter/runtime/platform_data.cc
Expand Down
21 changes: 21 additions & 0 deletions lib/ui/window/platform_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ class PlatformConfigurationClient {
ComputePlatformResolvedLocale(
const std::vector<std::string>& supported_locale_data) = 0;

//--------------------------------------------------------------------------
/// @brief Invoked when the Dart VM requests that a deferred library
/// be loaded. Notifies the engine that the deferred library
/// identified by the specified loading unit id should be
/// downloaded and loaded into the Dart VM via
/// `LoadDartDeferredLibrary`
///
/// Upon encountering errors or otherwise failing to load a
/// loading unit with the specified id, the failure should be
/// directly reported to dart by calling
/// `LoadDartDeferredLibraryFailure` to ensure the waiting dart
/// future completes with an error.
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit. This id is to be passed
/// back into LoadDartDeferredLibrary
/// in order to identify which deferred
/// library to load.
///
virtual void RequestDartDeferredLibrary(intptr_t loading_unit_id) = 0;

protected:
virtual ~PlatformConfigurationClient();
};
Expand Down
49 changes: 49 additions & 0 deletions runtime/dart_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate) {
return false;
}

if (tonic::LogIfError(Dart_SetDeferredLoadHandler(OnDartLoadLibrary))) {
return false;
}

if (!UpdateThreadPoolNames()) {
return false;
}
Expand All @@ -332,6 +336,37 @@ fml::RefPtr<fml::TaskRunner> DartIsolate::GetMessageHandlingTaskRunner() const {
return message_handling_task_runner_;
}

bool DartIsolate::LoadLoadingUnit(
intptr_t loading_unit_id,
std::unique_ptr<const fml::Mapping> snapshot_data,
std::unique_ptr<const fml::Mapping> snapshot_instructions) {
tonic::DartState::Scope scope(this);

fml::RefPtr<DartSnapshot> dart_snapshot =
DartSnapshot::IsolateSnapshotFromMappings(
std::move(snapshot_data), std::move(snapshot_instructions));

Dart_Handle result = Dart_DeferredLoadComplete(
loading_unit_id, dart_snapshot->GetDataMapping(),
dart_snapshot->GetInstructionsMapping());
if (tonic::LogIfError(result)) {
LoadLoadingUnitFailure(loading_unit_id, Dart_GetError(result),
/*transient*/ true);
return false;
}
loading_unit_snapshots_.insert(dart_snapshot);
return true;
}

void DartIsolate::LoadLoadingUnitFailure(intptr_t loading_unit_id,
const std::string error_message,
bool transient) {
tonic::DartState::Scope scope(this);
Dart_Handle result = Dart_DeferredLoadCompleteError(
loading_unit_id, error_message.c_str(), transient);
tonic::LogIfError(result);
}

void DartIsolate::SetMessageHandlingTaskRunner(
fml::RefPtr<fml::TaskRunner> runner) {
if (!IsRootIsolate() || !runner) {
Expand Down Expand Up @@ -1003,6 +1038,20 @@ void DartIsolate::OnShutdownCallback() {
}
}

Dart_Handle DartIsolate::OnDartLoadLibrary(intptr_t loading_unit_id) {
if (Current()->platform_configuration()) {
Current()->platform_configuration()->client()->RequestDartDeferredLibrary(
loading_unit_id);
return Dart_Null();
}
const std::string error_message =
"Platform Configuration was null. Deferred library load request"
"for loading unit id " +
std::to_string(loading_unit_id) + " was not sent.";
FML_LOG(ERROR) << error_message;
return Dart_NewApiError(error_message.c_str());
}

DartIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure)
: closure_(closure) {}

Expand Down
14 changes: 14 additions & 0 deletions runtime/dart_isolate.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <optional>
#include <set>
#include <string>
#include <unordered_set>

#include "flutter/common/task_runners.h"
#include "flutter/fml/compiler_specific.h"
Expand Down Expand Up @@ -384,6 +385,15 @@ class DartIsolate : public UIDartState {
///
fml::RefPtr<fml::TaskRunner> GetMessageHandlingTaskRunner() const;

bool LoadLoadingUnit(
intptr_t loading_unit_id,
std::unique_ptr<const fml::Mapping> snapshot_data,
std::unique_ptr<const fml::Mapping> snapshot_instructions);

void LoadLoadingUnitFailure(intptr_t loading_unit_id,
const std::string error_message,
bool transient);

private:
friend class IsolateConfiguration;
class AutoFireClosure {
Expand All @@ -401,6 +411,7 @@ class DartIsolate : public UIDartState {
Phase phase_ = Phase::Unknown;
std::vector<std::shared_ptr<const fml::Mapping>> kernel_buffers_;
std::vector<std::unique_ptr<AutoFireClosure>> shutdown_callbacks_;
std::unordered_set<fml::RefPtr<DartSnapshot>> loading_unit_snapshots_;
fml::RefPtr<fml::TaskRunner> message_handling_task_runner_;
const bool may_insecurely_connect_to_all_domains_;
std::string domain_network_policy_;
Expand Down Expand Up @@ -492,6 +503,9 @@ class DartIsolate : public UIDartState {
static void DartIsolateGroupCleanupCallback(
std::shared_ptr<DartIsolateGroupData>* isolate_group_data);

// |Dart_DeferredLoadHandler|
static Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id);

FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate);
};

Expand Down
97 changes: 97 additions & 0 deletions runtime/dart_isolate_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -430,5 +430,102 @@ TEST_F(DartIsolateTest,
ASSERT_EQ(create_callback_count, 1u);
}

TEST_F(DartIsolateTest, InvalidLoadingUnitFails) {
if (!DartVM::IsRunningPrecompiledCode()) {
FML_LOG(INFO) << "Split AOT does not work in JIT mode";
return;
}
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
auto settings = CreateSettingsForFixture();
auto vm_ref = DartVMRef::Create(settings);
ASSERT_TRUE(vm_ref);
auto vm_data = vm_ref.GetVMData();
ASSERT_TRUE(vm_data);
TaskRunners task_runners(GetCurrentTestName(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner() //
);
auto isolate_configuration =
IsolateConfiguration::InferFromSettings(settings);
auto weak_isolate = DartIsolate::CreateRunningRootIsolate(
vm_data->GetSettings(), // settings
vm_data->GetIsolateSnapshot(), // isolate snapshot
std::move(task_runners), // task runners
nullptr, // window
{}, // snapshot delegate
{}, // hint freed delegate
{}, // io manager
{}, // unref queue
{}, // image decoder
"main.dart", // advisory uri
"main", // advisory entrypoint
DartIsolate::Flags{}, // flags
settings.isolate_create_callback, // isolate create callback
settings.isolate_shutdown_callback, // isolate shutdown callback
"main", // dart entrypoint
std::nullopt, // dart entrypoint library
std::move(isolate_configuration) // isolate configuration
);
auto root_isolate = weak_isolate.lock();
ASSERT_TRUE(root_isolate);
ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running);

auto isolate_data = std::make_unique<const fml::NonOwnedMapping>(
split_aot_symbols_.vm_isolate_data, 0);
auto isolate_instructions = std::make_unique<const fml::NonOwnedMapping>(
split_aot_symbols_.vm_isolate_instrs, 0);

// Invalid loading unit should fail gracefully with error message.
ASSERT_FALSE(root_isolate->LoadLoadingUnit(3, std::move(isolate_data),
std::move(isolate_instructions)));
ASSERT_TRUE(root_isolate->Shutdown());
}

TEST_F(DartIsolateTest, ValidLoadingUnitSucceeds) {
if (!DartVM::IsRunningPrecompiledCode()) {
FML_LOG(INFO) << "Split AOT does not work in JIT mode";
return;
}

ASSERT_FALSE(DartVMRef::IsInstanceRunning());
AddNativeCallback("NotifyNative",
CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) {
FML_LOG(ERROR) << "Hello from Dart!";
Signal();
})));
AddNativeCallback(
"NotifySuccess", CREATE_NATIVE_ENTRY([this](Dart_NativeArguments args) {
auto bool_handle = Dart_GetNativeArgument(args, 0);
ASSERT_FALSE(tonic::LogIfError(bool_handle));
ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(bool_handle));
Signal();
}));
const auto settings = CreateSettingsForFixture();
auto vm_ref = DartVMRef::Create(settings);
auto thread = CreateNewThread();
TaskRunners task_runners(GetCurrentTestName(), //
thread, //
thread, //
thread, //
thread //
);
auto isolate =
RunDartCodeInIsolate(vm_ref, settings, task_runners,
"canCallDeferredLibrary", {}, GetFixturesPath());
ASSERT_TRUE(isolate);
ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
Wait();

auto isolate_data = std::make_unique<const fml::NonOwnedMapping>(
split_aot_symbols_.vm_isolate_data, 0);
auto isolate_instructions = std::make_unique<const fml::NonOwnedMapping>(
split_aot_symbols_.vm_isolate_instrs, 0);

ASSERT_TRUE(isolate->get()->LoadLoadingUnit(2, std::move(isolate_data),
std::move(isolate_instructions)));
}

} // namespace testing
} // namespace flutter
11 changes: 11 additions & 0 deletions runtime/dart_snapshot.cc
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,17 @@ fml::RefPtr<DartSnapshot> DartSnapshot::IsolateSnapshotFromSettings(
return nullptr;
}

fml::RefPtr<DartSnapshot> DartSnapshot::IsolateSnapshotFromMappings(
std::shared_ptr<const fml::Mapping> snapshot_data,
std::shared_ptr<const fml::Mapping> snapshot_instructions) {
auto snapshot =
fml::MakeRefCounted<DartSnapshot>(snapshot_data, snapshot_instructions);
if (snapshot->IsValid()) {
return snapshot;
}
return nullptr;
}

DartSnapshot::DartSnapshot(std::shared_ptr<const fml::Mapping> data,
std::shared_ptr<const fml::Mapping> instructions)
: data_(std::move(data)), instructions_(std::move(instructions)) {}
Expand Down
12 changes: 12 additions & 0 deletions runtime/dart_snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ class DartSnapshot : public fml::RefCountedThreadSafe<DartSnapshot> {
static fml::RefPtr<DartSnapshot> IsolateSnapshotFromSettings(
const Settings& settings);

//----------------------------------------------------------------------------
/// @brief Create an isolate snapshot from existing fml::Mappings.
///
/// @param[in] snapshot_data The mapping for the heap snapshot.
/// @param[in] snapshot_instructions The mapping for the instructions
/// snapshot.
///
/// @return A valid isolate snapshot or nullptr.
static fml::RefPtr<DartSnapshot> IsolateSnapshotFromMappings(
std::shared_ptr<const fml::Mapping> snapshot_data,
std::shared_ptr<const fml::Mapping> snapshot_instructions);

//----------------------------------------------------------------------------
/// @brief Determines if this snapshot contains a heap component. Since
/// the instructions component is optional, the method does not
Expand Down
20 changes: 20 additions & 0 deletions runtime/fixtures/runtime_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:isolate';
import 'dart:ui';

import 'split_lib_test.dart' deferred as splitlib;

void main() {
}

Expand All @@ -25,6 +28,23 @@ void canRegisterNativeCallback() async {
print('Called native method from canRegisterNativeCallback');
}

Future<void>? splitLoadFuture = null;

@pragma('vm:entry-point')
void canCallDeferredLibrary() {
print('In function canCallDeferredLibrary');
splitLoadFuture = splitlib.loadLibrary()
.then((_) {
print('Deferred load complete');
notifySuccess(splitlib.splitAdd(10, 23) == 33);
})
.catchError((_) {
print('Deferred load error');
notifySuccess(false);
});
notifyNative();
}

void notifyNative() native 'NotifyNative';

@pragma('vm:entry-point')
Expand Down
9 changes: 9 additions & 0 deletions runtime/fixtures/split_lib_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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.

library splitlib;

int splitAdd(int i, int j) {
return i + j;
}
13 changes: 13 additions & 0 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,19 @@ std::optional<uint32_t> RuntimeController::GetRootIsolateReturnCode() {
return root_isolate_return_code_;
}

void RuntimeController::LoadDartDeferredLibrary(
intptr_t loading_unit_id,
std::unique_ptr<const fml::Mapping> snapshot_data,
std::unique_ptr<const fml::Mapping> snapshot_instructions) {
root_isolate_.lock()->LoadLoadingUnit(loading_unit_id,
std::move(snapshot_data),
std::move(snapshot_instructions));
}

void RuntimeController::RequestDartDeferredLibrary(intptr_t loading_unit_id) {
return client_.RequestDartDeferredLibrary(loading_unit_id);
}

RuntimeController::Locale::Locale(std::string language_code_,
std::string country_code_,
std::string script_code_,
Expand Down
Loading