⛏️ index : haiku.git

/*
 * Copyright 2011-2015, Rene Gollent, rene@gollent.com
 * Distributed under the terms of the MIT License.
 */


#include "BMessageValueNode.h"

#include <new>

#include <AutoDeleter.h>
#include <MessageAdapter.h>
#include <MessagePrivate.h>

#include "Architecture.h"
#include "StringValue.h"
#include "TeamTypeInformation.h"
#include "Tracing.h"
#include "Type.h"
#include "TypeLookupConstraints.h"
#include "ValueLoader.h"
#include "ValueLocation.h"
#include "ValueNodeContainer.h"


static const int64 kMaxStringSize = 64;


// #pragma mark - BMessageWhatNodeChild


class BMessageWhatNodeChild : public ValueNodeChild {
public:
	BMessageWhatNodeChild(BMessageValueNode* parent, DataMember* member,
		Type* type)
		:
		ValueNodeChild(),
		fMember(member),
		fName("what"),
		fParent(parent),
		fType(type)
	{
		fParent->AcquireReference();
		fType->AcquireReference();
	}

	virtual ~BMessageWhatNodeChild()
	{
		fParent->ReleaseReference();
		fType->ReleaseReference();
	}

	virtual const BString& Name() const
	{
		return fName;
	}

	virtual Type* GetType() const
	{
		return fType;
	}

	virtual ValueNode* Parent() const
	{
		return fParent;
	}

	virtual status_t ResolveLocation(ValueLoader* valueLoader,
		ValueLocation*& _location)
	{
		ValueLocation* parentLocation = fParent->Location();
		ValueLocation* location;
		CompoundType* type = dynamic_cast<CompoundType*>(fParent->GetType());
		status_t error = B_OK;
		if (fParent->fIsFlatMessage) {
			location = new ValueLocation();
			if (location == NULL)
				return B_NO_MEMORY;

			ValuePieceLocation piece;
			piece.SetToMemory(parentLocation->PieceAt(0).address
				+ sizeof(uint32));
			piece.SetSize(sizeof(uint32));
			location->AddPiece(piece);
		} else {
			error = type->ResolveDataMemberLocation(fMember,
				*parentLocation, location);
		}

		if (error != B_OK)
			return error;

		_location = location;
		return B_OK;
	}

private:
	DataMember*			fMember;
	BString				fName;
	BMessageValueNode*	fParent;
	Type*				fType;
};


// #pragma mark - BMessageValueNode


BMessageValueNode::BMessageValueNode(ValueNodeChild* nodeChild,
	Type* type)
	:
	ValueNode(nodeChild),
	fType(type),
	fHeader(NULL),
	fFields(NULL),
	fData(NULL),
	fIsFlatMessage(false)
{
	fType->AcquireReference();
}


BMessageValueNode::~BMessageValueNode()
{
	fType->ReleaseReference();
	for (int32 i = 0; i < fChildren.CountItems(); i++)
		fChildren.ItemAt(i)->ReleaseReference();

	delete fHeader;
	delete[] fFields;
	delete[] fData;
}


Type*
BMessageValueNode::GetType() const
{
	return fType;
}


