//===-- SymbolContext.h -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_SymbolContext_h_
#define liblldb_SymbolContext_h_

// C Includes
// C++ Includes
#include <memory>
#include <string>
#include <vector>

// Other libraries and framework includes
// Project includes
#include "lldb/Core/Address.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/Iterable.h"
#include "lldb/lldb-private.h"

namespace lldb_private {

class SymbolContextScope;

//----------------------------------------------------------------------
/// @class SymbolContext SymbolContext.h "lldb/Symbol/SymbolContext.h"
/// @brief Defines a symbol context baton that can be handed other debug
/// core functions.
///
/// Many debugger functions require a context when doing lookups. This
/// class provides a common structure that can be used as the result
/// of a query that can contain a single result. Examples of such
/// queries include
///     @li Looking up a load address.
//----------------------------------------------------------------------
class SymbolContext {
public:
  //------------------------------------------------------------------
  /// Default constructor.
  ///
  /// Initialize all pointer members to nullptr and all struct members
  /// to their default state.
  //------------------------------------------------------------------
  SymbolContext();

  //------------------------------------------------------------------
  /// Construct with an object that knows how to reconstruct its
  /// symbol context.
  ///
  /// @param[in] sc_scope
  ///     A symbol context scope object that knows how to reconstruct
  ///     it's context.
  //------------------------------------------------------------------
  explicit SymbolContext(SymbolContextScope *sc_scope);

  //------------------------------------------------------------------
  /// Construct with module, and optional compile unit, function,
  /// block, line table, line entry and symbol.
  ///
  /// Initialize all pointer to the specified values.
  ///
  /// @param[in] module
  ///     A Module pointer to the module for this context.
  ///
  /// @param[in] comp_unit
  ///     A CompileUnit pointer to the compile unit for this context.
  ///
  /// @param[in] function
  ///     A Function pointer to the function for this context.
  ///
  /// @param[in] block
  ///     A Block pointer to the deepest block for this context.
  ///
  /// @param[in] line_entry
  ///     A LineEntry pointer to the line entry for this context.
  ///
  /// @param[in] symbol
  ///     A Symbol pointer to the symbol for this context.
  //------------------------------------------------------------------
  explicit SymbolContext(const lldb::TargetSP &target_sp,
                         const lldb::ModuleSP &module_sp,
                         CompileUnit *comp_unit = nullptr,
                         Function *function = nullptr, Block *block = nullptr,
                         LineEntry *line_entry = nullptr,
                         Symbol *symbol = nullptr);

  // This version sets the target to a NULL TargetSP if you don't know it.
  explicit SymbolContext(const lldb::ModuleSP &module_sp,
                         CompileUnit *comp_unit = nullptr,
                         Function *function = nullptr, Block *block = nullptr,
                         LineEntry *line_entry = nullptr,
                         Symbol *symbol = nullptr);

  //------------------------------------------------------------------
  /// Copy constructor
  ///
  /// Makes a copy of the another SymbolContext object \a rhs.
  ///
  /// @param[in] rhs
  ///     A const SymbolContext object reference to copy.
  //------------------------------------------------------------------
  SymbolContext(const SymbolContext &rhs);

  ~SymbolContext();

  //------------------------------------------------------------------
  /// Assignment operator.
  ///
  /// Copies the address value from another SymbolContext object \a
  /// rhs into \a this object.
  ///
  /// @param[in] rhs
  ///     A const SymbolContext object reference to copy.
  ///
  /// @return
  ///     A const SymbolContext object reference to \a this.
  //------------------------------------------------------------------
  const SymbolContext &operator=(const SymbolContext &rhs);

  //------------------------------------------------------------------
  /// Clear the object's state.
  ///
  /// Resets all pointer members to nullptr, and clears any class objects
  /// to their default state.
  //------------------------------------------------------------------
  void Clear(bool clear_target);

