//===-- ClangASTSource.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_ClangASTSource_h_
#define liblldb_ClangASTSource_h_

#include <set>

#include "clang/Basic/IdentifierTable.h"
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
#include "lldb/Symbol/ClangASTImporter.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Target/Target.h"

#include "llvm/ADT/SmallSet.h"

namespace lldb_private {
    
//----------------------------------------------------------------------
/// @class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h"
/// @brief Provider for named objects defined in the debug info for Clang
///
/// As Clang parses an expression, it may encounter names that are not
/// defined inside the expression, including variables, functions, and
/// types.  Clang knows the name it is looking for, but nothing else.
/// The ExternalSemaSource class provides Decls (VarDecl, FunDecl, TypeDecl)
/// to Clang for these names, consulting the ClangExpressionDeclMap to do
/// the actual lookups.
//----------------------------------------------------------------------
class ClangASTSource : 
    public ClangExternalASTSourceCommon,
    public ClangASTImporter::MapCompleter
{
public:
    //------------------------------------------------------------------
    /// Constructor
    ///
    /// Initializes class variables.
    ///
    /// @param[in] declMap
    ///     A reference to the LLDB object that handles entity lookup.
    //------------------------------------------------------------------
    ClangASTSource (const lldb::TargetSP &target) :
        m_import_in_progress (false),
        m_lookups_enabled (false),
        m_target (target),
        m_ast_context (NULL),
        m_active_lexical_decls (),
        m_active_lookups ()
    {
        m_ast_importer_sp = m_target->GetClangASTImporter();
    }
  
    //------------------------------------------------------------------
    /// Destructor
    //------------------------------------------------------------------
    ~ClangASTSource() override;
    
    //------------------------------------------------------------------
    /// Interface stubs.
    //------------------------------------------------------------------
    clang::Decl *GetExternalDecl (uint32_t) override {   return NULL;                }
    clang::Stmt *GetExternalDeclStmt (uint64_t) override {   return NULL;                }
    clang::Selector GetExternalSelector (uint32_t) override {   return clang::Selector();   }
    uint32_t GetNumExternalSelectors () override {   return 0;                   }
    clang::CXXBaseSpecifier *GetExternalCXXBaseSpecifiers (uint64_t Offset) override
                                                    {   return NULL;                }
    void MaterializeVisibleDecls (const clang::DeclContext *DC)
                                                    {   return;                     }
      
    void InstallASTContext (clang::ASTContext *ast_context)
    {
        m_ast_context = ast_context;
        m_ast_importer_sp->InstallMapCompleter(ast_context, *this);
    }
  
    //
    // APIs for ExternalASTSource
    //

    //------------------------------------------------------------------
    /// Look up all Decls that match a particular name.  Only handles
    /// Identifiers and DeclContexts that are either NamespaceDecls or
    /// TranslationUnitDecls.  Calls SetExternalVisibleDeclsForName with
    /// the result.
    ///
    /// The work for this function is done by
    /// void FindExternalVisibleDecls (NameSearchContext &);
    ///
    /// @param[in] DC
    ///     The DeclContext to register the found Decls in.
    ///
    /// @param[in] Name
    ///     The name to find entries for.
    ///
    /// @return
    ///     Whatever SetExternalVisibleDeclsForName returns.
    //------------------------------------------------------------------
    bool FindExternalVisibleDeclsByName(const clang::DeclContext *DC, clang::DeclarationName Name) override;

    //------------------------------------------------------------------
    /// Enumerate all Decls in a given lexical context.
    ///
    /// @param[in] DC
    ///     The DeclContext being searched.
    ///
    /// @param[in] isKindWeWant
    ///     A callback function that returns true given the
    ///     DeclKinds of desired Decls, and false otherwise.
    ///
    /// @param[in] Decls
    ///     A vector that is filled in with matching Decls.
    //------------------------------------------------------------------
    void FindExternalLexicalDecls(
        const clang::DeclContext *DC, llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
        llvm::SmallVectorImpl<clang::Decl *> &Decls) override;

    //------------------------------------------------------------------
    /// Specify the layout of the contents of a RecordDecl.
    ///
    /// @param[in] Record
    ///     The record (in the parser's AST context) that needs to be
    ///     laid out.
    ///
    /// @param[out] Size
    ///     The total size of the record in bits.
    ///
    /// @param[out] Alignment
    ///     The alignment of the record in bits.
    ///
    /// @param[in] FieldOffsets
    ///     A map that must be populated with pairs of the record's
    ///     fields (in the parser's AST context) and their offsets
    ///     (measured in bits).
    ///
    /// @param[in] BaseOffsets
    ///     A map that must be populated with pairs of the record's
    ///     C++ concrete base classes (in the parser's AST context, 
    ///     and only if the record is a CXXRecordDecl and has base
    ///     classes) and their offsets (measured in bytes).
    ///
    /// @param[in] VirtualBaseOffsets
    ///     A map that must be populated with pairs of the record's
    ///     C++ virtual base classes (in the parser's AST context, 
    ///     and only if the record is a CXXRecordDecl and has base
    ///     classes) and their offsets (measured in bytes).
    ///
    /// @return
    ///     True <=> the layout is valid.
    //-----------------------------------------------------------------
    bool layoutRecordType(const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
                          llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
                          llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets,
                          llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &VirtualBaseOffsets) override;

    //------------------------------------------------------------------
    /// Complete a TagDecl.
    ///
    /// @param[in] Tag
    ///     The Decl to be completed in place.
    //------------------------------------------------------------------
    void CompleteType(clang::TagDecl *Tag) override;

    //------------------------------------------------------------------
    /// Complete an ObjCInterfaceDecl.
    ///
    /// @param[in] Class
    ///     The Decl to be completed in place.
    //------------------------------------------------------------------
    void CompleteType(clang::ObjCInterfaceDecl *Class) override;

    //------------------------------------------------------------------
    /// Called on entering a translation unit.  Tells Clang by calling
    /// setHasExternalVisibleStorage() and setHasExternalLexicalStorage()
    /// that this object has something to say about undefined names.
    ///
    /// @param[in] ASTConsumer
    ///     Unused.
    //------------------------------------------------------------------
    void StartTranslationUnit(clang::ASTConsumer *Consumer) override;

    //
    // APIs for NamespaceMapCompleter
    //
    
    //------------------------------------------------------------------
    /// Look up the modules containing a given namespace and put the 
    /// appropriate entries in the namespace map.
    ///
    /// @param[in] namespace_map
    ///     The map to be completed.
    ///
    /// @param[in] name
    ///     The name of the namespace to be found.
    ///
    /// @param[in] parent_map
    ///     The map for the namespace's parent namespace, if there is
    ///     one.
    //------------------------------------------------------------------
    void CompleteNamespaceMap(ClangASTImporter::NamespaceMapSP &namespace_map, const ConstString &name,
                              ClangASTImporter::NamespaceMapSP &parent_map) const override;

    //
    // Helper APIs
    //
    
    clang::NamespaceDecl *
    AddNamespace (NameSearchContext &context, 
                  ClangASTImporter::NamespaceMapSP &namespace_decls);
    
    //------------------------------------------------------------------
    /// The worker function for FindExternalVisibleDeclsByName.
    ///
    /// @param[in] context
    ///     The NameSearchContext to use when filing results.
    //------------------------------------------------------------------
    virtual void FindExternalVisibleDecls (NameSearchContext &context);
    
    void SetImportInProgress (bool import_in_progress) { m_import_in_progress = import_in_progress; }
    bool GetImportInProgress () { return m_import_in_progress; }
    
    void SetLookupsEnabled (bool lookups_enabled) { m_lookups_enabled = lookups_enabled; }
    bool GetLookupsEnabled () { return m_lookups_enabled; } 
    
    //----------------------------------------------------------------------
    /// @class ClangASTSourceProxy ClangASTSource.h "lldb/Expression/ClangASTSource.h"
    /// @brief Proxy for ClangASTSource
    ///
    /// Clang AST contexts like to own their AST sources, so this is a
    /// state-free proxy object.
    //----------------------------------------------------------------------
    class ClangASTSourceProxy : public ClangExternalASTSourceCommon
    {
    public:
        ClangASTSourceProxy (ClangASTSource &original) :
            m_original(original)
        {
        }

        bool
        FindExternalVisibleDeclsByName(const clang::DeclContext *DC, clang::DeclarationName Name) override
        {
            return m_original.FindExternalVisibleDeclsByName(DC, Name);
        }

        void
        FindExternalLexicalDecls(const clang::DeclContext *DC,
                                 llvm::function_ref<bool(clang::Decl::Kind)> IsKindWeWant,
                                 llvm::SmallVectorImpl<clang::Decl *> &Decls) override
        {
            return m_original.FindExternalLexicalDecls(DC, IsKindWeWant, Decls);
        }

        void
        CompleteType(clang::TagDecl *Tag) override
        {
            return m_original.CompleteType(Tag);
        }

        void
        CompleteType(clang::ObjCInterfaceDecl *Class) override
        {
            return m_original.CompleteType(Class);
        }

        bool
        layoutRecordType(const clang::RecordDecl *Record, uint64_t &Size, uint64_t &Alignment,
                         llvm::DenseMap<const clang::FieldDecl *, uint64_t> &FieldOffsets,
                         llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets,
                         llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> &VirtualBaseOffsets) override
        {
            return m_original.layoutRecordType(Record,
                                               Size, 
                                               Alignment, 
                                               FieldOffsets, 
                                               BaseOffsets, 
                                               VirtualBaseOffsets);
        }

        void
        StartTranslationUnit(clang::ASTConsumer *Consumer) override
        {
            return m_original.StartTranslationUnit(Consumer);
        }
        
        ClangASTMetadata *
        GetMetadata(const void * object)
        {
            return m_original.GetMetadata(object);
        }
        
        void
        SetMetadata(const void * object, ClangASTMetadata &metadata)
        {
            return m_original.SetMetadata(object, metadata);
        }
        
        bool
        HasMetadata(const void * object)
        {
            return m_original.HasMetadata(object);
        }
    private:
        ClangASTSource &m_original;
    };
    
    clang::ExternalASTSource *CreateProxy()
    {
        return new ClangASTSourceProxy(*this);
    }
    
protected:
    //------------------------------------------------------------------
    /// Look for the complete version of an Objective-C interface, and
    /// return it if found.
    ///
    /// @param[in] interface_decl
    ///     An ObjCInterfaceDecl that may not be the complete one.
    ///
    /// @return
    ///     NULL if the complete interface couldn't be found;
    ///     the complete interface otherwise.
    //------------------------------------------------------------------
    clang::ObjCInterfaceDecl *
    GetCompleteObjCInterface (clang::ObjCInterfaceDecl *interface_decl);
    
    //------------------------------------------------------------------
    /// Find all entities matching a given name in a given module,
    /// using a NameSearchContext to make Decls for them.
    ///
    /// @param[in] context
    ///     The NameSearchContext that can construct Decls for this name.
    ///
    /// @param[in] module
    ///     If non-NULL, the module to query.
    ///
    /// @param[in] namespace_decl
    ///     If valid and module is non-NULL, the parent namespace.
    ///
    /// @param[in] current_id
    ///     The ID for the current FindExternalVisibleDecls invocation,
    ///     for logging purposes.
    ///
    /// @return
    ///     True on success; false otherwise.
    //------------------------------------------------------------------
    void 
    FindExternalVisibleDecls (NameSearchContext &context, 
                              lldb::ModuleSP module,
                              CompilerDeclContext &namespace_decl,
                              unsigned int current_id);
    
    //------------------------------------------------------------------
    /// Find all Objective-C methods matching a given selector.
    ///
    /// @param[in] context
    ///     The NameSearchContext that can construct Decls for this name.
    ///     Its m_decl_name contains the selector and its m_decl_context
    ///     is the containing object.
    //------------------------------------------------------------------
    void
    FindObjCMethodDecls (NameSearchContext &context);
    
    //------------------------------------------------------------------
    /// Find all Objective-C properties and ivars with a given name.
    ///
    /// @param[in] context
    ///     The NameSearchContext that can construct Decls for this name.
    ///     Its m_decl_name contains the name and its m_decl_context
    ///     is the containing object.
    //------------------------------------------------------------------
    void
    FindObjCPropertyAndIvarDecls (NameSearchContext &context);
    
    //------------------------------------------------------------------
    /// A wrapper for ClangASTContext::CopyType that sets a flag that
    /// indicates that we should not respond to queries during import.
    ///
    /// @param[in] dest_context
    ///     The target AST context, typically the parser's AST context.
    ///
    /// @param[in] source_context
    ///     The source AST context, typically the AST context of whatever
    ///     symbol file the type was found in.
    ///
    /// @param[in] src_type
    ///     The source type.
    ///
    /// @return
    ///     The imported type.
    //------------------------------------------------------------------
    CompilerType
    GuardedCopyType (const CompilerType &src_type);
    
    friend struct NameSearchContext;
    
    bool                    m_import_in_progress;
    bool                    m_lookups_enabled;

    const lldb::TargetSP                m_target;           ///< The target to use in finding variables and types.
    clang::ASTContext                  *m_ast_context;      ///< The AST context requests are coming in for.
    lldb::ClangASTImporterSP            m_ast_importer_sp;  ///< The target's AST importer.
    std::set<const clang::Decl *>       m_active_lexical_decls;
    std::set<const char *>              m_active_lookups;
};

//----------------------------------------------------------------------
/// @class NameSearchContext ClangASTSource.h "lldb/Expression/ClangASTSource.h"
/// @brief Container for all objects relevant to a single name lookup
///     
/// LLDB needs to create Decls for entities it finds.  This class communicates
/// what name is being searched for and provides helper functions to construct
/// Decls given appropriate type information.
//----------------------------------------------------------------------
struct NameSearchContext {
    ClangASTSource &m_ast_source;                               ///< The AST source making the request
    llvm::SmallVectorImpl<clang::NamedDecl*> &m_decls;          ///< The list of declarations already constructed
    ClangASTImporter::NamespaceMapSP m_namespace_map;           ///< The mapping of all namespaces found for this request back to their modules
    const clang::DeclarationName &m_decl_name;                  ///< The name being looked for
    const clang::DeclContext *m_decl_context;                   ///< The DeclContext to put declarations into
    llvm::SmallSet <CompilerType, 5> m_function_types;    ///< All the types of functions that have been reported, so we don't report conflicts
    
