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

Commit fcbfa9f

Browse files
authored
Split AOT Engine Runtime (#22624)
1 parent 079c669 commit fcbfa9f

31 files changed

+475
-104
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ FILE: ../../../flutter/runtime/dart_vm_unittests.cc
590590
FILE: ../../../flutter/runtime/embedder_resources.cc
591591
FILE: ../../../flutter/runtime/embedder_resources.h
592592
FILE: ../../../flutter/runtime/fixtures/runtime_test.dart
593+
FILE: ../../../flutter/runtime/fixtures/split_lib_test.dart
593594
FILE: ../../../flutter/runtime/isolate_configuration.cc
594595
FILE: ../../../flutter/runtime/isolate_configuration.h
595596
FILE: ../../../flutter/runtime/platform_data.cc

lib/ui/window/platform_configuration.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,27 @@ class PlatformConfigurationClient {
174174
ComputePlatformResolvedLocale(
175175
const std::vector<std::string>& supported_locale_data) = 0;
176176

177+
//--------------------------------------------------------------------------
178+
/// @brief Invoked when the Dart VM requests that a deferred library
179+
/// be loaded. Notifies the engine that the deferred library
180+
/// identified by the specified loading unit id should be
181+
/// downloaded and loaded into the Dart VM via
182+
/// `LoadDartDeferredLibrary`
183+
///
184+
/// Upon encountering errors or otherwise failing to load a
185+
/// loading unit with the specified id, the failure should be
186+
/// directly reported to dart by calling
187+
/// `LoadDartDeferredLibraryFailure` to ensure the waiting dart
188+
/// future completes with an error.
189+
///
190+
/// @param[in] loading_unit_id The unique id of the deferred library's
191+
/// loading unit. This id is to be passed
192+
/// back into LoadDartDeferredLibrary
193+
/// in order to identify which deferred
194+
/// library to load.
195+
///
196+
virtual void RequestDartDeferredLibrary(intptr_t loading_unit_id) = 0;
197+
177198
protected:
178199
virtual ~PlatformConfigurationClient();
179200
};

runtime/dart_isolate.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ bool DartIsolate::Initialize(Dart_Isolate dart_isolate) {
320320
return false;
321321
}
322322

323+
if (tonic::LogIfError(Dart_SetDeferredLoadHandler(OnDartLoadLibrary))) {
324+
return false;
325+
}
326+
323327
if (!UpdateThreadPoolNames()) {
324328
return false;
325329
}
@@ -332,6 +336,37 @@ fml::RefPtr<fml::TaskRunner> DartIsolate::GetMessageHandlingTaskRunner() const {
332336
return message_handling_task_runner_;
333337
}
334338

339+
bool DartIsolate::LoadLoadingUnit(
340+
intptr_t loading_unit_id,
341+
std::unique_ptr<const fml::Mapping> snapshot_data,
342+
std::unique_ptr<const fml::Mapping> snapshot_instructions) {
343+
tonic::DartState::Scope scope(this);
344+
345+
fml::RefPtr<DartSnapshot> dart_snapshot =
346+
DartSnapshot::IsolateSnapshotFromMappings(
347+
std::move(snapshot_data), std::move(snapshot_instructions));
348+
349+
Dart_Handle result = Dart_DeferredLoadComplete(
350+
loading_unit_id, dart_snapshot->GetDataMapping(),
351+
dart_snapshot->GetInstructionsMapping());
352+
if (tonic::LogIfError(result)) {
353+
LoadLoadingUnitFailure(loading_unit_id, Dart_GetError(result),
354+
/*transient*/ true);
355+
return false;
356+
}
357+
loading_unit_snapshots_.insert(dart_snapshot);
358+
return true;
359+
}
360+
361+
void DartIsolate::LoadLoadingUnitFailure(intptr_t loading_unit_id,
362+
const std::string error_message,
363+
bool transient) {
364+
tonic::DartState::Scope scope(this);
365+
Dart_Handle result = Dart_DeferredLoadCompleteError(
366+
loading_unit_id, error_message.c_str(), transient);
367+
tonic::LogIfError(result);
368+
}
369+
335370
void DartIsolate::SetMessageHandlingTaskRunner(
336371
fml::RefPtr<fml::TaskRunner> runner) {
337372
if (!IsRootIsolate() || !runner) {
@@ -1003,6 +1038,20 @@ void DartIsolate::OnShutdownCallback() {
10031038
}
10041039
}
10051040

1041+
Dart_Handle DartIsolate::OnDartLoadLibrary(intptr_t loading_unit_id) {
1042+
if (Current()->platform_configuration()) {
1043+
Current()->platform_configuration()->client()->RequestDartDeferredLibrary(
1044+
loading_unit_id);
1045+
return Dart_Null();
1046+
}
1047+
const std::string error_message =
1048+
"Platform Configuration was null. Deferred library load request"
1049+
"for loading unit id " +
1050+
std::to_string(loading_unit_id) + " was not sent.";
1051+
FML_LOG(ERROR) << error_message;
1052+
return Dart_NewApiError(error_message.c_str());
1053+
}
1054+
10061055
DartIsolate::AutoFireClosure::AutoFireClosure(const fml::closure& closure)
10071056
: closure_(closure) {}
10081057

runtime/dart_isolate.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <optional>
1010
#include <set>
1111
#include <string>
12+
#include <unordered_set>
1213

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

388+
bool LoadLoadingUnit(
389+
intptr_t loading_unit_id,
390+
std::unique_ptr<const fml::Mapping> snapshot_data,
391+
std::unique_ptr<const fml::Mapping> snapshot_instructions);
392+
393+
void LoadLoadingUnitFailure(intptr_t loading_unit_id,
394+
const std::string error_message,
395+
bool transient);
396+
387397
private:
388398
friend class IsolateConfiguration;
389399
class AutoFireClosure {
@@ -401,6 +411,7 @@ class DartIsolate : public UIDartState {
401411
Phase phase_ = Phase::Unknown;
402412
std::vector<std::shared_ptr<const fml::Mapping>> kernel_buffers_;
403413
std::vector<std::unique_ptr<AutoFireClosure>> shutdown_callbacks_;
414+
std::unordered_set<fml::RefPtr<DartSnapshot>> loading_unit_snapshots_;
404415
fml::RefPtr<fml::TaskRunner> message_handling_task_runner_;
405416
const bool may_insecurely_connect_to_all_domains_;
406417
std::string domain_network_policy_;
@@ -492,6 +503,9 @@ class DartIsolate : public UIDartState {
492503
static void DartIsolateGroupCleanupCallback(
493504
std::shared_ptr<DartIsolateGroupData>* isolate_group_data);
494505

506+
// |Dart_DeferredLoadHandler|
507+
static Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id);
508+
495509
FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate);
496510
};
497511

runtime/dart_isolate_unittests.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,5 +430,102 @@ TEST_F(DartIsolateTest,
430430
ASSERT_EQ(create_callback_count, 1u);
431431
}
432432

433+
TEST_F(DartIsolateTest, InvalidLoadingUnitFails) {
434+
if (!DartVM::IsRunningPrecompiledCode()) {
435+
FML_LOG(INFO) << "Split AOT does not work in JIT mode";
436+
return;
437+
}
438+
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
439+
auto settings = CreateSettingsForFixture();
440+
auto vm_ref = DartVMRef::Create(settings);
441+
ASSERT_TRUE(vm_ref);
442+
auto vm_data = vm_ref.GetVMData();
443+
ASSERT_TRUE(vm_data);
444+
TaskRunners task_runners(GetCurrentTestName(), //
445+
GetCurrentTaskRunner(), //
446+
GetCurrentTaskRunner(), //
447+
GetCurrentTaskRunner(), //
448+
GetCurrentTaskRunner() //
449+
);
450+
auto isolate_configuration =
451+
IsolateConfiguration::InferFromSettings(settings);
452+
auto weak_isolate = DartIsolate::CreateRunningRootIsolate(
453+
vm_data->GetSettings(), // settings
454+
vm_data->GetIsolateSnapshot(), // isolate snapshot
455+
std::move(task_runners), // task runners
456+
nullptr, // window
457+
{}, // snapshot delegate
458+
{}, // hint freed delegate
459+
{}, // io manager
460+
{}, // unref queue
461+
{}, // image decoder
462+
"main.dart", // advisory uri
463+
"main", // advisory entrypoint
464+
DartIsolate::Flags{}, // flags
465+
settings.isolate_create_callback, // isolate create callback
466+
settings.isolate_shutdown_callback, // isolate shutdown callback
467+
"main", // dart entrypoint
468+
std::nullopt, // dart entrypoint library
469+
std::move(isolate_configuration) // isolate configuration
470+
);
471+
auto root_isolate = weak_isolate.lock();
472+
ASSERT_TRUE(root_isolate);
473+
ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::Running);
474+
475+
auto isolate_data = std::make_unique<const fml::NonOwnedMapping>(
476+
split_aot_symbols_.vm_isolate_data, 0);
477+
auto isolate_instructions = std::make_unique<const fml::NonOwnedMapping>(
478+
split_aot_symbols_.vm_isolate_instrs, 0);
479+
480+
// Invalid loading unit should fail gracefully with error message.
481+
ASSERT_FALSE(root_isolate->LoadLoadingUnit(3, std::move(isolate_data),
482+
std::move(isolate_instructions)));
483+
ASSERT_TRUE(root_isolate->Shutdown());
484+
}
485+
486+
TEST_F(DartIsolateTest, ValidLoadingUnitSucceeds) {
487+
if (!DartVM::IsRunningPrecompiledCode()) {
488+
FML_LOG(INFO) << "Split AOT does not work in JIT mode";
489+
return;
490+
}
491+
492+
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
493+
AddNativeCallback("NotifyNative",
494+
CREATE_NATIVE_ENTRY(([this](Dart_NativeArguments args) {
495+
FML_LOG(ERROR) << "Hello from Dart!";
496+
Signal();
497+
})));
498+
AddNativeCallback(
499+
"NotifySuccess", CREATE_NATIVE_ENTRY([this](Dart_NativeArguments args) {
500+
auto bool_handle = Dart_GetNativeArgument(args, 0);
501+
ASSERT_FALSE(tonic::LogIfError(bool_handle));
502+
ASSERT_TRUE(tonic::DartConverter<bool>::FromDart(bool_handle));
503+
Signal();
504+
}));
505+
const auto settings = CreateSettingsForFixture();
506+
auto vm_ref = DartVMRef::Create(settings);
507+
auto thread = CreateNewThread();
508+
TaskRunners task_runners(GetCurrentTestName(), //
509+
thread, //
510+
thread, //
511+
thread, //
512+
thread //
513+
);
514+
auto isolate =
515+
RunDartCodeInIsolate(vm_ref, settings, task_runners,
516+
"canCallDeferredLibrary", {}, GetFixturesPath());
517+
ASSERT_TRUE(isolate);
518+
ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
519+
Wait();
520+
521+
auto isolate_data = std::make_unique<const fml::NonOwnedMapping>(
522+
split_aot_symbols_.vm_isolate_data, 0);
523+
auto isolate_instructions = std::make_unique<const fml::NonOwnedMapping>(
524+
split_aot_symbols_.vm_isolate_instrs, 0);
525+
526+
ASSERT_TRUE(isolate->get()->LoadLoadingUnit(2, std::move(isolate_data),
527+
std::move(isolate_instructions)));
528+
}
529+
433530
} // namespace testing
434531
} // namespace flutter