  //------------------------------------------------------------------
  /// Dump a description of this object to a Stream.
  ///
  /// Dump a description of the contents of this object to the
  /// supplied stream \a s.
  ///
  /// @param[in] s
  ///     The stream to which to dump the object description.
  //------------------------------------------------------------------
  void Dump(Stream *s, Target *target) const;

  //------------------------------------------------------------------
  /// Dump the stop context in this object to a Stream.
  ///
  /// Dump the best description of this object to the stream. The
  /// information displayed depends on the amount and quality of the
  /// information in this context. If a module, function, file and
  /// line number are available, they will be dumped. If only a
  /// module and function or symbol name with offset is available,
  /// that will be output. Else just the address at which the target
  /// was stopped will be displayed.
  ///
  /// @param[in] s
  ///     The stream to which to dump the object description.
  ///
  /// @param[in] so_addr
  ///     The resolved section offset address.
  ///
  /// @param[in] show_fullpaths
  ///     When printing file paths (with the Module), whether the
  ///     base name of the Module should be printed or the full path.
  ///
  /// @param[in] show_module
  ///     Whether the module name should be printed followed by a
  ///     grave accent "`" character.
  ///
  /// @param[in] show_inlined_frames
  ///     If a given pc is in inlined function(s), whether the inlined
  ///     functions should be printed on separate lines in addition to
  ///     the concrete function containing the pc.
  ///
  /// @param[in] show_function_arguments
  ///     If false, this method will try to elide the function argument
  ///     types when printing the function name.  This may be ambiguous
  ///     for languages that have function overloading - but it may
  ///     make the "function name" too long to include all the argument
  ///     types.
  ///
  /// @param[in] show_function_name
  ///     Normally this should be true - the function/symbol name should
  ///     be printed.  In disassembly formatting, where we want a format
  ///     like "<*+36>", this should be false and "*" will be printed
  ///     instead.
  //------------------------------------------------------------------
  bool DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
                       const Address &so_addr, bool show_fullpaths,
                       bool show_module, bool show_inlined_frames,
                       bool show_function_arguments,
                       bool show_function_name) const;

  //------------------------------------------------------------------
  /// Get the address range contained within a symbol context.
  ///
  /// Address range priority is as follows:
  ///     - line_entry address range if line_entry is valid and
  ///     eSymbolContextLineEntry is set in \a scope
  ///     - block address range if block is not nullptr and eSymbolContextBlock
  ///     is set in \a scope
  ///     - function address range if function is not nullptr and
  ///     eSymbolContextFunction is set in \a scope
  ///     - symbol address range if symbol is not nullptr and
  ///     eSymbolContextSymbol is set in \a scope
  ///
  /// @param[in] scope
  ///     A mask of symbol context bits telling this function which
  ///     address ranges it can use when trying to extract one from
  ///     the valid (non-nullptr) symbol context classes.
  ///
  /// @param[in] range_idx
  ///     The address range index to grab. Since many functions and
  ///     blocks are not always contiguous, they may have more than
  ///     one address range.
  ///
  /// @param[in] use_inline_block_range
  ///     If \a scope has the eSymbolContextBlock bit set, and there
  ///     is a valid block in the symbol context, return the block
  ///     address range for the containing inline function block, not
  ///     the deepest most block. This allows us to extract information
  ///     for the address range of the inlined function block, not
  ///     the deepest lexical block.
  ///
  /// @param[out] range
  ///     An address range object that will be filled in if \b true
  ///     is returned.
  ///
  /// @return
  ///     \b True if this symbol context contains items that describe
  ///     an address range, \b false otherwise.
  //------------------------------------------------------------------
  bool GetAddressRange(uint32_t scope, uint32_t range_idx,
                       bool use_inline_block_range, AddressRange &range) const;

  bool GetAddressRangeFromHereToEndLine(uint32_t end_line, AddressRange &range,
                                        Status &error);
  