status_t
BMessageValueNode::ResolvedLocationAndValue(ValueLoader* valueLoader,
	ValueLocation*& _location, Value*& _value)
{
	fIsFlatMessage = dynamic_cast<BMessageFieldNodeChild*>(NodeChild())
		!= NULL;

	// get the location
	ValueLocation* location = NodeChild()->Location();
	if (location == NULL)
		return B_BAD_VALUE;


	// get the value type
	type_code valueType;
	if (valueLoader->GetArchitecture()->AddressSize() == 4) {
		valueType = B_UINT32_TYPE;
		TRACE_LOCALS("    -> 32 bit\n");
	} else {
		valueType = B_UINT64_TYPE;
		TRACE_LOCALS("    -> 64 bit\n");
	}

	// load the value data

	status_t error = B_OK;
	ValueLocation* memberLocation = NULL;

	BVariant headerAddress;
	BVariant fieldAddress;
	BVariant what;

	CompoundType* baseType = dynamic_cast<CompoundType*>(fType);

	if (fIsFlatMessage) {
		headerAddress.SetTo(location->PieceAt(0).address);
		fieldAddress.SetTo(headerAddress.ToUInt64()
			+ sizeof(BMessage::message_header));
	} else {
		for (int32 i = 0; i < baseType->CountDataMembers(); i++) {
			DataMember* member = baseType->DataMemberAt(i);
			if (strcmp(member->Name(), "fHeader") == 0) {
				error = baseType->ResolveDataMemberLocation(member,
					*location, memberLocation);
				BReference<ValueLocation> locationRef(memberLocation, true);
				if (error != B_OK) {
					TRACE_LOCALS(
						"BMessageValueNode::ResolvedLocationAndValue(): "
						"failed to resolve location of header member: %s\n",
						strerror(error));
					return error;
				}

				error = valueLoader->LoadValue(memberLocation, valueType,
					false, headerAddress);
				if (error != B_OK)
					return error;
			} else if (strcmp(member->Name(), "what") == 0) {
				error = baseType->ResolveDataMemberLocation(member,
					*location, memberLocation);
				BReference<ValueLocation> locationRef(memberLocation, true);
				if (error != B_OK) {
					TRACE_LOCALS(
						"BMessageValueNode::ResolvedLocationAndValue(): "
						"failed to resolve location of header member: %s\n",
							strerror(error));
					return error;
				}
				error = valueLoader->LoadValue(memberLocation, B_UINT32_TYPE,
					false, what);
				if (error != B_OK)
					return error;
			} else if (strcmp(member->Name(), "fFields") == 0) {
				error = baseType->ResolveDataMemberLocation(member,
					*location, memberLocation);
				BReference<ValueLocation> locationRef(memberLocation, true);
				if (error != B_OK) {
					TRACE_LOCALS(
						"BMessageValueNode::ResolvedLocationAndValue(): "
						"failed to resolve location of field member: %s\n",
							strerror(error));
					return error;
				}
				error = valueLoader->LoadValue(memberLocation, valueType,
					false, fieldAddress);
				if (error != B_OK)
					return error;
			} else if (strcmp(member->Name(), "fData") == 0) {
				error = baseType->ResolveDataMemberLocation(member,
					*location, memberLocation);
				BReference<ValueLocation> locationRef(memberLocation, true);
				if (error != B_OK) {
					TRACE_LOCALS(
						"BMessageValueNode::ResolvedLocationAndValue(): "
						"failed to resolve location of data member: %s\n",
							strerror(error));
					return error;
				}
				error = valueLoader->LoadValue(memberLocation, valueType,
					false, fDataLocation);
				if (error != B_OK)
					return error;
			}
			memberLocation = NULL;
		}
	}

	fHeader = new(std::nothrow) BMessage::message_header();
	if (fHeader == NULL)
		return B_NO_MEMORY;
	error = valueLoader->LoadRawValue(headerAddress, sizeof(
		BMessage::message_header), fHeader);
	TRACE_LOCALS("BMessage: Header Address: 0x%" B_PRIx64 ", result: %s\n",
		headerAddress.ToUInt64(), strerror(error));
	if (error != B_OK)
		return error;

	if (fHeader->format != MESSAGE_FORMAT_HAIKU
		|| (fHeader->flags & MESSAGE_FLAG_VALID) == 0)
		return B_NOT_A_MESSAGE;

	if (fIsFlatMessage)
		what.SetTo(fHeader->what);
	else
		fHeader->what = what.ToUInt32();

	TRACE_LOCALS("BMessage: what: 0x%" B_PRIx32 ", result: %s\n",
		what.ToUInt32(), strerror(error));

	size_t fieldsSize = fHeader->field_count * sizeof(
		BMessage::field_header);
	if (fIsFlatMessage)
		fDataLocation.SetTo(fieldAddress.ToUInt64() + fieldsSize);

	size_t totalSize = sizeof(BMessage::message_header) + fieldsSize
		+ fHeader->data_size;
	uint8* messageBuffer = new(std::nothrow) uint8[totalSize];
	if (messageBuffer == NULL)
		return B_NO_MEMORY;

	ArrayDeleter<uint8> deleter(messageBuffer);

	memset(messageBuffer, 0, totalSize);
	memcpy(messageBuffer, fHeader, sizeof(BMessage::message_header));
	uint8* tempBuffer = messageBuffer + sizeof(BMessage::message_header);
	if (fieldsSize > 0) {
		fFields = new(std::nothrow)
			BMessage::field_header[fHeader->field_count];
		if (fFields == NULL)
			return B_NO_MEMORY;

		error = valueLoader->LoadRawValue(fieldAddress, fieldsSize,
			fFields);
		TRACE_LOCALS("BMessage: Field Header Address: 0x%" B_PRIx64
			", result: %s\n",	headerAddress.ToUInt64(), strerror(error));
		if (error != B_OK)
			return error;

		fData = new(std::nothrow) uint8[fHeader->data_size];
		if (fData == NULL)
			return B_NO_MEMORY;

		error = valueLoader->LoadRawValue(fDataLocation, fHeader->data_size,
			fData);
		TRACE_LOCALS("BMessage: Data Address: 0x%" B_PRIx64
			", result: %s\n",	fDataLocation.ToUInt64(), strerror(error));
		if (error != B_OK)
			return error;
		memcpy(tempBuffer, fFields, fieldsSize);
		tempBuffer += fieldsSize;
		memcpy(tempBuffer, fData, fHeader->data_size);
	}

	error = fMessage.Unflatten((const char*)messageBuffer);
	if (error != B_OK)
		return error;

	location->AcquireReference();
	_location = location;
	_value = NULL;

	return B_OK;
}


