//===-- Function.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_FUNCTION_H
#define LLDB_SYMBOL_FUNCTION_H

#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Declaration.h"
#include "lldb/Utility/UserID.h"
#include "llvm/ADT/ArrayRef.h"

#include <mutex>

namespace lldb_private {

class ExecutionContext;

/// \class FunctionInfo Function.h "lldb/Symbol/Function.h"
/// A class that contains generic function information.
///
/// This provides generic function information that gets reused between inline
/// functions and function types.
class FunctionInfo {
public:
  /// Construct with the function method name and optional declaration
  /// information.
  ///
  /// \param[in] name
  ///     A C string name for the method name for this function. This
  ///     value should not be the mangled named, but the simple method
  ///     name.
  ///
  /// \param[in] decl_ptr
  ///     Optional declaration information that describes where the
  ///     function was declared. This can be NULL.
  FunctionInfo(const char *name, const Declaration *decl_ptr);

  /// Construct with the function method name and optional declaration
  /// information.
  ///
  /// \param[in] name
  ///     A name for the method name for this function. This value
  ///     should not be the mangled named, but the simple method name.
  ///
  /// \param[in] decl_ptr
  ///     Optional declaration information that describes where the
  ///     function was declared. This can be NULL.
  FunctionInfo(ConstString name, const Declaration *decl_ptr);

  /// Destructor.
  ///
  /// The destructor is virtual since classes inherit from this class.
  virtual ~FunctionInfo();

  /// Compare two function information objects.
  ///
  /// First compares the method names, and if equal, then compares the
  /// declaration information.
  ///
  /// \param[in] lhs
  ///     The Left Hand Side const FunctionInfo object reference.
  ///
  /// \param[in] rhs
  ///     The Right Hand Side const FunctionInfo object reference.
  ///
  /// \return
  ///     -1 if lhs < rhs
  ///     0 if lhs == rhs
  ///     1 if lhs > rhs
  static int Compare(const FunctionInfo &lhs, const FunctionInfo &rhs);

  /// 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, bool show_fullpaths) const;

  /// Get accessor for the declaration information.
  ///
  /// \return
  ///     A reference to the declaration object.
  Declaration &GetDeclaration();

  /// Get const accessor for the declaration information.
  ///
  /// \return
  ///     A const reference to the declaration object.
  const Declaration &GetDeclaration() const;

  /// Get accessor for the method name.
  ///
  /// \return
  ///     A const reference to the method name object.
  ConstString GetName() const;

  /// Get the memory cost of this object.
  ///
  /// \return
  ///     The number of bytes that this object occupies in memory.
  ///     The returned value does not include the bytes for any
  ///     shared string values.
  ///
  /// \see ConstString::StaticMemorySize ()
  virtual size_t MemorySize() const;

protected:
  /// Function method name (not a mangled name).
  ConstString m_name;

  /// Information describing where this function information was defined.
  Declaration m_declaration;
};

/// \class InlineFunctionInfo Function.h "lldb/Symbol/Function.h"
/// A class that describes information for an inlined function.
class InlineFunctionInfo : public FunctionInfo {
public:
  /// Construct with the function method name, mangled name, and optional
  /// declaration information.
  ///
  /// \param[in] name
  ///     A C string name for the method name for this function. This
  ///     value should not be the mangled named, but the simple method
  ///     name.
  ///
  /// \param[in] mangled
  ///     A C string name for the mangled name for this function. This
  ///     value can be NULL if there is no mangled information.
  ///
  /// \param[in] decl_ptr
  ///     Optional declaration information that describes where the
  ///     function was declared. This can be NULL.
  ///
  /// \param[in] call_decl_ptr
  ///     Optional calling location declaration information that
  ///     describes from where this inlined function was called.
  InlineFunctionInfo(const char *name, llvm::StringRef mangled,
                     const Declaration *decl_ptr,
                     const Declaration *call_decl_ptr);

