text: //===-- CommandObjectType.cpp -----------------------------------*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "CommandObjectType.h"
  
  #include "lldb/Core/Debugger.h"
  #include "lldb/Core/IOHandler.h"
  #include "lldb/DataFormatters/DataVisualization.h"
  #include "lldb/Host/Config.h"
  #include "lldb/Host/OptionParser.h"
  #include "lldb/Interpreter/CommandInterpreter.h"
  #include "lldb/Interpreter/CommandObject.h"
  #include "lldb/Interpreter/CommandReturnObject.h"
  #include "lldb/Interpreter/OptionArgParser.h"
  #include "lldb/Interpreter/OptionGroupFormat.h"
  #include "lldb/Interpreter/OptionValueBoolean.h"
  #include "lldb/Interpreter/OptionValueLanguage.h"
  #include "lldb/Interpreter/OptionValueString.h"
  #include "lldb/Interpreter/Options.h"
  #include "lldb/Symbol/Symbol.h"
  #include "lldb/Target/Language.h"
  #include "lldb/Target/StackFrame.h"
  #include "lldb/Target/Target.h"
  #include "lldb/Target/Thread.h"
  #include "lldb/Utility/ConstString.h"
  #include "lldb/Utility/RegularExpression.h"
  #include "lldb/Utility/StringList.h"
  
  #include "llvm/ADT/STLExtras.h"
  
  #include <algorithm>
  #include <functional>
  #include <memory>
  
  using namespace lldb;
  using namespace lldb_private;
  
  class ScriptAddOptions {
  public:
    TypeSummaryImpl::Flags m_flags;
    StringList m_target_types;
    bool m_regex;
    ConstString m_name;
    std::string m_category;
  
    ScriptAddOptions(const TypeSummaryImpl::Flags &flags, bool regx,
                     ConstString name, std::string catg)
        : m_flags(flags), m_regex(regx), m_name(name), m_category(catg) {}
  
    typedef std::shared_ptr<ScriptAddOptions> SharedPointer;
  };
  
  class SynthAddOptions {
  public:
    bool m_skip_pointers;
    bool m_skip_references;
    bool m_cascade;
    bool m_regex;
    StringList m_target_types;
    std::string m_category;
  
    SynthAddOptions(bool sptr, bool sref, bool casc, bool regx, std::string catg)
        : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc),
          m_regex(regx), m_target_types(), m_category(catg) {}
  
    typedef std::shared_ptr<SynthAddOptions> SharedPointer;
  };
  
  static bool WarnOnPotentialUnquotedUnsignedType(Args &command,
                                                  CommandReturnObject &result) {
    if (command.empty())
      return false;
  
    for (auto entry : llvm::enumerate(command.entries().drop_back())) {
      if (entry.value().ref() != "unsigned")
        continue;
      auto next = command.entries()[entry.index() + 1].ref();
      if (next == "int" || next == "short" || next == "char" || next == "long") {
        result.AppendWarningWithFormat(
            "unsigned %s being treated as two types. if you meant the combined "
            "type "
            "name use  quotes, as in \"unsigned %s\"\n",
            next.str().c_str(), next.str().c_str());
        return true;
      }
    }
    return false;
  }
  
  #define LLDB_OPTIONS_type_summary_add
  #include "CommandOptions.inc"
  
  class CommandObjectTypeSummaryAdd : public CommandObjectParsed,
                                      public IOHandlerDelegateMultiline {
  private:
    class CommandOptions : public Options {
    public:
      CommandOptions(CommandInterpreter &interpreter) : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override;
  
      void OptionParsingStarting(ExecutionContext *execution_context) override;
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_summary_add_options);
      }
  
      // Instance variables to hold the values for command options.
  
      TypeSummaryImpl::Flags m_flags;
      bool m_regex;
      std::string m_format_string;
      ConstString m_name;
      std::string m_python_script;
      std::string m_python_function;
      bool m_is_add_script;
      std::string m_category;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
    bool Execute_ScriptSummary(Args &command, CommandReturnObject &result);
  
    bool Execute_StringSummary(Args &command, CommandReturnObject &result);
  
  public:
    enum SummaryFormatType { eRegularSummary, eRegexSummary, eNamedSummary };
  
    CommandObjectTypeSummaryAdd(CommandInterpreter &interpreter);
  
    ~CommandObjectTypeSummaryAdd() override = default;
  
    void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
      static const char *g_summary_addreader_instructions =
          "Enter your Python command(s). Type 'DONE' to end.\n"
          "def function (valobj,internal_dict):\n"
          "     \"\"\"valobj: an SBValue which you want to provide a summary "
          "for\n"
          "        internal_dict: an LLDB support object not to be used\"\"\"\n";
  
      StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
      if (output_sp && interactive) {
        output_sp->PutCString(g_summary_addreader_instructions);
        output_sp->Flush();
      }
    }
  
    void IOHandlerInputComplete(IOHandler &io_handler,
                                std::string &data) override {
      StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
  
  #if LLDB_ENABLE_PYTHON
      ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
      if (interpreter) {
        StringList lines;
        lines.SplitIntoLines(data);
        if (lines.GetSize() > 0) {
          ScriptAddOptions *options_ptr =
              ((ScriptAddOptions *)io_handler.GetUserData());
          if (options_ptr) {
            ScriptAddOptions::SharedPointer options(
                options_ptr); // this will ensure that we get rid of the pointer
                              // when going out of scope
  
            ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
            if (interpreter) {
              std::string funct_name_str;
              if (interpreter->GenerateTypeScriptFunction(lines,
                                                          funct_name_str)) {
                if (funct_name_str.empty()) {
                  error_sp->Printf("unable to obtain a valid function name from "
                                   "the script interpreter.\n");
                  error_sp->Flush();
                } else {
                  // now I have a valid function name, let's add this as script
                  // for every type in the list
  
                  TypeSummaryImplSP script_format;
                  script_format = std::make_shared<ScriptSummaryFormat>(
                      options->m_flags, funct_name_str.c_str(),
                      lines.CopyList("    ").c_str());
  
                  Status error;
  
                  for (const std::string &type_name : options->m_target_types) {
                    CommandObjectTypeSummaryAdd::AddSummary(
                        ConstString(type_name), script_format,
                        (options->m_regex
                             ? CommandObjectTypeSummaryAdd::eRegexSummary
                             : CommandObjectTypeSummaryAdd::eRegularSummary),
                        options->m_category, &error);
                    if (error.Fail()) {
                      error_sp->Printf("error: %s", error.AsCString());
                      error_sp->Flush();
                    }
                  }
  
                  if (options->m_name) {
                    CommandObjectTypeSummaryAdd::AddSummary(
                        options->m_name, script_format,
                        CommandObjectTypeSummaryAdd::eNamedSummary,
                        options->m_category, &error);
                    if (error.Fail()) {
                      CommandObjectTypeSummaryAdd::AddSummary(
                          options->m_name, script_format,
                          CommandObjectTypeSummaryAdd::eNamedSummary,
                          options->m_category, &error);
                      if (error.Fail()) {
                        error_sp->Printf("error: %s", error.AsCString());
                        error_sp->Flush();
                      }
                    } else {
                      error_sp->Printf("error: %s", error.AsCString());
                      error_sp->Flush();
                    }
                  } else {
                    if (error.AsCString()) {
                      error_sp->Printf("error: %s", error.AsCString());
                      error_sp->Flush();
                    }
                  }
                }
              } else {
                error_sp->Printf("error: unable to generate a function.\n");
                error_sp->Flush();
              }
            } else {
              error_sp->Printf("error: no script interpreter.\n");
              error_sp->Flush();
            }
          } else {
            error_sp->Printf("error: internal synchronization information "
                             "missing or invalid.\n");
            error_sp->Flush();
          }
        } else {
          error_sp->Printf("error: empty function, didn't add python command.\n");
          error_sp->Flush();
        }
      } else {
        error_sp->Printf(
            "error: script interpreter missing, didn't add python command.\n");
        error_sp->Flush();
      }
  #endif
      io_handler.SetIsDone(true);
    }
  
    static bool AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry,
                           SummaryFormatType type, std::string category,
                           Status *error = nullptr);
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override;
  };
  
  static const char *g_synth_addreader_instructions =
      "Enter your Python command(s). Type 'DONE' to end.\n"
      "You must define a Python class with these methods:\n"
      "    def __init__(self, valobj, dict):\n"
      "    def num_children(self):\n"
      "    def get_child_at_index(self, index):\n"
      "    def get_child_index(self, name):\n"
      "    def update(self):\n"
      "        '''Optional'''\n"
      "class synthProvider:\n";
  
  #define LLDB_OPTIONS_type_synth_add
  #include "CommandOptions.inc"
  
  class CommandObjectTypeSynthAdd : public CommandObjectParsed,
                                    public IOHandlerDelegateMultiline {
  private:
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
        bool success;
  
        switch (short_option) {
        case 'C':
          m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
          if (!success)
            error.SetErrorStringWithFormat("invalid value for cascade: %s",
                                           option_arg.str().c_str());
          break;
        case 'P':
          handwrite_python = true;
          break;
        case 'l':
          m_class_name = std::string(option_arg);
          is_class_based = true;
          break;
        case 'p':
          m_skip_pointers = true;
          break;
        case 'r':
          m_skip_references = true;
          break;
        case 'w':
          m_category = std::string(option_arg);
          break;
        case 'x':
          m_regex = true;
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_cascade = true;
        m_class_name = "";
        m_skip_pointers = false;
        m_skip_references = false;
        m_category = "default";
        is_class_based = false;
        handwrite_python = false;
        m_regex = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_synth_add_options);
      }
  
      // Instance variables to hold the values for command options.
  
      bool m_cascade;
      bool m_skip_references;
      bool m_skip_pointers;
      std::string m_class_name;
      bool m_input_python;
      std::string m_category;
      bool is_class_based;
      bool handwrite_python;
      bool m_regex;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
    bool Execute_HandwritePython(Args &command, CommandReturnObject &result);
  
    bool Execute_PythonClass(Args &command, CommandReturnObject &result);
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      WarnOnPotentialUnquotedUnsignedType(command, result);
  
      if (m_options.handwrite_python)
        return Execute_HandwritePython(command, result);
      else if (m_options.is_class_based)
        return Execute_PythonClass(command, result);
      else {
        result.AppendError("must either provide a children list, a Python class "
                           "name, or use -P and type a Python class "
                           "line-by-line");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
      StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
      if (output_sp && interactive) {
        output_sp->PutCString(g_synth_addreader_instructions);
        output_sp->Flush();
      }
    }
  
    void IOHandlerInputComplete(IOHandler &io_handler,
                                std::string &data) override {
      StreamFileSP error_sp = io_handler.GetErrorStreamFileSP();
  
  #if LLDB_ENABLE_PYTHON
      ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
      if (interpreter) {
        StringList lines;
        lines.SplitIntoLines(data);
        if (lines.GetSize() > 0) {
          SynthAddOptions *options_ptr =
              ((SynthAddOptions *)io_handler.GetUserData());
          if (options_ptr) {
            SynthAddOptions::SharedPointer options(
                options_ptr); // this will ensure that we get rid of the pointer
                              // when going out of scope
  
            ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
            if (interpreter) {
              std::string class_name_str;
              if (interpreter->GenerateTypeSynthClass(lines, class_name_str)) {
                if (class_name_str.empty()) {
                  error_sp->Printf(
                      "error: unable to obtain a proper name for the class.\n");
                  error_sp->Flush();
                } else {
                  // everything should be fine now, let's add the synth provider
                  // class
  
                  SyntheticChildrenSP synth_provider;
                  synth_provider = std::make_shared<ScriptedSyntheticChildren>(
                      SyntheticChildren::Flags()
                          .SetCascades(options->m_cascade)
                          .SetSkipPointers(options->m_skip_pointers)
                          .SetSkipReferences(options->m_skip_references),
                      class_name_str.c_str());
  
                  lldb::TypeCategoryImplSP category;
                  DataVisualization::Categories::GetCategory(
                      ConstString(options->m_category.c_str()), category);
  
                  Status error;
  
                  for (const std::string &type_name : options->m_target_types) {
                    if (!type_name.empty()) {
                      if (!CommandObjectTypeSynthAdd::AddSynth(
                              ConstString(type_name), synth_provider,
                              options->m_regex
                                  ? CommandObjectTypeSynthAdd::eRegexSynth
                                  : CommandObjectTypeSynthAdd::eRegularSynth,
                              options->m_category, &error)) {
                        error_sp->Printf("error: %s\n", error.AsCString());
                        error_sp->Flush();
                        break;
                      }
                    } else {
                      error_sp->Printf("error: invalid type name.\n");
                      error_sp->Flush();
                      break;
                    }
                  }
                }
              } else {
                error_sp->Printf("error: unable to generate a class.\n");
                error_sp->Flush();
              }
            } else {
              error_sp->Printf("error: no script interpreter.\n");
              error_sp->Flush();
            }
          } else {
            error_sp->Printf("error: internal synchronization data missing.\n");
            error_sp->Flush();
          }
        } else {
          error_sp->Printf("error: empty function, didn't add python command.\n");
          error_sp->Flush();
        }
      } else {
        error_sp->Printf(
            "error: script interpreter missing, didn't add python command.\n");
        error_sp->Flush();
      }
  
  #endif
      io_handler.SetIsDone(true);
    }
  
  public:
    enum SynthFormatType { eRegularSynth, eRegexSynth };
  
    CommandObjectTypeSynthAdd(CommandInterpreter &interpreter);
  
    ~CommandObjectTypeSynthAdd() override = default;
  
    static bool AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry,
                         SynthFormatType type, std::string category_name,
                         Status *error);
  };
  
  // CommandObjectTypeFormatAdd
  
  #define LLDB_OPTIONS_type_format_add
  #include "CommandOptions.inc"
  
  class CommandObjectTypeFormatAdd : public CommandObjectParsed {
  private:
    class CommandOptions : public OptionGroup {
    public:
      CommandOptions() : OptionGroup() {}
  
      ~CommandOptions() override = default;
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_format_add_options);
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_cascade = true;
        m_skip_pointers = false;
        m_skip_references = false;
        m_regex = false;
        m_category.assign("default");
        m_custom_type_name.clear();
      }
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option =
            g_type_format_add_options[option_idx].short_option;
        bool success;
  
        switch (short_option) {
        case 'C':
          m_cascade = OptionArgParser::ToBoolean(option_value, true, &success);
          if (!success)
            error.SetErrorStringWithFormat("invalid value for cascade: %s",
                                           option_value.str().c_str());
          break;
        case 'p':
          m_skip_pointers = true;
          break;
        case 'w':
          m_category.assign(option_value);
          break;
        case 'r':
          m_skip_references = true;
          break;
        case 'x':
          m_regex = true;
          break;
        case 't':
          m_custom_type_name.assign(option_value);
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      // Instance variables to hold the values for command options.
  
      bool m_cascade;
      bool m_skip_references;
      bool m_skip_pointers;
      bool m_regex;
      std::string m_category;
      std::string m_custom_type_name;
    };
  
    OptionGroupOptions m_option_group;
    OptionGroupFormat m_format_options;
    CommandOptions m_command_options;
  
    Options *GetOptions() override { return &m_option_group; }
  
  public:
    CommandObjectTypeFormatAdd(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type format add",
                              "Add a new formatting style for a type.", nullptr),
          m_option_group(), m_format_options(eFormatInvalid),
          m_command_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
  
      SetHelpLong(
          R"(
  The following examples of 'type format add' refer to this code snippet for context:
  
      typedef int Aint;
      typedef float Afloat;
      typedef Aint Bint;
      typedef Afloat Bfloat;
  
      Aint ix = 5;
      Bint iy = 5;
  
      Afloat fx = 3.14;
      BFloat fy = 3.14;
  
  Adding default formatting:
  
  (lldb) type format add -f hex AInt
  (lldb) frame variable iy
  
  )"
          "    Produces hexadecimal display of iy, because no formatter is available for Bint and \
  the one for Aint is used instead."
          R"(
  
  To prevent this use the cascade option '-C no' to prevent evaluation of typedef chains:
  
  
  (lldb) type format add -f hex -C no AInt
  
  Similar reasoning applies to this:
  
  (lldb) type format add -f hex -C no float -p
  
  )"
          "    All float values and float references are now formatted as hexadecimal, but not \
  pointers to floats.  Nor will it change the default display for Afloat and Bfloat objects.");
  
      // Add the "--format" to all options groups
      m_option_group.Append(&m_format_options,
                            OptionGroupFormat::OPTION_GROUP_FORMAT,
                            LLDB_OPT_SET_1);
      m_option_group.Append(&m_command_options);
      m_option_group.Finalize();
    }
  
    ~CommandObjectTypeFormatAdd() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1) {
        result.AppendErrorWithFormat("%s takes one or more args.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      const Format format = m_format_options.GetFormat();
      if (format == eFormatInvalid &&
          m_command_options.m_custom_type_name.empty()) {
        result.AppendErrorWithFormat("%s needs a valid format.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      TypeFormatImplSP entry;
  
      if (m_command_options.m_custom_type_name.empty())
        entry = std::make_shared<TypeFormatImpl_Format>(
            format, TypeFormatImpl::Flags()
                        .SetCascades(m_command_options.m_cascade)
                        .SetSkipPointers(m_command_options.m_skip_pointers)
                        .SetSkipReferences(m_command_options.m_skip_references));
      else
        entry = std::make_shared<TypeFormatImpl_EnumType>(
            ConstString(m_command_options.m_custom_type_name.c_str()),
            TypeFormatImpl::Flags()
                .SetCascades(m_command_options.m_cascade)
                .SetSkipPointers(m_command_options.m_skip_pointers)
                .SetSkipReferences(m_command_options.m_skip_references));
  
      // now I have a valid format, let's add it to every type
  
      TypeCategoryImplSP category_sp;
      DataVisualization::Categories::GetCategory(
          ConstString(m_command_options.m_category), category_sp);
      if (!category_sp)
        return false;
  
      WarnOnPotentialUnquotedUnsignedType(command, result);
  
      for (auto &arg_entry : command.entries()) {
        if (arg_entry.ref().empty()) {
          result.AppendError("empty typenames not allowed");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
  
        ConstString typeCS(arg_entry.ref());
        if (m_command_options.m_regex) {
          RegularExpression typeRX(arg_entry.ref());
          if (!typeRX.IsValid()) {
            result.AppendError(
                "regex format error (maybe this is not really a regex?)");
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
          category_sp->GetRegexTypeSummariesContainer()->Delete(typeCS);
          category_sp->GetRegexTypeFormatsContainer()->Add(std::move(typeRX),
                                                           entry);
        } else
          category_sp->GetTypeFormatsContainer()->Add(std::move(typeCS), entry);
      }
  
      result.SetStatus(eReturnStatusSuccessFinishNoResult);
      return result.Succeeded();
    }
  };
  
  #define LLDB_OPTIONS_type_formatter_delete
  #include "CommandOptions.inc"
  
  class CommandObjectTypeFormatterDelete : public CommandObjectParsed {
  protected:
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'a':
          m_delete_all = true;
          break;
        case 'w':
          m_category = std::string(option_arg);
          break;
        case 'l':
          m_language = Language::GetLanguageTypeFromString(option_arg);
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_delete_all = false;
        m_category = "default";
        m_language = lldb::eLanguageTypeUnknown;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_formatter_delete_options);
      }
  
      // Instance variables to hold the values for command options.
  
      bool m_delete_all;
      std::string m_category;
      lldb::LanguageType m_language;
    };
  
    CommandOptions m_options;
    uint32_t m_formatter_kind_mask;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeFormatterDelete(CommandInterpreter &interpreter,
                                     uint32_t formatter_kind_mask,
                                     const char *name, const char *help)
        : CommandObjectParsed(interpreter, name, help, nullptr), m_options(),
          m_formatter_kind_mask(formatter_kind_mask) {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlain;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeFormatterDelete() override = default;
  
  protected:
    virtual bool FormatterSpecificDeletion(ConstString typeCS) { return false; }
  
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc != 1) {
        result.AppendErrorWithFormat("%s takes 1 arg.\n", m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      const char *typeA = command.GetArgumentAtIndex(0);
      ConstString typeCS(typeA);
  
      if (!typeCS) {
        result.AppendError("empty typenames not allowed");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (m_options.m_delete_all) {
        DataVisualization::Categories::ForEach(
            [this, typeCS](const lldb::TypeCategoryImplSP &category_sp) -> bool {
              category_sp->Delete(typeCS, m_formatter_kind_mask);
              return true;
            });
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
        return result.Succeeded();
      }
  
      bool delete_category = false;
      bool extra_deletion = false;
  
      if (m_options.m_language != lldb::eLanguageTypeUnknown) {
        lldb::TypeCategoryImplSP category;
        DataVisualization::Categories::GetCategory(m_options.m_language,
                                                   category);
        if (category)
          delete_category = category->Delete(typeCS, m_formatter_kind_mask);
        extra_deletion = FormatterSpecificDeletion(typeCS);
      } else {
        lldb::TypeCategoryImplSP category;
        DataVisualization::Categories::GetCategory(
            ConstString(m_options.m_category.c_str()), category);
        if (category)
          delete_category = category->Delete(typeCS, m_formatter_kind_mask);
        extra_deletion = FormatterSpecificDeletion(typeCS);
      }
  
      if (delete_category || extra_deletion) {
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
        return result.Succeeded();
      } else {
        result.AppendErrorWithFormat("no custom formatter for %s.\n", typeA);
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  };
  
  #define LLDB_OPTIONS_type_formatter_clear
  #include "CommandOptions.inc"
  
  class CommandObjectTypeFormatterClear : public CommandObjectParsed {
  private:
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'a':
          m_delete_all = true;
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_delete_all = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_formatter_clear_options);
      }
  
      // Instance variables to hold the values for command options.
      bool m_delete_all;
    };
  
    CommandOptions m_options;
    uint32_t m_formatter_kind_mask;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeFormatterClear(CommandInterpreter &interpreter,
                                    uint32_t formatter_kind_mask,
                                    const char *name, const char *help)
        : CommandObjectParsed(interpreter, name, help, nullptr), m_options(),
          m_formatter_kind_mask(formatter_kind_mask) {}
  
    ~CommandObjectTypeFormatterClear() override = default;
  
  protected:
    virtual void FormatterSpecificDeletion() {}
  
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      if (m_options.m_delete_all) {
        DataVisualization::Categories::ForEach(
            [this](const TypeCategoryImplSP &category_sp) -> bool {
              category_sp->Clear(m_formatter_kind_mask);
              return true;
            });
      } else {
        lldb::TypeCategoryImplSP category;
        if (command.GetArgumentCount() > 0) {
          const char *cat_name = command.GetArgumentAtIndex(0);
          ConstString cat_nameCS(cat_name);
          DataVisualization::Categories::GetCategory(cat_nameCS, category);
        } else {
          DataVisualization::Categories::GetCategory(ConstString(nullptr),
                                                     category);
        }
        category->Clear(m_formatter_kind_mask);
      }
  
      FormatterSpecificDeletion();
  
      result.SetStatus(eReturnStatusSuccessFinishResult);
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeFormatDelete
  
  class CommandObjectTypeFormatDelete : public CommandObjectTypeFormatterDelete {
  public:
    CommandObjectTypeFormatDelete(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterDelete(
              interpreter,
              eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
              "type format delete",
              "Delete an existing formatting style for a type.") {}
  
    ~CommandObjectTypeFormatDelete() override = default;
  };
  
  // CommandObjectTypeFormatClear
  
  class CommandObjectTypeFormatClear : public CommandObjectTypeFormatterClear {
  public:
    CommandObjectTypeFormatClear(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterClear(
              interpreter,
              eFormatCategoryItemValue | eFormatCategoryItemRegexValue,
              "type format clear", "Delete all existing format styles.") {}
  };
  
  #define LLDB_OPTIONS_type_formatter_list
  #include "CommandOptions.inc"
  
  template <typename FormatterType>
  class CommandObjectTypeFormatterList : public CommandObjectParsed {
    typedef typename FormatterType::SharedPointer FormatterSharedPointer;
  
    class CommandOptions : public Options {
    public:
      CommandOptions()
          : Options(), m_category_regex("", ""),
            m_category_language(lldb::eLanguageTypeUnknown,
                                lldb::eLanguageTypeUnknown) {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
        switch (short_option) {
        case 'w':
          m_category_regex.SetCurrentValue(option_arg);
          m_category_regex.SetOptionWasSet();
          break;
        case 'l':
          error = m_category_language.SetValueFromString(option_arg);
          if (error.Success())
            m_category_language.SetOptionWasSet();
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_category_regex.Clear();
        m_category_language.Clear();
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_formatter_list_options);
      }
  
      // Instance variables to hold the values for command options.
  
      OptionValueString m_category_regex;
      OptionValueLanguage m_category_language;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeFormatterList(CommandInterpreter &interpreter,
                                   const char *name, const char *help)
        : CommandObjectParsed(interpreter, name, help, nullptr), m_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatOptional;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeFormatterList() override = default;
  
  protected:
    virtual bool FormatterSpecificList(CommandReturnObject &result) {
      return false;
    }
  
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      std::unique_ptr<RegularExpression> category_regex;
      std::unique_ptr<RegularExpression> formatter_regex;
  
      if (m_options.m_category_regex.OptionWasSet()) {
        category_regex.reset(new RegularExpression(
            m_options.m_category_regex.GetCurrentValueAsRef()));
        if (!category_regex->IsValid()) {
          result.AppendErrorWithFormat(
              "syntax error in category regular expression '%s'",
              m_options.m_category_regex.GetCurrentValueAsRef().str().c_str());
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
  
      if (argc == 1) {
        const char *arg = command.GetArgumentAtIndex(0);
        formatter_regex.reset(
            new RegularExpression(llvm::StringRef::withNullAsEmpty(arg)));
        if (!formatter_regex->IsValid()) {
          result.AppendErrorWithFormat("syntax error in regular expression '%s'",
                                       arg);
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
  
      bool any_printed = false;
  
      auto category_closure =
          [&result, &formatter_regex,
           &any_printed](const lldb::TypeCategoryImplSP &category) -> void {
        result.GetOutputStream().Printf(
            "-----------------------\nCategory: %s%s\n-----------------------\n",
            category->GetName(), category->IsEnabled() ? "" : " (disabled)");
  
        TypeCategoryImpl::ForEachCallbacks<FormatterType> foreach;
        foreach
          .SetExact([&result, &formatter_regex, &any_printed](
                        ConstString name,
                        const FormatterSharedPointer &format_sp) -> bool {
            if (formatter_regex) {
              bool escape = true;
              if (name.GetStringRef() == formatter_regex->GetText()) {
                escape = false;
              } else if (formatter_regex->Execute(name.GetStringRef())) {
                escape = false;
              }
  
              if (escape)
                return true;
            }
  
            any_printed = true;
            result.GetOutputStream().Printf("%s: %s\n", name.AsCString(),
                                            format_sp->GetDescription().c_str());
            return true;
          });
  
        foreach
          .SetWithRegex([&result, &formatter_regex, &any_printed](
                            const RegularExpression &regex,
                            const FormatterSharedPointer &format_sp) -> bool {
            if (formatter_regex) {
              bool escape = true;
              if (regex.GetText() == formatter_regex->GetText()) {
                escape = false;
              } else if (formatter_regex->Execute(regex.GetText())) {
                escape = false;
              }
  
              if (escape)
                return true;
            }
  
            any_printed = true;
            result.GetOutputStream().Printf("%s: %s\n",
                                            regex.GetText().str().c_str(),
                                            format_sp->GetDescription().c_str());
            return true;
          });
  
        category->ForEach(foreach);
      };
  
      if (m_options.m_category_language.OptionWasSet()) {
        lldb::TypeCategoryImplSP category_sp;
        DataVisualization::Categories::GetCategory(
            m_options.m_category_language.GetCurrentValue(), category_sp);
        if (category_sp)
          category_closure(category_sp);
      } else {
        DataVisualization::Categories::ForEach(
            [&category_regex, &category_closure](
                const lldb::TypeCategoryImplSP &category) -> bool {
              if (category_regex) {
                bool escape = true;
                if (category->GetName() == category_regex->GetText()) {
                  escape = false;
                } else if (category_regex->Execute(
                               llvm::StringRef::withNullAsEmpty(
                                   category->GetName()))) {
                  escape = false;
                }
  
                if (escape)
                  return true;
              }
  
              category_closure(category);
  
              return true;
            });
  
        any_printed = FormatterSpecificList(result) | any_printed;
      }
  
      if (any_printed)
        result.SetStatus(eReturnStatusSuccessFinishResult);
      else {
        result.GetOutputStream().PutCString("no matching results found.\n");
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      }
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeFormatList
  
  class CommandObjectTypeFormatList
      : public CommandObjectTypeFormatterList<TypeFormatImpl> {
  public:
    CommandObjectTypeFormatList(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterList(interpreter, "type format list",
                                         "Show a list of current formats.") {}
  };
  
  Status CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue(
      uint32_t option_idx, llvm::StringRef option_arg,
      ExecutionContext *execution_context) {
    Status error;
    const int short_option = m_getopt_table[option_idx].val;
    bool success;
  
    switch (short_option) {
    case 'C':
      m_flags.SetCascades(OptionArgParser::ToBoolean(option_arg, true, &success));
      if (!success)
        error.SetErrorStringWithFormat("invalid value for cascade: %s",
                                       option_arg.str().c_str());
      break;
    case 'e':
      m_flags.SetDontShowChildren(false);
      break;
    case 'h':
      m_flags.SetHideEmptyAggregates(true);
      break;
    case 'v':
      m_flags.SetDontShowValue(true);
      break;
    case 'c':
      m_flags.SetShowMembersOneLiner(true);
      break;
    case 's':
      m_format_string = std::string(option_arg);
      break;
    case 'p':
      m_flags.SetSkipPointers(true);
      break;
    case 'r':
      m_flags.SetSkipReferences(true);
      break;
    case 'x':
      m_regex = true;
      break;
    case 'n':
      m_name.SetString(option_arg);
      break;
    case 'o':
      m_python_script = option_arg;
      m_is_add_script = true;
      break;
    case 'F':
      m_python_function = option_arg;
      m_is_add_script = true;
      break;
    case 'P':
      m_is_add_script = true;
      break;
    case 'w':
      m_category = std::string(option_arg);
      break;
    case 'O':
      m_flags.SetHideItemNames(true);
      break;
    default:
      llvm_unreachable("Unimplemented option");
    }
  
    return error;
  }
  
  void CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting(
      ExecutionContext *execution_context) {
    m_flags.Clear().SetCascades().SetDontShowChildren().SetDontShowValue(false);
    m_flags.SetShowMembersOneLiner(false)
        .SetSkipPointers(false)
        .SetSkipReferences(false)
        .SetHideItemNames(false);
  
    m_regex = false;
    m_name.Clear();
    m_python_script = "";
    m_python_function = "";
    m_format_string = "";
    m_is_add_script = false;
    m_category = "default";
  }
  
  #if LLDB_ENABLE_PYTHON
  
  bool CommandObjectTypeSummaryAdd::Execute_ScriptSummary(
      Args &command, CommandReturnObject &result) {
    const size_t argc = command.GetArgumentCount();
  
    if (argc < 1 && !m_options.m_name) {
      result.AppendErrorWithFormat("%s takes one or more args.\n",
                                   m_cmd_name.c_str());
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    TypeSummaryImplSP script_format;
  
    if (!m_options.m_python_function
             .empty()) // we have a Python function ready to use
    {
      const char *funct_name = m_options.m_python_function.c_str();
      if (!funct_name || !funct_name[0]) {
        result.AppendError("function name empty.\n");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::string code =
          ("    " + m_options.m_python_function + "(valobj,internal_dict)");
  
      script_format = std::make_shared<ScriptSummaryFormat>(
          m_options.m_flags, funct_name, code.c_str());
  
      ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
  
      if (interpreter && !interpreter->CheckObjectExists(funct_name))
        result.AppendWarningWithFormat(
            "The provided function \"%s\" does not exist - "
            "please define it before attempting to use this summary.\n",
            funct_name);
    } else if (!m_options.m_python_script
                    .empty()) // we have a quick 1-line script, just use it
    {
      ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
      if (!interpreter) {
        result.AppendError("script interpreter missing - unable to generate "
                           "function wrapper.\n");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
      StringList funct_sl;
      funct_sl << m_options.m_python_script.c_str();
      std::string funct_name_str;
      if (!interpreter->GenerateTypeScriptFunction(funct_sl, funct_name_str)) {
        result.AppendError("unable to generate function wrapper.\n");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
      if (funct_name_str.empty()) {
        result.AppendError(
            "script interpreter failed to generate a valid function name.\n");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::string code = "    " + m_options.m_python_script;
  
      script_format = std::make_shared<ScriptSummaryFormat>(
          m_options.m_flags, funct_name_str.c_str(), code.c_str());
    } else {
      // Use an IOHandler to grab Python code from the user
      ScriptAddOptions *options =
          new ScriptAddOptions(m_options.m_flags, m_options.m_regex,
                               m_options.m_name, m_options.m_category);
  
      for (auto &entry : command.entries()) {
        if (entry.ref().empty()) {
          result.AppendError("empty typenames not allowed");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
  
        options->m_target_types << entry.ref();
      }
  
      m_interpreter.GetPythonCommandsFromIOHandler(
          "    ",   // Prompt
          *this,    // IOHandlerDelegate
          true,     // Run IOHandler in async mode
          options); // Baton for the "io_handler" that will be passed back into
                    // our IOHandlerDelegate functions
      result.SetStatus(eReturnStatusSuccessFinishNoResult);
  
      return result.Succeeded();
    }
  
    // if I am here, script_format must point to something good, so I can add
    // that as a script summary to all interested parties
  
    Status error;
  
    for (auto &entry : command.entries()) {
      CommandObjectTypeSummaryAdd::AddSummary(
          ConstString(entry.ref()), script_format,
          (m_options.m_regex ? eRegexSummary : eRegularSummary),
          m_options.m_category, &error);
      if (error.Fail()) {
        result.AppendError(error.AsCString());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    if (m_options.m_name) {
      AddSummary(m_options.m_name, script_format, eNamedSummary,
                 m_options.m_category, &error);
      if (error.Fail()) {
        result.AppendError(error.AsCString());
        result.AppendError("added to types, but not given a name");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    return result.Succeeded();
  }
  
  #endif
  
  bool CommandObjectTypeSummaryAdd::Execute_StringSummary(
      Args &command, CommandReturnObject &result) {
    const size_t argc = command.GetArgumentCount();
  
    if (argc < 1 && !m_options.m_name) {
      result.AppendErrorWithFormat("%s takes one or more args.\n",
                                   m_cmd_name.c_str());
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    if (!m_options.m_flags.GetShowMembersOneLiner() &&
        m_options.m_format_string.empty()) {
      result.AppendError("empty summary strings not allowed");
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    const char *format_cstr = (m_options.m_flags.GetShowMembersOneLiner()
                                   ? ""
                                   : m_options.m_format_string.c_str());
  
    // ${var%S} is an endless recursion, prevent it
    if (strcmp(format_cstr, "${var%S}") == 0) {
      result.AppendError("recursive summary not allowed");
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    std::unique_ptr<StringSummaryFormat> string_format(
        new StringSummaryFormat(m_options.m_flags, format_cstr));
    if (!string_format) {
      result.AppendError("summary creation failed");
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
    if (string_format->m_error.Fail()) {
      result.AppendErrorWithFormat("syntax error: %s",
                                   string_format->m_error.AsCString("<unknown>"));
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
    lldb::TypeSummaryImplSP entry(string_format.release());
  
    // now I have a valid format, let's add it to every type
    Status error;
    for (auto &arg_entry : command.entries()) {
      if (arg_entry.ref().empty()) {
        result.AppendError("empty typenames not allowed");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
      ConstString typeCS(arg_entry.ref());
  
      AddSummary(typeCS, entry,
                 (m_options.m_regex ? eRegexSummary : eRegularSummary),
                 m_options.m_category, &error);
  
      if (error.Fail()) {
        result.AppendError(error.AsCString());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    if (m_options.m_name) {
      AddSummary(m_options.m_name, entry, eNamedSummary, m_options.m_category,
                 &error);
      if (error.Fail()) {
        result.AppendError(error.AsCString());
        result.AppendError("added to types, but not given a name");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    result.SetStatus(eReturnStatusSuccessFinishNoResult);
    return result.Succeeded();
  }
  
  CommandObjectTypeSummaryAdd::CommandObjectTypeSummaryAdd(
      CommandInterpreter &interpreter)
      : CommandObjectParsed(interpreter, "type summary add",
                            "Add a new summary style for a type.", nullptr),
        IOHandlerDelegateMultiline("DONE"), m_options(interpreter) {
    CommandArgumentEntry type_arg;
    CommandArgumentData type_style_arg;
  
    type_style_arg.arg_type = eArgTypeName;
    type_style_arg.arg_repetition = eArgRepeatPlus;
  
    type_arg.push_back(type_style_arg);
  
    m_arguments.push_back(type_arg);
  
    SetHelpLong(
        R"(
  The following examples of 'type summary add' refer to this code snippet for context:
  
      struct JustADemo
      {
          int* ptr;
          float value;
          JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}
      };
      JustADemo demo_instance(42, 3.14);
  
      typedef JustADemo NewDemo;
      NewDemo new_demo_instance(42, 3.14);
  
  (lldb) type summary add --summary-string "the answer is ${*var.ptr}" JustADemo
  
      Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42"
  
  (lldb) type summary add --summary-string "the answer is ${*var.ptr}, and the question is ${var.value}" JustADemo
  
      Subsequently displaying demo_instance with 'frame variable' or 'expression' will display "the answer is 42 and the question is 3.14"
  
  )"
        "Alternatively, you could define formatting for all pointers to integers and \
  rely on that when formatting JustADemo to obtain the same result:"
        R"(
  
  (lldb) type summary add --summary-string "${var%V} -> ${*var}" "int *"
  (lldb) type summary add --summary-string "the answer is ${var.ptr}, and the question is ${var.value}" JustADemo
  
  )"
        "Type summaries are automatically applied to derived typedefs, so the examples \
  above apply to both JustADemo and NewDemo.  The cascade option can be used to \
  suppress this behavior:"
        R"(
  
  (lldb) type summary add --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo -C no
  
      The summary will now be used for values of JustADemo but not NewDemo.
  
  )"
        "By default summaries are shown for pointers and references to values of the \
  specified type.  To suppress formatting for pointers use the -p option, or apply \
  the corresponding -r option to suppress formatting for references:"
        R"(
  
  (lldb) type summary add -p -r --summary-string "${var.ptr}, ${var.value},{${var.byte}}" JustADemo
  
  )"
        "One-line summaries including all fields in a type can be inferred without supplying an \
  explicit summary string by passing the -c option:"
        R"(
  
  (lldb) type summary add -c JustADemo
  (lldb) frame variable demo_instance
  (ptr=<address>, value=3.14)
  
  )"
        "Type summaries normally suppress the nested display of individual fields.  To \
  supply a summary to supplement the default structure add the -e option:"
        R"(
  
  (lldb) type summary add -e --summary-string "*ptr = ${*var.ptr}" JustADemo
  
  )"
        "Now when displaying JustADemo values the int* is displayed, followed by the \
  standard LLDB sequence of children, one per line:"
        R"(
  
  *ptr = 42 {
    ptr = <address>
    value = 3.14
  }
  
  )"
        "You can also add summaries written in Python.  These scripts use lldb public API to \
  gather information from your variables and produce a meaningful summary.  To start a \
  multi-line script use the -P option.  The function declaration will be displayed along with \
  a comment describing the two arguments.  End your script with the  word 'DONE' on a line by \
  itself:"
        R"(
  
  (lldb) type summary add JustADemo -P
  def function (valobj,internal_dict):
  """valobj: an SBValue which you want to provide a summary for
  internal_dict: an LLDB support object not to be used"""
      value = valobj.GetChildMemberWithName('value');
      return 'My value is ' + value.GetValue();
      DONE
  
  Alternatively, the -o option can be used when providing a simple one-line Python script:
  
  (lldb) type summary add JustADemo -o "value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();")");
  }
  
  bool CommandObjectTypeSummaryAdd::DoExecute(Args &command,
                                              CommandReturnObject &result) {
    WarnOnPotentialUnquotedUnsignedType(command, result);
  
    if (m_options.m_is_add_script) {
  #if LLDB_ENABLE_PYTHON
      return Execute_ScriptSummary(command, result);
  #else
      result.AppendError("python is disabled");
      result.SetStatus(eReturnStatusFailed);
      return false;
  #endif
    }
  
    return Execute_StringSummary(command, result);
  }
  
  static bool FixArrayTypeNameWithRegex(ConstString &type_name) {
    llvm::StringRef type_name_ref(type_name.GetStringRef());
  
    if (type_name_ref.endswith("[]")) {
      std::string type_name_str(type_name.GetCString());
      type_name_str.resize(type_name_str.length() - 2);
      if (type_name_str.back() != ' ')
        type_name_str.append(" \\[[0-9]+\\]");
      else
        type_name_str.append("\\[[0-9]+\\]");
      type_name.SetCString(type_name_str.c_str());
      return true;
    }
    return false;
  }
  
  bool CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name,
                                               TypeSummaryImplSP entry,
                                               SummaryFormatType type,
                                               std::string category_name,
                                               Status *error) {
    lldb::TypeCategoryImplSP category;
    DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
                                               category);
  
    if (type == eRegularSummary) {
      if (FixArrayTypeNameWithRegex(type_name))
        type = eRegexSummary;
    }
  
    if (type == eRegexSummary) {
      RegularExpression typeRX(type_name.GetStringRef());
      if (!typeRX.IsValid()) {
        if (error)
          error->SetErrorString(
              "regex format error (maybe this is not really a regex?)");
        return false;
      }
  
      category->GetRegexTypeSummariesContainer()->Delete(type_name);
      category->GetRegexTypeSummariesContainer()->Add(std::move(typeRX), entry);
  
      return true;
    } else if (type == eNamedSummary) {
      // system named summaries do not exist (yet?)
      DataVisualization::NamedSummaryFormats::Add(type_name, entry);
      return true;
    } else {
      category->GetTypeSummariesContainer()->Add(std::move(type_name), entry);
      return true;
    }
  }
  
  // CommandObjectTypeSummaryDelete
  
  class CommandObjectTypeSummaryDelete : public CommandObjectTypeFormatterDelete {
  public:
    CommandObjectTypeSummaryDelete(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterDelete(
              interpreter,
              eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
              "type summary delete", "Delete an existing summary for a type.") {}
  
    ~CommandObjectTypeSummaryDelete() override = default;
  
  protected:
    bool FormatterSpecificDeletion(ConstString typeCS) override {
      if (m_options.m_language != lldb::eLanguageTypeUnknown)
        return false;
      return DataVisualization::NamedSummaryFormats::Delete(typeCS);
    }
  };
  
  class CommandObjectTypeSummaryClear : public CommandObjectTypeFormatterClear {
  public:
    CommandObjectTypeSummaryClear(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterClear(
              interpreter,
              eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary,
              "type summary clear", "Delete all existing summaries.") {}
  
  protected:
    void FormatterSpecificDeletion() override {
      DataVisualization::NamedSummaryFormats::Clear();
    }
  };
  
  // CommandObjectTypeSummaryList
  
  class CommandObjectTypeSummaryList
      : public CommandObjectTypeFormatterList<TypeSummaryImpl> {
  public:
    CommandObjectTypeSummaryList(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterList(interpreter, "type summary list",
                                         "Show a list of current summaries.") {}
  
  protected:
    bool FormatterSpecificList(CommandReturnObject &result) override {
      if (DataVisualization::NamedSummaryFormats::GetCount() > 0) {
        result.GetOutputStream().Printf("Named summaries:\n");
        DataVisualization::NamedSummaryFormats::ForEach(
            [&result](ConstString name,
                      const TypeSummaryImplSP &summary_sp) -> bool {
              result.GetOutputStream().Printf(
                  "%s: %s\n", name.AsCString(),
                  summary_sp->GetDescription().c_str());
              return true;
            });
        return true;
      }
      return false;
    }
  };
  
  // CommandObjectTypeCategoryDefine
  #define LLDB_OPTIONS_type_category_define
  #include "CommandOptions.inc"
  
  class CommandObjectTypeCategoryDefine : public CommandObjectParsed {
    class CommandOptions : public Options {
    public:
      CommandOptions()
          : Options(), m_define_enabled(false, false),
            m_cate_language(eLanguageTypeUnknown, eLanguageTypeUnknown) {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'e':
          m_define_enabled.SetValueFromString(llvm::StringRef("true"));
          break;
        case 'l':
          error = m_cate_language.SetValueFromString(option_arg);
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_define_enabled.Clear();
        m_cate_language.Clear();
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_category_define_options);
      }
  
      // Instance variables to hold the values for command options.
  
      OptionValueBoolean m_define_enabled;
      OptionValueLanguage m_cate_language;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeCategoryDefine(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type category define",
                              "Define a new category as a source of formatters.",
                              nullptr),
          m_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeCategoryDefine() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1) {
        result.AppendErrorWithFormat("%s takes 1 or more args.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      for (auto &entry : command.entries()) {
        TypeCategoryImplSP category_sp;
        if (DataVisualization::Categories::GetCategory(ConstString(entry.ref()),
                                                       category_sp) &&
            category_sp) {
          category_sp->AddLanguage(m_options.m_cate_language.GetCurrentValue());
          if (m_options.m_define_enabled.GetCurrentValue())
            DataVisualization::Categories::Enable(category_sp,
                                                  TypeCategoryMap::Default);
        }
      }
  
      result.SetStatus(eReturnStatusSuccessFinishResult);
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeCategoryEnable
  #define LLDB_OPTIONS_type_category_enable
  #include "CommandOptions.inc"
  
  class CommandObjectTypeCategoryEnable : public CommandObjectParsed {
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'l':
          if (!option_arg.empty()) {
            m_language = Language::GetLanguageTypeFromString(option_arg);
            if (m_language == lldb::eLanguageTypeUnknown)
              error.SetErrorStringWithFormat("unrecognized language '%s'",
                                             option_arg.str().c_str());
          }
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_language = lldb::eLanguageTypeUnknown;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_category_enable_options);
      }
  
      // Instance variables to hold the values for command options.
  
      lldb::LanguageType m_language;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeCategoryEnable(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type category enable",
                              "Enable a category as a source of formatters.",
                              nullptr),
          m_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeCategoryEnable() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
        result.AppendErrorWithFormat("%s takes arguments and/or a language",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
        DataVisualization::Categories::EnableStar();
      } else if (argc > 0) {
        for (int i = argc - 1; i >= 0; i--) {
          const char *typeA = command.GetArgumentAtIndex(i);
          ConstString typeCS(typeA);
  
          if (!typeCS) {
            result.AppendError("empty category name not allowed");
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
          DataVisualization::Categories::Enable(typeCS);
          lldb::TypeCategoryImplSP cate;
          if (DataVisualization::Categories::GetCategory(typeCS, cate) && cate) {
            if (cate->GetCount() == 0) {
              result.AppendWarning("empty category enabled (typo?)");
            }
          }
        }
      }
  
      if (m_options.m_language != lldb::eLanguageTypeUnknown)
        DataVisualization::Categories::Enable(m_options.m_language);
  
      result.SetStatus(eReturnStatusSuccessFinishResult);
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeCategoryDelete
  
  class CommandObjectTypeCategoryDelete : public CommandObjectParsed {
  public:
    CommandObjectTypeCategoryDelete(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type category delete",
                              "Delete a category and all associated formatters.",
                              nullptr) {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeCategoryDelete() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1) {
        result.AppendErrorWithFormat("%s takes 1 or more arg.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      bool success = true;
  
      // the order is not relevant here
      for (int i = argc - 1; i >= 0; i--) {
        const char *typeA = command.GetArgumentAtIndex(i);
        ConstString typeCS(typeA);
  
        if (!typeCS) {
          result.AppendError("empty category name not allowed");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
        if (!DataVisualization::Categories::Delete(typeCS))
          success = false; // keep deleting even if we hit an error
      }
      if (success) {
        result.SetStatus(eReturnStatusSuccessFinishResult);
        return result.Succeeded();
      } else {
        result.AppendError("cannot delete one or more categories\n");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  };
  
  // CommandObjectTypeCategoryDisable
  #define LLDB_OPTIONS_type_category_disable
  #include "CommandOptions.inc"
  
  class CommandObjectTypeCategoryDisable : public CommandObjectParsed {
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'l':
          if (!option_arg.empty()) {
            m_language = Language::GetLanguageTypeFromString(option_arg);
            if (m_language == lldb::eLanguageTypeUnknown)
              error.SetErrorStringWithFormat("unrecognized language '%s'",
                                             option_arg.str().c_str());
          }
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_language = lldb::eLanguageTypeUnknown;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_category_disable_options);
      }
  
      // Instance variables to hold the values for command options.
  
      lldb::LanguageType m_language;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
  public:
    CommandObjectTypeCategoryDisable(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type category disable",
                              "Disable a category as a source of formatters.",
                              nullptr),
          m_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeCategoryDisable() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1 && m_options.m_language == lldb::eLanguageTypeUnknown) {
        result.AppendErrorWithFormat("%s takes arguments and/or a language",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (argc == 1 && strcmp(command.GetArgumentAtIndex(0), "*") == 0) {
        DataVisualization::Categories::DisableStar();
      } else if (argc > 0) {
        // the order is not relevant here
        for (int i = argc - 1; i >= 0; i--) {
          const char *typeA = command.GetArgumentAtIndex(i);
          ConstString typeCS(typeA);
  
          if (!typeCS) {
            result.AppendError("empty category name not allowed");
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
          DataVisualization::Categories::Disable(typeCS);
        }
      }
  
      if (m_options.m_language != lldb::eLanguageTypeUnknown)
        DataVisualization::Categories::Disable(m_options.m_language);
  
      result.SetStatus(eReturnStatusSuccessFinishResult);
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeCategoryList
  
  class CommandObjectTypeCategoryList : public CommandObjectParsed {
  public:
    CommandObjectTypeCategoryList(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type category list",
                              "Provide a list of all existing categories.",
                              nullptr) {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatOptional;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
    }
  
    ~CommandObjectTypeCategoryList() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      std::unique_ptr<RegularExpression> regex;
  
      if (argc == 1) {
        const char *arg = command.GetArgumentAtIndex(0);
        regex.reset(new RegularExpression(llvm::StringRef::withNullAsEmpty(arg)));
        if (!regex->IsValid()) {
          result.AppendErrorWithFormat(
              "syntax error in category regular expression '%s'", arg);
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      } else if (argc != 0) {
        result.AppendErrorWithFormat("%s takes 0 or one arg.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      DataVisualization::Categories::ForEach(
          [&regex, &result](const lldb::TypeCategoryImplSP &category_sp) -> bool {
            if (regex) {
              bool escape = true;
              if (regex->GetText() == category_sp->GetName()) {
                escape = false;
              } else if (regex->Execute(llvm::StringRef::withNullAsEmpty(
                             category_sp->GetName()))) {
                escape = false;
              }
  
              if (escape)
                return true;
            }
  
            result.GetOutputStream().Printf(
                "Category: %s\n", category_sp->GetDescription().c_str());
  
            return true;
          });
  
      result.SetStatus(eReturnStatusSuccessFinishResult);
      return result.Succeeded();
    }
  };
  
  // CommandObjectTypeFilterList
  
  class CommandObjectTypeFilterList
      : public CommandObjectTypeFormatterList<TypeFilterImpl> {
  public:
    CommandObjectTypeFilterList(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterList(interpreter, "type filter list",
                                         "Show a list of current filters.") {}
  };
  
  #if LLDB_ENABLE_PYTHON
  
  // CommandObjectTypeSynthList
  
  class CommandObjectTypeSynthList
      : public CommandObjectTypeFormatterList<SyntheticChildren> {
  public:
    CommandObjectTypeSynthList(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterList(
              interpreter, "type synthetic list",
              "Show a list of current synthetic providers.") {}
  };
  
  #endif
  
  // CommandObjectTypeFilterDelete
  
  class CommandObjectTypeFilterDelete : public CommandObjectTypeFormatterDelete {
  public:
    CommandObjectTypeFilterDelete(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterDelete(
              interpreter,
              eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
              "type filter delete", "Delete an existing filter for a type.") {}
  
    ~CommandObjectTypeFilterDelete() override = default;
  };
  
  #if LLDB_ENABLE_PYTHON
  
  // CommandObjectTypeSynthDelete
  
  class CommandObjectTypeSynthDelete : public CommandObjectTypeFormatterDelete {
  public:
    CommandObjectTypeSynthDelete(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterDelete(
              interpreter,
              eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
              "type synthetic delete",
              "Delete an existing synthetic provider for a type.") {}
  
    ~CommandObjectTypeSynthDelete() override = default;
  };
  
  #endif
  
  // CommandObjectTypeFilterClear
  
  class CommandObjectTypeFilterClear : public CommandObjectTypeFormatterClear {
  public:
    CommandObjectTypeFilterClear(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterClear(
              interpreter,
              eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
              "type filter clear", "Delete all existing filter.") {}
  };
  
  #if LLDB_ENABLE_PYTHON
  // CommandObjectTypeSynthClear
  
  class CommandObjectTypeSynthClear : public CommandObjectTypeFormatterClear {
  public:
    CommandObjectTypeSynthClear(CommandInterpreter &interpreter)
        : CommandObjectTypeFormatterClear(
              interpreter,
              eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
              "type synthetic clear",
              "Delete all existing synthetic providers.") {}
  };
  
  bool CommandObjectTypeSynthAdd::Execute_HandwritePython(
      Args &command, CommandReturnObject &result) {
    SynthAddOptions *options = new SynthAddOptions(
        m_options.m_skip_pointers, m_options.m_skip_references,
        m_options.m_cascade, m_options.m_regex, m_options.m_category);
  
    for (auto &entry : command.entries()) {
      if (entry.ref().empty()) {
        result.AppendError("empty typenames not allowed");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      options->m_target_types << entry.ref();
    }
  
    m_interpreter.GetPythonCommandsFromIOHandler(
        "    ",   // Prompt
        *this,    // IOHandlerDelegate
        true,     // Run IOHandler in async mode
        options); // Baton for the "io_handler" that will be passed back into our
                  // IOHandlerDelegate functions
    result.SetStatus(eReturnStatusSuccessFinishNoResult);
    return result.Succeeded();
  }
  
  bool CommandObjectTypeSynthAdd::Execute_PythonClass(
      Args &command, CommandReturnObject &result) {
    const size_t argc = command.GetArgumentCount();
  
    if (argc < 1) {
      result.AppendErrorWithFormat("%s takes one or more args.\n",
                                   m_cmd_name.c_str());
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    if (m_options.m_class_name.empty() && !m_options.m_input_python) {
      result.AppendErrorWithFormat("%s needs either a Python class name or -P to "
                                   "directly input Python code.\n",
                                   m_cmd_name.c_str());
      result.SetStatus(eReturnStatusFailed);
      return false;
    }
  
    SyntheticChildrenSP entry;
  
    ScriptedSyntheticChildren *impl = new ScriptedSyntheticChildren(
        SyntheticChildren::Flags()
            .SetCascades(m_options.m_cascade)
            .SetSkipPointers(m_options.m_skip_pointers)
            .SetSkipReferences(m_options.m_skip_references),
        m_options.m_class_name.c_str());
  
    entry.reset(impl);
  
    ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter();
  
    if (interpreter &&
        !interpreter->CheckObjectExists(impl->GetPythonClassName()))
      result.AppendWarning("The provided class does not exist - please define it "
                           "before attempting to use this synthetic provider");
  
    // now I have a valid provider, let's add it to every type
  
    lldb::TypeCategoryImplSP category;
    DataVisualization::Categories::GetCategory(
        ConstString(m_options.m_category.c_str()), category);
  
    Status error;
  
    for (auto &arg_entry : command.entries()) {
      if (arg_entry.ref().empty()) {
        result.AppendError("empty typenames not allowed");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      ConstString typeCS(arg_entry.ref());
      if (!AddSynth(typeCS, entry,
                    m_options.m_regex ? eRegexSynth : eRegularSynth,
                    m_options.m_category, &error)) {
        result.AppendError(error.AsCString());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
    }
  
    result.SetStatus(eReturnStatusSuccessFinishNoResult);
    return result.Succeeded();
  }
  
  CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd(
      CommandInterpreter &interpreter)
      : CommandObjectParsed(interpreter, "type synthetic add",
                            "Add a new synthetic provider for a type.", nullptr),
        IOHandlerDelegateMultiline("DONE"), m_options() {
    CommandArgumentEntry type_arg;
    CommandArgumentData type_style_arg;
  
    type_style_arg.arg_type = eArgTypeName;
    type_style_arg.arg_repetition = eArgRepeatPlus;
  
    type_arg.push_back(type_style_arg);
  
    m_arguments.push_back(type_arg);
  }
  
  bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,
                                           SyntheticChildrenSP entry,
                                           SynthFormatType type,
                                           std::string category_name,
                                           Status *error) {
    lldb::TypeCategoryImplSP category;
    DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()),
                                               category);
  
    if (type == eRegularSynth) {
      if (FixArrayTypeNameWithRegex(type_name))
        type = eRegexSynth;
    }
  
    if (category->AnyMatches(
            type_name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter,
            false)) {
      if (error)
        error->SetErrorStringWithFormat("cannot add synthetic for type %s when "
                                        "filter is defined in same category!",
                                        type_name.AsCString());
      return false;
    }
  
    if (type == eRegexSynth) {
      RegularExpression typeRX(type_name.GetStringRef());
      if (!typeRX.IsValid()) {
        if (error)
          error->SetErrorString(
              "regex format error (maybe this is not really a regex?)");
        return false;
      }
  
      category->GetRegexTypeSyntheticsContainer()->Delete(type_name);
      category->GetRegexTypeSyntheticsContainer()->Add(std::move(typeRX), entry);
  
      return true;
    } else {
      category->GetTypeSyntheticsContainer()->Add(std::move(type_name), entry);
      return true;
    }
  }
  
  #endif
  #define LLDB_OPTIONS_type_filter_add
  #include "CommandOptions.inc"
  
  class CommandObjectTypeFilterAdd : public CommandObjectParsed {
  private:
    class CommandOptions : public Options {
      typedef std::vector<std::string> option_vector;
  
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
        bool success;
  
        switch (short_option) {
        case 'C':
          m_cascade = OptionArgParser::ToBoolean(option_arg, true, &success);
          if (!success)
            error.SetErrorStringWithFormat("invalid value for cascade: %s",
                                           option_arg.str().c_str());
          break;
        case 'c':
          m_expr_paths.push_back(option_arg);
          has_child_list = true;
          break;
        case 'p':
          m_skip_pointers = true;
          break;
        case 'r':
          m_skip_references = true;
          break;
        case 'w':
          m_category = std::string(option_arg);
          break;
        case 'x':
          m_regex = true;
          break;
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_cascade = true;
        m_skip_pointers = false;
        m_skip_references = false;
        m_category = "default";
        m_expr_paths.clear();
        has_child_list = false;
        m_regex = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_filter_add_options);
      }
  
      // Instance variables to hold the values for command options.
  
      bool m_cascade;
      bool m_skip_references;
      bool m_skip_pointers;
      bool m_input_python;
      option_vector m_expr_paths;
      std::string m_category;
      bool has_child_list;
      bool m_regex;
  
      typedef option_vector::iterator ExpressionPathsIterator;
    };
  
    CommandOptions m_options;
  
    Options *GetOptions() override { return &m_options; }
  
    enum FilterFormatType { eRegularFilter, eRegexFilter };
  
    bool AddFilter(ConstString type_name, TypeFilterImplSP entry,
                   FilterFormatType type, std::string category_name,
                   Status *error) {
      lldb::TypeCategoryImplSP category;
      DataVisualization::Categories::GetCategory(
          ConstString(category_name.c_str()), category);
  
      if (type == eRegularFilter) {
        if (FixArrayTypeNameWithRegex(type_name))
          type = eRegexFilter;
      }
  
      if (category->AnyMatches(
              type_name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth,
              false)) {
        if (error)
          error->SetErrorStringWithFormat("cannot add filter for type %s when "
                                          "synthetic is defined in same "
                                          "category!",
                                          type_name.AsCString());
        return false;
      }
  
      if (type == eRegexFilter) {
        RegularExpression typeRX(type_name.GetStringRef());
        if (!typeRX.IsValid()) {
          if (error)
            error->SetErrorString(
                "regex format error (maybe this is not really a regex?)");
          return false;
        }
  
        category->GetRegexTypeFiltersContainer()->Delete(type_name);
        category->GetRegexTypeFiltersContainer()->Add(std::move(typeRX), entry);
  
        return true;
      } else {
        category->GetTypeFiltersContainer()->Add(std::move(type_name), entry);
        return true;
      }
    }
  
  public:
    CommandObjectTypeFilterAdd(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "type filter add",
                              "Add a new filter for a type.", nullptr),
          m_options() {
      CommandArgumentEntry type_arg;
      CommandArgumentData type_style_arg;
  
      type_style_arg.arg_type = eArgTypeName;
      type_style_arg.arg_repetition = eArgRepeatPlus;
  
      type_arg.push_back(type_style_arg);
  
      m_arguments.push_back(type_arg);
  
      SetHelpLong(
          R"(
  The following examples of 'type filter add' refer to this code snippet for context:
  
      class Foo {
          int a;
          int b;
          int c;
          int d;
          int e;
          int f;
          int g;
          int h;
          int i;
      }
      Foo my_foo;
  
  Adding a simple filter:
  
  (lldb) type filter add --child a --child g Foo
  (lldb) frame variable my_foo
  
  )"
          "Produces output where only a and g are displayed.  Other children of my_foo \
  (b, c, d, e, f, h and i) are available by asking for them explicitly:"
          R"(
  
  (lldb) frame variable my_foo.b my_foo.c my_foo.i
  
  )"
          "The formatting option --raw on frame variable bypasses the filter, showing \
  all children of my_foo as if no filter was defined:"
          R"(
  
  (lldb) frame variable my_foo --raw)");
    }
  
    ~CommandObjectTypeFilterAdd() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      const size_t argc = command.GetArgumentCount();
  
      if (argc < 1) {
        result.AppendErrorWithFormat("%s takes one or more args.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (m_options.m_expr_paths.empty()) {
        result.AppendErrorWithFormat("%s needs one or more children.\n",
                                     m_cmd_name.c_str());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      TypeFilterImplSP entry(new TypeFilterImpl(
          SyntheticChildren::Flags()
              .SetCascades(m_options.m_cascade)
              .SetSkipPointers(m_options.m_skip_pointers)
              .SetSkipReferences(m_options.m_skip_references)));
  
      // go through the expression paths
      CommandOptions::ExpressionPathsIterator begin,
          end = m_options.m_expr_paths.end();
  
      for (begin = m_options.m_expr_paths.begin(); begin != end; begin++)
        entry->AddExpressionPath(*begin);
  
      // now I have a valid provider, let's add it to every type
  
      lldb::TypeCategoryImplSP category;
      DataVisualization::Categories::GetCategory(
          ConstString(m_options.m_category.c_str()), category);
  
      Status error;
  
      WarnOnPotentialUnquotedUnsignedType(command, result);
  
      for (auto &arg_entry : command.entries()) {
        if (arg_entry.ref().empty()) {
          result.AppendError("empty typenames not allowed");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
  
        ConstString typeCS(arg_entry.ref());
        if (!AddFilter(typeCS, entry,
                       m_options.m_regex ? eRegexFilter : eRegularFilter,
                       m_options.m_category, &error)) {
          result.AppendError(error.AsCString());
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
  
      result.SetStatus(eReturnStatusSuccessFinishNoResult);
      return result.Succeeded();
    }
  };
  
  // "type lookup"
  #define LLDB_OPTIONS_type_lookup
  #include "CommandOptions.inc"
  
  class CommandObjectTypeLookup : public CommandObjectRaw {
  protected:
    // this function is allowed to do a more aggressive job at guessing languages
    // than the expression parser is comfortable with - so leave the original
    // call alone and add one that is specific to type lookup
    lldb::LanguageType GuessLanguage(StackFrame *frame) {
      lldb::LanguageType lang_type = lldb::eLanguageTypeUnknown;
  
      if (!frame)
        return lang_type;
  
      lang_type = frame->GuessLanguage();
      if (lang_type != lldb::eLanguageTypeUnknown)
        return lang_type;
  
      Symbol *s = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
      if (s)
        lang_type = s->GetMangled().GuessLanguage();
  
      return lang_type;
    }
  
    class CommandOptions : public OptionGroup {
    public:
      CommandOptions()
          : OptionGroup(), m_show_help(false), m_language(eLanguageTypeUnknown) {}
  
      ~CommandOptions() override = default;
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_type_lookup_options);
      }
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
                            ExecutionContext *execution_context) override {
        Status error;
  
        const int short_option = g_type_lookup_options[option_idx].short_option;
  
        switch (short_option) {
        case 'h':
          m_show_help = true;
          break;
  
        case 'l':
          m_language = Language::GetLanguageTypeFromString(option_value);
          break;
  
        default:
          llvm_unreachable("Unimplemented option");
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_show_help = false;
        m_language = eLanguageTypeUnknown;
      }
  
      // Options table: Required for subclasses of Options.
  
      bool m_show_help;
      lldb::LanguageType m_language;
    };
  
    OptionGroupOptions m_option_group;
    CommandOptions m_command_options;
  
  public:
    CommandObjectTypeLookup(CommandInterpreter &interpreter)
        : CommandObjectRaw(interpreter, "type lookup",
                           "Lookup types and declarations in the current target, "
                           "following language-specific naming conventions.",
                           "type lookup <type-specifier>",
                           eCommandRequiresTarget),
          m_option_group(), m_command_options() {
      m_option_group.Append(&m_command_options);
      m_option_group.Finalize();
    }
  
    ~CommandObjectTypeLookup() override = default;
  
    Options *GetOptions() override { return &m_option_group; }
  
    llvm::StringRef GetHelpLong() override {
      if (!m_cmd_help_long.empty())
        return m_cmd_help_long;
  
      StreamString stream;
      Language::ForEach([&](Language *lang) {
        if (const char *help = lang->GetLanguageSpecificTypeLookupHelp())
          stream.Printf("%s\n", help);
        return true;
      });
  
      m_cmd_help_long = stream.GetString();
      return m_cmd_help_long;
    }
  
    bool DoExecute(llvm::StringRef raw_command_line,
                   CommandReturnObject &result) override {
      if (raw_command_line.empty()) {
        result.SetError(
            "type lookup cannot be invoked without a type name as argument");
        return false;
      }
  
      auto exe_ctx = GetCommandInterpreter().GetExecutionContext();
      m_option_group.NotifyOptionParsingStarting(&exe_ctx);
  
      OptionsWithRaw args(raw_command_line);
      const char *name_of_type = args.GetRawPart().c_str();
  
      if (args.HasArgs())
        if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,
                                   exe_ctx))
          return false;
  
      ExecutionContextScope *best_scope = exe_ctx.GetBestExecutionContextScope();
  
      bool any_found = false;
  
      std::vector<Language *> languages;
  
      bool is_global_search = false;
      LanguageType guessed_language = lldb::eLanguageTypeUnknown;
  
      if ((is_global_search =
               (m_command_options.m_language == eLanguageTypeUnknown))) {
        Language::ForEach([&](Language *lang) {
          languages.push_back(lang);
          return true;
        });
      } else {
        languages.push_back(Language::FindPlugin(m_command_options.m_language));
      }
  
      // This is not the most efficient way to do this, but we support very few
      // languages so the cost of the sort is going to be dwarfed by the actual
      // lookup anyway
      if (StackFrame *frame = m_exe_ctx.GetFramePtr()) {
        guessed_language = GuessLanguage(frame);
        if (guessed_language != eLanguageTypeUnknown) {
          llvm::sort(
              languages.begin(), languages.end(),
              [guessed_language](Language *lang1, Language *lang2) -> bool {
                if (!lang1 || !lang2)
                  return false;
                LanguageType lt1 = lang1->GetLanguageType();
                LanguageType lt2 = lang2->GetLanguageType();
                if (lt1 == guessed_language)
                  return true; // make the selected frame's language come first
                if (lt2 == guessed_language)
                  return false; // make the selected frame's language come first
                return (lt1 < lt2); // normal comparison otherwise
              });
        }
      }
  
      bool is_first_language = true;
  
      for (Language *language : languages) {
        if (!language)
          continue;
  
        if (auto scavenger = language->GetTypeScavenger()) {
          Language::TypeScavenger::ResultSet search_results;
          if (scavenger->Find(best_scope, name_of_type, search_results) > 0) {
            for (const auto &search_result : search_results) {
              if (search_result && search_result->IsValid()) {
                any_found = true;
                search_result->DumpToStream(result.GetOutputStream(),
                                            this->m_command_options.m_show_help);
              }
            }
          }
        }
        // this is "type lookup SomeName" and we did find a match, so get out
        if (any_found && is_global_search)
          break;
        else if (is_first_language && is_global_search &&
                 guessed_language != lldb::eLanguageTypeUnknown) {
          is_first_language = false;
          result.GetOutputStream().Printf(
              "no type was found in the current language %s matching '%s'; "
              "performing a global search across all languages\n",
              Language::GetNameForLanguageType(guessed_language), name_of_type);
        }
      }
  
      if (!any_found)
        result.AppendMessageWithFormat("no type was found matching '%s'\n",
                                       name_of_type);
  
      result.SetStatus(any_found ? lldb::eReturnStatusSuccessFinishResult
                                 : lldb::eReturnStatusSuccessFinishNoResult);
      return true;
    }
  };
  
  template <typename FormatterType>
  class CommandObjectFormatterInfo : public CommandObjectRaw {
  public:
    typedef std::function<typename FormatterType::SharedPointer(ValueObject &)>
        DiscoveryFunction;
    CommandObjectFormatterInfo(CommandInterpreter &interpreter,
                               const char *formatter_name,
                               DiscoveryFunction discovery_func)
        : CommandObjectRaw(interpreter, "", "", "", eCommandRequiresFrame),
          m_formatter_name(formatter_name ? formatter_name : ""),
          m_discovery_function(discovery_func) {
      StreamString name;
      name.Printf("type %s info", formatter_name);
      SetCommandName(name.GetString());
      StreamString help;
      help.Printf("This command evaluates the provided expression and shows "
                  "which %s is applied to the resulting value (if any).",
                  formatter_name);
      SetHelp(help.GetString());
      StreamString syntax;
      syntax.Printf("type %s info <expr>", formatter_name);
      SetSyntax(syntax.GetString());
    }
  
    ~CommandObjectFormatterInfo() override = default;
  
  protected:
    bool DoExecute(llvm::StringRef command,
                   CommandReturnObject &result) override {
      TargetSP target_sp = GetDebugger().GetSelectedTarget();
      Thread *thread = GetDefaultThread();
      if (!thread) {
        result.AppendError("no default thread");
        result.SetStatus(lldb::eReturnStatusFailed);
        return false;
      }
  
      StackFrameSP frame_sp = thread->GetSelectedFrame();
      ValueObjectSP result_valobj_sp;
      EvaluateExpressionOptions options;
      lldb::ExpressionResults expr_result = target_sp->EvaluateExpression(
          command, frame_sp.get(), result_valobj_sp, options);
      if (expr_result == eExpressionCompleted && result_valobj_sp) {
        result_valobj_sp =
            result_valobj_sp->GetQualifiedRepresentationIfAvailable(
                target_sp->GetPreferDynamicValue(),
                target_sp->GetEnableSyntheticValue());
        typename FormatterType::SharedPointer formatter_sp =
            m_discovery_function(*result_valobj_sp);
        if (formatter_sp) {
          std::string description(formatter_sp->GetDescription());
          result.GetOutputStream()
              << m_formatter_name << " applied to ("
              << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
              << ") " << command << " is: " << description << "\n";
          result.SetStatus(lldb::eReturnStatusSuccessFinishResult);
        } else {
          result.GetOutputStream()
              << "no " << m_formatter_name << " applies to ("
              << result_valobj_sp->GetDisplayTypeName().AsCString("<unknown>")
              << ") " << command << "\n";
          result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
        }
        return true;
      } else {
        result.AppendError("failed to evaluate expression");
        result.SetStatus(lldb::eReturnStatusFailed);
        return false;
      }
    }
  
  private:
    std::string m_formatter_name;
    DiscoveryFunction m_discovery_function;
  };
  
  class CommandObjectTypeFormat : public CommandObjectMultiword {
  public:
    CommandObjectTypeFormat(CommandInterpreter &interpreter)
        : CommandObjectMultiword(
              interpreter, "type format",
              "Commands for customizing value display formats.",
              "type format [<sub-command-options>] ") {
      LoadSubCommand(
          "add", CommandObjectSP(new CommandObjectTypeFormatAdd(interpreter)));
      LoadSubCommand("clear", CommandObjectSP(
                                  new CommandObjectTypeFormatClear(interpreter)));
      LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFormatDelete(
                                   interpreter)));
      LoadSubCommand(
          "list", CommandObjectSP(new CommandObjectTypeFormatList(interpreter)));
      LoadSubCommand(
          "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeFormatImpl>(
                      interpreter, "format",
                      [](ValueObject &valobj) -> TypeFormatImpl::SharedPointer {
                        return valobj.GetValueFormat();
                      })));
    }
  
    ~CommandObjectTypeFormat() override = default;
  };
  
  #if LLDB_ENABLE_PYTHON
  
  class CommandObjectTypeSynth : public CommandObjectMultiword {
  public:
    CommandObjectTypeSynth(CommandInterpreter &interpreter)
        : CommandObjectMultiword(
              interpreter, "type synthetic",
              "Commands for operating on synthetic type representations.",
              "type synthetic [<sub-command-options>] ") {
      LoadSubCommand("add",
                     CommandObjectSP(new CommandObjectTypeSynthAdd(interpreter)));
      LoadSubCommand(
          "clear", CommandObjectSP(new CommandObjectTypeSynthClear(interpreter)));
      LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSynthDelete(
                                   interpreter)));
      LoadSubCommand(
          "list", CommandObjectSP(new CommandObjectTypeSynthList(interpreter)));
      LoadSubCommand(
          "info",
          CommandObjectSP(new CommandObjectFormatterInfo<SyntheticChildren>(
              interpreter, "synthetic",
              [](ValueObject &valobj) -> SyntheticChildren::SharedPointer {
                return valobj.GetSyntheticChildren();
              })));
    }
  
    ~CommandObjectTypeSynth() override = default;
  };
  
  #endif
  
  class CommandObjectTypeFilter : public CommandObjectMultiword {
  public:
    CommandObjectTypeFilter(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "type filter",
                                 "Commands for operating on type filters.",
                                 "type synthetic [<sub-command-options>] ") {
      LoadSubCommand(
          "add", CommandObjectSP(new CommandObjectTypeFilterAdd(interpreter)));
      LoadSubCommand("clear", CommandObjectSP(
                                  new CommandObjectTypeFilterClear(interpreter)));
      LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeFilterDelete(
                                   interpreter)));
      LoadSubCommand(
          "list", CommandObjectSP(new CommandObjectTypeFilterList(interpreter)));
    }
  
    ~CommandObjectTypeFilter() override = default;
  };
  
  class CommandObjectTypeCategory : public CommandObjectMultiword {
  public:
    CommandObjectTypeCategory(CommandInterpreter &interpreter)
        : CommandObjectMultiword(interpreter, "type category",
                                 "Commands for operating on type categories.",
                                 "type category [<sub-command-options>] ") {
      LoadSubCommand(
          "define",
          CommandObjectSP(new CommandObjectTypeCategoryDefine(interpreter)));
      LoadSubCommand(
          "enable",
          CommandObjectSP(new CommandObjectTypeCategoryEnable(interpreter)));
      LoadSubCommand(
          "disable",
          CommandObjectSP(new CommandObjectTypeCategoryDisable(interpreter)));
      LoadSubCommand(
          "delete",
          CommandObjectSP(new CommandObjectTypeCategoryDelete(interpreter)));
      LoadSubCommand("list", CommandObjectSP(
                                 new CommandObjectTypeCategoryList(interpreter)));
    }
  
    ~CommandObjectTypeCategory() override = default;
  };
  
  class CommandObjectTypeSummary : public CommandObjectMultiword {
  public:
    CommandObjectTypeSummary(CommandInterpreter &interpreter)
        : CommandObjectMultiword(
              interpreter, "type summary",
              "Commands for editing variable summary display options.",
              "type summary [<sub-command-options>] ") {
      LoadSubCommand(
          "add", CommandObjectSP(new CommandObjectTypeSummaryAdd(interpreter)));
      LoadSubCommand("clear", CommandObjectSP(new CommandObjectTypeSummaryClear(
                                  interpreter)));
      LoadSubCommand("delete", CommandObjectSP(new CommandObjectTypeSummaryDelete(
                                   interpreter)));
      LoadSubCommand(
          "list", CommandObjectSP(new CommandObjectTypeSummaryList(interpreter)));
      LoadSubCommand(
          "info", CommandObjectSP(new CommandObjectFormatterInfo<TypeSummaryImpl>(
                      interpreter, "summary",
                      [](ValueObject &valobj) -> TypeSummaryImpl::SharedPointer {
                        return valobj.GetSummaryFormat();
                      })));
    }
  
    ~CommandObjectTypeSummary() override = default;
  };
  
  // CommandObjectType
  
  CommandObjectType::CommandObjectType(CommandInterpreter &interpreter)
      : CommandObjectMultiword(interpreter, "type",
                               "Commands for operating on the type system.",
                               "type [<sub-command-options>]") {
    LoadSubCommand("category",
                   CommandObjectSP(new CommandObjectTypeCategory(interpreter)));
    LoadSubCommand("filter",
                   CommandObjectSP(new CommandObjectTypeFilter(interpreter)));
    LoadSubCommand("format",
                   CommandObjectSP(new CommandObjectTypeFormat(interpreter)));
    LoadSubCommand("summary",
                   CommandObjectSP(new CommandObjectTypeSummary(interpreter)));
  #if LLDB_ENABLE_PYTHON
    LoadSubCommand("synthetic",
                   CommandObjectSP(new CommandObjectTypeSynth(interpreter)));
  #endif
    LoadSubCommand("lookup",
                   CommandObjectSP(new CommandObjectTypeLookup(interpreter)));
  }
  
  CommandObjectType::~CommandObjectType() = default;
  