//===-- ValueObjectRegister.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/ValueObjectRegister.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/Module.h"
#include "lldb/Symbol/ClangASTType.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"

using namespace lldb;
using namespace lldb_private;

#pragma mark ValueObjectRegisterContext

ValueObjectRegisterContext::ValueObjectRegisterContext (RegisterContext *reg_ctx) :
    ValueObject (),
    m_reg_ctx (reg_ctx)
{
    assert (reg_ctx);
    m_name.SetCString("Registers");
    SetValueIsValid (true);
}

ValueObjectRegisterContext::~ValueObjectRegisterContext()
{
}

void *
ValueObjectRegisterContext::GetOpaqueClangQualType ()
{
    return NULL;
}

ConstString
ValueObjectRegisterContext::GetTypeName()
{
    ConstString empty_type_name;
    return empty_type_name;
}

uint32_t
ValueObjectRegisterContext::CalculateNumChildren()
{
    return m_reg_ctx->GetRegisterSetCount();
}

clang::ASTContext *
ValueObjectRegisterContext::GetClangAST ()
{
    return NULL;
}

size_t
ValueObjectRegisterContext::GetByteSize()
{
    return 0;
}

void
ValueObjectRegisterContext::UpdateValue (ExecutionContextScope *exe_scope)
{
    m_error.Clear();
    StackFrame *frame = exe_scope->CalculateStackFrame();
    if (frame)
        m_reg_ctx = frame->GetRegisterContext();
    else
        m_reg_ctx = NULL;

    SetValueIsValid (m_reg_ctx != NULL);
}

ValueObjectSP
ValueObjectRegisterContext::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index)
{
    ValueObjectSP valobj_sp;

    const uint32_t num_children = GetNumChildren();
    if (idx < num_children)
        valobj_sp.reset (new ValueObjectRegisterSet(m_reg_ctx, idx));
    return valobj_sp;
}


#pragma mark -
#pragma mark ValueObjectRegisterSet

ValueObjectRegisterSet::ValueObjectRegisterSet (RegisterContext *reg_ctx, uint32_t reg_set_idx) :
    ValueObject (),
    m_reg_ctx (reg_ctx),
    m_reg_set (NULL),
    m_reg_set_idx (reg_set_idx)
{
    assert (reg_ctx);
    m_reg_set = reg_ctx->GetRegisterSet(m_reg_set_idx);
    if (m_reg_set)
    {
        m_name.SetCString (m_reg_set->name);
    }
}

ValueObjectRegisterSet::~ValueObjectRegisterSet()
{
}

void *
ValueObjectRegisterSet::GetOpaqueClangQualType ()
{
    return NULL;
}

ConstString
ValueObjectRegisterSet::GetTypeName()
{
    return ConstString();
}

uint32_t
ValueObjectRegisterSet::CalculateNumChildren()
{
    const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet(m_reg_set_idx);
    if (reg_set)
        return reg_set->num_registers;
    return 0;
}

clang::ASTContext *
ValueObjectRegisterSet::GetClangAST ()
{
    return NULL;
}

size_t
ValueObjectRegisterSet::GetByteSize()
{
    return 0;
}

void
ValueObjectRegisterSet::UpdateValue (ExecutionContextScope *exe_scope)
{
    m_error.Clear();
    SetValueDidChange (false);
    StackFrame *frame = exe_scope->CalculateStackFrame();
    if (frame == NULL)
        m_reg_ctx = NULL;
    else
    {
        m_reg_ctx = frame->GetRegisterContext ();
        if (m_reg_ctx)
        {
            const RegisterSet *reg_set = m_reg_ctx->GetRegisterSet (m_reg_set_idx);
            if (reg_set == NULL)
                m_reg_ctx = NULL;
            else if (m_reg_set != reg_set)
            {
                SetValueDidChange (true);
                m_name.SetCString(reg_set->name);
            }
        }
    }
    if (m_reg_ctx)
    {
        SetValueIsValid (true);
    }
    else
    {
        SetValueIsValid (false);
        m_children.clear();
    }
}