  /// Construct with the function method name, mangled name, and optional
  /// declaration information.
  ///
  /// \param[in] name
  ///     A name for the method name for this function. This value
  ///     should not be the mangled named, but the simple method name.
  ///
  /// \param[in] mangled
  ///     A name for the mangled name for this function. This value
  ///     can be empty if there is no mangled information.
  ///
  /// \param[in] decl_ptr
  ///     Optional declaration information that describes where the
  ///     function was declared. This can be NULL.
  ///
  /// \param[in] call_decl_ptr
  ///     Optional calling location declaration information that
  ///     describes from where this inlined function was called.
  InlineFunctionInfo(ConstString name, const Mangled &mangled,
                     const Declaration *decl_ptr,
                     const Declaration *call_decl_ptr);

  /// Destructor.
  ~InlineFunctionInfo() override;

  /// Compare two inlined function information objects.
  ///
  /// First compares the FunctionInfo objects, and if equal, compares the
  /// mangled names.
  ///
  /// \param[in] lhs
  ///     The Left Hand Side const InlineFunctionInfo object
  ///     reference.
  ///
  /// \param[in] rhs
  ///     The Right Hand Side const InlineFunctionInfo object
  ///     reference.
  ///
  /// \return
  ///     -1 if lhs < rhs
  ///     0 if lhs == rhs
  ///     1 if lhs > rhs
  int Compare(const InlineFunctionInfo &lhs, const InlineFunctionInfo &rhs);

  /// 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, bool show_fullpaths) const;

  void DumpStopContext(Stream *s) const;

  ConstString GetName() const;

  ConstString GetDisplayName() const;

  /// Get accessor for the call site declaration information.
  ///
  /// \return
  ///     A reference to the declaration object.
  Declaration &GetCallSite();

  /// Get const accessor for the call site declaration information.
  ///
  /// \return
  ///     A const reference to the declaration object.
  const Declaration &GetCallSite() const;

  /// Get accessor for the mangled name object.
  ///
  /// \return
  ///     A reference to the mangled name object.
  Mangled &GetMangled();

  /// Get const accessor for the mangled name object.
  ///
  /// \return
  ///     A const reference to the mangled name object.
  const Mangled &GetMangled() const;

  /// Get the memory cost of this object.
  ///
  /// \return
  ///     The number of bytes that this object occupies in memory.
  ///     The returned value does not include the bytes for any
  ///     shared string values.
  ///
  /// \see ConstString::StaticMemorySize ()
  size_t MemorySize() const override;

private:
  /// Mangled inlined function name (can be empty if there is no mangled
  /// information).
  Mangled m_mangled;

  Declaration m_call_decl;
};

class Function;

/// \class CallSiteParameter Function.h "lldb/Symbol/Function.h"
///
/// Represent the locations of a parameter at a call site, both in the caller
/// and in the callee.
struct CallSiteParameter {
  DWARFExpression LocationInCallee;
  DWARFExpression LocationInCaller;
};

/// A vector of \c CallSiteParameter.
using CallSiteParameterArray = llvm::SmallVector<CallSiteParameter, 0>;

/// \class CallEdge Function.h "lldb/Symbol/Function.h"
///
/// Represent a call made within a Function. This can be used to find a path
/// in the call graph between two functions, or to evaluate DW_OP_entry_value.
class CallEdge {
public:
  enum class AddrType : uint8_t { Call, AfterCall };
  virtual ~CallEdge() {}

  /// Get the callee's definition.
  ///
  /// Note that this might lazily invoke the DWARF parser. A register context
  /// from the caller's activation is needed to find indirect call targets.
  virtual Function *GetCallee(ModuleList &images,
                              ExecutionContext &exe_ctx) = 0;

  /// Get the load PC address of the instruction which executes after the call
  /// returns. Returns LLDB_INVALID_ADDRESS iff this is a tail call. \p caller
  /// is the Function containing this call, and \p target is the Target which
  /// made the call.
  lldb::addr_t GetReturnPCAddress(Function &caller, Target &target) const;

  /// Return an address in the caller. This can either be the address of the
  /// call instruction, or the address of the instruction after the call.
  std::pair<AddrType, lldb::addr_t> GetCallerAddress(Function &caller,
                                                     Target &target) const {
    return {caller_address_type,
            GetLoadAddress(caller_address, caller, target)};
  }

  bool IsTailCall() const { return is_tail_call; }

