⛏️ index : haiku.git

/*
 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "ValueNodeManager.h"

#include "AutoLocker.h"

#include "model/Thread.h"
#include "StackFrame.h"
#include "Team.h"
#include "TypeHandlerRoster.h"
#include "ValueNode.h"
#include "Variable.h"
#include "VariableValueNodeChild.h"


ValueNodeManager::ValueNodeManager(bool addFrameNodes)
	:
	fAddFrameNodes(addFrameNodes),
	fContainer(NULL),
	fStackFrame(NULL),
	fThread(NULL)
{
	SetStackFrame(NULL, NULL);
}


ValueNodeManager::~ValueNodeManager()
{
	SetStackFrame(NULL, NULL);
}


status_t
ValueNodeManager::SetStackFrame(Thread* thread,
	StackFrame* stackFrame)
{
	if (fContainer != NULL) {
		AutoLocker<ValueNodeContainer> containerLocker(fContainer);

		fContainer->RemoveListener(this);

		fContainer->RemoveAllChildren();
		containerLocker.Unlock();
		fContainer->ReleaseReference();
		fContainer = NULL;
	}

	fStackFrame = stackFrame;
	fThread = thread;

	if (fStackFrame == NULL)
		return B_OK;

	fContainer = new(std::nothrow) ValueNodeContainer;
	if (fContainer == NULL)
		return B_NO_MEMORY;

	status_t error = fContainer->Init();
	if (error != B_OK) {
		delete fContainer;
		fContainer = NULL;
		return error;
	}

	AutoLocker<ValueNodeContainer> containerLocker(fContainer);

	fContainer->AddListener(this);

	if (fStackFrame != NULL && fAddFrameNodes) {
		for (int32 i = 0; Variable* variable = fStackFrame->ParameterAt(i);
				i++) {
			_AddNode(variable);
		}

		for (int32 i = 0; Variable* variable
				= fStackFrame->LocalVariableAt(i); i++) {
			_AddNode(variable);
		}
	}

	return B_OK;
}


bool
ValueNodeManager::AddListener(ValueNodeContainer::Listener* listener)
{
	return fListeners.AddItem(listener);
}


void
ValueNodeManager::RemoveListener(ValueNodeContainer::Listener* listener)
{
	fListeners.RemoveItem(listener);
}


void
ValueNodeManager::ValueNodeChanged(ValueNodeChild* nodeChild,
	ValueNode* oldNode, ValueNode* newNode)
{
	if (fContainer == NULL)
		return;

	AutoLocker<ValueNodeContainer> containerLocker(fContainer);

	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
		fListeners.ItemAt(i)->ValueNodeChanged(nodeChild, oldNode, newNode);

	if (oldNode != NULL && !newNode->ChildCreationNeedsValue())
		newNode->CreateChildren(fThread->GetTeam()->GetTeamTypeInformation());
}


void
ValueNodeManager::ValueNodeChildrenCreated(ValueNode* node)
{
	if (fContainer == NULL)
		return;

	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
		fListeners.ItemAt(i)->ValueNodeChildrenCreated(node);
}


void
ValueNodeManager::ValueNodeChildrenDeleted(ValueNode* node)
{
	if (fContainer == NULL)
		return;

	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
		fListeners.ItemAt(i)->ValueNodeChildrenDeleted(node);
}


void
ValueNodeManager::ValueNodeValueChanged(ValueNode* valueNode)
{
	if (fContainer == NULL)
		return;

	AutoLocker<ValueNodeContainer> containerLocker(fContainer);

	// check whether we know the node
	ValueNodeChild* nodeChild = valueNode->NodeChild();
	if (nodeChild == NULL)
		return;

	if (valueNode->ChildCreationNeedsValue()
		&& !valueNode->ChildrenCreated()) {
		status_t error = valueNode->CreateChildren(
			fThread->GetTeam()->GetTeamTypeInformation());
		if (error == B_OK) {
			for (int32 i = 0; i < valueNode->CountChildren(); i++) {
				ValueNodeChild* child = valueNode->ChildAt(i);
				_CreateValueNode(child);
				AddChildNodes(child);
			}
		}
	}

	for (int32 i = fListeners.CountItems() - 1; i >= 0; i--)
		fListeners.ItemAt(i)->ValueNodeValueChanged(valueNode);
}


void
ValueNodeManager::_AddNode(Variable* variable)
{
	// create the node child for the variable
	ValueNodeChild* nodeChild = new (std::nothrow) VariableValueNodeChild(
		variable);
	BReference<ValueNodeChild> nodeChildReference(nodeChild, true);
	if (nodeChild == NULL || !fContainer->AddChild(nodeChild)) {
		delete nodeChild;
		return;
	}

	// automatically add child nodes for the top level nodes
	AddChildNodes(nodeChild);
}


status_t
ValueNodeManager::_CreateValueNode(ValueNodeChild* nodeChild)
{
	if (nodeChild->Node() != NULL)
		return B_OK;

	// create the node
	ValueNode* valueNode;
	status_t error;
	if (nodeChild->IsInternal()) {
		error = nodeChild->CreateInternalNode(valueNode);
	} else {
		error = TypeHandlerRoster::Default()->CreateValueNode(nodeChild,
			nodeChild->GetType(), NULL, valueNode);
	}

	if (error != B_OK)
		return error;

	nodeChild->SetNode(valueNode);
	valueNode->ReleaseReference();

	return B_OK;
}


status_t
ValueNodeManager::AddChildNodes(ValueNodeChild* nodeChild)
{
	AutoLocker<ValueNodeContainer> containerLocker(fContainer);

	// create a value node for the value node child, if doesn't have one yet
	ValueNode* valueNode = nodeChild->Node();
	if (valueNode == NULL) {
		status_t error = _CreateValueNode(nodeChild);
		if (error != B_OK)
			return error;
		valueNode = nodeChild->Node();
	}

	// check if this node requires child creation
	// to be deferred until after its location/value have been resolved
	if (valueNode->ChildCreationNeedsValue())
		return B_OK;

	// create the children, if not done yet
	if (valueNode->ChildrenCreated())
		return B_OK;

	return valueNode->CreateChildren(
		fThread->GetTeam()->GetTeamTypeInformation());
}