//===-- 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/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/Iterable.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, Error &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_
