Only allow mappings for ICU initialization. (flutter/engine#8656)
If the mapping callback is not set or it the callback returns invalid data, ICU initialization will be embedder responsibility. This affects all embedders and the following have been audited: * Android: Via a symbol mapping. * iOS: Via a file mapping. * Embedder: Via a file mapping. * Fuchsia: Via a VMO mapping * Test shells and Flutter tester: Via file mapping with ICU data needing to be next to the executable.
This commit is contained in:
@@ -10,7 +10,8 @@ namespace benchmarking {
|
||||
|
||||
int Main(int argc, char** argv) {
|
||||
benchmark::Initialize(&argc, argv);
|
||||
fml::icu::InitializeICU("icudtl.dat");
|
||||
fml::icu::InitializeICU(fml::FileMapping::CreateReadOnly(
|
||||
fml::OpenDirectoryOfExecutable(), "icudtl.dat"));
|
||||
::benchmark::RunSpecifiedBenchmarks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -132,12 +132,6 @@ struct Settings {
|
||||
bool verbose_logging = false;
|
||||
std::string log_tag = "flutter";
|
||||
|
||||
// The icu_initialization_required setting does not have a corresponding
|
||||
// switch because it is intended to be decided during build time, not runtime.
|
||||
// Some companies apply source modification here because their build system
|
||||
// brings its own ICU data files.
|
||||
bool icu_initialization_required = true;
|
||||
std::string icu_data_path;
|
||||
MappingCallback icu_mapper;
|
||||
|
||||
// Assets settings
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "flutter/fml/file.h"
|
||||
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/paths.h"
|
||||
|
||||
namespace fml {
|
||||
|
||||
@@ -58,4 +59,14 @@ ScopedTemporaryDirectory::~ScopedTemporaryDirectory() {
|
||||
}
|
||||
}
|
||||
|
||||
fml::UniqueFD OpenDirectoryOfExecutable() {
|
||||
auto result = paths::GetExecutableDirectoryPath();
|
||||
|
||||
if (!result.first) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return OpenFile(result.second.c_str(), false, FilePermission::kRead);
|
||||
}
|
||||
|
||||
} // namespace fml
|
||||
|
||||
@@ -46,6 +46,8 @@ fml::UniqueFD OpenDirectory(const fml::UniqueFD& base_directory,
|
||||
bool create_if_necessary,
|
||||
FilePermission permission);
|
||||
|
||||
fml::UniqueFD OpenDirectoryOfExecutable();
|
||||
|
||||
fml::UniqueFD Duplicate(fml::UniqueFD::element_type descriptor);
|
||||
|
||||
bool IsDirectory(const fml::UniqueFD& directory);
|
||||
|
||||
@@ -4,115 +4,24 @@
|
||||
|
||||
#include "flutter/fml/icu_util.h"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "flutter/fml/build_config.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/mapping.h"
|
||||
#include "flutter/fml/native_library.h"
|
||||
#include "flutter/fml/paths.h"
|
||||
#include "third_party/icu/source/common/unicode/udata.h"
|
||||
|
||||
namespace fml {
|
||||
namespace icu {
|
||||
|
||||
class ICUContext {
|
||||
public:
|
||||
ICUContext(const std::string& icu_data_path) : valid_(false) {
|
||||
valid_ = SetupMapping(icu_data_path) && SetupICU();
|
||||
void InitializeICU(std::unique_ptr<const Mapping> mapping) {
|
||||
if (mapping == nullptr || mapping->GetSize() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ICUContext(std::unique_ptr<Mapping> mapping) : mapping_(std::move(mapping)) {
|
||||
valid_ = SetupICU();
|
||||
}
|
||||
|
||||
~ICUContext() = default;
|
||||
|
||||
bool SetupMapping(const std::string& icu_data_path) {
|
||||
// Check if the path exists and it readable directly.
|
||||
auto fd =
|
||||
fml::OpenFile(icu_data_path.c_str(), false, fml::FilePermission::kRead);
|
||||
|
||||
// Check the path relative to the current executable.
|
||||
if (!fd.is_valid()) {
|
||||
auto directory = fml::paths::GetExecutableDirectoryPath();
|
||||
|
||||
if (!directory.first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path_relative_to_executable =
|
||||
paths::JoinPaths({directory.second, icu_data_path});
|
||||
|
||||
fd = fml::OpenFile(path_relative_to_executable.c_str(), false,
|
||||
fml::FilePermission::kRead);
|
||||
}
|
||||
|
||||
if (!fd.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::initializer_list<FileMapping::Protection> protection = {
|
||||
fml::FileMapping::Protection::kRead};
|
||||
|
||||
auto file_mapping =
|
||||
std::make_unique<FileMapping>(fd, std::move(protection));
|
||||
|
||||
if (file_mapping->GetSize() != 0) {
|
||||
mapping_ = std::move(file_mapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetupICU() {
|
||||
if (GetSize() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UErrorCode err_code = U_ZERO_ERROR;
|
||||
udata_setCommonData(GetMapping(), &err_code);
|
||||
return (err_code == U_ZERO_ERROR);
|
||||
}
|
||||
|
||||
const uint8_t* GetMapping() const {
|
||||
return mapping_ ? mapping_->GetMapping() : nullptr;
|
||||
}
|
||||
|
||||
size_t GetSize() const { return mapping_ ? mapping_->GetSize() : 0; }
|
||||
|
||||
bool IsValid() const { return valid_; }
|
||||
|
||||
private:
|
||||
bool valid_;
|
||||
std::unique_ptr<Mapping> mapping_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ICUContext);
|
||||
};
|
||||
|
||||
void InitializeICUOnce(const std::string& icu_data_path) {
|
||||
static ICUContext* context = new ICUContext(icu_data_path);
|
||||
FML_CHECK(context->IsValid())
|
||||
<< "Must be able to initialize the ICU context. Tried: " << icu_data_path;
|
||||
}
|
||||
|
||||
std::once_flag g_icu_init_flag;
|
||||
void InitializeICU(const std::string& icu_data_path) {
|
||||
std::call_once(g_icu_init_flag,
|
||||
[&icu_data_path]() { InitializeICUOnce(icu_data_path); });
|
||||
}
|
||||
|
||||
void InitializeICUFromMappingOnce(std::unique_ptr<Mapping> mapping) {
|
||||
static ICUContext* context = new ICUContext(std::move(mapping));
|
||||
FML_CHECK(context->IsValid())
|
||||
<< "Unable to initialize the ICU context from a mapping.";
|
||||
}
|
||||
|
||||
void InitializeICUFromMapping(std::unique_ptr<Mapping> mapping) {
|
||||
static std::once_flag g_icu_init_flag;
|
||||
std::call_once(g_icu_init_flag, [mapping = std::move(mapping)]() mutable {
|
||||
InitializeICUFromMappingOnce(std::move(mapping));
|
||||
static auto icu_mapping = std::move(mapping);
|
||||
UErrorCode err_code = U_ZERO_ERROR;
|
||||
udata_setCommonData(icu_mapping->GetMapping(), &err_code);
|
||||
FML_CHECK(err_code == U_ZERO_ERROR) << "Must be able to initialize ICU.";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
namespace fml {
|
||||
namespace icu {
|
||||
|
||||
void InitializeICU(const std::string& icu_data_path = "");
|
||||
|
||||
void InitializeICUFromMapping(std::unique_ptr<Mapping> mapping);
|
||||
void InitializeICU(std::unique_ptr<const Mapping> mapping);
|
||||
|
||||
} // namespace icu
|
||||
} // namespace fml
|
||||
|
||||
@@ -193,14 +193,8 @@ static void PerformInitializationTasks(const Settings& settings) {
|
||||
FML_DLOG(INFO) << "Skia deterministic rendering is enabled.";
|
||||
}
|
||||
|
||||
if (settings.icu_initialization_required) {
|
||||
if (settings.icu_data_path.size() != 0) {
|
||||
fml::icu::InitializeICU(settings.icu_data_path);
|
||||
} else if (settings.icu_mapper) {
|
||||
fml::icu::InitializeICUFromMapping(settings.icu_mapper());
|
||||
} else {
|
||||
FML_DLOG(WARNING) << "Skipping ICU initialization in the shell.";
|
||||
}
|
||||
if (settings.icu_mapper) {
|
||||
fml::icu::InitializeICU(settings.icu_mapper());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,9 +242,20 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
|
||||
command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
|
||||
&settings.temp_directory_path);
|
||||
|
||||
if (settings.icu_initialization_required) {
|
||||
{
|
||||
// ICU from a data file.
|
||||
std::string icu_data_path;
|
||||
command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
|
||||
&settings.icu_data_path);
|
||||
&icu_data_path);
|
||||
if (icu_data_path.size() > 0) {
|
||||
settings.icu_mapper = [icu_data_path]() {
|
||||
return fml::FileMapping::CreateReadOnly(icu_data_path);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// ICU from a symbol in a dynamic library
|
||||
if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
|
||||
std::string icu_symbol_prefix, native_lib_path;
|
||||
command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
|
||||
|
||||
@@ -59,10 +59,13 @@ static flutter::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
|
||||
// defaults.
|
||||
|
||||
// Flutter ships the ICU data file in the the bundle of the engine. Look for it there.
|
||||
if (settings.icu_data_path.size() == 0) {
|
||||
if (!settings.icu_mapper) {
|
||||
NSString* icuDataPath = [engineBundle pathForResource:@"icudtl" ofType:@"dat"];
|
||||
if (icuDataPath.length > 0) {
|
||||
settings.icu_data_path = icuDataPath.UTF8String;
|
||||
auto icu_data_path = std::string{icuDataPath.UTF8String};
|
||||
settings.icu_mapper = [icu_data_path]() {
|
||||
return fml::FileMapping::CreateReadOnly(icu_data_path);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +370,12 @@ FlutterEngineResult FlutterEngineRun(size_t version,
|
||||
|
||||
PopulateSnapshotMappingCallbacks(args, settings);
|
||||
|
||||
settings.icu_data_path = icu_data_path;
|
||||
if (!settings.icu_mapper) {
|
||||
settings.icu_mapper = [icu_data_path]() {
|
||||
return fml::FileMapping::CreateReadOnly(icu_data_path);
|
||||
};
|
||||
}
|
||||
|
||||
settings.assets_path = args->assets_path;
|
||||
|
||||
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
|
||||
|
||||
@@ -251,8 +251,14 @@ int main(int argc, char* argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (settings.icu_data_path.size() == 0) {
|
||||
settings.icu_data_path = "icudtl.dat";
|
||||
// Using command line arguments, the user can specify the ICU data as being
|
||||
// present in either a file or a dynamic library. If no such specification has
|
||||
// been, default to icudtl.dat.
|
||||
if (!settings.icu_mapper) {
|
||||
settings.icu_mapper = []() {
|
||||
return fml::FileMapping::CreateReadOnly(fml::OpenDirectoryOfExecutable(),
|
||||
"icudtl.dat");
|
||||
};
|
||||
}
|
||||
|
||||
// The tools that read logs get confused if there is a log tag specified.
|
||||
|
||||
@@ -36,7 +36,9 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
FML_DCHECK(txt::GetFontDir().length() > 0);
|
||||
|
||||
fml::icu::InitializeICU("icudtl.dat");
|
||||
fml::icu::InitializeICU(fml::FileMapping::CreateReadOnly(
|
||||
fml::OpenDirectoryOfExecutable(), "icudtl.dat"));
|
||||
|
||||
SkGraphics::Init();
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
||||
Reference in New Issue
Block a user