//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Core/AddressResolverName.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Symbol.h"

using namespace lldb;
using namespace lldb_private;

AddressResolverName::AddressResolverName(const char *func_name,
                                         AddressResolver::MatchType type) :
    AddressResolver(),
    m_func_name(func_name),
    m_class_name(nullptr),
    m_regex(),
    m_match_type(type)
{
    if (m_match_type == AddressResolver::Regexp)
    {
        if (!m_regex.Compile (m_func_name.AsCString()))
        {
            Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));

            if (log)
                log->Warning ("function name regexp: \"%s\" did not compile.", m_func_name.AsCString());
        }
    }
}

AddressResolverName::AddressResolverName(RegularExpression &func_regex) :
    AddressResolver(),
    m_func_name(nullptr),
    m_class_name(nullptr),
    m_regex(func_regex),
    m_match_type(AddressResolver::Regexp)
{
}

AddressResolverName::AddressResolverName(const char *class_name,
                                         const char *method,
                                         AddressResolver::MatchType type) :
    AddressResolver (),
    m_func_name (method),
    m_class_name (class_name),
    m_regex (),
    m_match_type (type)
{
}

AddressResolverName::~AddressResolverName() = default;

// FIXME: Right now we look at the module level, and call the module's "FindFunctions".
// Greg says he will add function tables, maybe at the CompileUnit level to accelerate function
// lookup.  At that point, we should switch the depth to CompileUnit, and look in these tables.

Searcher::CallbackReturn
AddressResolverName::SearchCallback(SearchFilter &filter,
                                    SymbolContext &context,
                                    Address *addr,
                                    bool containing)
{
    SymbolContextList func_list;
    SymbolContextList sym_list;

    bool skip_prologue = true;
    uint32_t i;
    SymbolContext sc;
    Address func_addr;

    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));

    if (m_class_name)
    {
        if (log)
            log->Warning ("Class/method function specification not supported yet.\n");
        return Searcher::eCallbackReturnStop;
    }

    const bool include_symbols = false;
    const bool include_inlines = true;
    const bool append = false;
    switch (m_match_type)
    {
    case AddressResolver::Exact:
        if (context.module_sp)
        {
            context.module_sp->FindSymbolsWithNameAndType (m_func_name,
                                                           eSymbolTypeCode, 
                                                           sym_list);
            context.module_sp->FindFunctions(m_func_name,
                                             nullptr,
                                             eFunctionNameTypeAuto,
                                             include_symbols,
                                             include_inlines,
                                             append,
                                             func_list);
        }
        break;

    case AddressResolver::Regexp:
        if (context.module_sp)
        {
            context.module_sp->FindSymbolsMatchingRegExAndType (m_regex, 
                                                                eSymbolTypeCode, 
                                                                sym_list);
            context.module_sp->FindFunctions (m_regex, 
                                              include_symbols,
                                              include_inlines,
                                              append, 
                                              func_list);
        }
        break;

    case AddressResolver::Glob:
        if (log)
            log->Warning ("glob is not supported yet.");
        break;
    }

    // Remove any duplicates between the function list and the symbol list
    if (func_list.GetSize())
    {
        for (i = 0; i < func_list.GetSize(); i++)
        {
            if (!func_list.GetContextAtIndex(i, sc))
                continue;

            if (sc.function == nullptr)
                continue;
            uint32_t j = 0;
            while (j < sym_list.GetSize())
            {
                SymbolContext symbol_sc;
                if (sym_list.GetContextAtIndex(j, symbol_sc))
                {
                    if (symbol_sc.symbol && symbol_sc.symbol->ValueIsAddress())
                    {
                        if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRef())
                        {
                            sym_list.RemoveContextAtIndex(j);
                            continue;   // Don't increment j
                        }
                    }
                }

                j++;
            }
        }

        for (i = 0; i < func_list.GetSize(); i++)
        {
            if (func_list.GetContextAtIndex(i, sc))
            {
                if (sc.function)
                {
                    func_addr = sc.function->GetAddressRange().GetBaseAddress();
                    addr_t byte_size = sc.function->GetAddressRange().GetByteSize();
                    if (skip_prologue)
                    {
                        const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
                        if (prologue_byte_size)
                        {
                            func_addr.SetOffset (func_addr.GetOffset() + prologue_byte_size);
                            byte_size -= prologue_byte_size;
                        }
                    }

                    if (filter.AddressPasses (func_addr))
                    {
                        AddressRange new_range (func_addr, byte_size);
                        m_address_ranges.push_back (new_range);
                    }
                }
            }
        }
    }

    for (i = 0; i < sym_list.GetSize(); i++)
    {
        if (sym_list.GetContextAtIndex(i, sc))
        {
            if (sc.symbol && sc.symbol->ValueIsAddress())
            {
                func_addr = sc.symbol->GetAddressRef();
                addr_t byte_size = sc.symbol->GetByteSize();

                if (skip_prologue)
                {
                    const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize();
                    if (prologue_byte_size)
                    {
                        func_addr.SetOffset (func_addr.GetOffset() + prologue_byte_size);
                        byte_size -= prologue_byte_size;
                    }
                }

                if (filter.AddressPasses (func_addr))
                {
                    AddressRange new_range (func_addr, byte_size);
                    m_address_ranges.push_back (new_range);
                }
            }
        }
    }
    return Searcher::eCallbackReturnContinue;
}

Searcher::Depth
AddressResolverName::GetDepth()
{
    return Searcher::eDepthModule;
}

void
AddressResolverName::GetDescription (Stream *s)
{
    s->PutCString("Address by function name: ");

    if (m_match_type == AddressResolver::Regexp)
        s->Printf("'%s' (regular expression)", m_regex.GetText());
    else
        s->Printf("'%s'", m_func_name.AsCString());
}