  /// Get the call site parameters available at this call edge.
  llvm::ArrayRef<CallSiteParameter> GetCallSiteParameters() const {
    return parameters;
  }

  /// Non-tail-calls go first, sorted by the return address. They are followed
  /// by tail calls, which have no specific order.
  std::pair<bool, lldb::addr_t> GetSortKey() const {
    return {is_tail_call, GetUnresolvedReturnPCAddress()};
  }

protected:
  CallEdge(AddrType caller_address_type, lldb::addr_t caller_address,
           bool is_tail_call, CallSiteParameterArray &&parameters)
      : caller_address(caller_address),
        caller_address_type(caller_address_type), is_tail_call(is_tail_call),
        parameters(std::move(parameters)) {}

  /// Helper that finds the load address of \p unresolved_pc, a file address
  /// which refers to an instruction within \p caller.
  static lldb::addr_t GetLoadAddress(lldb::addr_t unresolved_pc,
                                     Function &caller, Target &target);

  /// Like \ref GetReturnPCAddress, but returns an unresolved file address.
  lldb::addr_t GetUnresolvedReturnPCAddress() const {
    return caller_address_type == AddrType::AfterCall && !is_tail_call
               ? caller_address
               : LLDB_INVALID_ADDRESS;
  }

private:
  lldb::addr_t caller_address;
  AddrType caller_address_type;
  bool is_tail_call;

  CallSiteParameterArray parameters;
};

/// A direct call site. Used to represent call sites where the address of the
/// callee is fixed (e.g. a function call in C in which the call target is not
/// a function pointer).
class DirectCallEdge : public CallEdge {
public:
  /// Construct a call edge using a symbol name to identify the callee, and a
  /// return PC within the calling function to identify a specific call site.
  DirectCallEdge(const char *symbol_name, AddrType caller_address_type,
                 lldb::addr_t caller_address, bool is_tail_call,
                 CallSiteParameterArray &&parameters)
      : CallEdge(caller_address_type, caller_address, is_tail_call,
                 std::move(parameters)) {
    lazy_callee.symbol_name = symbol_name;
  }

  Function *GetCallee(ModuleList &images, ExecutionContext &exe_ctx) override;

private:
  void ParseSymbolFileAndResolve(ModuleList &images);

  // Used to describe a direct call.
  //
  // Either the callee's mangled name or its definition, discriminated by
  // \ref resolved.
  union {
    const char *symbol_name;
    Function *def;
  } lazy_callee;

  /// Whether or not an attempt was made to find the callee's definition.
  bool resolved = false;
};

/// An indirect call site. Used to represent call sites where the address of
/// the callee is not fixed, e.g. a call to a C++ virtual function (where the
/// address is loaded out of a vtable), or a call to a function pointer in C.
class IndirectCallEdge : public CallEdge {
public:
  /// Construct a call edge using a DWARFExpression to identify the callee, and
  /// a return PC within the calling function to identify a specific call site.
  IndirectCallEdge(DWARFExpression call_target, AddrType caller_address_type,
                   lldb::addr_t caller_address, bool is_tail_call,
                   CallSiteParameterArray &&parameters)
      : CallEdge(caller_address_type, caller_address, is_tail_call,
                 std::move(parameters)),
        call_target(std::move(call_target)) {}

