text: // Copyright 2013 The Flutter Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <algorithm>
  #include <iomanip>
  #include <iostream>
  #include <iterator>
  #include <sstream>
  #include <string>
  
  #include "flutter/fml/native_library.h"
  #include "flutter/fml/paths.h"
  #include "flutter/fml/size.h"
  #include "flutter/shell/version/version.h"
  
  // Include once for the default enum definition.
  #include "flutter/shell/common/switches.h"
  
  #undef SHELL_COMMON_SWITCHES_H_
  
  struct SwitchDesc {
    flutter::Switch sw;
    const std::string_view flag;
    const char* help;
  };
  
  #undef DEF_SWITCHES_START
  #undef DEF_SWITCH
  #undef DEF_SWITCHES_END
  
  // clang-format off
  #define DEF_SWITCHES_START static const struct SwitchDesc gSwitchDescs[] = {
  #define DEF_SWITCH(p_swtch, p_flag, p_help) \
    { flutter::Switch:: p_swtch, p_flag, p_help },
  #define DEF_SWITCHES_END };
  // clang-format on
  
  // List of common and safe VM flags to allow to be passed directly to the VM.
  #if FLUTTER_RELEASE
  
  // clang-format off
  static const std::string gAllowedDartFlags[] = {
      "--enable-isolate-groups",
      "--no-enable-isolate-groups",
      "--lazy_async_stacks",
  };
  // clang-format on
  
  #else
  
  // clang-format off
  static const std::string gAllowedDartFlags[] = {
      "--enable-isolate-groups",
      "--no-enable-isolate-groups",
      "--enable_mirrors",
      "--enable-service-port-fallback",
      "--lazy_async_stacks",
      "--max_profile_depth",
      "--profile_period",
      "--random_seed",
      "--sample-buffer-duration",
      "--trace-kernel",
      "--trace-reload",
      "--trace-reload-verbose",
      "--write-service-info",
      "--null_assertions",
      "--strict_null_safety_checks",
      "--enable-display-list",
      "--no-enable-display-list",
  };
  // clang-format on
  
  #endif  // FLUTTER_RELEASE
  
  // Include again for struct definition.
  #include "flutter/shell/common/switches.h"
  
  // Define symbols for the ICU data that is linked into the Flutter library on
  // Android.  This is a workaround for crashes seen when doing dynamic lookups
  // of the engine's own symbols on some older versions of Android.
  #if OS_ANDROID
  extern uint8_t _binary_icudtl_dat_start[];
  extern uint8_t _binary_icudtl_dat_end[];
  
  static std::unique_ptr<fml::Mapping> GetICUStaticMapping() {
    return std::make_unique<fml::NonOwnedMapping>(
        _binary_icudtl_dat_start,
        _binary_icudtl_dat_end - _binary_icudtl_dat_start);
  }
  #endif
  
  namespace flutter {
  
  void PrintUsage(const std::string& executable_name) {
    std::cerr << std::endl << "  " << executable_name << std::endl << std::endl;
  
    std::cerr << "Versions: " << std::endl << std::endl;
  
    std::cerr << "Flutter Engine Version: " << GetFlutterEngineVersion()
              << std::endl;
    std::cerr << "Skia Version: " << GetSkiaVersion() << std::endl;
  
    std::cerr << "Dart Version: " << GetDartVersion() << std::endl << std::endl;
  
    std::cerr << "Available Flags:" << std::endl;
  
    const uint32_t column_width = 80;
  
    const uint32_t flags_count = static_cast<uint32_t>(Switch::Sentinel);
  
    uint32_t max_width = 2;
    for (uint32_t i = 0; i < flags_count; i++) {
      auto desc = gSwitchDescs[i];
      max_width = std::max<uint32_t>(desc.flag.size() + 2, max_width);
    }
  
    const uint32_t help_width = column_width - max_width - 3;
  
    std::cerr << std::string(column_width, '-') << std::endl;
    for (uint32_t i = 0; i < flags_count; i++) {
      auto desc = gSwitchDescs[i];
  
      std::cerr << std::setw(max_width)
                << std::string("--") +
                       std::string{desc.flag.data(), desc.flag.size()}
                << " : ";
  
      std::istringstream stream(desc.help);
      int32_t remaining = help_width;
  
      std::string word;
      while (stream >> word && remaining > 0) {
        remaining -= (word.size() + 1);
        if (remaining <= 0) {
          std::cerr << std::endl
                    << std::string(max_width, ' ') << "   " << word << " ";
          remaining = help_width;
        } else {
          std::cerr << word << " ";
        }
      }
  
      std::cerr << std::endl;
    }
    std::cerr << std::string(column_width, '-') << std::endl;
  }
  
  const std::string_view FlagForSwitch(Switch swtch) {
    for (uint32_t i = 0; i < static_cast<uint32_t>(Switch::Sentinel); i++) {
      if (gSwitchDescs[i].sw == swtch) {
        return gSwitchDescs[i].flag;
      }
    }
    return std::string_view();
  }
  
  static std::vector<std::string> ParseCommaDelimited(const std::string& input) {
    std::istringstream ss(input);
    std::vector<std::string> result;
    std::string token;
    while (std::getline(ss, token, ',')) {
      result.push_back(token);
    }
    return result;
  }
  
  static bool IsAllowedDartVMFlag(const std::string& flag) {
    for (uint32_t i = 0; i < fml::size(gAllowedDartFlags); ++i) {
      const std::string& allowed = gAllowedDartFlags[i];
      // Check that the prefix of the flag matches one of the allowed flags.
      // We don't need to worry about cases like "--safe --sneaky_dangerous" as
      // the VM will discard these as a single unrecognized flag.
      if (std::equal(allowed.begin(), allowed.end(), flag.begin())) {
        return true;
      }
    }
    return false;
  }
  
  template <typename T>
  static bool GetSwitchValue(const fml::CommandLine& command_line,
                             Switch sw,
                             T* result) {
    std::string switch_string;
  
    if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) {
      return false;
    }
  
    std::stringstream stream(switch_string);
    T value = 0;
    if (stream >> value) {
      *result = value;
      return true;
    }
  
    return false;
  }
  
  std::unique_ptr<fml::Mapping> GetSymbolMapping(std::string symbol_prefix,
                                                 std::string native_lib_path) {
    const uint8_t* mapping;
    intptr_t size;
  
    auto lookup_symbol = [&mapping, &size, symbol_prefix](
                             const fml::RefPtr<fml::NativeLibrary>& library) {
      mapping = library->ResolveSymbol((symbol_prefix + "_start").c_str());
      size = reinterpret_cast<intptr_t>(
          library->ResolveSymbol((symbol_prefix + "_size").c_str()));
    };
  
    fml::RefPtr<fml::NativeLibrary> library =
        fml::NativeLibrary::CreateForCurrentProcess();
    lookup_symbol(library);
  
    if (!(mapping && size)) {
      // Symbol lookup for the current process fails on some devices.  As a
      // fallback, try doing the lookup based on the path to the Flutter library.
      library = fml::NativeLibrary::Create(native_lib_path.c_str());
      lookup_symbol(library);
    }
  
    FML_CHECK(mapping && size) << "Unable to resolve symbols: " << symbol_prefix;
    return std::make_unique<fml::NonOwnedMapping>(mapping, size);
  }
  
  Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
    Settings settings = {};
  
    // Enable Observatory
    settings.enable_observatory =
        !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory));
  
    // Enable mDNS Observatory Publication
    settings.enable_observatory_publication = !command_line.HasOption(
        FlagForSwitch(Switch::DisableObservatoryPublication));
  
    // Set Observatory Host
    if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryHost))) {
      command_line.GetOptionValue(FlagForSwitch(Switch::DeviceObservatoryHost),
                                  &settings.observatory_host);
    }
    // Default the observatory port based on --ipv6 if not set.
    if (settings.observatory_host.empty()) {
      settings.observatory_host =
          command_line.HasOption(FlagForSwitch(Switch::IPv6)) ? "::1"
                                                              : "127.0.0.1";
    }
  
    // Set Observatory Port
    if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) {
      if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort,
                          &settings.observatory_port)) {
        FML_LOG(INFO)
            << "Observatory port specified was malformed. Will default to "
            << settings.observatory_port;
      }
    }
  
    settings.may_insecurely_connect_to_all_domains = !command_line.HasOption(
        FlagForSwitch(Switch::DisallowInsecureConnections));
  
    command_line.GetOptionValue(FlagForSwitch(Switch::DomainNetworkPolicy),
                                &settings.domain_network_policy);
  
    // Disable need for authentication codes for VM service communication, if
    // specified.
    settings.disable_service_auth_codes =
        command_line.HasOption(FlagForSwitch(Switch::DisableServiceAuthCodes));
  
    // Allow fallback to automatic port selection if binding to a specified port
    // fails.
    settings.enable_service_port_fallback =
        command_line.HasOption(FlagForSwitch(Switch::EnableServicePortFallback));
  
    // Checked mode overrides.
    settings.disable_dart_asserts =
        command_line.HasOption(FlagForSwitch(Switch::DisableDartAsserts));
  
    settings.start_paused =
        command_line.HasOption(FlagForSwitch(Switch::StartPaused));
  
    settings.enable_checked_mode =
        command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));
  
    settings.enable_dart_profiling =
        command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));
  
    settings.enable_software_rendering =
        command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering));
  
    settings.endless_trace_buffer =
        command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer));
  
    settings.trace_startup =
        command_line.HasOption(FlagForSwitch(Switch::TraceStartup));
  
  #if !FLUTTER_RELEASE
    settings.trace_skia = true;
  
    std::string trace_skia_allowlist;
    command_line.GetOptionValue(FlagForSwitch(Switch::TraceSkiaAllowlist),
                                &trace_skia_allowlist);
    if (trace_skia_allowlist.size()) {
      settings.trace_skia_allowlist = ParseCommaDelimited(trace_skia_allowlist);
    } else {
      settings.trace_skia_allowlist = {"skia.shaders"};
    }
  #endif  // !FLUTTER_RELEASE
  
    std::string trace_allowlist;
    command_line.GetOptionValue(FlagForSwitch(Switch::TraceAllowlist),
                                &trace_allowlist);
    settings.trace_allowlist = ParseCommaDelimited(trace_allowlist);
  
    settings.trace_systrace =
        command_line.HasOption(FlagForSwitch(Switch::TraceSystrace));
  
    settings.skia_deterministic_rendering_on_cpu =
        command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering));
  
    settings.verbose_logging =
        command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));
  
    command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
                                &settings.assets_path);
  
    std::vector<std::string_view> aot_shared_library_name =
        command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));
  
    std::string snapshot_asset_path;
    command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath),
                                &snapshot_asset_path);
  
    std::string vm_snapshot_data_filename;
    command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotData),
                                &vm_snapshot_data_filename);
  
    std::string vm_snapshot_instr_filename;
    command_line.GetOptionValue(FlagForSwitch(Switch::VmSnapshotInstructions),
                                &vm_snapshot_instr_filename);
  
    std::string isolate_snapshot_data_filename;
    command_line.GetOptionValue(FlagForSwitch(Switch::IsolateSnapshotData),
                                &isolate_snapshot_data_filename);
  
    std::string isolate_snapshot_instr_filename;
    command_line.GetOptionValue(
        FlagForSwitch(Switch::IsolateSnapshotInstructions),
        &isolate_snapshot_instr_filename);
  
    if (aot_shared_library_name.size() > 0) {
      for (std::string_view name : aot_shared_library_name) {
        settings.application_library_path.emplace_back(name);
      }
    } else if (snapshot_asset_path.size() > 0) {
      settings.vm_snapshot_data_path =
          fml::paths::JoinPaths({snapshot_asset_path, vm_snapshot_data_filename});
      settings.vm_snapshot_instr_path = fml::paths::JoinPaths(
          {snapshot_asset_path, vm_snapshot_instr_filename});
      settings.isolate_snapshot_data_path = fml::paths::JoinPaths(
          {snapshot_asset_path, isolate_snapshot_data_filename});
      settings.isolate_snapshot_instr_path = fml::paths::JoinPaths(
          {snapshot_asset_path, isolate_snapshot_instr_filename});
    }
  
    command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath),
                                &settings.temp_directory_path);
  
    if (settings.icu_initialization_required) {
      command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath),
                                  &settings.icu_data_path);
      if (command_line.HasOption(FlagForSwitch(Switch::ICUSymbolPrefix))) {
        std::string icu_symbol_prefix, native_lib_path;
        command_line.GetOptionValue(FlagForSwitch(Switch::ICUSymbolPrefix),
                                    &icu_symbol_prefix);
        command_line.GetOptionValue(FlagForSwitch(Switch::ICUNativeLibPath),
                                    &native_lib_path);
  
  #if OS_ANDROID
        settings.icu_mapper = GetICUStaticMapping;
  #else
        settings.icu_mapper = [icu_symbol_prefix, native_lib_path] {
          return GetSymbolMapping(icu_symbol_prefix, native_lib_path);
        };
  #endif
      }
    }
  
    settings.use_test_fonts =
        command_line.HasOption(FlagForSwitch(Switch::UseTestFonts));
  
    settings.enable_skparagraph =
        command_line.HasOption(FlagForSwitch(Switch::EnableSkParagraph));
  
    std::string all_dart_flags;
    if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags),
                                    &all_dart_flags)) {
      // Assume that individual flags are comma separated.
      std::vector<std::string> flags = ParseCommaDelimited(all_dart_flags);
      for (auto flag : flags) {
        if (!IsAllowedDartVMFlag(flag)) {
          FML_LOG(FATAL) << "Encountered disallowed Dart VM flag: " << flag;
        }
        settings.dart_flags.push_back(flag);
      }
    }
    if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(),
                  "--enable-display-list") != settings.dart_flags.end()) {
      FML_LOG(ERROR) << "Manually enabling display lists";
      settings.enable_display_list = true;
    } else if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(),
                         "--no-enable-display-list") !=
               settings.dart_flags.end()) {
      FML_LOG(ERROR) << "Manually disabling display lists";
      settings.enable_display_list = false;
    }
  
  #if !FLUTTER_RELEASE
    command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag);
  #endif
  
    settings.dump_skp_on_shader_compilation =
        command_line.HasOption(FlagForSwitch(Switch::DumpSkpOnShaderCompilation));
  
    settings.cache_sksl =
        command_line.HasOption(FlagForSwitch(Switch::CacheSkSL));
  
    settings.purge_persistent_cache =
        command_line.HasOption(FlagForSwitch(Switch::PurgePersistentCache));
  
    if (command_line.HasOption(FlagForSwitch(Switch::OldGenHeapSize))) {
      std::string old_gen_heap_size;
      command_line.GetOptionValue(FlagForSwitch(Switch::OldGenHeapSize),
                                  &old_gen_heap_size);
      settings.old_gen_heap_size = std::stoi(old_gen_heap_size);
    }
    return settings;
  }
  
  }  // namespace flutter
  