mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Allow embedders to specify arbitrary data to the isolate on launch. (flutter/engine#13047)
Since this is currently only meant to be used by the embedding internally, the setter in Objective-C is only exposed via the FlutterDartProject private class extension. Unit tests have been added to the shell_unittests harness. Fixes https://github.com/flutter/flutter/issues/37641
This commit is contained in:
parent
e5b64c79a6
commit
b659646ffb
2
DEPS
2
DEPS
@ -145,7 +145,7 @@ deps = {
|
||||
# and not have to specific specific hashes.
|
||||
|
||||
'src/third_party/tonic':
|
||||
Var('fuchsia_git') + '/tonic' + '@' + 'bd27b4549199df72fcaeefd259ebc12a31c2e4ee',
|
||||
Var('fuchsia_git') + '/tonic' + '@' + '1a8ed9be2e2b56b32e888266d6db465d36012df4',
|
||||
|
||||
'src/third_party/benchmark':
|
||||
Var('fuchsia_git') + '/third_party/benchmark' + '@' + 'a779ffce872b4c811beef482e18bd0b63626aa42',
|
||||
|
||||
@ -183,6 +183,14 @@ struct Settings {
|
||||
// soon as a frame is rasterized.
|
||||
FrameRasterizedCallback frame_rasterized_callback;
|
||||
|
||||
// This data will be available to the isolate immediately on launch via the
|
||||
// Window.getPersistentIsolateData callback. This is meant for information
|
||||
// that the isolate cannot request asynchronously (platform messages can be
|
||||
// used for that purpose). This data is held for the lifetime of the shell and
|
||||
// is available on isolate restarts in the the shell instance. Due to this,
|
||||
// the buffer must be as small as possible.
|
||||
std::shared_ptr<const fml::Mapping> persistent_isolate_data;
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "flutter/fml/mapping.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
namespace fml {
|
||||
@ -67,6 +68,9 @@ std::unique_ptr<FileMapping> FileMapping::CreateReadExecute(
|
||||
|
||||
DataMapping::DataMapping(std::vector<uint8_t> data) : data_(std::move(data)) {}
|
||||
|
||||
DataMapping::DataMapping(const std::string& string)
|
||||
: data_(string.begin(), string.end()) {}
|
||||
|
||||
DataMapping::~DataMapping() = default;
|
||||
|
||||
size_t DataMapping::GetSize() const {
|
||||
|
||||
@ -86,6 +86,8 @@ class DataMapping final : public Mapping {
|
||||
public:
|
||||
DataMapping(std::vector<uint8_t> data);
|
||||
|
||||
DataMapping(const std::string& string);
|
||||
|
||||
~DataMapping() override;
|
||||
|
||||
// |Mapping|
|
||||
|
||||
@ -1176,6 +1176,18 @@ class Window {
|
||||
registrationZone.runUnaryGuarded(callback, data);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// The embedder can specify data that the isolate can request synchronously
|
||||
/// on launch. This accessor fetches that data.
|
||||
///
|
||||
/// This data is persistent for the duration of the Flutter application and is
|
||||
/// available even after isolate restarts. Because of this lifecycle, the size
|
||||
/// of this data must be kept to a minimum.
|
||||
///
|
||||
/// For asynchronous communication between the embedder and isolate, a
|
||||
/// platform channel may be used.
|
||||
ByteData getPersistentIsolateData() native 'Window_getPersistentIsolateData';
|
||||
}
|
||||
|
||||
/// Additional accessibility features that may be enabled by the platform.
|
||||
|
||||
@ -142,6 +142,20 @@ void _RespondToPlatformMessage(Dart_NativeArguments args) {
|
||||
tonic::DartCallStatic(&RespondToPlatformMessage, args);
|
||||
}
|
||||
|
||||
void GetPersistentIsolateData(Dart_NativeArguments args) {
|
||||
auto persistent_isolate_data =
|
||||
UIDartState::Current()->window()->client()->GetPersistentIsolateData();
|
||||
|
||||
if (!persistent_isolate_data) {
|
||||
Dart_SetReturnValue(args, Dart_Null());
|
||||
return;
|
||||
}
|
||||
|
||||
Dart_SetReturnValue(
|
||||
args, tonic::DartByteData::Create(persistent_isolate_data->GetMapping(),
|
||||
persistent_isolate_data->GetSize()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Dart_Handle ToByteData(const std::vector<uint8_t>& buffer) {
|
||||
@ -398,6 +412,7 @@ void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
|
||||
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
|
||||
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
|
||||
{"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},
|
||||
{"Window_getPersistentIsolateData", GetPersistentIsolateData, 1, true},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +57,7 @@ class WindowClient {
|
||||
virtual void UpdateIsolateDescription(const std::string isolate_name,
|
||||
int64_t isolate_port) = 0;
|
||||
virtual void SetNeedsReportTimings(bool value) = 0;
|
||||
virtual std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~WindowClient();
|
||||
|
||||
@ -995,6 +995,8 @@ abstract class Window {
|
||||
String _initialLifecycleState;
|
||||
|
||||
void setIsolateDebugName(String name) {}
|
||||
|
||||
ByteData getPersistentIsolateData() => null;
|
||||
}
|
||||
|
||||
VoidCallback webOnlyScheduleFrameCallback;
|
||||
|
||||
@ -26,7 +26,8 @@ RuntimeController::RuntimeController(
|
||||
std::string p_advisory_script_entrypoint,
|
||||
std::function<void(int64_t)> p_idle_notification_callback,
|
||||
fml::closure p_isolate_create_callback,
|
||||
fml::closure p_isolate_shutdown_callback)
|
||||
fml::closure p_isolate_shutdown_callback,
|
||||
std::shared_ptr<const fml::Mapping> p_persistent_isolate_data)
|
||||
: RuntimeController(p_client,
|
||||
p_vm,
|
||||
std::move(p_isolate_snapshot),
|
||||
@ -39,7 +40,8 @@ RuntimeController::RuntimeController(
|
||||
p_idle_notification_callback,
|
||||
WindowData{/* default window data */},
|
||||
p_isolate_create_callback,
|
||||
p_isolate_shutdown_callback) {}
|
||||
p_isolate_shutdown_callback,
|
||||
std::move(p_persistent_isolate_data)) {}
|
||||
|
||||
RuntimeController::RuntimeController(
|
||||
RuntimeDelegate& p_client,
|
||||
@ -54,7 +56,8 @@ RuntimeController::RuntimeController(
|
||||
std::function<void(int64_t)> idle_notification_callback,
|
||||
WindowData p_window_data,
|
||||
fml::closure p_isolate_create_callback,
|
||||
fml::closure p_isolate_shutdown_callback)
|
||||
fml::closure p_isolate_shutdown_callback,
|
||||
std::shared_ptr<const fml::Mapping> p_persistent_isolate_data)
|
||||
: client_(p_client),
|
||||
vm_(p_vm),
|
||||
isolate_snapshot_(std::move(p_isolate_snapshot)),
|
||||
@ -67,7 +70,8 @@ RuntimeController::RuntimeController(
|
||||
idle_notification_callback_(idle_notification_callback),
|
||||
window_data_(std::move(p_window_data)),
|
||||
isolate_create_callback_(p_isolate_create_callback),
|
||||
isolate_shutdown_callback_(p_isolate_shutdown_callback) {
|
||||
isolate_shutdown_callback_(p_isolate_shutdown_callback),
|
||||
persistent_isolate_data_(std::move(p_persistent_isolate_data)) {
|
||||
// Create the root isolate as soon as the runtime controller is initialized.
|
||||
// It will be run at a later point when the engine provides a run
|
||||
// configuration and then runs the isolate.
|
||||
@ -144,7 +148,8 @@ std::unique_ptr<RuntimeController> RuntimeController::Clone() const {
|
||||
idle_notification_callback_, //
|
||||
window_data_, //
|
||||
isolate_create_callback_, //
|
||||
isolate_shutdown_callback_ //
|
||||
isolate_shutdown_callback_, //
|
||||
persistent_isolate_data_ //
|
||||
));
|
||||
}
|
||||
|
||||
@ -296,42 +301,56 @@ Window* RuntimeController::GetWindowIfAvailable() {
|
||||
return root_isolate ? root_isolate->window() : nullptr;
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
std::string RuntimeController::DefaultRouteName() {
|
||||
return client_.DefaultRouteName();
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::ScheduleFrame() {
|
||||
client_.ScheduleFrame();
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::Render(Scene* scene) {
|
||||
client_.Render(scene->takeLayerTree());
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::UpdateSemantics(SemanticsUpdate* update) {
|
||||
if (window_data_.semantics_enabled) {
|
||||
client_.UpdateSemantics(update->takeNodes(), update->takeActions());
|
||||
}
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::HandlePlatformMessage(
|
||||
fml::RefPtr<PlatformMessage> message) {
|
||||
client_.HandlePlatformMessage(std::move(message));
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
FontCollection& RuntimeController::GetFontCollection() {
|
||||
return client_.GetFontCollection();
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::UpdateIsolateDescription(const std::string isolate_name,
|
||||
int64_t isolate_port) {
|
||||
client_.UpdateIsolateDescription(isolate_name, isolate_port);
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
void RuntimeController::SetNeedsReportTimings(bool value) {
|
||||
client_.SetNeedsReportTimings(value);
|
||||
}
|
||||
|
||||
// |WindowClient|
|
||||
std::shared_ptr<const fml::Mapping>
|
||||
RuntimeController::GetPersistentIsolateData() {
|
||||
return persistent_isolate_data_;
|
||||
}
|
||||
|
||||
Dart_Port RuntimeController::GetMainPort() {
|
||||
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
|
||||
return root_isolate ? root_isolate->main_port() : ILLEGAL_PORT;
|
||||
|
||||
@ -28,18 +28,20 @@ class Window;
|
||||
|
||||
class RuntimeController final : public WindowClient {
|
||||
public:
|
||||
RuntimeController(RuntimeDelegate& client,
|
||||
DartVM* vm,
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
||||
fml::RefPtr<const DartSnapshot> shared_snapshot,
|
||||
TaskRunners task_runners,
|
||||
fml::WeakPtr<IOManager> io_manager,
|
||||
fml::WeakPtr<ImageDecoder> iamge_decoder,
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::function<void(int64_t)> idle_notification_callback,
|
||||
fml::closure isolate_create_callback,
|
||||
fml::closure isolate_shutdown_callback);
|
||||
RuntimeController(
|
||||
RuntimeDelegate& client,
|
||||
DartVM* vm,
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
||||
fml::RefPtr<const DartSnapshot> shared_snapshot,
|
||||
TaskRunners task_runners,
|
||||
fml::WeakPtr<IOManager> io_manager,
|
||||
fml::WeakPtr<ImageDecoder> iamge_decoder,
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::function<void(int64_t)> idle_notification_callback,
|
||||
fml::closure isolate_create_callback,
|
||||
fml::closure isolate_shutdown_callback,
|
||||
std::shared_ptr<const fml::Mapping> persistent_isolate_data);
|
||||
|
||||
~RuntimeController() override;
|
||||
|
||||
@ -138,20 +140,23 @@ class RuntimeController final : public WindowClient {
|
||||
std::pair<bool, uint32_t> root_isolate_return_code_ = {false, 0};
|
||||
const fml::closure isolate_create_callback_;
|
||||
const fml::closure isolate_shutdown_callback_;
|
||||
std::shared_ptr<const fml::Mapping> persistent_isolate_data_;
|
||||
|
||||
RuntimeController(RuntimeDelegate& client,
|
||||
DartVM* vm,
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
||||
fml::RefPtr<const DartSnapshot> shared_snapshot,
|
||||
TaskRunners task_runners,
|
||||
fml::WeakPtr<IOManager> io_manager,
|
||||
fml::WeakPtr<ImageDecoder> image_decoder,
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::function<void(int64_t)> idle_notification_callback,
|
||||
WindowData data,
|
||||
fml::closure isolate_create_callback,
|
||||
fml::closure isolate_shutdown_callback);
|
||||
RuntimeController(
|
||||
RuntimeDelegate& client,
|
||||
DartVM* vm,
|
||||
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
||||
fml::RefPtr<const DartSnapshot> shared_snapshot,
|
||||
TaskRunners task_runners,
|
||||
fml::WeakPtr<IOManager> io_manager,
|
||||
fml::WeakPtr<ImageDecoder> image_decoder,
|
||||
std::string advisory_script_uri,
|
||||
std::string advisory_script_entrypoint,
|
||||
std::function<void(int64_t)> idle_notification_callback,
|
||||
WindowData data,
|
||||
fml::closure isolate_create_callback,
|
||||
fml::closure isolate_shutdown_callback,
|
||||
std::shared_ptr<const fml::Mapping> persistent_isolate_data);
|
||||
|
||||
Window* GetWindowIfAvailable();
|
||||
|
||||
@ -182,6 +187,9 @@ class RuntimeController final : public WindowClient {
|
||||
// |WindowClient|
|
||||
void SetNeedsReportTimings(bool value) override;
|
||||
|
||||
// |WindowClient|
|
||||
std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(RuntimeController);
|
||||
};
|
||||
|
||||
|
||||
@ -69,7 +69,8 @@ Engine::Engine(Delegate& delegate,
|
||||
settings_.advisory_script_entrypoint, // advisory script entrypoint
|
||||
settings_.idle_notification_callback, // idle notification callback
|
||||
settings_.isolate_create_callback, // isolate create callback
|
||||
settings_.isolate_shutdown_callback // isolate shutdown callback
|
||||
settings_.isolate_shutdown_callback, // isolate shutdown callback
|
||||
settings_.persistent_isolate_data // persistent isolate data
|
||||
);
|
||||
|
||||
pointer_data_dispatcher_ = dispatcher_maker(*this);
|
||||
|
||||
@ -110,3 +110,10 @@ void canCreateImageFromDecompressedData() {
|
||||
notifyWidthHeight(image.width, image.height);
|
||||
});
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void canAccessIsolateLaunchData() {
|
||||
notifyMessage(utf8.decode(window.getPersistentIsolateData().buffer.asUint8List()));
|
||||
}
|
||||
|
||||
void notifyMessage(String string) native 'NotifyMessage';
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#define FML_USED_ON_EMBEDDER
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
@ -873,5 +874,43 @@ TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) {
|
||||
EXPECT_EQ(mockTexture->unregistered(), true);
|
||||
}
|
||||
|
||||
TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) {
|
||||
const std::string message = "dummy isolate launch data.";
|
||||
|
||||
Settings settings = CreateSettingsForFixture();
|
||||
settings.persistent_isolate_data =
|
||||
std::make_shared<fml::DataMapping>(message);
|
||||
TaskRunners task_runners("test", // label
|
||||
GetCurrentTaskRunner(), // platform
|
||||
CreateNewThread(), // gpu
|
||||
CreateNewThread(), // ui
|
||||
CreateNewThread() // io
|
||||
);
|
||||
|
||||
fml::AutoResetWaitableEvent message_latch;
|
||||
AddNativeCallback("NotifyMessage",
|
||||
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
|
||||
const auto message_from_dart =
|
||||
tonic::DartConverter<std::string>::FromDart(
|
||||
Dart_GetNativeArgument(args, 0));
|
||||
ASSERT_EQ(message, message_from_dart);
|
||||
message_latch.Signal();
|
||||
}));
|
||||
|
||||
std::unique_ptr<Shell> shell =
|
||||
CreateShell(std::move(settings), std::move(task_runners));
|
||||
|
||||
ASSERT_TRUE(shell->IsSetup());
|
||||
auto configuration = RunConfiguration::InferFromSettings(settings);
|
||||
configuration.SetEntrypoint("canAccessIsolateLaunchData");
|
||||
|
||||
fml::AutoResetWaitableEvent event;
|
||||
shell->RunEngine(std::move(configuration), [&](auto result) {
|
||||
ASSERT_EQ(result, Engine::RunStatus::Success);
|
||||
});
|
||||
|
||||
message_latch.Wait();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
|
||||
|
||||
#include "flutter/common/task_runners.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/message_loop.h"
|
||||
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
|
||||
#include "flutter/runtime/dart_vm.h"
|
||||
@ -234,4 +235,22 @@ static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
|
||||
return @"io.flutter.flutter.app";
|
||||
}
|
||||
|
||||
#pragma mark - Settings utilities
|
||||
|
||||
- (void)setPersistentIsolateData:(NSData*)data {
|
||||
if (data == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* persistent_isolate_data = [data copy];
|
||||
fml::NonOwnedMapping::ReleaseProc data_release_proc = [persistent_isolate_data](auto, auto) {
|
||||
[persistent_isolate_data release];
|
||||
};
|
||||
_settings.persistent_isolate_data = std::make_shared<fml::NonOwnedMapping>(
|
||||
static_cast<const uint8_t*>(persistent_isolate_data.bytes), // bytes
|
||||
persistent_isolate_data.length, // byte length
|
||||
data_release_proc // release proc
|
||||
);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -22,6 +22,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
+ (NSString*)flutterAssetsName:(NSBundle*)bundle;
|
||||
|
||||
/**
|
||||
* The embedder can specify data that the isolate can request synchronously on launch. Engines
|
||||
* launched using this configuration can access the persistent isolate data via the
|
||||
* `Window.getPersistentIsolateData` accessor.
|
||||
*
|
||||
* @param data The persistent isolate data. This data is persistent for the duration of the Flutter
|
||||
* application and is available even after isolate restarts. Because of this lifecycle,
|
||||
* the size of this data must be kept to a minimum and platform channels used for
|
||||
* communication that does not require synchronous embedder to isolate communication
|
||||
* close to isolate launch.
|
||||
**/
|
||||
- (void)setPersistentIsolateData:(NSData*)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user