diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp')
| -rw-r--r-- | contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp | 1330 | 
1 files changed, 1330 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp new file mode 100644 index 000000000000..6ff32080905e --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectSource.cpp @@ -0,0 +1,1330 @@ +//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectSource.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/FileLineResolver.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/StringConvert.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark CommandObjectSourceInfo +//---------------------------------------------------------------------- +// CommandObjectSourceInfo - debug line entries dumping command +//---------------------------------------------------------------------- + +static OptionDefinition g_source_info_options[] = { +    // clang-format off +  { LLDB_OPT_SET_ALL,                false, "count",    'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeCount,               "The number of line entries to display." }, +  { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib",    's', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eModuleCompletion,     eArgTypeShlibName,           "Look up the source in the given module or shared library (can be specified more than once)." }, +  { LLDB_OPT_SET_1,                  false, "file",     'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,            "The file from which to display source." }, +  { LLDB_OPT_SET_1,                  false, "line",     'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeLineNum,             "The line number at which to start the displaying lines." }, +  { LLDB_OPT_SET_1,                  false, "end-line", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeLineNum,             "The line number at which to stop displaying lines." }, +  { LLDB_OPT_SET_2,                  false, "name",     'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion,     eArgTypeSymbol,              "The name of a function whose source to display." }, +  { LLDB_OPT_SET_3,                  false, "address",  'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line." }, +    // clang-format on +}; + +class CommandObjectSourceInfo : public CommandObjectParsed { +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options() {} + +    ~CommandOptions() override = default; + +    Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                         ExecutionContext *execution_context) override { +      Error error; +      const int short_option = GetDefinitions()[option_idx].short_option; +      switch (short_option) { +      case 'l': +        if (option_arg.getAsInteger(0, start_line)) +          error.SetErrorStringWithFormat("invalid line number: '%s'", +                                         option_arg.str().c_str()); +        break; + +      case 'e': +        if (option_arg.getAsInteger(0, end_line)) +          error.SetErrorStringWithFormat("invalid line number: '%s'", +                                         option_arg.str().c_str()); +        break; + +      case 'c': +        if (option_arg.getAsInteger(0, num_lines)) +          error.SetErrorStringWithFormat("invalid line count: '%s'", +                                         option_arg.str().c_str()); +        break; + +      case 'f': +        file_name = option_arg; +        break; + +      case 'n': +        symbol_name = option_arg; +        break; + +      case 'a': { +        address = Args::StringToAddress(execution_context, option_arg, +                                        LLDB_INVALID_ADDRESS, &error); +      } break; +      case 's': +        modules.push_back(std::string(option_arg)); +        break; +      default: +        error.SetErrorStringWithFormat("unrecognized short option '%c'", +                                       short_option); +        break; +      } + +      return error; +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      file_spec.Clear(); +      file_name.clear(); +      symbol_name.clear(); +      address = LLDB_INVALID_ADDRESS; +      start_line = 0; +      end_line = 0; +      num_lines = 0; +      modules.clear(); +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_source_info_options); +    } + +    // Instance variables to hold the values for command options. +    FileSpec file_spec; +    std::string file_name; +    std::string symbol_name; +    lldb::addr_t address; +    uint32_t start_line; +    uint32_t end_line; +    uint32_t num_lines; +    STLStringArray modules; +  }; + +public: +  CommandObjectSourceInfo(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "source info", +            "Display source line information for the current target " +            "process.  Defaults to instruction pointer in current stack " +            "frame.", +            nullptr, eCommandRequiresTarget), +        m_options() {} + +  ~CommandObjectSourceInfo() override = default; + +  Options *GetOptions() override { return &m_options; } + +protected: +  // Dump the line entries in each symbol context. +  // Return the number of entries found. +  // If module_list is set, only dump lines contained in one of the modules. +  // If file_spec is set, only dump lines in the file. +  // If the start_line option was specified, don't print lines less than +  // start_line. +  // If the end_line option was specified, don't print lines greater than +  // end_line. +  // If the num_lines option was specified, dont print more than num_lines +  // entries. +  uint32_t DumpLinesInSymbolContexts(Stream &strm, +                                     const SymbolContextList &sc_list, +                                     const ModuleList &module_list, +                                     const FileSpec &file_spec) { +    uint32_t start_line = m_options.start_line; +    uint32_t end_line = m_options.end_line; +    uint32_t num_lines = m_options.num_lines; +    Target *target = m_exe_ctx.GetTargetPtr(); + +    uint32_t num_matches = 0; +    bool has_path = false; +    if (file_spec) { +      assert(file_spec.GetFilename().AsCString()); +      has_path = (file_spec.GetDirectory().AsCString() != nullptr); +    } + +    // Dump all the line entries for the file in the list. +    ConstString last_module_file_name; +    uint32_t num_scs = sc_list.GetSize(); +    for (uint32_t i = 0; i < num_scs; ++i) { +      SymbolContext sc; +      sc_list.GetContextAtIndex(i, sc); +      if (sc.comp_unit) { +        Module *module = sc.module_sp.get(); +        CompileUnit *cu = sc.comp_unit; +        const LineEntry &line_entry = sc.line_entry; +        assert(module && cu); + +        // Are we looking for specific modules, files or lines? +        if (module_list.GetSize() && +            module_list.GetIndexForModule(module) == LLDB_INVALID_INDEX32) +          continue; +        if (file_spec && +            !lldb_private::FileSpec::Equal(file_spec, line_entry.file, +                                           has_path)) +          continue; +        if (start_line > 0 && line_entry.line < start_line) +          continue; +        if (end_line > 0 && line_entry.line > end_line) +          continue; +        if (num_lines > 0 && num_matches > num_lines) +          continue; + +        // Print a new header if the module changed. +        const ConstString &module_file_name = +            module->GetFileSpec().GetFilename(); +        assert(module_file_name); +        if (module_file_name != last_module_file_name) { +          if (num_matches > 0) +            strm << "\n\n"; +          strm << "Lines found in module `" << module_file_name << "\n"; +        } +        // Dump the line entry. +        line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, +                                  target, /*show_address_only=*/false); +        strm << "\n"; +        last_module_file_name = module_file_name; +        num_matches++; +      } +    } +    return num_matches; +  } + +  // Dump the requested line entries for the file in the compilation unit. +  // Return the number of entries found. +  // If module_list is set, only dump lines contained in one of the modules. +  // If the start_line option was specified, don't print lines less than +  // start_line. +  // If the end_line option was specified, don't print lines greater than +  // end_line. +  // If the num_lines option was specified, dont print more than num_lines +  // entries. +  uint32_t DumpFileLinesInCompUnit(Stream &strm, Module *module, +                                   CompileUnit *cu, const FileSpec &file_spec) { +    uint32_t start_line = m_options.start_line; +    uint32_t end_line = m_options.end_line; +    uint32_t num_lines = m_options.num_lines; +    Target *target = m_exe_ctx.GetTargetPtr(); + +    uint32_t num_matches = 0; +    assert(module); +    if (cu) { +      assert(file_spec.GetFilename().AsCString()); +      bool has_path = (file_spec.GetDirectory().AsCString() != nullptr); +      const FileSpecList &cu_file_list = cu->GetSupportFiles(); +      size_t file_idx = cu_file_list.FindFileIndex(0, file_spec, has_path); +      if (file_idx != UINT32_MAX) { +        // Update the file to how it appears in the CU. +        const FileSpec &cu_file_spec = +            cu_file_list.GetFileSpecAtIndex(file_idx); + +        // Dump all matching lines at or above start_line for the file in the +        // CU. +        const ConstString &file_spec_name = file_spec.GetFilename(); +        const ConstString &module_file_name = +            module->GetFileSpec().GetFilename(); +        bool cu_header_printed = false; +        uint32_t line = start_line; +        while (true) { +          LineEntry line_entry; + +          // Find the lowest index of a line entry with a line equal to +          // or higher than 'line'. +          uint32_t start_idx = 0; +          start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, +                                        /*exact=*/false, &line_entry); +          if (start_idx == UINT32_MAX) +            // No more line entries for our file in this CU. +            break; + +          if (end_line > 0 && line_entry.line > end_line) +            break; + +          // Loop through to find any other entries for this line, dumping each. +          line = line_entry.line; +          do { +            num_matches++; +            if (num_lines > 0 && num_matches > num_lines) +              break; +            assert(lldb_private::FileSpec::Equal(cu_file_spec, line_entry.file, +                                                 has_path)); +            if (!cu_header_printed) { +              if (num_matches > 0) +                strm << "\n\n"; +              strm << "Lines found for file " << file_spec_name +                   << " in compilation unit " << cu->GetFilename() << " in `" +                   << module_file_name << "\n"; +              cu_header_printed = true; +            } +            line_entry.GetDescription(&strm, lldb::eDescriptionLevelBrief, cu, +                                      target, /*show_address_only=*/false); +            strm << "\n"; + +            // Anymore after this one? +            start_idx++; +            start_idx = cu->FindLineEntry(start_idx, line, &cu_file_spec, +                                          /*exact=*/true, &line_entry); +          } while (start_idx != UINT32_MAX); + +          // Try the next higher line, starting over at start_idx 0. +          line++; +        } +      } +    } +    return num_matches; +  } + +  // Dump the requested line entries for the file in the module. +  // Return the number of entries found. +  // If module_list is set, only dump lines contained in one of the modules. +  // If the start_line option was specified, don't print lines less than +  // start_line. +  // If the end_line option was specified, don't print lines greater than +  // end_line. +  // If the num_lines option was specified, dont print more than num_lines +  // entries. +  uint32_t DumpFileLinesInModule(Stream &strm, Module *module, +                                 const FileSpec &file_spec) { +    uint32_t num_matches = 0; +    if (module) { +      // Look through all the compilation units (CUs) in this module for ones +      // that +      // contain lines of code from this source file. +      for (size_t i = 0; i < module->GetNumCompileUnits(); i++) { +        // Look for a matching source file in this CU. +        CompUnitSP cu_sp(module->GetCompileUnitAtIndex(i)); +        if (cu_sp) { +          num_matches += +              DumpFileLinesInCompUnit(strm, module, cu_sp.get(), file_spec); +        } +      } +    } +    return num_matches; +  } + +  // Given an address and a list of modules, append the symbol contexts of all +  // line entries +  // containing the address found in the modules and return the count of +  // matches.  If none +  // is found, return an error in 'error_strm'. +  size_t GetSymbolContextsForAddress(const ModuleList &module_list, +                                     lldb::addr_t addr, +                                     SymbolContextList &sc_list, +                                     StreamString &error_strm) { +    Address so_addr; +    size_t num_matches = 0; +    assert(module_list.GetSize() > 0); +    Target *target = m_exe_ctx.GetTargetPtr(); +    if (target->GetSectionLoadList().IsEmpty()) { +      // The target isn't loaded yet, we need to lookup the file address in +      // all modules.  Note: the module list option does not apply to addresses. +      const size_t num_modules = module_list.GetSize(); +      for (size_t i = 0; i < num_modules; ++i) { +        ModuleSP module_sp(module_list.GetModuleAtIndex(i)); +        if (!module_sp) +          continue; +        if (module_sp->ResolveFileAddress(addr, so_addr)) { +          SymbolContext sc; +          sc.Clear(true); +          if (module_sp->ResolveSymbolContextForAddress( +                  so_addr, eSymbolContextEverything, sc) & +              eSymbolContextLineEntry) { +            sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); +            ++num_matches; +          } +        } +      } +      if (num_matches == 0) +        error_strm.Printf("Source information for file address 0x%" PRIx64 +                          " not found in any modules.\n", +                          addr); +    } else { +      // The target has some things loaded, resolve this address to a +      // compile unit + file + line and display +      if (target->GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) { +        ModuleSP module_sp(so_addr.GetModule()); +        // Check to make sure this module is in our list. +        if (module_sp && +            module_list.GetIndexForModule(module_sp.get()) != +                LLDB_INVALID_INDEX32) { +          SymbolContext sc; +          sc.Clear(true); +          if (module_sp->ResolveSymbolContextForAddress( +                  so_addr, eSymbolContextEverything, sc) & +              eSymbolContextLineEntry) { +            sc_list.AppendIfUnique(sc, /*merge_symbol_into_function=*/false); +            ++num_matches; +          } else { +            StreamString addr_strm; +            so_addr.Dump(&addr_strm, nullptr, +                         Address::DumpStyleModuleWithFileAddress); +            error_strm.Printf( +                "Address 0x%" PRIx64 " resolves to %s, but there is" +                " no source information available for this address.\n", +                addr, addr_strm.GetData()); +          } +        } else { +          StreamString addr_strm; +          so_addr.Dump(&addr_strm, nullptr, +                       Address::DumpStyleModuleWithFileAddress); +          error_strm.Printf("Address 0x%" PRIx64 +                            " resolves to %s, but it cannot" +                            " be found in any modules.\n", +                            addr, addr_strm.GetData()); +        } +      } else +        error_strm.Printf("Unable to resolve address 0x%" PRIx64 ".\n", addr); +    } +    return num_matches; +  } + +  // Dump the line entries found in functions matching the name specified in the +  // option. +  bool DumpLinesInFunctions(CommandReturnObject &result) { +    SymbolContextList sc_list_funcs; +    ConstString name(m_options.symbol_name.c_str()); +    SymbolContextList sc_list_lines; +    Target *target = m_exe_ctx.GetTargetPtr(); +    uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); + +    // Note: module_list can't be const& because FindFunctionSymbols isn't +    // const. +    ModuleList module_list = +        (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages(); +    size_t num_matches = +        module_list.FindFunctions(name, eFunctionNameTypeAuto, +                                  /*include_symbols=*/false, +                                  /*include_inlines=*/true, +                                  /*append=*/true, sc_list_funcs); +    if (!num_matches) { +      // If we didn't find any functions with that name, try searching for +      // symbols that line up exactly with function addresses. +      SymbolContextList sc_list_symbols; +      size_t num_symbol_matches = module_list.FindFunctionSymbols( +          name, eFunctionNameTypeAuto, sc_list_symbols); +      for (size_t i = 0; i < num_symbol_matches; i++) { +        SymbolContext sc; +        sc_list_symbols.GetContextAtIndex(i, sc); +        if (sc.symbol && sc.symbol->ValueIsAddress()) { +          const Address &base_address = sc.symbol->GetAddressRef(); +          Function *function = base_address.CalculateSymbolContextFunction(); +          if (function) { +            sc_list_funcs.Append(SymbolContext(function)); +            num_matches++; +          } +        } +      } +    } +    if (num_matches == 0) { +      result.AppendErrorWithFormat("Could not find function named \'%s\'.\n", +                                   m_options.symbol_name.c_str()); +      return false; +    } +    for (size_t i = 0; i < num_matches; i++) { +      SymbolContext sc; +      sc_list_funcs.GetContextAtIndex(i, sc); +      bool context_found_for_symbol = false; +      // Loop through all the ranges in the function. +      AddressRange range; +      for (uint32_t r = 0; +           sc.GetAddressRange(eSymbolContextEverything, r, +                              /*use_inline_block_range=*/true, range); +           ++r) { +        // Append the symbol contexts for each address in the range to +        // sc_list_lines. +        const Address &base_address = range.GetBaseAddress(); +        const addr_t size = range.GetByteSize(); +        lldb::addr_t start_addr = base_address.GetLoadAddress(target); +        if (start_addr == LLDB_INVALID_ADDRESS) +          start_addr = base_address.GetFileAddress(); +        lldb::addr_t end_addr = start_addr + size; +        for (lldb::addr_t addr = start_addr; addr < end_addr; +             addr += addr_byte_size) { +          StreamString error_strm; +          if (!GetSymbolContextsForAddress(module_list, addr, sc_list_lines, +                                           error_strm)) +            result.AppendWarningWithFormat("in symbol '%s': %s", +                                           sc.GetFunctionName().AsCString(), +                                           error_strm.GetData()); +          else +            context_found_for_symbol = true; +        } +      } +      if (!context_found_for_symbol) +        result.AppendWarningWithFormat("Unable to find line information" +                                       " for matching symbol '%s'.\n", +                                       sc.GetFunctionName().AsCString()); +    } +    if (sc_list_lines.GetSize() == 0) { +      result.AppendErrorWithFormat("No line information could be found" +                                   " for any symbols matching '%s'.\n", +                                   name.AsCString()); +      return false; +    } +    FileSpec file_spec; +    if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list_lines, +                                   module_list, file_spec)) { +      result.AppendErrorWithFormat( +          "Unable to dump line information for symbol '%s'.\n", +          name.AsCString()); +      return false; +    } +    return true; +  } + +  // Dump the line entries found for the address specified in the option. +  bool DumpLinesForAddress(CommandReturnObject &result) { +    Target *target = m_exe_ctx.GetTargetPtr(); +    SymbolContextList sc_list; + +    StreamString error_strm; +    if (!GetSymbolContextsForAddress(target->GetImages(), m_options.address, +                                     sc_list, error_strm)) { +      result.AppendErrorWithFormat("%s.\n", error_strm.GetData()); +      return false; +    } +    ModuleList module_list; +    FileSpec file_spec; +    if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, +                                   module_list, file_spec)) { +      result.AppendErrorWithFormat("No modules contain load address 0x%" PRIx64 +                                   ".\n", +                                   m_options.address); +      return false; +    } +    return true; +  } + +  // Dump the line entries found in the file specified in the option. +  bool DumpLinesForFile(CommandReturnObject &result) { +    FileSpec file_spec(m_options.file_name, false); +    const char *filename = m_options.file_name.c_str(); +    Target *target = m_exe_ctx.GetTargetPtr(); +    const ModuleList &module_list = +        (m_module_list.GetSize() > 0) ? m_module_list : target->GetImages(); + +    bool displayed_something = false; +    const size_t num_modules = module_list.GetSize(); +    for (uint32_t i = 0; i < num_modules; ++i) { +      // Dump lines for this module. +      Module *module = module_list.GetModulePointerAtIndex(i); +      assert(module); +      if (DumpFileLinesInModule(result.GetOutputStream(), module, file_spec)) +        displayed_something = true; +    } +    if (!displayed_something) { +      result.AppendErrorWithFormat("No source filenames matched '%s'.\n", +                                   filename); +      return false; +    } +    return true; +  } + +  // Dump the line entries for the current frame. +  bool DumpLinesForFrame(CommandReturnObject &result) { +    StackFrame *cur_frame = m_exe_ctx.GetFramePtr(); +    if (cur_frame == nullptr) { +      result.AppendError( +          "No selected frame to use to find the default source."); +      return false; +    } else if (!cur_frame->HasDebugInformation()) { +      result.AppendError("No debug info for the selected frame."); +      return false; +    } else { +      const SymbolContext &sc = +          cur_frame->GetSymbolContext(eSymbolContextLineEntry); +      SymbolContextList sc_list; +      sc_list.Append(sc); +      ModuleList module_list; +      FileSpec file_spec; +      if (!DumpLinesInSymbolContexts(result.GetOutputStream(), sc_list, +                                     module_list, file_spec)) { +        result.AppendError( +            "No source line info available for the selected frame."); +        return false; +      } +    } +    return true; +  } + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); + +    if (argc != 0) { +      result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", +                                   GetCommandName().str().c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    Target *target = m_exe_ctx.GetTargetPtr(); +    if (target == nullptr) { +      target = m_interpreter.GetDebugger().GetSelectedTarget().get(); +      if (target == nullptr) { +        result.AppendError("invalid target, create a debug target using the " +                           "'target create' command."); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } + +    uint32_t addr_byte_size = target->GetArchitecture().GetAddressByteSize(); +    result.GetOutputStream().SetAddressByteSize(addr_byte_size); +    result.GetErrorStream().SetAddressByteSize(addr_byte_size); + +    // Collect the list of modules to search. +    m_module_list.Clear(); +    if (!m_options.modules.empty()) { +      for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { +        FileSpec module_file_spec(m_options.modules[i], false); +        if (module_file_spec) { +          ModuleSpec module_spec(module_file_spec); +          if (target->GetImages().FindModules(module_spec, m_module_list) == 0) +            result.AppendWarningWithFormat("No module found for '%s'.\n", +                                           m_options.modules[i].c_str()); +        } +      } +      if (!m_module_list.GetSize()) { +        result.AppendError("No modules match the input."); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } +    } else if (target->GetImages().GetSize() == 0) { +      result.AppendError("The target has no associated executable images."); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    // Check the arguments to see what lines we should dump. +    if (!m_options.symbol_name.empty()) { +      // Print lines for symbol. +      if (DumpLinesInFunctions(result)) +        result.SetStatus(eReturnStatusSuccessFinishResult); +      else +        result.SetStatus(eReturnStatusFailed); +    } else if (m_options.address != LLDB_INVALID_ADDRESS) { +      // Print lines for an address. +      if (DumpLinesForAddress(result)) +        result.SetStatus(eReturnStatusSuccessFinishResult); +      else +        result.SetStatus(eReturnStatusFailed); +    } else if (!m_options.file_name.empty()) { +      // Dump lines for a file. +      if (DumpLinesForFile(result)) +        result.SetStatus(eReturnStatusSuccessFinishResult); +      else +        result.SetStatus(eReturnStatusFailed); +    } else { +      // Dump the line for the current frame. +      if (DumpLinesForFrame(result)) +        result.SetStatus(eReturnStatusSuccessFinishResult); +      else +        result.SetStatus(eReturnStatusFailed); +    } +    return result.Succeeded(); +  } + +  CommandOptions m_options; +  ModuleList m_module_list; +}; + +#pragma mark CommandObjectSourceList +//------------------------------------------------------------------------- +// CommandObjectSourceList +//------------------------------------------------------------------------- + +static OptionDefinition g_source_list_options[] = { +    // clang-format off +  { LLDB_OPT_SET_ALL,                false, "count",            'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeCount,               "The number of source lines to display." }, +  { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "shlib",            's', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eModuleCompletion,     eArgTypeShlibName,           "Look up the source file in the given shared library." }, +  { LLDB_OPT_SET_ALL,                false, "show-breakpoints", 'b', OptionParser::eNoArgument,       nullptr, nullptr, 0,                                         eArgTypeNone,                "Show the line table locations from the debug information that indicate valid places to set source level breakpoints." }, +  { LLDB_OPT_SET_1,                  false, "file",             'f', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSourceFileCompletion, eArgTypeFilename,            "The file from which to display source." }, +  { LLDB_OPT_SET_1,                  false, "line",             'l', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeLineNum,             "The line number at which to start the display source." }, +  { LLDB_OPT_SET_2,                  false, "name",             'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion,     eArgTypeSymbol,              "The name of a function whose source to display." }, +  { LLDB_OPT_SET_3,                  false, "address",          'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0,                                         eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line." }, +  { LLDB_OPT_SET_4,                  false, "reverse",          'r', OptionParser::eNoArgument,       nullptr, nullptr, 0,                                         eArgTypeNone,                "Reverse the listing to look backwards from the last displayed block of source." }, +    // clang-format on +}; + +class CommandObjectSourceList : public CommandObjectParsed { +  class CommandOptions : public Options { +  public: +    CommandOptions() : Options() {} + +    ~CommandOptions() override = default; + +    Error SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, +                         ExecutionContext *execution_context) override { +      Error error; +      const int short_option = GetDefinitions()[option_idx].short_option; +      switch (short_option) { +      case 'l': +        if (option_arg.getAsInteger(0, start_line)) +          error.SetErrorStringWithFormat("invalid line number: '%s'", +                                         option_arg.str().c_str()); +        break; + +      case 'c': +        if (option_arg.getAsInteger(0, num_lines)) +          error.SetErrorStringWithFormat("invalid line count: '%s'", +                                         option_arg.str().c_str()); +        break; + +      case 'f': +        file_name = option_arg; +        break; + +      case 'n': +        symbol_name = option_arg; +        break; + +      case 'a': { +        address = Args::StringToAddress(execution_context, option_arg, +                                        LLDB_INVALID_ADDRESS, &error); +      } break; +      case 's': +        modules.push_back(std::string(option_arg)); +        break; + +      case 'b': +        show_bp_locs = true; +        break; +      case 'r': +        reverse = true; +        break; +      default: +        error.SetErrorStringWithFormat("unrecognized short option '%c'", +                                       short_option); +        break; +      } + +      return error; +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      file_spec.Clear(); +      file_name.clear(); +      symbol_name.clear(); +      address = LLDB_INVALID_ADDRESS; +      start_line = 0; +      num_lines = 0; +      show_bp_locs = false; +      reverse = false; +      modules.clear(); +    } + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_source_list_options); +    } + +    // Instance variables to hold the values for command options. +    FileSpec file_spec; +    std::string file_name; +    std::string symbol_name; +    lldb::addr_t address; +    uint32_t start_line; +    uint32_t num_lines; +    STLStringArray modules; +    bool show_bp_locs; +    bool reverse; +  }; + +public: +  CommandObjectSourceList(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "source list", +                            "Display source code for the current target " +                            "process as specified by options.", +                            nullptr, eCommandRequiresTarget), +        m_options() {} + +  ~CommandObjectSourceList() override = default; + +  Options *GetOptions() override { return &m_options; } + +  const char *GetRepeatCommand(Args ¤t_command_args, +                               uint32_t index) override { +    // This is kind of gross, but the command hasn't been parsed yet so we can't +    // look at the option values for this invocation...  I have to scan the +    // arguments directly. +    auto iter = +        llvm::find_if(current_command_args, [](const Args::ArgEntry &e) { +          return e.ref == "-r" || e.ref == "--reverse"; +        }); +    if (iter == current_command_args.end()) +      return m_cmd_name.c_str(); + +    if (m_reverse_name.empty()) { +      m_reverse_name = m_cmd_name; +      m_reverse_name.append(" -r"); +    } +    return m_reverse_name.c_str(); +  } + +protected: +  struct SourceInfo { +    ConstString function; +    LineEntry line_entry; + +    SourceInfo(const ConstString &name, const LineEntry &line_entry) +        : function(name), line_entry(line_entry) {} + +    SourceInfo() : function(), line_entry() {} + +    bool IsValid() const { return (bool)function && line_entry.IsValid(); } + +    bool operator==(const SourceInfo &rhs) const { +      return function == rhs.function && +             line_entry.original_file == rhs.line_entry.original_file && +             line_entry.line == rhs.line_entry.line; +    } + +    bool operator!=(const SourceInfo &rhs) const { +      return function != rhs.function || +             line_entry.original_file != rhs.line_entry.original_file || +             line_entry.line != rhs.line_entry.line; +    } + +    bool operator<(const SourceInfo &rhs) const { +      if (function.GetCString() < rhs.function.GetCString()) +        return true; +      if (line_entry.file.GetDirectory().GetCString() < +          rhs.line_entry.file.GetDirectory().GetCString()) +        return true; +      if (line_entry.file.GetFilename().GetCString() < +          rhs.line_entry.file.GetFilename().GetCString()) +        return true; +      if (line_entry.line < rhs.line_entry.line) +        return true; +      return false; +    } +  }; + +  size_t DisplayFunctionSource(const SymbolContext &sc, SourceInfo &source_info, +                               CommandReturnObject &result) { +    if (!source_info.IsValid()) { +      source_info.function = sc.GetFunctionName(); +      source_info.line_entry = sc.GetFunctionStartLineEntry(); +    } + +    if (sc.function) { +      Target *target = m_exe_ctx.GetTargetPtr(); + +      FileSpec start_file; +      uint32_t start_line; +      uint32_t end_line; +      FileSpec end_file; + +      if (sc.block == nullptr) { +        // Not an inlined function +        sc.function->GetStartLineSourceInfo(start_file, start_line); +        if (start_line == 0) { +          result.AppendErrorWithFormat("Could not find line information for " +                                       "start of function: \"%s\".\n", +                                       source_info.function.GetCString()); +          result.SetStatus(eReturnStatusFailed); +          return 0; +        } +        sc.function->GetEndLineSourceInfo(end_file, end_line); +      } else { +        // We have an inlined function +        start_file = source_info.line_entry.file; +        start_line = source_info.line_entry.line; +        end_line = start_line + m_options.num_lines; +      } + +      // This is a little hacky, but the first line table entry for a function +      // points to the "{" that +      // starts the function block.  It would be nice to actually get the +      // function +      // declaration in there too.  So back up a bit, but not further than what +      // you're going to display. +      uint32_t extra_lines; +      if (m_options.num_lines >= 10) +        extra_lines = 5; +      else +        extra_lines = m_options.num_lines / 2; +      uint32_t line_no; +      if (start_line <= extra_lines) +        line_no = 1; +      else +        line_no = start_line - extra_lines; + +      // For fun, if the function is shorter than the number of lines we're +      // supposed to display, +      // only display the function... +      if (end_line != 0) { +        if (m_options.num_lines > end_line - line_no) +          m_options.num_lines = end_line - line_no + extra_lines; +      } + +      m_breakpoint_locations.Clear(); + +      if (m_options.show_bp_locs) { +        const bool show_inlines = true; +        m_breakpoint_locations.Reset(start_file, 0, show_inlines); +        SearchFilterForUnconstrainedSearches target_search_filter( +            m_exe_ctx.GetTargetSP()); +        target_search_filter.Search(m_breakpoint_locations); +      } + +      result.AppendMessageWithFormat("File: %s\n", +                                     start_file.GetPath().c_str()); +      // We don't care about the column here. +      const uint32_t column = 0; +      return target->GetSourceManager().DisplaySourceLinesWithLineNumbers( +          start_file, line_no, 0, m_options.num_lines, column, "", +          &result.GetOutputStream(), GetBreakpointLocations()); +    } else { +      result.AppendErrorWithFormat( +          "Could not find function info for: \"%s\".\n", +          m_options.symbol_name.c_str()); +    } +    return 0; +  } + +  // From Jim: The FindMatchingFunctions / FindMatchingFunctionSymbols functions +  // "take a possibly empty vector of strings which are names of modules, and +  // run the two search functions on the subset of the full module list that +  // matches the strings in the input vector". If we wanted to put these +  // somewhere, +  // there should probably be a module-filter-list that can be passed to the +  // various ModuleList::Find* calls, which would either be a vector of string +  // names or a ModuleSpecList. +  size_t FindMatchingFunctions(Target *target, const ConstString &name, +                               SymbolContextList &sc_list) { +    // Displaying the source for a symbol: +    bool include_inlines = true; +    bool append = true; +    bool include_symbols = false; +    size_t num_matches = 0; + +    if (m_options.num_lines == 0) +      m_options.num_lines = 10; + +    const size_t num_modules = m_options.modules.size(); +    if (num_modules > 0) { +      ModuleList matching_modules; +      for (size_t i = 0; i < num_modules; ++i) { +        FileSpec module_file_spec(m_options.modules[i], false); +        if (module_file_spec) { +          ModuleSpec module_spec(module_file_spec); +          matching_modules.Clear(); +          target->GetImages().FindModules(module_spec, matching_modules); +          num_matches += matching_modules.FindFunctions( +              name, eFunctionNameTypeAuto, include_symbols, include_inlines, +              append, sc_list); +        } +      } +    } else { +      num_matches = target->GetImages().FindFunctions( +          name, eFunctionNameTypeAuto, include_symbols, include_inlines, append, +          sc_list); +    } +    return num_matches; +  } + +  size_t FindMatchingFunctionSymbols(Target *target, const ConstString &name, +                                     SymbolContextList &sc_list) { +    size_t num_matches = 0; +    const size_t num_modules = m_options.modules.size(); +    if (num_modules > 0) { +      ModuleList matching_modules; +      for (size_t i = 0; i < num_modules; ++i) { +        FileSpec module_file_spec(m_options.modules[i], false); +        if (module_file_spec) { +          ModuleSpec module_spec(module_file_spec); +          matching_modules.Clear(); +          target->GetImages().FindModules(module_spec, matching_modules); +          num_matches += matching_modules.FindFunctionSymbols( +              name, eFunctionNameTypeAuto, sc_list); +        } +      } +    } else { +      num_matches = target->GetImages().FindFunctionSymbols( +          name, eFunctionNameTypeAuto, sc_list); +    } +    return num_matches; +  } + +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    const size_t argc = command.GetArgumentCount(); + +    if (argc != 0) { +      result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", +                                   GetCommandName().str().c_str()); +      result.SetStatus(eReturnStatusFailed); +      return false; +    } + +    Target *target = m_exe_ctx.GetTargetPtr(); + +    if (!m_options.symbol_name.empty()) { +      SymbolContextList sc_list; +      ConstString name(m_options.symbol_name.c_str()); + +      // Displaying the source for a symbol. Search for function named name. +      size_t num_matches = FindMatchingFunctions(target, name, sc_list); +      if (!num_matches) { +        // If we didn't find any functions with that name, try searching for +        // symbols +        // that line up exactly with function addresses. +        SymbolContextList sc_list_symbols; +        size_t num_symbol_matches = +            FindMatchingFunctionSymbols(target, name, sc_list_symbols); +        for (size_t i = 0; i < num_symbol_matches; i++) { +          SymbolContext sc; +          sc_list_symbols.GetContextAtIndex(i, sc); +          if (sc.symbol && sc.symbol->ValueIsAddress()) { +            const Address &base_address = sc.symbol->GetAddressRef(); +            Function *function = base_address.CalculateSymbolContextFunction(); +            if (function) { +              sc_list.Append(SymbolContext(function)); +              num_matches++; +              break; +            } +          } +        } +      } + +      if (num_matches == 0) { +        result.AppendErrorWithFormat("Could not find function named: \"%s\".\n", +                                     m_options.symbol_name.c_str()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      if (num_matches > 1) { +        std::set<SourceInfo> source_match_set; + +        bool displayed_something = false; +        for (size_t i = 0; i < num_matches; i++) { +          SymbolContext sc; +          sc_list.GetContextAtIndex(i, sc); +          SourceInfo source_info(sc.GetFunctionName(), +                                 sc.GetFunctionStartLineEntry()); + +          if (source_info.IsValid()) { +            if (source_match_set.find(source_info) == source_match_set.end()) { +              source_match_set.insert(source_info); +              if (DisplayFunctionSource(sc, source_info, result)) +                displayed_something = true; +            } +          } +        } + +        if (displayed_something) +          result.SetStatus(eReturnStatusSuccessFinishResult); +        else +          result.SetStatus(eReturnStatusFailed); +      } else { +        SymbolContext sc; +        sc_list.GetContextAtIndex(0, sc); +        SourceInfo source_info; + +        if (DisplayFunctionSource(sc, source_info, result)) { +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } else { +          result.SetStatus(eReturnStatusFailed); +        } +      } +      return result.Succeeded(); +    } else if (m_options.address != LLDB_INVALID_ADDRESS) { +      Address so_addr; +      StreamString error_strm; +      SymbolContextList sc_list; + +      if (target->GetSectionLoadList().IsEmpty()) { +        // The target isn't loaded yet, we need to lookup the file address +        // in all modules +        const ModuleList &module_list = target->GetImages(); +        const size_t num_modules = module_list.GetSize(); +        for (size_t i = 0; i < num_modules; ++i) { +          ModuleSP module_sp(module_list.GetModuleAtIndex(i)); +          if (module_sp && +              module_sp->ResolveFileAddress(m_options.address, so_addr)) { +            SymbolContext sc; +            sc.Clear(true); +            if (module_sp->ResolveSymbolContextForAddress( +                    so_addr, eSymbolContextEverything, sc) & +                eSymbolContextLineEntry) +              sc_list.Append(sc); +          } +        } + +        if (sc_list.GetSize() == 0) { +          result.AppendErrorWithFormat( +              "no modules have source information for file address 0x%" PRIx64 +              ".\n", +              m_options.address); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +      } else { +        // The target has some things loaded, resolve this address to a +        // compile unit + file + line and display +        if (target->GetSectionLoadList().ResolveLoadAddress(m_options.address, +                                                            so_addr)) { +          ModuleSP module_sp(so_addr.GetModule()); +          if (module_sp) { +            SymbolContext sc; +            sc.Clear(true); +            if (module_sp->ResolveSymbolContextForAddress( +                    so_addr, eSymbolContextEverything, sc) & +                eSymbolContextLineEntry) { +              sc_list.Append(sc); +            } else { +              so_addr.Dump(&error_strm, nullptr, +                           Address::DumpStyleModuleWithFileAddress); +              result.AppendErrorWithFormat("address resolves to %s, but there " +                                           "is no line table information " +                                           "available for this address.\n", +                                           error_strm.GetData()); +              result.SetStatus(eReturnStatusFailed); +              return false; +            } +          } +        } + +        if (sc_list.GetSize() == 0) { +          result.AppendErrorWithFormat( +              "no modules contain load address 0x%" PRIx64 ".\n", +              m_options.address); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +      } +      uint32_t num_matches = sc_list.GetSize(); +      for (uint32_t i = 0; i < num_matches; ++i) { +        SymbolContext sc; +        sc_list.GetContextAtIndex(i, sc); +        if (sc.comp_unit) { +          if (m_options.show_bp_locs) { +            m_breakpoint_locations.Clear(); +            const bool show_inlines = true; +            m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines); +            SearchFilterForUnconstrainedSearches target_search_filter( +                target->shared_from_this()); +            target_search_filter.Search(m_breakpoint_locations); +          } + +          bool show_fullpaths = true; +          bool show_module = true; +          bool show_inlined_frames = true; +          const bool show_function_arguments = true; +          const bool show_function_name = true; +          sc.DumpStopContext(&result.GetOutputStream(), +                             m_exe_ctx.GetBestExecutionContextScope(), +                             sc.line_entry.range.GetBaseAddress(), +                             show_fullpaths, show_module, show_inlined_frames, +                             show_function_arguments, show_function_name); +          result.GetOutputStream().EOL(); + +          if (m_options.num_lines == 0) +            m_options.num_lines = 10; + +          size_t lines_to_back_up = +              m_options.num_lines >= 10 ? 5 : m_options.num_lines / 2; + +          const uint32_t column = +              (m_interpreter.GetDebugger().GetStopShowColumn() != +               eStopShowColumnNone) +                  ? sc.line_entry.column +                  : 0; +          target->GetSourceManager().DisplaySourceLinesWithLineNumbers( +              sc.comp_unit, sc.line_entry.line, lines_to_back_up, column, +              m_options.num_lines - lines_to_back_up, "->", +              &result.GetOutputStream(), GetBreakpointLocations()); +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } +      } +    } else if (m_options.file_name.empty()) { +      // Last valid source manager context, or the current frame if no +      // valid last context in source manager. +      // One little trick here, if you type the exact same list command twice in +      // a row, it is +      // more likely because you typed it once, then typed it again +      if (m_options.start_line == 0) { +        if (target->GetSourceManager().DisplayMoreWithLineNumbers( +                &result.GetOutputStream(), m_options.num_lines, +                m_options.reverse, GetBreakpointLocations())) { +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } +      } else { +        if (m_options.num_lines == 0) +          m_options.num_lines = 10; + +        if (m_options.show_bp_locs) { +          SourceManager::FileSP last_file_sp( +              target->GetSourceManager().GetLastFile()); +          if (last_file_sp) { +            const bool show_inlines = true; +            m_breakpoint_locations.Reset(last_file_sp->GetFileSpec(), 0, +                                         show_inlines); +            SearchFilterForUnconstrainedSearches target_search_filter( +                target->shared_from_this()); +            target_search_filter.Search(m_breakpoint_locations); +          } +        } else +          m_breakpoint_locations.Clear(); + +        const uint32_t column = 0; +        if (target->GetSourceManager() +                .DisplaySourceLinesWithLineNumbersUsingLastFile( +                    m_options.start_line, // Line to display +                    m_options.num_lines,  // Lines after line to +                    UINT32_MAX,           // Don't mark "line" +                    column, +                    "", // Don't mark "line" +                    &result.GetOutputStream(), GetBreakpointLocations())) { +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } +      } +    } else { +      const char *filename = m_options.file_name.c_str(); + +      bool check_inlines = false; +      SymbolContextList sc_list; +      size_t num_matches = 0; + +      if (!m_options.modules.empty()) { +        ModuleList matching_modules; +        for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) { +          FileSpec module_file_spec(m_options.modules[i], false); +          if (module_file_spec) { +            ModuleSpec module_spec(module_file_spec); +            matching_modules.Clear(); +            target->GetImages().FindModules(module_spec, matching_modules); +            num_matches += matching_modules.ResolveSymbolContextForFilePath( +                filename, 0, check_inlines, +                eSymbolContextModule | eSymbolContextCompUnit, sc_list); +          } +        } +      } else { +        num_matches = target->GetImages().ResolveSymbolContextForFilePath( +            filename, 0, check_inlines, +            eSymbolContextModule | eSymbolContextCompUnit, sc_list); +      } + +      if (num_matches == 0) { +        result.AppendErrorWithFormat("Could not find source file \"%s\".\n", +                                     m_options.file_name.c_str()); +        result.SetStatus(eReturnStatusFailed); +        return false; +      } + +      if (num_matches > 1) { +        bool got_multiple = false; +        FileSpec *test_cu_spec = nullptr; + +        for (unsigned i = 0; i < num_matches; i++) { +          SymbolContext sc; +          sc_list.GetContextAtIndex(i, sc); +          if (sc.comp_unit) { +            if (test_cu_spec) { +              if (test_cu_spec != static_cast<FileSpec *>(sc.comp_unit)) +                got_multiple = true; +              break; +            } else +              test_cu_spec = sc.comp_unit; +          } +        } +        if (got_multiple) { +          result.AppendErrorWithFormat( +              "Multiple source files found matching: \"%s.\"\n", +              m_options.file_name.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +      } + +      SymbolContext sc; +      if (sc_list.GetContextAtIndex(0, sc)) { +        if (sc.comp_unit) { +          if (m_options.show_bp_locs) { +            const bool show_inlines = true; +            m_breakpoint_locations.Reset(*sc.comp_unit, 0, show_inlines); +            SearchFilterForUnconstrainedSearches target_search_filter( +                target->shared_from_this()); +            target_search_filter.Search(m_breakpoint_locations); +          } else +            m_breakpoint_locations.Clear(); + +          if (m_options.num_lines == 0) +            m_options.num_lines = 10; +          const uint32_t column = 0; +          target->GetSourceManager().DisplaySourceLinesWithLineNumbers( +              sc.comp_unit, m_options.start_line, 0, m_options.num_lines, +              column, "", &result.GetOutputStream(), GetBreakpointLocations()); + +          result.SetStatus(eReturnStatusSuccessFinishResult); +        } else { +          result.AppendErrorWithFormat("No comp unit found for: \"%s.\"\n", +                                       m_options.file_name.c_str()); +          result.SetStatus(eReturnStatusFailed); +          return false; +        } +      } +    } +    return result.Succeeded(); +  } + +  const SymbolContextList *GetBreakpointLocations() { +    if (m_breakpoint_locations.GetFileLineMatches().GetSize() > 0) +      return &m_breakpoint_locations.GetFileLineMatches(); +    return nullptr; +  } + +  CommandOptions m_options; +  FileLineResolver m_breakpoint_locations; +  std::string m_reverse_name; +}; + +#pragma mark CommandObjectMultiwordSource +//------------------------------------------------------------------------- +// CommandObjectMultiwordSource +//------------------------------------------------------------------------- + +CommandObjectMultiwordSource::CommandObjectMultiwordSource( +    CommandInterpreter &interpreter) +    : CommandObjectMultiword(interpreter, "source", "Commands for examining " +                                                    "source code described by " +                                                    "debug information for the " +                                                    "current target process.", +                             "source <subcommand> [<subcommand-options>]") { +  LoadSubCommand("info", +                 CommandObjectSP(new CommandObjectSourceInfo(interpreter))); +  LoadSubCommand("list", +                 CommandObjectSP(new CommandObjectSourceList(interpreter))); +} + +CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default;  | 