runtime/dart_snapshot.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,17 @@ fml::RefPtr<DartSnapshot> DartSnapshot::IsolateSnapshotFromSettings(
181181
return nullptr;
182182
}
183183

184+
fml::RefPtr<DartSnapshot> DartSnapshot::IsolateSnapshotFromMappings(
185+
std::shared_ptr<const fml::Mapping> snapshot_data,
186+
std::shared_ptr<const fml::Mapping> snapshot_instructions) {
187+
auto snapshot =
188+
fml::MakeRefCounted<DartSnapshot>(snapshot_data, snapshot_instructions);
189+
if (snapshot->IsValid()) {
190+
return snapshot;
191+
}
192+
return nullptr;
193+
}
194+
184195
DartSnapshot::DartSnapshot(std::shared_ptr<const fml::Mapping> data,
185196
std::shared_ptr<const fml::Mapping> instructions)
186197
: data_(std::move(data)), instructions_(std::move(instructions)) {}

runtime/dart_snapshot.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,18 @@ class DartSnapshot : public fml::RefCountedThreadSafe<DartSnapshot> {
102102
static fml::RefPtr<DartSnapshot> IsolateSnapshotFromSettings(
103103
const Settings& settings);
104104

105+
//----------------------------------------------------------------------------
106+
/// @brief Create an isolate snapshot from existing fml::Mappings.
107+
///
108+
/// @param[in] snapshot_data The mapping for the heap snapshot.
109+
/// @param[in] snapshot_instructions The mapping for the instructions
110+
/// snapshot.
111+
///
112+
/// @return A valid isolate snapshot or nullptr.
113+
static fml::RefPtr<DartSnapshot> IsolateSnapshotFromMappings(
114+
std::shared_ptr<const fml::Mapping> snapshot_data,
115+
std::shared_ptr<const fml::Mapping> snapshot_instructions);
116+
105117
//----------------------------------------------------------------------------
106118
/// @brief Determines if this snapshot contains a heap component. Since
107119
/// the instructions component is optional, the method does not

runtime/fixtures/runtime_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
56
import 'dart:isolate';
67
import 'dart:ui';
78

9+
import 'split_lib_test.dart' deferred as splitlib;
10+
811
void main() {
912
}
1013

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

31+
Future<void>? splitLoadFuture = null;
32+
33+
@pragma('vm:entry-point')
34+
void canCallDeferredLibrary() {
35+
print('In function canCallDeferredLibrary');
36+
splitLoadFuture = splitlib.loadLibrary()
37+
.then((_) {
38+
print('Deferred load complete');
39+
notifySuccess(splitlib.splitAdd(10, 23) == 33);
40+
})
41+
.catchError((_) {
42+
print('Deferred load error');
43+
notifySuccess(false);
44+
});
45+
notifyNative();
46+
}
47+
2848
void notifyNative() native 'NotifyNative';
2949

3050
@pragma('vm:entry-point')

runtime/fixtures/split_lib_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
library splitlib;
6+
7+
int splitAdd(int i, int j) {
8+
return i + j;
9+
}

runtime/runtime_controller.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,19 @@ std::optional<uint32_t> RuntimeController::GetRootIsolateReturnCode() {
415415
return root_isolate_return_code_;
416416
}
417417

418+
void RuntimeController::LoadDartDeferredLibrary(
419+
intptr_t loading_unit_id,
420+
std::unique_ptr<const fml::Mapping> snapshot_data,
421+
std::unique_ptr<const fml::Mapping> snapshot_instructions) {
422+
root_isolate_.lock()->LoadLoadingUnit(loading_unit_id,
423+
std::move(snapshot_data),
424+
std::move(snapshot_instructions));
425+
}
426+
427+
void RuntimeController::RequestDartDeferredLibrary(intptr_t loading_unit_id) {
428+
return client_.RequestDartDeferredLibrary(loading_unit_id);
429+
}
430+
418431
RuntimeController::Locale::Locale(std::string language_code_,
419432
std::string country_code_,
420433
std::string script_code_,

0 commit comments

Comments
 (0)