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:
Chinmay Garde 2019-10-10 12:31:14 -07:00 committed by GitHub
parent e5b64c79a6
commit b659646ffb
15 changed files with 182 additions and 32 deletions

2
DEPS
View File

@ -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',

View File

@ -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;
};

View File

@ -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 {

View File

@ -86,6 +86,8 @@ class DataMapping final : public Mapping {
public:
DataMapping(std::vector<uint8_t> data);
DataMapping(const std::string& string);
~DataMapping() override;
// |Mapping|

View File

@ -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.

View File

@ -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},
});
}

View File

@ -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();

View File

@ -995,6 +995,8 @@ abstract class Window {
String _initialLifecycleState;
void setIsolateDebugName(String name) {}
ByteData getPersistentIsolateData() => null;
}
VoidCallback webOnlyScheduleFrameCallback;

View File

@ -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;

View File

@ -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);
};

View File

@ -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);

View File

@ -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';

View File

@ -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

View File

@ -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

View File

@ -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