    struct {
        bool variable                   : 1;
        bool function_with_type_info    : 1;
        bool function                   : 1;
        bool local_vars_nsp             : 1;
        bool type                       : 1;
    } m_found;
    
    //------------------------------------------------------------------
    /// Constructor
    ///
    /// Initializes class variables.
    ///
    /// @param[in] astSource
    ///     A reference to the AST source making a request.
    ///
    /// @param[in] decls
    ///     A reference to a list into which new Decls will be placed.  This
    ///     list is typically empty when the function is called.
    ///
    /// @param[in] name
    ///     The name being searched for (always an Identifier).
    ///
    /// @param[in] dc
    ///     The DeclContext to register Decls in.
    //------------------------------------------------------------------
    NameSearchContext (ClangASTSource &astSource,
                       llvm::SmallVectorImpl<clang::NamedDecl*> &decls,
                       clang::DeclarationName &name,
                       const clang::DeclContext *dc) :
        m_ast_source(astSource),
        m_decls(decls),
        m_decl_name(name),
        m_decl_context(dc)
    {
        memset(&m_found, 0, sizeof(m_found));
    }
    
    //------------------------------------------------------------------
    /// Create a VarDecl with the name being searched for and the provided
    /// type and register it in the right places.
    ///
    /// @param[in] type
    ///     The opaque QualType for the VarDecl being registered.
    //------------------------------------------------------------------
    clang::NamedDecl *AddVarDecl(const CompilerType &type);
    
