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 #include <algorithm>
6 #include <iomanip>
7 #include <iostream>
8 #include <iterator>
9 #include <sstream>
10 #include <string>
11
12 #include "flutter/fml/native_library.h"
13 #include "flutter/fml/paths.h"
14 #include "flutter/fml/size.h"
15 #include "flutter/shell/version/version.h"
16
17 // Include once for the default enum definition.
18 #include "flutter/shell/common/switches.h"
19
20 #undef SHELL_COMMON_SWITCHES_H_
21
22 struct SwitchDesc {
23 flutter::Switch sw;
24 const std::string_view flag;
25 const char* help;
26 };
27
28 #undef DEF_SWITCHES_START
29 #undef DEF_SWITCH
30 #undef DEF_SWITCHES_END
31
32 // clang-format off
33 #define DEF_SWITCHES_START static const struct SwitchDesc gSwitchDescs[] = {
34 #define DEF_SWITCH(p_swtch, p_flag, p_help) \
35 { flutter::Switch:: p_swtch, p_flag, p_help },
36 #define DEF_SWITCHES_END };
37 // clang-format on
38
39 #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
40
41 // List of common and safe VM flags to allow to be passed directly to the VM.
42 // clang-format off
43 static const std::string gDartFlagsWhitelist[] = {
44 "--max_profile_depth",
45 "--profile_period",
46 "--random_seed",
47 "--enable_mirrors",
48 };
49 // clang-format on
50
51 #endif
52
53 // Include again for struct definition.
54 #include "flutter/shell/common/switches.h"
55
56 // Define symbols for the ICU data that is linked into the Flutter library on
57 // Android. This is a workaround for crashes seen when doing dynamic lookups
58 // of the engine's own symbols on some older versions of Android.
59 #if OS_ANDROID
60 extern uint8_t _binary_icudtl_dat_start[];
61 extern uint8_t _binary_icudtl_dat_end[];
62
GetICUStaticMapping()63 static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
64 return std::make_unique<fml::NonOwnedMapping>(
65 _binary_icudtl_dat_start,
66 _binary_icudtl_dat_end - _binary_icudtl_dat_start);
67 }
68 #endif
69
70 namespace flutter {
71
PrintUsage(const std::string & executable_name)72 void PrintUsage(const std::string& executable_name) {
73 std::cerr << std::endl << " " << executable_name << std::endl << std::endl;
74
75 std::cerr << "Versions: " << std::endl << std::endl;
76
77 std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
78 << std::endl;
79 std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
80
81 std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
82
83 std::cerr << "Available Flags:" << std::endl;
84
85 const uint32_t column_width = 80;
86
87 const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
88
89 uint32_t max_width = 2;
90 for (uint32_t i = 0; i < flags_count; i++) {
91 auto desc = gSwitchDescs[i];
92 max_width = std::max<uint32_t>(desc.flag.size() + 2, max_width);
93 }
94
95 const uint32_t help_width = column_width - max_width - 3;
96
97 std::cerr << std::string(column_width, '-') << std::endl;
98 for (uint32_t i = 0; i < flags_count; i++) {
99 auto desc = gSwitchDescs[i];
100
101 std::cerr << std::setw(max_width)
102 << std::string("--") +
103 std::string{desc.flag.data(), desc.flag.size()}
104 << " : ";
105
106 std::istringstream stream(desc.help);
107 int32_t remaining = help_width;
108
109 std::string word;
110 while (stream >> word && remaining > 0) {
111 remaining -= (word.size() + 1);
112 if (remaining <= 0) {
113 std::cerr << std::endl
114 << std::string(max_width, ' ') << " " << word << " ";
115 remaining = help_width;
116 } else {
117 std::cerr << word << " ";
118 }
119 }
120
121 std::cerr << std::endl;
122 }
123 std::cerr << std::string(column_width, '-') << std::endl;
124 }
125
FlagForSwitch(Switch swtch)126 const std::string_view FlagForSwitch(Switch swtch) {
127 for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
128 if (gSwitchDescs[i].sw == swtch) {
129 return gSwitchDescs[i].flag;
130 }
131 }
132 return std::string_view();
133 }
134
135 #if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE
136
IsWhitelistedDartVMFlag(const std::string & flag)137 static bool IsWhitelistedDartVMFlag(const std::string& flag) {
138 for (uint32_t i = 0; i < fml::size(gDartFlagsWhitelist); ++i) {
139 const std::string& allowed = gDartFlagsWhitelist[i];
140 // Check that the prefix of the flag matches one of the whitelisted flags.
141 // We don't need to worry about cases like "--safe --sneaky_dangerous" as
142 // the VM will discard these as a single unrecognized flag.
143 if (std::equal(allowed.begin(), allowed.end(), flag.begin())) {
144 return true;
145 }
146 }
147 return false;
148 }
149
150 #endif
151
152 template <typename T>
GetSwitchValue(const fml::CommandLine & command_line,Switch sw,T * result)153 static bool GetSwitchValue(const fml::CommandLine& command_line,
154 Switch sw,
155 T* result) {
156 std::string switch_string;
157
158 if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
159 return false;
160 }
161
162 std::stringstream stream(switch_string);
163 T value = 0;
164 if (stream >> value) {
165 *result = value;
166 return true;
167 }
168
169 return false;
170 }
171
GetSymbolMapping(std::string symbol_prefix,std::string native_lib_path)172 std::unique_ptr<fml::Mapping> GetSymbolMapping(std::string symbol_prefix,
173 std::string native_lib_path) {
174 const uint8_t* mapping;
175 intptr_t size;
176
177 auto lookup_symbol = [&mapping, &size, symbol_prefix](
178 const fml::RefPtr<fml::NativeLibrary>& library) {
179 mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
180 size = reinterpret_cast<intptr_t>(
181 library->ResolveSymbol((symbol_prefix + "_size").c_str()));
182 };
183
184 fml::RefPtr<fml::NativeLibrary> library =
185 fml::NativeLibrary::CreateForCurrentProcess();
186 lookup_symbol(library);
187
188 if (!(mapping && size)) {
189 // Symbol lookup for the current process fails on some devices. As a
190 // fallback, try doing the lookup based on the path to the Flutter library.
191 library = fml::NativeLibrary::Create(native_lib_path.c_str());
192 lookup_symbol(library);
193 }
194
195 FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
196 return std::make_unique<fml::NonOwnedMapping>(mapping, size);
197 }
198
SettingsFromCommandLine(const fml::CommandLine & command_line)199 Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
200 Settings settings = {};
201
202 settings.enable_software_rendering =
203 command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
204
205 settings.skia_deterministic_rendering_on_cpu =
206 command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
207
208 settings.verbose_logging =
209 command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
210
211 command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
212 &settings.assets_path);
213
214 std::vector<std::string_view> aot_shared_library_name =
215 command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
216
217 std::string snapshot_asset_path;
218 command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
219 &snapshot_asset_path);
220
221 std::string vm_snapshot_data_filename;
222 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
223 &vm_snapshot_data_filename);
224
225 std::string vm_snapshot_instr_filename;
226 command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
227 &vm_snapshot_instr_filename);
228
229 std::string isolate_snapshot_data_filename;
230 command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
231 &isolate_snapshot_data_filename);
232
233 std::string isolate_snapshot_instr_filename;
234 command_line.GetOptionValue(
235 FlagForSwitch(Switch::IsolateSnapshotInstructions),
236 &isolate_snapshot_instr_filename);
237
238 if (settings.icu_initialization_required) {
239 command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
240 &settings.icu_data_path);
241 if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
242 std::string icu_symbol_prefix, native_lib_path;
243 command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
244 &icu_symbol_prefix);
245 command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
246 &native_lib_path);
247
248 #if OS_ANDROID
249 settings.icu_mapper = GetICUStaticMapping;
250 #else
251 settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
252 return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
253 };
254 #endif
255 }
256 }
257
258 settings.use_test_fonts =
259 command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
260
261 return settings;
262 }
263
264 } // namespace flutter
265