//===-- StackFrame.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_StackFrame_h_
#define liblldb_StackFrame_h_

// C Includes
// C++ Includes
#include <memory>
#include <mutex>

// Other libraries and framework includes
// Project includes
#include "lldb/Core/Error.h"
#include "lldb/Core/Flags.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/UserID.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Target/StackID.h"

namespace lldb_private {

/// @class StackFrame StackFrame.h "lldb/Target/StackFrame.h"
///
/// @brief This base class provides an interface to stack frames.
///
/// StackFrames may have a Canonical Frame Address (CFA) or not.  
/// A frame may have a plain pc value or it may have a pc value + stop_id 
/// to indicate a specific point in the debug session so the correct section 
/// load list is used for symbolication.
///
/// Local variables may be available, or not.  A register context may be
/// available, or not.

class StackFrame :
    public ExecutionContextScope,
    public std::enable_shared_from_this<StackFrame>
{
public:
    enum ExpressionPathOption
    {
        eExpressionPathOptionCheckPtrVsMember        = (1u << 0),
        eExpressionPathOptionsNoFragileObjcIvar      = (1u << 1),
        eExpressionPathOptionsNoSyntheticChildren    = (1u << 2),
        eExpressionPathOptionsNoSyntheticArrayRange  = (1u << 3),
        eExpressionPathOptionsAllowDirectIVarAccess  = (1u << 4),
        eExpressionPathOptionsInspectAnonymousUnions = (1u << 5)
    };

    //------------------------------------------------------------------
    /// Construct a StackFrame object without supplying a RegisterContextSP.
    ///
    /// This is the one constructor that doesn't take a RegisterContext
    /// parameter.  This ctor may be called when creating a history StackFrame;
    /// these are used if we've collected a stack trace of pc addresses at
    /// some point in the past.  We may only have pc values.  We may have pc
    /// values and the stop_id when the stack trace was recorded.  We may have a
    /// CFA, or more likely, we won't.
    ///
    /// @param [in] thread_sp
    ///   The Thread that this frame belongs to.
    ///
    /// @param [in] frame_idx
    ///   This StackFrame's frame index number in the Thread.  If inlined stack
    ///   frames are being created, this may differ from the concrete_frame_idx
    ///   which is the frame index without any inlined stack frames.
    ///
    /// @param [in] concrete_frame_idx
    ///   The StackFrame's frame index number in the Thread without any inlined
    ///   stack frames being included in the index.  
    ///
    /// @param [in] cfa
    ///   The Canonical Frame Address (this terminology from DWARF) for this
    ///   stack frame.  The CFA for a stack frame does not change over the
    ///   span of the stack frame's existence.  It is often the value of the
    ///   caller's stack pointer before the call instruction into this frame's
    ///   function.  It is usually not the same as the frame pointer register's
    ///   value.
    ///
    /// @param [in] cfa_is_valid
    ///   A history stack frame may not have a CFA value collected.  We want to
    ///   distinguish between "no CFA available" and a CFA of 
    ///   LLDB_INVALID_ADDRESS.
    ///
    /// @param [in] pc
    ///   The current pc value of this stack frame.
    ///
    /// @param [in] stop_id
    ///   The stop_id which should be used when looking up symbols for the pc value,
    ///   if appropriate.  This argument is ignored if stop_id_is_valid is false.
    ///
    /// @param [in] stop_id_is_valid
    ///   If the stop_id argument provided is not needed for this StackFrame, this
    ///   should be false.  If this is a history stack frame and we know the stop_id
    ///   when the pc value was collected, that stop_id should be provided and this
    ///   will be true.
    ///
    /// @param [in] is_history_frame
    ///   If this is a historical stack frame -- possibly without CFA or registers or
    ///   local variables -- then this should be set to true.
    ///
    /// @param [in] sc_ptr
    ///   Optionally seed the StackFrame with the SymbolContext information that has
    ///   already been discovered.
    //------------------------------------------------------------------
    StackFrame (const lldb::ThreadSP &thread_sp,
                lldb::user_id_t frame_idx, 
                lldb::user_id_t concrete_frame_idx, 
                lldb::addr_t cfa, 
                bool cfa_is_valid,
                lldb::addr_t pc, 
                uint32_t stop_id,
                bool stop_id_is_valid,
                bool is_history_frame,
                const SymbolContext *sc_ptr);

    StackFrame (const lldb::ThreadSP &thread_sp,
                lldb::user_id_t frame_idx, 
                lldb::user_id_t concrete_frame_idx, 
                const lldb::RegisterContextSP &reg_context_sp, 
                lldb::addr_t cfa, 
                lldb::addr_t pc, 
                const SymbolContext *sc_ptr);
    
    StackFrame (const lldb::ThreadSP &thread_sp,
                lldb::user_id_t frame_idx, 
                lldb::user_id_t concrete_frame_idx, 
                const lldb::RegisterContextSP &reg_context_sp, 
                lldb::addr_t cfa, 
                const Address& pc, 
                const SymbolContext *sc_ptr);

    ~StackFrame() override;

    lldb::ThreadSP
    GetThread () const
    {
        return m_thread_wp.lock();
    }

    StackID&
    GetStackID();

    //------------------------------------------------------------------
    /// Get an Address for the current pc value in this StackFrame.
    ///
    /// May not be the same as the actual PC value for inlined stack frames.
    ///
    /// @return
    ///   The Address object set to the current PC value.
    //------------------------------------------------------------------
    const Address&
    GetFrameCodeAddress();

    //------------------------------------------------------------------
    /// Change the pc value for a given thread.
    ///
    /// Change the current pc value for the frame on this thread.
    ///
    /// @param[in] pc
    ///     The load address that the pc will be set to.
    ///
    /// @return
    ///     true if the pc was changed.  false if this failed -- possibly
    ///     because this frame is not a live StackFrame.
    //------------------------------------------------------------------
    bool
    ChangePC (lldb::addr_t pc);

    //------------------------------------------------------------------
    /// Provide a SymbolContext for this StackFrame's current pc value.
    ///
    /// The StackFrame maintains this SymbolContext and adds additional information
    /// to it on an as-needed basis.  This helps to avoid different functions
    /// looking up symbolic information for a given pc value multiple times.
    ///
    /// @params [in] resolve_scope
    ///   Flags from the SymbolContextItem enumerated type which specify what
    ///   type of symbol context is needed by this caller.
    ///
    /// @return
    ///   A SymbolContext reference which includes the types of information
    ///   requested by resolve_scope, if they are available.
    //------------------------------------------------------------------
    const SymbolContext&
    GetSymbolContext (uint32_t resolve_scope);

    //------------------------------------------------------------------
    /// Return the Canonical Frame Address (DWARF term) for this frame.
    ///
    /// The CFA is typically the value of the stack pointer register before
    /// the call invocation is made.  It will not change during the lifetime
    /// of a stack frame.  It is often not the same thing as the frame pointer
    /// register value.
    ///
    /// Live StackFrames will always have a CFA but other types of frames may
    /// not be able to supply one.
    ///
    /// @param [out] value
    ///   The address of the CFA for this frame, if available.
    ///
    /// @param [out] error_ptr
    ///   If there is an error determining the CFA address, this may contain a
    ///   string explaining the failure.
    ///
    /// @return
    ///   Returns true if the CFA value was successfully set in value.  Some
    ///   frames may be unable to provide this value; they will return false.
    //------------------------------------------------------------------
    bool
    GetFrameBaseValue(Scalar &value, Error *error_ptr);

    //------------------------------------------------------------------
    /// Get the current lexical scope block for this StackFrame, if possible.
    ///
    /// If debug information is available for this stack frame, return a
    /// pointer to the innermost lexical Block that the frame is currently
    /// executing.
    ///
    /// @return
    ///   A pointer to the current Block.  nullptr is returned if this can
    ///   not be provided.
    //------------------------------------------------------------------
    Block *
    GetFrameBlock ();

    //------------------------------------------------------------------
    /// Get the RegisterContext for this frame, if possible.
    ///
    /// Returns a shared pointer to the RegisterContext for this stack frame.
    /// Only a live StackFrame object will be able to return a RegisterContext -
    /// callers must be prepared for an empty shared pointer being returned.
    ///
    /// Even a live StackFrame RegisterContext may not be able to provide all
    /// registers.  Only the currently executing frame (frame 0) can reliably
    /// provide every register in the register context.
    ///
    /// @return
    ///   The RegisterContext shared point for this frame.
    //------------------------------------------------------------------
    lldb::RegisterContextSP
    GetRegisterContext ();

    const lldb::RegisterContextSP &
    GetRegisterContextSP () const
    {
        return m_reg_context_sp;
    }

    //------------------------------------------------------------------
    /// Retrieve the list of variables that are in scope at this StackFrame's pc.
    ///
    /// A frame that is not live may return an empty VariableList for a given
    /// pc value even though variables would be available at this point if
    /// it were a live stack frame.
    ///
    /// @param[in] get_file_globals
    ///     Whether to also retrieve compilation-unit scoped variables
    ///     that are visible to the entire compilation unit (e.g. file
    ///     static in C, globals that are homed in this CU).
    ///
    /// @return
    ///     A pointer to a list of variables.
    //------------------------------------------------------------------
    VariableList *
    GetVariableList (bool get_file_globals);

    //------------------------------------------------------------------
    /// Retrieve the list of variables that are in scope at this StackFrame's pc.
    ///
    /// A frame that is not live may return an empty VariableListSP for a
    /// given pc value even though variables would be available at this point
    /// if it were a live stack frame.
    ///
    /// @param[in] get_file_globals
    ///     Whether to also retrieve compilation-unit scoped variables
    ///     that are visible to the entire compilation unit (e.g. file
    ///     static in C, globals that are homed in this CU).
    ///
    /// @return
    ///     A pointer to a list of variables.
    //------------------------------------------------------------------
    lldb::VariableListSP
    GetInScopeVariableList (bool get_file_globals, bool must_have_valid_location = false);

    //------------------------------------------------------------------
    /// Create a ValueObject for a variable name / pathname, possibly
    /// including simple dereference/child selection syntax.
    ///
    /// @param[in] var_expr
    ///     The string specifying a variable to base the VariableObject off
    ///     of.
    ///
    /// @param[in] use_dynamic
    ///     Whether the correct dynamic type of an object pointer should be
    ///     determined before creating the object, or if the static type is
    ///     sufficient.  One of the DynamicValueType enumerated values.
    ///
    /// @param[in] options
    ///     An unsigned integer of flags, values from StackFrame::ExpressionPathOption
    ///     enum.
    /// @param[in] var_sp
    ///     A VariableSP that will be set to the variable described in the
    ///     var_expr path.
    ///
    /// @param[in] error
    ///     Record any errors encountered while evaluating var_expr.
    ///
    /// @return
    ///     A shared pointer to the ValueObject described by var_expr.
    //------------------------------------------------------------------
    lldb::ValueObjectSP
    GetValueForVariableExpressionPath (const char *var_expr,
                                       lldb::DynamicValueType use_dynamic,
                                       uint32_t options,
                                       lldb::VariableSP &var_sp,
                                       Error &error);

    //------------------------------------------------------------------
    /// Determine whether this StackFrame has debug information available or not
    ///
    /// @return
    //    true if debug information is available for this frame (function,
    //    compilation unit, block, etc.)
    //------------------------------------------------------------------
    bool
    HasDebugInformation ();

    //------------------------------------------------------------------
    /// Return the disassembly for the instructions of this StackFrame's function
    /// as a single C string.
    ///
    /// @return
    //    C string with the assembly instructions for this function.
    //------------------------------------------------------------------
    const char *
    Disassemble ();

    //------------------------------------------------------------------
    /// Print a description for this frame using the frame-format formatter settings.
    ///
    /// @param [in] strm
    ///   The Stream to print the description to.
    ///
    /// @param [in] frame_marker
    ///   Optional string that will be prepended to the frame output description.
    //------------------------------------------------------------------
    void
    DumpUsingSettingsFormat(Stream *strm, const char *frame_marker = nullptr);

    //------------------------------------------------------------------
    /// Print a description for this frame using a default format.
    ///
    /// @param [in] strm
    ///   The Stream to print the description to.
    ///
    /// @param [in] show_frame_index
    ///   Whether to print the frame number or not.
    ///
    /// @param [in] show_fullpaths
    ///   Whether to print the full source paths or just the file base name.
    //------------------------------------------------------------------
    void
    Dump (Stream *strm, bool show_frame_index, bool show_fullpaths);

    //------------------------------------------------------------------
    /// Print a description of this stack frame and/or the source context/assembly
    /// for this stack frame.
    ///
    /// @param[in] strm
    ///   The Stream to send the output to.
    ///
    /// @param[in] show_frame_info
    ///   If true, print the frame info by calling DumpUsingSettingsFormat().
    ///
    /// @param[in] show_source
    ///   If true, print source or disassembly as per the user's settings.
    ///
    /// @param[in] frame_marker 
    ///   Passed to DumpUsingSettingsFormat() for the frame info printing.
    ///
    /// @return
    ///   Returns true if successful.
    //------------------------------------------------------------------
    bool
    GetStatus(Stream &strm,
              bool show_frame_info,
              bool show_source,
              const char *frame_marker = nullptr);

    //------------------------------------------------------------------
    /// Query whether this frame is a concrete frame on the call stack,
    /// or if it is an inlined frame derived from the debug information
    /// and presented by the debugger.
    ///
    /// @return
    ///   true if this is an inlined frame.
    //------------------------------------------------------------------
    bool
    IsInlined ();

    //------------------------------------------------------------------
    /// Query this frame to find what frame it is in this Thread's StackFrameList.
    ///
    /// @return
    ///   StackFrame index 0 indicates the currently-executing function.  Inline
    ///   frames are included in this frame index count.
    //------------------------------------------------------------------
    uint32_t
    GetFrameIndex () const;

    //------------------------------------------------------------------
    /// Query this frame to find what frame it is in this Thread's StackFrameList,
    /// not counting inlined frames.
    ///
    /// @return
    ///   StackFrame index 0 indicates the currently-executing function.  Inline
    ///   frames are not included in this frame index count; their concrete
    ///   frame index will be the same as the concrete frame that they are
    ///   derived from.
    //------------------------------------------------------------------
    uint32_t
    GetConcreteFrameIndex () const
    {
        return m_concrete_frame_index;
    }

    //------------------------------------------------------------------
    /// Create a ValueObject for a given Variable in this StackFrame.
    ///
    /// @params [in] variable_sp
    ///   The Variable to base this ValueObject on
    ///
    /// @params [in] use_dynamic
    ///     Whether the correct dynamic type of the variable should be
    ///     determined before creating the ValueObject, or if the static type
    ///     is sufficient.  One of the DynamicValueType enumerated values.
    ///
    /// @return
    //    A ValueObject for this variable.
    //------------------------------------------------------------------
    lldb::ValueObjectSP
    GetValueObjectForFrameVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic);