status_t
BMessageValueNode::CreateChildren(TeamTypeInformation* info)
{
	DataMember* member = NULL;
	CompoundType* messageType = dynamic_cast<CompoundType*>(fType);
	for (int32 i = 0; i < messageType->CountDataMembers(); i++) {
		member = messageType->DataMemberAt(i);
		if (strcmp(member->Name(), "what") == 0) {
			ValueNodeChild* whatNode
				= new(std::nothrow) BMessageWhatNodeChild(this, member,
					member->GetType());
			if (whatNode == NULL)
				return B_NO_MEMORY;

			whatNode->SetContainer(fContainer);
			fChildren.AddItem(whatNode);
			break;
		}
	}

	char* name;
	type_code type;
	int32 count;
	Type* fieldType = NULL;
	BReference<Type> typeRef;
	for (int32 i = 0; fMessage.GetInfo(B_ANY_TYPE, i, &name, &type,
		&count) == B_OK; i++) {
		fieldType = NULL;

		_GetTypeForTypeCode(info, type, fieldType);
		if (fieldType != NULL)
			typeRef.SetTo(fieldType, true);

		BMessageFieldNodeChild* node = new(std::nothrow)
			BMessageFieldNodeChild(this,
				fieldType != NULL ? fieldType : fType, name, type,
				count);
		if (node == NULL)
			return B_NO_MEMORY;

		node->SetContainer(fContainer);
		fChildren.AddItem(node);
	}

	fChildrenCreated = true;

	if (fContainer != NULL)
		fContainer->NotifyValueNodeChildrenCreated(this);

	return B_OK;
}


int32
BMessageValueNode::CountChildren() const
{
	return fChildren.CountItems();
}


ValueNodeChild*
BMessageValueNode::ChildAt(int32 index) const
{
	return fChildren.ItemAt(index);
}


