root/src/kits/debugger/model/DisassembledCode.cpp
/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "DisassembledCode.h"

#include <stdlib.h>
#include <string.h>

#include <new>

#include <String.h>

#include "SourceLanguage.h"
#include "Statement.h"


struct DisassembledCode::Line {
        BString                                 line;
        ContiguousStatement*    statement;

        Line(const BString& line, ContiguousStatement* statement)
                :
                line(line),
                statement(statement)
        {
        }
};


DisassembledCode::DisassembledCode(SourceLanguage* language)
        :
        fLanguage(language),
        fLines(20)
{
        fLanguage->AcquireReference();
}


DisassembledCode::~DisassembledCode()
{
        for (int32 i = 0; Statement* statement = fStatements.ItemAt(i); i++)
                statement->ReleaseReference();

        fLanguage->ReleaseReference();
}


bool
DisassembledCode::Lock()
{
        // We're immutable, so no locking required.
        return true;
}


void
DisassembledCode::Unlock()
{
}


SourceLanguage*
DisassembledCode::GetSourceLanguage() const
{
        return fLanguage;
}


int32
DisassembledCode::CountLines() const
{
        return fLines.CountItems();
}


const char*
DisassembledCode::LineAt(int32 index) const
{
        Line* line = fLines.ItemAt(index);
        return line != NULL ? line->line.String() : NULL;
}


int32
DisassembledCode::LineLengthAt(int32 index) const
{
        Line* line = fLines.ItemAt(index);
        return line != NULL ? line->line.Length() : 0;
}


bool
DisassembledCode::GetStatementLocationRange(const SourceLocation& location,
        SourceLocation& _start, SourceLocation& _end) const
{
        Line* line = fLines.ItemAt(location.Line());
        if (line == NULL || line->statement == NULL)
                return false;

        _start = line->statement->StartSourceLocation();
        _end = SourceLocation(_start.Line() + 1);
                // TODO: Multi-line instructions!
        return true;
}


LocatableFile*
DisassembledCode::GetSourceFile() const
{
        return NULL;
}


Statement*
DisassembledCode::StatementAtLocation(const SourceLocation& location) const
{
        Line* line = fLines.ItemAt(location.Line());
        return line != NULL ? line->statement : NULL;
}


Statement*
DisassembledCode::StatementAtAddress(target_addr_t address) const
{
        return fStatements.BinarySearchByKey(address, &_CompareAddressStatement);
}


TargetAddressRange
DisassembledCode::StatementAddressRange() const
{
        if (fStatements.IsEmpty())
                return TargetAddressRange();

        ContiguousStatement* first = fStatements.ItemAt(0);
        ContiguousStatement* last
                = fStatements.ItemAt(fStatements.CountItems() - 1);
        return TargetAddressRange(first->AddressRange().Start(),
                last->AddressRange().End());
}


bool
DisassembledCode::AddCommentLine(const BString& line)
{
        return _AddLine(line, NULL);
}


bool
DisassembledCode::AddInstructionLine(const BString& line, target_addr_t address,
        target_size_t size)
{
        int32 lineIndex = fLines.CountItems();

        ContiguousStatement* statement = new(std::nothrow) ContiguousStatement(
                SourceLocation(lineIndex), TargetAddressRange(address, size));
        if (statement == NULL)
                return false;

        if (!fStatements.AddItem(statement)) {
                delete statement;
                return false;
        }

        if (!_AddLine(line, statement))
                return false;

        return true;
}


bool
DisassembledCode::_AddLine(const BString& _line, ContiguousStatement* statement)
{
        Line* line = new(std::nothrow) Line(_line, statement);
        if (line == NULL)
                return false;

        if (!fLines.AddItem(line)) {
                delete line;
                return false;
        }

        return true;
}


/*static*/ int
DisassembledCode::_CompareAddressStatement(const target_addr_t* address,
        const ContiguousStatement* statement)
{
        const TargetAddressRange& range = statement->AddressRange();

        if (*address < range.Start())
                return -1;
        return *address < range.End() ? 0 : 1;
}