ValueObjectSP
ValueObjectRegisterSet::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index)
{
    ValueObjectSP valobj_sp;
    if (m_reg_ctx && m_reg_set)
    {
        const uint32_t num_children = GetNumChildren();
        if (idx < num_children)
            valobj_sp.reset (new ValueObjectRegister(m_reg_ctx, m_reg_set->registers[idx]));
    }
    return valobj_sp;
}


#pragma mark -
#pragma mark ValueObjectRegister

ValueObjectRegister::ValueObjectRegister (RegisterContext *reg_ctx, uint32_t reg_num) :
    ValueObject (),
    m_reg_ctx (reg_ctx),
    m_reg_info (NULL),
    m_reg_num (reg_num),
    m_type_name (),
    m_clang_type (NULL)
{
    assert (reg_ctx);
    m_reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
    if (m_reg_info)
    {
        if (m_reg_info->name)
            m_name.SetCString(m_reg_info->name);
        else if (m_reg_info->alt_name)
            m_name.SetCString(m_reg_info->alt_name);
    }
}

ValueObjectRegister::~ValueObjectRegister()
{
}

void *
ValueObjectRegister::GetOpaqueClangQualType ()
{
    if (m_clang_type == NULL && m_reg_info)
    {
        Process *process = m_reg_ctx->CalculateProcess ();
        if (process)
        {
            Module *exe_module = process->GetTarget().GetExecutableModule ().get();
            if (exe_module)
            {
                TypeList *type_list = exe_module->GetTypeList();
                if (type_list)
                    m_clang_type = type_list->GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize (m_reg_info->encoding, m_reg_info->byte_size * 8);
            }
        }
    }
    return m_clang_type;
}

ConstString
ValueObjectRegister::GetTypeName()
{
    if (m_type_name.IsEmpty())
        m_type_name = ClangASTType::GetClangTypeName (GetOpaqueClangQualType());
    return m_type_name;
}

uint32_t
ValueObjectRegister::CalculateNumChildren()
{
    return 0;
}

clang::ASTContext *
ValueObjectRegister::GetClangAST ()
{
    Process *process = m_reg_ctx->CalculateProcess ();
    if (process)
    {
        Module *exe_module = process->GetTarget().GetExecutableModule ().get();
        if (exe_module)
        {
            TypeList *type_list = exe_module->GetTypeList();
            if (type_list)
                return type_list->GetClangASTContext().getASTContext();
        }
    }
    return NULL;
}

size_t
ValueObjectRegister::GetByteSize()
{
    return m_reg_info->byte_size;
}

void
ValueObjectRegister::UpdateValue (ExecutionContextScope *exe_scope)
{
    m_error.Clear();
    StackFrame *frame = exe_scope->CalculateStackFrame();
    if (frame)
    {
        m_reg_ctx = frame->GetRegisterContext();
        if (m_reg_ctx)
        {
            const RegisterInfo *reg_info = m_reg_ctx->GetRegisterInfoAtIndex(m_reg_num);
            if (m_reg_info != reg_info)
            {
                m_reg_info = reg_info;
                if (m_reg_info)
                {
                    if (m_reg_info->name)
                        m_name.SetCString(m_reg_info->name);
                    else if (m_reg_info->alt_name)
                        m_name.SetCString(m_reg_info->alt_name);
                }
            }
        }
    }
    else
    {
        m_reg_ctx = NULL;
        m_reg_info = NULL;
    }


    if (m_reg_ctx && m_reg_info)
    {
        if (m_reg_ctx->ReadRegisterBytes (m_reg_num, m_data))
        {
            m_value.SetContext(Value::eContextTypeDCRegisterInfo, (void *)m_reg_info);
            m_value.SetValueType(Value::eValueTypeHostAddress);
            m_value.GetScalar() = (uintptr_t)m_data.GetDataStart();
            SetValueIsValid (true);
            return;
        }
    }
    SetValueIsValid (false);
}