status_t
BMessageValueNode::_GetTypeForTypeCode(TeamTypeInformation* info,
	type_code type, Type*& _type)
{
	BString typeName;
	TypeLookupConstraints constraints;

	switch(type) {
		case B_BOOL_TYPE:
			typeName = "bool";
			constraints.SetTypeKind(TYPE_PRIMITIVE);
			break;

		case B_INT8_TYPE:
			typeName = "int8";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_UINT8_TYPE:
			typeName = "uint8";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_INT16_TYPE:
			typeName = "int16";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_UINT16_TYPE:
			typeName = "uint16";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_INT32_TYPE:
			typeName = "int32";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_UINT32_TYPE:
			typeName = "uint32";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_INT64_TYPE:
			typeName = "int64";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_UINT64_TYPE:
			typeName = "uint64";
			constraints.SetTypeKind(TYPE_TYPEDEF);
			break;

		case B_FLOAT_TYPE:
			typeName = "float";
			constraints.SetTypeKind(TYPE_PRIMITIVE);
			break;

		case B_DOUBLE_TYPE:
			typeName = "double";
			constraints.SetTypeKind(TYPE_PRIMITIVE);
			break;

		case B_MESSAGE_TYPE:
			typeName = "BMessage";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_MESSENGER_TYPE:
			typeName = "BMessenger";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_POINT_TYPE:
			typeName = "BPoint";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_RECT_TYPE:
			typeName = "BRect";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_REF_TYPE:
			typeName = "entry_ref";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_NODE_REF_TYPE:
			typeName = "node_ref";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_RGB_COLOR_TYPE:
			typeName = "rgb_color";
			constraints.SetTypeKind(TYPE_COMPOUND);
			break;

		case B_STRING_TYPE:
		{
			typeName = "char";
			constraints.SetTypeKind(TYPE_PRIMITIVE);
			Type* baseType = NULL;
			status_t result = info->LookupTypeByName(typeName, constraints,
				baseType);
			if (result != B_OK)
				return result;
			BReference<Type> typeReference(baseType, true);
			ArrayType* arrayType;
			result = baseType->CreateDerivedArrayType(0, kMaxStringSize, true,
				arrayType);
			if (result == B_OK)
				_type = arrayType;

			return result;
			break;
		}

		case B_POINTER_TYPE:
		default:
			typeName = "void*";
			constraints.SetTypeKind(TYPE_ADDRESS);
			break;
	}

	return info->LookupTypeByName(typeName, constraints, _type);
}


status_t
BMessageValueNode::_FindField(const char* name, type_code type,
	BMessage::field_header** result) const
{
	if (name == NULL)
		return B_BAD_VALUE;

	if (fHeader == NULL)
		return B_NO_INIT;

	if (fHeader->field_count == 0 || fFields == NULL || fData == NULL)
		return B_NAME_NOT_FOUND;

	uint32 hash = _HashName(name) % fHeader->hash_table_size;
	int32 nextField = fHeader->hash_table[hash];

	while (nextField >= 0) {
		BMessage::field_header* field = &fFields[nextField];
		if ((field->flags & FIELD_FLAG_VALID) == 0)
			break;

		if (strncmp((const char*)(fData + field->offset), name,
			field->name_length) == 0) {
			if (type != B_ANY_TYPE && field->type != type)
				return B_BAD_TYPE;

			*result = field;
			return B_OK;
		}

		nextField = field->next_field;
	}

	return B_NAME_NOT_FOUND;
}


uint32
BMessageValueNode::_HashName(const char* name) const
{
	char ch;
	uint32 result = 0;

	while ((ch = *name++) != 0) {
		result = (result << 7) ^ (result >> 24);
		result ^= ch;
	}

	result ^= result << 12;
	return result;
}


status_t
BMessageValueNode::_FindDataLocation(const char* name, type_code type,
	int32 index, ValueLocation& location) const
{
	BMessage::field_header* field = NULL;
	int32 offset = 0;
	int32 size = 0;
	status_t result = _FindField(name, type, &field);
	if (result != B_OK)
		return result;

	if (index < 0 || (uint32)index >= field->count)
		return B_BAD_INDEX;

	if ((field->flags & FIELD_FLAG_FIXED_SIZE) != 0) {
		size = field->data_size / field->count;
		offset = field->offset + field->name_length + index * size;
	} else {
		offset = field->offset + field->name_length;
		uint8 *pointer = fData + field->offset + field->name_length;
		for (int32 i = 0; i < index; i++) {
			pointer += *(uint32*)pointer + sizeof(uint32);
			offset += *(uint32*)pointer + sizeof(uint32);
		}

		size = *(uint32*)pointer;
		offset += sizeof(uint32);
	}

	ValuePieceLocation piece;
	piece.SetToMemory(fDataLocation.ToUInt64() + offset);
	piece.SetSize(size);
	location.Clear();
	location.AddPiece(piece);

	return B_OK;
}


// #pragma mark - BMessageValueNode::BMessageFieldNode