  //------------------------------------------------------------------
  /// Find the best global data symbol visible from this context.
  ///
  /// Symbol priority is:
  ///     - extern symbol in the current module if there is one
  ///     - non-extern symbol in the current module if there is one
  ///     - extern symbol in the target
  ///     - non-extern symbol in the target
  /// It is an error if the highest-priority result is ambiguous.
  ///
  /// @param[in] name
  ///     The name of the symbol to search for.
  ///
  /// @param[out] error
  ///     An error that will be populated with a message if there was an
  ///     ambiguous result.  The error will not be populated if no result
  ///     was found.
  ///
  /// @return
  ///     The symbol that was found, or \b nullptr if none was found.
  //------------------------------------------------------------------
  const Symbol *FindBestGlobalDataSymbol(const ConstString &name, Status &error);

  void GetDescription(Stream *s, lldb::DescriptionLevel level,
                      Target *target) const;

  uint32_t GetResolvedMask() const;

  lldb::LanguageType GetLanguage() const;

  //------------------------------------------------------------------
  /// Find a block that defines the function represented by this
  /// symbol context.
  ///
  /// If this symbol context points to a block that is an inlined
  /// function, or is contained within an inlined function, the block
  /// that defines the inlined function is returned.
  ///
  /// If this symbol context has no block in it, or the block is not
  /// itself an inlined function block or contained within one, we
  /// return the top level function block.
  ///
  /// This is a handy function to call when you want to get the block
  /// whose variable list will include the arguments for the function
  /// that is represented by this symbol context (whether the function
  /// is an inline function or not).
  ///
  /// @return
  ///     The block object pointer that defines the function that is
  ///     represented by this symbol context object, nullptr otherwise.
  //------------------------------------------------------------------
  Block *GetFunctionBlock();

  //------------------------------------------------------------------
  /// If this symbol context represents a function that is a method,
  /// return true and provide information about the method.
  ///
  /// @param[out] language
  ///     If \b true is returned, the language for the method.
  ///
  /// @param[out] is_instance_method
  ///     If \b true is returned, \b true if this is a instance method,
  ///     \b false if this is a static/class function.
  ///
  /// @param[out] language_object_name
  ///     If \b true is returned, the name of the artificial variable
  ///     for the language ("this" for C++, "self" for ObjC).
  ///
  /// @return
  ///     \b True if this symbol context represents a function that
  ///     is a method of a class, \b false otherwise.
  //------------------------------------------------------------------
  bool GetFunctionMethodInfo(lldb::LanguageType &language,
                             bool &is_instance_method,
                             ConstString &language_object_name);

  //------------------------------------------------------------------
  /// Sorts the types in TypeMap according to SymbolContext
  /// to TypeList
  ///
  //------------------------------------------------------------------
  void SortTypeList(TypeMap &type_map, TypeList &type_list) const;

  //------------------------------------------------------------------
  /// Find a name of the innermost function for the symbol context.
  ///
  /// For instance, if the symbol context contains an inlined block,
  /// it will return the inlined function name.
  ///
  /// @param[in] prefer_mangled
  ///    if \btrue, then the mangled name will be returned if there
  ///    is one.  Otherwise the unmangled name will be returned if it
  ///    is available.
  ///
  /// @return
  ///     The name of the function represented by this symbol context.
  //------------------------------------------------------------------
  ConstString GetFunctionName(
      Mangled::NamePreference preference = Mangled::ePreferDemangled) const;

  //------------------------------------------------------------------
  /// Get the line entry that corresponds to the function.
  ///
  /// If the symbol context contains an inlined block, the line entry
  /// for the start address of the inlined function will be returned,
  /// otherwise the line entry for the start address of the function
  /// will be returned. This can be used after doing a
  /// Module::FindFunctions(...) or ModuleList::FindFunctions(...)
  /// call in order to get the correct line table information for
  /// the symbol context.
  /// it will return the inlined function name.
  ///
  /// @param[in] prefer_mangled
  ///    if \btrue, then the mangled name will be returned if there
  ///    is one.  Otherwise the unmangled name will be returned if it
  ///    is available.
  ///
  /// @return
  ///     The name of the function represented by this symbol context.
  //------------------------------------------------------------------
  LineEntry GetFunctionStartLineEntry() const;