  Function *GetCallee(ModuleList &images, ExecutionContext &exe_ctx) override;

private:
  // Used to describe an indirect call.
  //
  // Specifies the location of the callee address in the calling frame.
  DWARFExpression call_target;
};

/// \class Function Function.h "lldb/Symbol/Function.h"
/// A class that describes a function.
///
/// Functions belong to CompileUnit objects (Function::m_comp_unit), have
/// unique user IDs (Function::UserID), know how to reconstruct their symbol
/// context (Function::SymbolContextScope), have a specific function type
/// (Function::m_type_uid), have a simple method name (FunctionInfo::m_name),
/// be declared at a specific location (FunctionInfo::m_declaration), possibly
/// have mangled names (Function::m_mangled), an optional return type
/// (Function::m_type), and contains lexical blocks (Function::m_blocks).
///
/// The function information is split into a few pieces:
///     \li The concrete instance information
///     \li The abstract information
///
/// The abstract information is found in the function type (Type) that
/// describes a function information, return type and parameter types.
///
/// The concrete information is the address range information and specific
/// locations for an instance of this function.
class Function : public UserID, public SymbolContextScope {
public:
  /// Construct with a compile unit, function UID, function type UID, optional
  /// mangled name, function type, and a section offset based address range.
  ///
  /// \param[in] comp_unit
  ///     The compile unit to which this function belongs.
  ///
  /// \param[in] func_uid
  ///     The UID for this function. This value is provided by the
  ///     SymbolFile plug-in and can be any value that allows
  ///     the plug-in to quickly find and parse more detailed
  ///     information when and if more information is needed.
  ///
  /// \param[in] func_type_uid
  ///     The type UID for the function Type to allow for lazy type
  ///     parsing from the debug information.
  ///
  /// \param[in] mangled
  ///     The optional mangled name for this function. If empty, there
  ///     is no mangled information.
  ///
  /// \param[in] func_type
  ///     The optional function type. If NULL, the function type will
  ///     be parsed on demand when accessed using the
  ///     Function::GetType() function by asking the SymbolFile
  ///     plug-in to get the type for \a func_type_uid.
  ///
  /// \param[in] range
  ///     The section offset based address for this function.
  Function(CompileUnit *comp_unit, lldb::user_id_t func_uid,
           lldb::user_id_t func_type_uid, const Mangled &mangled,
           Type *func_type, const AddressRange &range);

  /// Destructor.
  ~Function() override;

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

  lldb::ModuleSP CalculateSymbolContextModule() override;

  CompileUnit *CalculateSymbolContextCompileUnit() override;

  Function *CalculateSymbolContextFunction() override;

  const AddressRange &GetAddressRange() { return m_range; }

  lldb::LanguageType GetLanguage() const;
  /// Find the file and line number of the source location of the start of the
  /// function.  This will use the declaration if present and fall back on the
  /// line table if that fails.  So there may NOT be a line table entry for
  /// this source file/line combo.
  ///
  /// \param[out] source_file
  ///     The source file.
  ///
  /// \param[out] line_no
  ///     The line number.
  void GetStartLineSourceInfo(FileSpec &source_file, uint32_t &line_no);

  /// Find the file and line number of the source location of the end of the
  /// function.
  ///
  ///
  /// \param[out] source_file
  ///     The source file.
  ///
  /// \param[out] line_no
  ///     The line number.
  void GetEndLineSourceInfo(FileSpec &source_file, uint32_t &line_no);

  /// Get the outgoing call edges from this function, sorted by their return
  /// PC addresses (in increasing order).
  llvm::ArrayRef<std::unique_ptr<CallEdge>> GetCallEdges();

  /// Get the outgoing tail-calling edges from this function. If none exist,
  /// return None.
  llvm::ArrayRef<std::unique_ptr<CallEdge>> GetTailCallingEdges();

  /// Get the outgoing call edge from this function which has the given return
  /// address \p return_pc, or return nullptr. Note that this will not return a
  /// tail-calling edge.
  CallEdge *GetCallEdgeForReturnAddress(lldb::addr_t return_pc, Target &target);

  /// Get accessor for the block list.
  ///
  /// \return
  ///     The block list object that describes all lexical blocks
  ///     in the function.
  ///
  /// \see BlockList
  Block &GetBlock(bool can_create);

  /// Get accessor for the compile unit that owns this function.
  ///
  /// \return
  ///     A compile unit object pointer.
  CompileUnit *GetCompileUnit();

  /// Get const accessor for the compile unit that owns this function.
  ///
  /// \return
  ///     A const compile unit object pointer.
  const CompileUnit *GetCompileUnit() const;

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

  /// Get accessor for the frame base location.
  ///
  /// \return
  ///     A location expression that describes the function frame
  ///     base.
  DWARFExpression &GetFrameBaseExpression() { return m_frame_base; }

  /// Get const accessor for the frame base location.
  ///
  /// \return
  ///     A const compile unit object pointer.
  const DWARFExpression &GetFrameBaseExpression() const { return m_frame_base; }

  ConstString GetName() const;

