• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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