//===-- Block.h -------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_SYMBOL_BLOCK_H
#define LLDB_SYMBOL_BLOCK_H

#include "lldb/Core/AddressRange.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/SymbolContextScope.h"
#include "lldb/Utility/RangeMap.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-private.h"
#include <vector>

namespace lldb_private {

/// \class Block Block.h "lldb/Symbol/Block.h"
/// A class that describes a single lexical block.
///
/// A Function object owns a BlockList object which owns one or more
/// Block objects. The BlockList object contains a section offset address
/// range, and Block objects contain one or more ranges which are offsets into
/// that range. Blocks are can have discontiguous ranges within the BlockList
/// address range, and each block can contain child blocks each with their own
/// sets of ranges.
///
/// Each block has a variable list that represents local, argument, and static
/// variables that are scoped to the block.
///
/// Inlined functions are represented by attaching a InlineFunctionInfo shared
/// pointer object to a block. Inlined functions are represented as named
/// blocks.
class Block : public UserID, public SymbolContextScope {
public:
  typedef RangeVector<int32_t, uint32_t, 1> RangeList;
  typedef RangeList::Entry Range;

  // Creates a block representing the whole function. Only meant to be used from
  // the Function class.
  Block(Function &function, lldb::user_id_t function_uid);

  ~Block() override;

  /// Creates a block with the specified UID \a uid.
  ///
  /// \param[in] uid
  ///     The UID for a given block. This value is given by the
  ///     SymbolFile plug-in and can be any value that helps the
  ///     SymbolFile plug-in to match this block back to the debug
  ///     information data that it parses for further or more in
  ///     depth parsing. Common values would be the index into a
  ///     table, or an offset into the debug information.
  lldb::BlockSP CreateChild(lldb::user_id_t uid);

  /// Add a new offset range to this block.
  void AddRange(const Range &range);

  void FinalizeRanges();

  /// \copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*)
  ///
  /// \see SymbolContextScope
  void CalculateSymbolContext(SymbolContext *sc) override;

  lldb::ModuleSP CalculateSymbolContextModule() override;

  CompileUnit *CalculateSymbolContextCompileUnit() override;

  Function *CalculateSymbolContextFunction() override;

  Block *CalculateSymbolContextBlock() override;

  Function &GetFunction();

  /// Check if an offset is in one of the block offset ranges.
  ///
  /// \param[in] range_offset
  ///     An offset into the Function's address range.
  ///
  /// \return
  ///     Returns \b true if \a range_offset falls in one of this
  ///     block's ranges, \b false otherwise.
  bool Contains(lldb::addr_t range_offset) const;

  /// Check if a offset range is in one of the block offset ranges.
  ///
  /// \param[in] range
  ///     An offset range into the Function's address range.
  ///
  /// \return
  ///     Returns \b true if \a range falls in one of this
  ///     block's ranges, \b false otherwise.
  bool Contains(const Range &range) const;

  /// Check if this object contains "block" as a child block at any depth.
  ///
  /// \param[in] block
  ///     A potential child block.
  ///
  /// \return
  ///     Returns \b true if \a block is a child of this block, \b
  ///     false otherwise.
  bool Contains(const Block *block) const;

  /// Dump the block contents.
  ///
  /// \param[in] s
  ///     The stream to which to dump the object description.
  ///
  /// \param[in] base_addr
  ///     The resolved start address of the Function's address
  ///     range. This should be resolved as the file or load address
  ///     prior to passing the value into this function for dumping.
  ///
  /// \param[in] depth
  ///     Limit the number of levels deep that this function should
  ///     print as this block can contain child blocks. Specify
  ///     INT_MAX to dump all child blocks.
  ///
  /// \param[in] show_context
  ///     If \b true, variables will dump their context information.
  void Dump(Stream *s, lldb::addr_t base_addr, int32_t depth,
            bool show_context) const;

  /// \copydoc SymbolContextScope::DumpSymbolContext(Stream*)
  ///
  /// \see SymbolContextScope
  void DumpSymbolContext(Stream *s) override;

