root/src/kits/debugger/jobs/ExpressionEvaluationJob.cpp
/*
 * Copyright 2014-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */

#include "Jobs.h"

#include <String.h>

#include <AutoLocker.h>

#include "DebuggerInterface.h"
#include "ExpressionInfo.h"
#include "model/Thread.h"
#include "SourceLanguage.h"
#include "StackFrame.h"
#include "Team.h"
#include "Type.h"
#include "Value.h"
#include "ValueNode.h"
#include "ValueNodeManager.h"
#include "Variable.h"


ExpressionEvaluationJob::ExpressionEvaluationJob(Team* team,
        DebuggerInterface* debuggerInterface, SourceLanguage* language,
        ExpressionInfo* info, StackFrame* frame,
        ::Thread* thread)
        :
        fKey(info->Expression(), JOB_TYPE_EVALUATE_EXPRESSION),
        fTeam(team),
        fDebuggerInterface(debuggerInterface),
        fArchitecture(debuggerInterface->GetArchitecture()),
        fTypeInformation(team->GetTeamTypeInformation()),
        fLanguage(language),
        fExpressionInfo(info),
        fFrame(frame),
        fThread(thread),
        fManager(NULL),
        fResultValue(NULL)
{
        fLanguage->AcquireReference();
        fExpressionInfo->AcquireReference();

        if (fFrame != NULL)
                fFrame->AcquireReference();
        if (fThread != NULL)
                fThread->AcquireReference();
}


ExpressionEvaluationJob::~ExpressionEvaluationJob()
{
        fLanguage->ReleaseReference();
        fExpressionInfo->ReleaseReference();
        if (fFrame != NULL)
                fFrame->ReleaseReference();
        if (fThread != NULL)
                fThread->ReleaseReference();
        if (fManager != NULL)
                fManager->ReleaseReference();
        if (fResultValue != NULL)
                fResultValue->ReleaseReference();
}


const JobKey&
ExpressionEvaluationJob::Key() const
{
        return fKey;
}


status_t
ExpressionEvaluationJob::Do()
{
        BReference<Value> reference;
        status_t result = B_OK;
        if (fFrame != NULL && fManager == NULL) {
                fManager = new(std::nothrow) ValueNodeManager();
                if (fManager == NULL)
                        result = B_NO_MEMORY;
                else
                        result = fManager->SetStackFrame(fThread, fFrame);
        }

        if (result != B_OK) {
                fExpressionInfo->NotifyExpressionEvaluated(result, NULL);
                return result;
        }

        ValueNode* neededNode = NULL;
        result = fLanguage->EvaluateExpression(fExpressionInfo->Expression(),
                fManager, fTeam->GetTeamTypeInformation(), fResultValue, neededNode);
        if (neededNode != NULL) {
                result = ResolveNodeValue(neededNode);
                if (State() == JOB_STATE_WAITING)
                        return B_OK;
                // if result != B_OK, fall through
        }

        fExpressionInfo->NotifyExpressionEvaluated(result, fResultValue);

        return B_OK;
}


status_t
ExpressionEvaluationJob::ResolveNodeValue(ValueNode* node)
{
        AutoLocker<Worker> workerLocker(GetWorker());
        SimpleJobKey jobKey(node, JOB_TYPE_RESOLVE_VALUE_NODE_VALUE);

        status_t error = B_OK;
        if (GetWorker()->GetJob(jobKey) == NULL) {
                workerLocker.Unlock();

                // schedule the job
                error = GetWorker()->ScheduleJob(
                        new(std::nothrow) ResolveValueNodeValueJob(fDebuggerInterface,
                                fArchitecture, fThread->GetCpuState(), fTypeInformation,
                                fManager->GetContainer(), node));
                if (error != B_OK) {
                        // scheduling failed -- set the value to invalid
                        node->SetLocationAndValue(NULL, NULL, error);
                        return error;
                }
        }

        // wait for the job to finish
        workerLocker.Unlock();


        switch (WaitFor(jobKey)) {
                case JOB_DEPENDENCY_SUCCEEDED:
                case JOB_DEPENDENCY_NOT_FOUND:
                case JOB_DEPENDENCY_ACTIVE:
                        error = B_OK;
                        break;
                case JOB_DEPENDENCY_FAILED:
                case JOB_DEPENDENCY_ABORTED:
                default:
                        error = B_ERROR;
                        break;
        }

        return error;
}