  ConstString GetNameNoArguments() const;

  ConstString GetDisplayName() const;

  const Mangled &GetMangled() const { return m_mangled; }

  /// Get the DeclContext for this function, if available.
  ///
  /// \return
  ///     The DeclContext, or NULL if none exists.
  CompilerDeclContext GetDeclContext();

  /// Get accessor for the type that describes the function return value type,
  /// and parameter types.
  ///
  /// \return
  ///     A type object pointer.
  Type *GetType();

  /// Get const accessor for the type that describes the function return value
  /// type, and parameter types.
  ///
  /// \return
  ///     A const type object pointer.
  const Type *GetType() const;

  CompilerType GetCompilerType();

  /// Get the size of the prologue instructions for this function.  The
  /// "prologue" instructions include any instructions given line number 0
  /// immediately following the prologue end.
  ///
  /// \return
  ///     The size of the prologue.
  uint32_t GetPrologueByteSize();

  /// 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.
  ///
  /// \param[in] show_context
  ///     If \b true, variables will dump their symbol context
  ///     information.
  void Dump(Stream *s, bool show_context) const;

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

  /// Get the memory cost of this object.
  ///
  /// \return
  ///     The number of bytes that this object occupies in memory.
  ///     The returned value does not include the bytes for any
  ///     shared string values.
  ///
  /// \see ConstString::StaticMemorySize ()
  size_t MemorySize() const;

  /// Get whether compiler optimizations were enabled for this function
  ///
  /// The debug information may provide information about whether this
  /// function was compiled with optimization or not.  In this case,
  /// "optimized" means that the debug experience may be difficult for the
  /// user to understand.  Variables may not be available when the developer
  /// would expect them, stepping through the source lines in the function may
  /// appear strange, etc.
  ///
  /// \return
  ///     Returns 'true' if this function was compiled with
  ///     optimization.  'false' indicates that either the optimization
  ///     is unknown, or this function was built without optimization.
  bool GetIsOptimized();

  /// Get whether this function represents a 'top-level' function
  ///
  /// The concept of a top-level function is language-specific, mostly meant
  /// to represent the notion of scripting-style code that has global
  /// visibility of the variables/symbols/functions/... defined within the
  /// containing file/module
  ///
  /// If stopped in a top-level function, LLDB will expose global variables
  /// as-if locals in the 'frame variable' command
  ///
  /// \return
  ///     Returns 'true' if this function is a top-level function,
  ///     'false' otherwise.
  bool IsTopLevelFunction();

  lldb::DisassemblerSP GetInstructions(const ExecutionContext &exe_ctx,
                                       const char *flavor,
                                       bool prefer_file_cache);

  bool GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor,
                      bool prefer_file_cache, Stream &strm);

protected:
  enum {
    /// Whether we already tried to calculate the prologue size.
    flagsCalculatedPrologueSize = (1 << 0)
  };

  /// The compile unit that owns this function.
  CompileUnit *m_comp_unit;

  /// The user ID of for the prototype Type for this function.
  lldb::user_id_t m_type_uid;

  /// The function prototype type for this function that includes the function
  /// info (FunctionInfo), return type and parameters.
  Type *m_type;

  /// The mangled function name if any. If empty, there is no mangled
  /// information.
  Mangled m_mangled;

  /// All lexical blocks contained in this function.
  Block m_block;

  /// The function address range that covers the widest range needed to contain
  /// all blocks
  AddressRange m_range;

  /// The frame base expression for variables that are relative to the frame
  /// pointer.
  DWARFExpression m_frame_base;

  Flags m_flags;

  /// Compute the prologue size once and cache it.
  uint32_t m_prologue_byte_size;

  /// Exclusive lock that controls read/write access to m_call_edges and
  /// m_call_edges_resolved.
  std::mutex m_call_edges_lock;

  /// Whether call site info has been parsed.
  bool m_call_edges_resolved = false;

  /// Outgoing call edges.
  std::vector<std::unique_ptr<CallEdge>> m_call_edges;

private:
  Function(const Function &) = delete;
  const Function &operator=(const Function &) = delete;
};

} // namespace lldb_private

#endif // LLDB_SYMBOL_FUNCTION_H