  void DumpAddressRanges(Stream *s, lldb::addr_t base_addr);

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

  /// Get the parent block.
  ///
  /// \return
  ///     The parent block pointer, or nullptr if this block has no
  ///     parent.
  Block *GetParent() const;

  /// Get the inlined block that contains this block.
  ///
  /// \return
  ///     If this block contains inlined function info, it will return
  ///     this block, else parent blocks will be searched to see if
  ///     any contain this block. nullptr will be returned if this block
  ///     nor any parent blocks are inlined function blocks.
  Block *GetContainingInlinedBlock();

  /// Get the inlined parent block for this block.
  ///
  /// \return
  ///     The parent block pointer, or nullptr if this block has no
  ///     parent.
  Block *GetInlinedParent();

  //------------------------------------------------------------------
  /// Get the inlined block at the given call site that contains this block.
  ///
  /// @param[in] find_call_site
  ///     a declaration with the file and line of the call site to find.
  ///
  /// @return
  ///     If this block contains inlined function info and is at the call
  ///     site given by the file and line at the given \b declaration, then
  ///     it will return this block, otherwise the parent blocks will be
  ///     searched to see if any is at the call site. nullptr will be returned
  ///     if no block is found at the call site.
  //------------------------------------------------------------------
  Block *
  GetContainingInlinedBlockWithCallSite(const Declaration &find_call_site);

  /// Get the sibling block for this block.
  ///
  /// \return
  ///     The sibling block pointer, or nullptr if this block has no
  ///     sibling.
  Block *GetSibling() const;

  /// Get the first child block.
  ///
  /// \return
  ///     The first child block pointer, or nullptr if this block has no
  ///     children.
  Block *GetFirstChild() const {
    return (m_children.empty() ? nullptr : m_children.front().get());
  }

  /// Get the variable list for this block only.
  ///
  /// \param[in] can_create
  ///     If \b true, the variables can be parsed if they already
  ///     haven't been, else the current state of the block will be
  ///     returned.
  ///
  /// \return
  ///     A variable list shared pointer that contains all variables
  ///     for this block.
  lldb::VariableListSP GetBlockVariableList(bool can_create);

  /// Get the variable list for this block and optionally all child blocks if
  /// \a get_child_variables is \b true.
  ///
  /// \param[in] can_create
  ///     If \b true, the variables can be parsed if they already
  ///     haven't been, else the current state of the block will be
  ///     returned. Passing \b true for this parameter can be used
  ///     to see the current state of what has been parsed up to this
  ///     point.
  ///
  /// \param[in] get_child_block_variables
  ///     If \b true, all variables from all child blocks will be
  ///     added to the variable list.
  ///
  /// \return
  ///     A variable list shared pointer that contains all variables
  ///     for this block.
  uint32_t AppendBlockVariables(bool can_create, bool get_child_block_variables,
                                bool stop_if_child_block_is_inlined_function,
                                const std::function<bool(Variable *)> &filter,
                                VariableList *variable_list);

  /// Appends the variables from this block, and optionally from all parent
  /// blocks, to \a variable_list.
  ///
  /// \param[in] can_create
  ///     If \b true, the variables can be parsed if they already
  ///     haven't been, else the current state of the block will be
  ///     returned. Passing \b true for this parameter can be used
  ///     to see the current state of what has been parsed up to this
  ///     point.
  ///
  /// \param[in] get_parent_variables
  ///     If \b true, all variables from all parent blocks will be
  ///     added to the variable list.
  ///
  /// \param[in] stop_if_block_is_inlined_function
  ///     If \b true, all variables from all parent blocks will be
  ///     added to the variable list until there are no parent blocks
  ///     or the parent block has inlined function info.
  ///
  /// \param[in,out] variable_list
  ///     All variables in this block, and optionally all parent
  ///     blocks will be added to this list.
  ///
  /// \return
  ///     The number of variable that were appended to \a
  ///     variable_list.
  uint32_t AppendVariables(bool can_create, bool get_parent_variables,
                           bool stop_if_block_is_inlined_function,
                           const std::function<bool(Variable *)> &filter,
                           VariableList *variable_list);