    //------------------------------------------------------------------
    /// Create a FunDecl with the name being searched for and the provided
    /// type and register it in the right places.
    ///
    /// @param[in] type
    ///     The opaque QualType for the FunDecl being registered.
    ///
    /// @param[in] extern_c
    ///     If true, build an extern "C" linkage specification for this.
    //------------------------------------------------------------------
    clang::NamedDecl *AddFunDecl(const CompilerType &type,
                                 bool extern_c = false);
    
    //------------------------------------------------------------------
    /// Create a FunDecl with the name being searched for and generic
    /// type (i.e. intptr_t NAME_GOES_HERE(...)) and register it in the
    /// right places.
    //------------------------------------------------------------------
    clang::NamedDecl *AddGenericFunDecl();
    
    //------------------------------------------------------------------
    /// Create a TypeDecl with the name being searched for and the provided
    /// type and register it in the right places.
    ///
    /// @param[in] compiler_type
    ///     The opaque QualType for the TypeDecl being registered.
    //------------------------------------------------------------------
    clang::NamedDecl *AddTypeDecl(const CompilerType &compiler_type);
    
    
    //------------------------------------------------------------------
    /// Add Decls from the provided DeclContextLookupResult to the list
    /// of results.
    ///
    /// @param[in] result
    ///     The DeclContextLookupResult, usually returned as the result
    ///     of querying a DeclContext.
    //------------------------------------------------------------------
    void AddLookupResult (clang::DeclContextLookupResult result);
    
    //------------------------------------------------------------------
    /// Add a NamedDecl to the list of results.
    ///
    /// @param[in] decl
    ///     The NamedDecl, usually returned as the result
    ///     of querying a DeclContext.
    //------------------------------------------------------------------
    void AddNamedDecl (clang::NamedDecl *decl);
};

} // namespace lldb_private

#endif // liblldb_ClangASTSource_h_