  //------------------------------------------------------------------
  /// Find the block containing the inlined block that contains this block.
  ///
  /// For instance, if the symbol context contains an inlined block,
  /// it will return the inlined function name.
  ///
  /// @param[in] curr_frame_pc
  ///    The address within the block of this object.
  ///
  /// @param[out] next_frame_sc
  ///     A new symbol context that does what the title says it does.
  ///
  /// @param[out] next_frame_addr
  ///     This is what you should report as the PC in \a next_frame_sc.
  ///
  /// @return
  ///     \b true if this SymbolContext specifies a block contained in an
  ///     inlined block.  If this returns \b true, \a next_frame_sc and
  ///     \a next_frame_addr will be filled in correctly.
  //------------------------------------------------------------------
  bool GetParentOfInlinedScope(const Address &curr_frame_pc,
                               SymbolContext &next_frame_sc,
                               Address &inlined_frame_addr) const;

  //------------------------------------------------------------------
  // Member variables
  //------------------------------------------------------------------
  lldb::TargetSP target_sp; ///< The Target for a given query
  lldb::ModuleSP module_sp; ///< The Module for a given query
  CompileUnit *comp_unit;   ///< The CompileUnit for a given query
  Function *function;       ///< The Function for a given query
  Block *block;             ///< The Block for a given query
  LineEntry line_entry;     ///< The LineEntry for a given query
  Symbol *symbol;           ///< The Symbol for a given query
  Variable *variable;       ///< The global variable matching the given query
};

class SymbolContextSpecifier {
public:
  typedef enum SpecificationType {
    eNothingSpecified = 0,
    eModuleSpecified = 1 << 0,
    eFileSpecified = 1 << 1,
    eLineStartSpecified = 1 << 2,
    eLineEndSpecified = 1 << 3,
    eFunctionSpecified = 1 << 4,
    eClassOrNamespaceSpecified = 1 << 5,
    eAddressRangeSpecified = 1 << 6
  } SpecificationType;

  // This one produces a specifier that matches everything...
  SymbolContextSpecifier(const lldb::TargetSP &target_sp);

  ~SymbolContextSpecifier();

  bool AddSpecification(const char *spec_string, SpecificationType type);

  bool AddLineSpecification(uint32_t line_no, SpecificationType type);

  void Clear();

  bool SymbolContextMatches(SymbolContext &sc);

  bool AddressMatches(lldb::addr_t addr);

  void GetDescription(Stream *s, lldb::DescriptionLevel level) const;

private:
  lldb::TargetSP m_target_sp;
  std::string m_module_spec;
  lldb::ModuleSP m_module_sp;
  std::unique_ptr<FileSpec> m_file_spec_ap;
  size_t m_start_line;
  size_t m_end_line;
  std::string m_function_spec;
  std::string m_class_name;
  std::unique_ptr<AddressRange> m_address_range_ap;
  uint32_t m_type; // Or'ed bits from SpecificationType
};

//----------------------------------------------------------------------
/// @class SymbolContextList SymbolContext.h "lldb/Symbol/SymbolContext.h"
/// @brief Defines a list of symbol context objects.
///
/// This class provides a common structure that can be used to contain
/// the result of a query that can contain a multiple results. Examples
/// of such queries include:
///     @li Looking up a function by name.
///     @li Finding all addresses for a specified file and line number.
//----------------------------------------------------------------------
class SymbolContextList {
public:
  //------------------------------------------------------------------
  /// Default constructor.
  ///
  /// Initialize with an empty list.
  //------------------------------------------------------------------
  SymbolContextList();

  //------------------------------------------------------------------
  /// Destructor.
  //------------------------------------------------------------------
  ~SymbolContextList();

  //------------------------------------------------------------------
  /// Append a new symbol context to the list.
  ///
  /// @param[in] sc
  ///     A symbol context to append to the list.
  //------------------------------------------------------------------
  void Append(const SymbolContext &sc);