    //------------------------------------------------------------------
    /// Add an arbitrary Variable object (e.g. one that specifics a global or static)
    /// to a StackFrame's list of ValueObjects.
    ///
    /// @params [in] variable_sp
    ///   The Variable to base this ValueObject on
    ///
    /// @params [in] use_dynamic
    ///     Whether the correct dynamic type of the variable should be
    ///     determined before creating the ValueObject, or if the static type
    ///     is sufficient.  One of the DynamicValueType enumerated values.
    ///
    /// @return
    //    A ValueObject for this variable.
    //------------------------------------------------------------------
    lldb::ValueObjectSP
    TrackGlobalVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic);

    //------------------------------------------------------------------
    /// Query this frame to determine what the default language should be
    /// when parsing expressions given the execution context.
    ///
    /// @return
    ///   The language of the frame if known, else lldb::eLanguageTypeUnknown.
    //------------------------------------------------------------------
    lldb::LanguageType
    GetLanguage ();

    // similar to GetLanguage(), but is allowed to take a potentially incorrect guess
    // if exact information is not available
    lldb::LanguageType
    GuessLanguage ();
    
    //------------------------------------------------------------------
    // lldb::ExecutionContextScope pure virtual functions
    //------------------------------------------------------------------
    lldb::TargetSP
    CalculateTarget() override;

    lldb::ProcessSP
    CalculateProcess() override;

    lldb::ThreadSP
    CalculateThread() override;

    lldb::StackFrameSP
    CalculateStackFrame() override;

    void
    CalculateExecutionContext(ExecutionContext &exe_ctx) override;