  /// Get const accessor for any inlined function information.
  ///
  /// \return
  ///     A const pointer to any inlined function information, or nullptr
  ///     if this is a regular block.
  const InlineFunctionInfo *GetInlinedFunctionInfo() const {
    return m_inlineInfoSP.get();
  }

  /// Get the symbol file which contains debug info for this block's
  /// symbol context module.
  ///
  /// \return A pointer to the symbol file or nullptr.
  SymbolFile *GetSymbolFile();

  CompilerDeclContext GetDeclContext();

  /// Set accessor for any inlined function information.
  ///
  /// \param[in] name
  ///     The method name for the inlined function. This value should
  ///     not be nullptr.
  ///
  /// \param[in] mangled
  ///     The mangled method name for the inlined function. This can
  ///     be nullptr if there is no mangled name for an inlined function
  ///     or if the name is the same as \a name.
  ///
  /// \param[in] decl_ptr
  ///     A optional pointer to declaration information for the
  ///     inlined function information. This value can be nullptr to
  ///     indicate that no declaration information is available.
  ///
  /// \param[in] call_decl_ptr
  ///     Optional calling location declaration information that
  ///     describes from where this inlined function was called.
  void SetInlinedFunctionInfo(const char *name, const char *mangled,
                              const Declaration *decl_ptr,
                              const Declaration *call_decl_ptr);

  /// Set accessor for the variable list.
  ///
  /// Called by the SymbolFile plug-ins after they have parsed the variable
  /// lists and are ready to hand ownership of the list over to this object.
  ///
  /// \param[in] variable_list_sp
  ///     A shared pointer to a VariableList.
  void SetVariableList(lldb::VariableListSP &variable_list_sp) {
    m_variable_list_sp = variable_list_sp;
  }

  bool BlockInfoHasBeenParsed() const { return m_parsed_block_info; }

  void SetBlockInfoHasBeenParsed(bool b, bool set_children);

  Block *FindBlockByID(lldb::user_id_t block_id);

  Block *FindInnermostBlockByOffset(const lldb::addr_t offset);

  size_t GetNumRanges() const { return m_ranges.GetSize(); }

  bool GetRangeContainingOffset(const lldb::addr_t offset, Range &range);

  bool GetRangeContainingAddress(const Address &addr, AddressRange &range);

  bool GetRangeContainingLoadAddress(lldb::addr_t load_addr, Target &target,
                                     AddressRange &range);

  uint32_t GetRangeIndexContainingAddress(const Address &addr);

  // Since blocks might have multiple discontiguous address ranges, we need to
  // be able to get at any of the address ranges in a block.
  bool GetRangeAtIndex(uint32_t range_idx, AddressRange &range);

  AddressRanges GetRanges();

  bool GetStartAddress(Address &addr);

  void SetDidParseVariables(bool b, bool set_children);

protected:
  typedef std::vector<lldb::BlockSP> collection;
  // Member variables.
  SymbolContextScope &m_parent_scope;
  collection m_children;

  /// Address ranges of this block. They are relative to the function entry
  /// point so one must add/subtract GetFunction().GetAddress().GetFileAddress()
  /// when converting from/to to the AddressRange representation.
  RangeList m_ranges;

  lldb::InlineFunctionInfoSP m_inlineInfoSP; ///< Inlined function information.
  lldb::VariableListSP m_variable_list_sp; ///< The variable list for all local,
                                           ///static and parameter variables
                                           ///scoped to this block.
  bool m_parsed_block_info : 1, ///< Set to true if this block and it's children
                                ///have all been parsed
      m_parsed_block_variables : 1, m_parsed_child_blocks : 1;

  // A parent of child blocks can be asked to find a sibling block given
  // one of its child blocks
  Block *GetSiblingForChild(const Block *child_block) const;

private:
  Block(const Block &) = delete;
  const Block &operator=(const Block &) = delete;

  Block(lldb::user_id_t uid, SymbolContextScope &parent_scope);
};

} // namespace lldb_private

#endif // LLDB_SYMBOL_BLOCK_H