BMessageValueNode::BMessageFieldNode::BMessageFieldNode(
	BMessageFieldNodeChild *child, BMessageValueNode* parent,
	const BString &name, type_code type, int32 count)
	:
	ValueNode(child),
	fName(name),
	fType(parent->GetType()),
	fParent(parent),
	fFieldType(type),
	fFieldCount(count)
{
	fParent->AcquireReference();
	fType->AcquireReference();
}


BMessageValueNode::BMessageFieldNode::~BMessageFieldNode()
{
	fParent->ReleaseReference();
	fType->ReleaseReference();
}


Type*
BMessageValueNode::BMessageFieldNode::GetType() const
{
	return fType;
}


status_t
BMessageValueNode::BMessageFieldNode::CreateChildren(TeamTypeInformation* info)
{
	Type* type = NULL;
	status_t error = fParent->_GetTypeForTypeCode(info, fFieldType, type);
	if (error != B_OK)
		return error;

	BReference<Type> typeRef(type, true);
	for (int32 i = 0; i < fFieldCount; i++) {
		BMessageFieldNodeChild* child = new(std::nothrow)
			BMessageFieldNodeChild(fParent, type, fName, fFieldType,
				fFieldCount, i);

		if (child == NULL)
			return B_NO_MEMORY;

		if (fContainer != NULL)
			child->SetContainer(fContainer);

		fChildren.AddItem(child);
	}

	fChildrenCreated = true;

	if (fContainer != NULL)
		fContainer->NotifyValueNodeChildrenCreated(this);

	return B_OK;
}


int32
BMessageValueNode::BMessageFieldNode::CountChildren() const
{
	return fChildren.CountItems();
}

ValueNodeChild*
BMessageValueNode::BMessageFieldNode::ChildAt(int32 index) const
{
	return fChildren.ItemAt(index);
}


status_t
BMessageValueNode::BMessageFieldNode::ResolvedLocationAndValue(
	ValueLoader* loader, ValueLocation *& _location, Value*& _value)
{
	_location = NULL;
	_value = NULL;

	return B_OK;
}


// #pragma mark - BMessageValueNode::BMessageFieldNodeChild


BMessageValueNode::BMessageFieldNodeChild::BMessageFieldNodeChild(
	BMessageValueNode* parent, Type* nodeType, const BString &name,
	type_code type, int32 count, int32 index)
	:
	ValueNodeChild(),
	fName(name),
	fPresentationName(name),
	fType(nodeType),
	fParent(parent),
	fFieldType(type),
	fFieldCount(count),
	fFieldIndex(index)
{
	fParent->AcquireReference();
	fType->AcquireReference();

	if (fFieldIndex >= 0)
		fPresentationName.SetToFormat("[%" B_PRId32 "]", fFieldIndex);
}


BMessageValueNode::BMessageFieldNodeChild::~BMessageFieldNodeChild()
{
	fParent->ReleaseReference();
	fType->ReleaseReference();
}


const BString&
BMessageValueNode::BMessageFieldNodeChild::Name() const
{
	return fPresentationName;
}


Type*
BMessageValueNode::BMessageFieldNodeChild::GetType() const
{
	return fType;
}


ValueNode*
BMessageValueNode::BMessageFieldNodeChild::Parent() const
{
	return fParent;
}


bool
BMessageValueNode::BMessageFieldNodeChild::IsInternal() const
{
	return fFieldCount > 1 && fFieldIndex == -1;
}


status_t
BMessageValueNode::BMessageFieldNodeChild::CreateInternalNode(
	ValueNode*& _node)
{
	BMessageFieldNode* node = new(std::nothrow)
		BMessageFieldNode(this, fParent, fName, fFieldType, fFieldCount);
	if (node == NULL)
		return B_NO_MEMORY;

	_node = node;
	return B_OK;
}


status_t
BMessageValueNode::BMessageFieldNodeChild::ResolveLocation(
	ValueLoader* valueLoader, ValueLocation*& _location)
{
	_location = new(std::nothrow)ValueLocation();

	if (_location == NULL)
		return B_NO_MEMORY;

	return fParent->_FindDataLocation(fName, fFieldType, fFieldIndex >= 0
		? fFieldIndex : 0, *_location);
}