protected:
    friend class StackFrameList;

    void
    SetSymbolContextScope (SymbolContextScope *symbol_scope);

    void
    UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame);

    void
    UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame);

    bool
    HasCachedData () const;
    
private:
    //------------------------------------------------------------------
    // For StackFrame only
    //------------------------------------------------------------------
    lldb::ThreadWP m_thread_wp;
    uint32_t m_frame_index;
    uint32_t m_concrete_frame_index;
    lldb::RegisterContextSP m_reg_context_sp;
    StackID m_id;
    Address m_frame_code_addr;   // The frame code address (might not be the same as the actual PC for inlined frames) as a section/offset address
    SymbolContext m_sc;
    Flags m_flags;
    Scalar m_frame_base;
    Error m_frame_base_error;
    bool m_cfa_is_valid;        // Does this frame have a CFA?  Different from CFA == LLDB_INVALID_ADDRESS
    uint32_t m_stop_id;
    bool m_stop_id_is_valid;      // Does this frame have a stop_id?  Use it when referring to the m_frame_code_addr.
    bool m_is_history_frame;
    lldb::VariableListSP m_variable_list_sp;
    ValueObjectList m_variable_list_value_objects;  // Value objects for each variable in m_variable_list_sp
    StreamString m_disassembly;
    std::recursive_mutex m_mutex;

    DISALLOW_COPY_AND_ASSIGN (StackFrame);
};

} // namespace lldb_private

#endif // liblldb_StackFrame_h_