  void Append(const SymbolContextList &sc_list);

  bool AppendIfUnique(const SymbolContext &sc, bool merge_symbol_into_function);

  bool MergeSymbolContextIntoFunctionContext(const SymbolContext &symbol_sc,
                                             uint32_t start_idx = 0,
                                             uint32_t stop_idx = UINT32_MAX);

  uint32_t AppendIfUnique(const SymbolContextList &sc_list,
                          bool merge_symbol_into_function);

  //------------------------------------------------------------------
  /// Clear the object's state.
  ///
  /// Clears the symbol context list.
  //------------------------------------------------------------------
  void Clear();

  //------------------------------------------------------------------
  /// Dump a description of this object to a Stream.
  ///
  /// Dump a description of the contents of each symbol context in
  /// the list to the supplied stream \a s.
  ///
  /// @param[in] s
  ///     The stream to which to dump the object description.
  //------------------------------------------------------------------
  void Dump(Stream *s, Target *target) const;

  //------------------------------------------------------------------
  /// Get accessor for a symbol context at index \a idx.
  ///
  /// Dump a description of the contents of each symbol context in
  /// the list to the supplied stream \a s.
  ///
  /// @param[in] idx
  ///     The zero based index into the symbol context list.
  ///
  /// @param[out] sc
  ///     A reference to the symbol context to fill in.
  ///
  /// @return
  ///     Returns \b true if \a idx was a valid index into this
  ///     symbol context list and \a sc was filled in, \b false
  ///     otherwise.
  //------------------------------------------------------------------
  bool GetContextAtIndex(size_t idx, SymbolContext &sc) const;

  //------------------------------------------------------------------
  /// Direct reference accessor for a symbol context at index \a idx.
  ///
  /// The index \a idx must be a valid index, no error checking will
  /// be done to ensure that it is valid.
  ///
  /// @param[in] idx
  ///     The zero based index into the symbol context list.
  ///
  /// @return
  ///     A const reference to the symbol context to fill in.
  //------------------------------------------------------------------
  SymbolContext &operator[](size_t idx) { return m_symbol_contexts[idx]; }

  const SymbolContext &operator[](size_t idx) const {
    return m_symbol_contexts[idx];
  }

  //------------------------------------------------------------------
  /// Get accessor for the last symbol context in the list.
  ///
  /// @param[out] sc
  ///     A reference to the symbol context to fill in.
  ///
  /// @return
  ///     Returns \b true if \a sc was filled in, \b false if the
  ///     list is empty.
  //------------------------------------------------------------------
  bool GetLastContext(SymbolContext &sc) const;

  bool RemoveContextAtIndex(size_t idx);

  //------------------------------------------------------------------
  /// Get accessor for a symbol context list size.
  ///
  /// @return
  ///     Returns the number of symbol context objects in the list.
  //------------------------------------------------------------------
  uint32_t GetSize() const;

  uint32_t NumLineEntriesWithLine(uint32_t line) const;

  void GetDescription(Stream *s, lldb::DescriptionLevel level,
                      Target *target) const;

protected:
  typedef std::vector<SymbolContext>
      collection; ///< The collection type for the list.

  //------------------------------------------------------------------
  // Member variables.
  //------------------------------------------------------------------
  collection m_symbol_contexts; ///< The list of symbol contexts.

public:
  typedef AdaptedIterable<collection, SymbolContext, vector_adapter>
      SymbolContextIterable;
  SymbolContextIterable SymbolContexts() {
    return SymbolContextIterable(m_symbol_contexts);
  }
};

bool operator==(const SymbolContext &lhs, const SymbolContext &rhs);
bool operator!=(const SymbolContext &lhs, const SymbolContext &rhs);

bool operator==(const SymbolContextList &lhs, const SymbolContextList &rhs);
bool operator!=(const SymbolContextList &lhs, const SymbolContextList &rhs);

} // namespace lldb_private

#endif // liblldb_SymbolContext_h_
