/*
 * Copyright 2012, Rene Gollent, rene@gollent.com.
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "DwarfStackFrameDebugInfo.h"
 
#include <new>
 
#include "Architecture.h"
#include "CompilationUnit.h"
#include "CpuState.h"
#include "DebugInfoEntries.h"
#include "Dwarf.h"
#include "DwarfFile.h"
#include "DwarfTargetInterface.h"
#include "DwarfTypeFactory.h"
#include "DwarfUtils.h"
#include "DwarfTypes.h"
#include "FunctionID.h"
#include "FunctionParameterID.h"
#include "GlobalTypeLookup.h"
#include "LocalVariableID.h"
#include "Register.h"
#include "RegisterMap.h"
#include "ReturnValueID.h"
#include "StringUtils.h"
#include "Tracing.h"
#include "ValueLocation.h"
#include "Variable.h"
 
 
// #pragma mark - DwarfFunctionParameterID
 
 
struct DwarfStackFrameDebugInfo::DwarfFunctionParameterID
	: public FunctionParameterID {
 
	DwarfFunctionParameterID(FunctionID* functionID, const BString& name)
		:
		fFunctionID(functionID),
		fName(name)
	{
		fFunctionID->AcquireReference();
	}
 
	virtual ~DwarfFunctionParameterID()
	{
		fFunctionID->ReleaseReference();
	}
 
	virtual bool operator==(const ObjectID& other) const
	{
		const DwarfFunctionParameterID* parameterID
			= dynamic_cast<const DwarfFunctionParameterID*>(&other);
		return parameterID != NULL && *fFunctionID == *parameterID->fFunctionID
			&& fName == parameterID->fName;
	}
 
protected:
	virtual uint32 ComputeHashValue() const
	{
		uint32 hash = fFunctionID->HashValue();
		return hash * 19 + StringUtils::HashValue(fName);
	}
 
private:
	FunctionID*		fFunctionID;
	const BString	fName;
};
 
 
// #pragma mark - DwarfLocalVariableID
 
 
struct DwarfStackFrameDebugInfo::DwarfLocalVariableID : public LocalVariableID {
 
	DwarfLocalVariableID(FunctionID* functionID, const BString& name,
		int32 line, int32 column)
		:
		fFunctionID(functionID),
		fName(name),
		fLine(line),
		fColumn(column)
	{
		fFunctionID->AcquireReference();
	}
 
	virtual ~DwarfLocalVariableID()
	{
		fFunctionID->ReleaseReference();
	}
 
	virtual bool operator==(const ObjectID& other) const
	{
		const DwarfLocalVariableID* otherID
			= dynamic_cast<const DwarfLocalVariableID*>(&other);
		return otherID != NULL && *fFunctionID == *otherID->fFunctionID
			&& fName == otherID->fName && fLine == otherID->fLine
			&& fColumn == otherID->fColumn;
	}
 
protected:
	virtual uint32 ComputeHashValue() const
	{
		uint32 hash = fFunctionID->HashValue();
		hash = hash * 19 + StringUtils::HashValue(fName);
		hash = hash * 19 + fLine;
		hash = hash * 19 + fColumn;
		return hash;
	}
 
private:
	FunctionID*		fFunctionID;
	const BString	fName;
	int32			fLine;
	int32			fColumn;
};
 
 
// #pragma mark - DwarfReturnValueID
 
 
struct DwarfStackFrameDebugInfo::DwarfReturnValueID
	: public ReturnValueID {
 
	DwarfReturnValueID(FunctionID* functionID)
		:
		fFunctionID(functionID),
		fName("(returned)")
	{
		fFunctionID->AcquireReference();
	}
 
	virtual ~DwarfReturnValueID()
	{
		fFunctionID->ReleaseReference();
	}
 
	virtual bool operator==(const ObjectID& other) const
	{
		const DwarfReturnValueID* returnValueID
			= dynamic_cast<const DwarfReturnValueID*>(&other);
		return returnValueID != NULL
			&& *fFunctionID == *returnValueID->fFunctionID
			&& fName == returnValueID->fName;
	}
 
protected:
	virtual uint32 ComputeHashValue() const
	{
		uint32 hash = fFunctionID->HashValue();
		return hash * 25 + StringUtils::HashValue(fName);
	}
 
private:
	FunctionID*		fFunctionID;
	const BString	fName;
};
 
 
// #pragma mark - DwarfStackFrameDebugInfo
 
 
DwarfStackFrameDebugInfo::DwarfStackFrameDebugInfo(Architecture* architecture,
	image_id imageID, DwarfFile* file, CompilationUnit* compilationUnit,
	DIESubprogram* subprogramEntry, GlobalTypeLookup* typeLookup,
	GlobalTypeCache* typeCache, target_addr_t instructionPointer,
	target_addr_t framePointer, target_addr_t relocationDelta,
	DwarfTargetInterface* targetInterface, RegisterMap* fromDwarfRegisterMap)
	:
	StackFrameDebugInfo(),
	fTypeContext(new(std::nothrow) DwarfTypeContext(architecture, imageID, file,
		compilationUnit, subprogramEntry, instructionPointer, framePointer,
		relocationDelta, targetInterface, fromDwarfRegisterMap)),
	fTypeLookup(typeLookup),
	fTypeCache(typeCache)
{
	fTypeCache->AcquireReference();
}
 
 
DwarfStackFrameDebugInfo::~DwarfStackFrameDebugInfo()
{
	fTypeCache->ReleaseReference();
 
	if (fTypeContext != NULL)
		fTypeContext->ReleaseReference();
 
	delete fTypeFactory;
}
 
 
status_t
DwarfStackFrameDebugInfo::Init()
{
	if (fTypeContext == NULL)
		return B_NO_MEMORY;
 
	// create a type context without dependency to the stack frame
	DwarfTypeContext* typeContext = new(std::nothrow) DwarfTypeContext(
		fTypeContext->GetArchitecture(), fTypeContext->ImageID(),
		fTypeContext->File(), fTypeContext->GetCompilationUnit(), NULL, 0, 0,
		fTypeContext->RelocationDelta(), fTypeContext->TargetInterface(),
		fTypeContext->FromDwarfRegisterMap());
	if (typeContext == NULL)
		return B_NO_MEMORY;
	BReference<DwarfTypeContext> typeContextReference(typeContext, true);
 
	// create the type factory
	fTypeFactory = new(std::nothrow) DwarfTypeFactory(typeContext, fTypeLookup,
		fTypeCache);
	if (fTypeFactory == NULL)
		return B_NO_MEMORY;
 
	return B_OK;
}
 
 
status_t
DwarfStackFrameDebugInfo::CreateParameter(FunctionID* functionID,
	DIEFormalParameter* parameterEntry, Variable*& _parameter)
{
	// get the name
	BString name;
	DwarfUtils::GetDIEName(parameterEntry, name);
 
	TRACE_LOCALS("DwarfStackFrameDebugInfo::CreateParameter(DIE: %p): name: "
		"\"%s\"\n", parameterEntry, name.String());
 
	// create the ID
	DwarfFunctionParameterID* id = new(std::nothrow) DwarfFunctionParameterID(
		functionID, name);
	if (id == NULL)
		return B_NO_MEMORY;
	BReference<DwarfFunctionParameterID> idReference(id, true);
 
	// create the variable
	return _CreateVariable(id, name, _GetDIEType(parameterEntry),
		parameterEntry->GetLocationDescription(), _parameter);
}
 
 
status_t
DwarfStackFrameDebugInfo::CreateLocalVariable(FunctionID* functionID,
	DIEVariable* variableEntry, Variable*& _variable)
{
	// get the name
	BString name;
	DwarfUtils::GetDIEName(variableEntry, name);
 
	TRACE_LOCALS("DwarfStackFrameDebugInfo::CreateLocalVariable(DIE: %p): "
		"name: \"%s\"\n", variableEntry, name.String());
 
	// get the declaration location
	int32 line = -1;
	int32 column = -1;
	const char* file;
	const char* directory;
	DwarfUtils::GetDeclarationLocation(fTypeContext->File(), variableEntry,
		directory, file, line, column);
		// TODO: If the declaration location is unavailable, we should probably
		// add a component to the ID to make it unique nonetheless (the name
		// might not suffice).
 
	// create the ID
	DwarfLocalVariableID* id = new(std::nothrow) DwarfLocalVariableID(
		functionID, name, line, column);
	if (id == NULL)
		return B_NO_MEMORY;
	BReference<DwarfLocalVariableID> idReference(id, true);
 
	// create the variable
	return _CreateVariable(id, name, _GetDIEType(variableEntry),
		variableEntry->GetLocationDescription(), _variable);
}
 
 
status_t
DwarfStackFrameDebugInfo::CreateReturnValue(FunctionID* functionID,
	DIEType* returnType, ValueLocation* location, CpuState* state,
	Variable*& _variable)
{
	if (returnType == NULL)
		return B_BAD_VALUE;
 
	// create the type
	DwarfType* type;
	status_t error = fTypeFactory->CreateType(returnType, type);
	if (error != B_OK)
		return error;
	BReference<DwarfType> typeReference(type, true);
 
	DwarfReturnValueID* id = new(std::nothrow) DwarfReturnValueID(
		functionID);
	if (id == NULL)
		return B_NO_MEMORY;
 
	BString name;
	name.SetToFormat("%s returned", functionID->FunctionName().String());
 
	Variable* variable = new(std::nothrow) Variable(id, name,
		type, location, state);
	if (variable == NULL)
		return B_NO_MEMORY;
 
	_variable = variable;
 
	return B_OK;
}
 
 
status_t
DwarfStackFrameDebugInfo::_CreateVariable(ObjectID* id, const BString& name,
	DIEType* typeEntry, LocationDescription* locationDescription,
	Variable*& _variable)
{
	if (typeEntry == NULL)
		return B_BAD_VALUE;
 
	// create the type
	DwarfType* type;
	status_t error = fTypeFactory->CreateType(typeEntry, type);
	if (error != B_OK)
		return error;
	BReference<DwarfType> typeReference(type, true);
 
	// get the location, if possible
	ValueLocation* location = new(std::nothrow) ValueLocation(
		fTypeContext->GetArchitecture()->IsBigEndian());
	if (location == NULL)
		return B_NO_MEMORY;
	BReference<ValueLocation> locationReference(location, true);
 
	if (locationDescription->IsValid()) {
		status_t error = type->ResolveLocation(fTypeContext,
			locationDescription, 0, false, *location);
		if (error != B_OK)
			return error;
 
		TRACE_LOCALS_ONLY(location->Dump());
	}
 
	// create the variable
	Variable* variable = new(std::nothrow) Variable(id, name, type, location);
	if (variable == NULL)
		return B_NO_MEMORY;
 
	_variable = variable;
	return B_OK;
}
 
 
template<typename EntryType>
/*static*/ DIEType*
DwarfStackFrameDebugInfo::_GetDIEType(EntryType* entry)
{
	if (DIEType* typeEntry = entry->GetType())
		return typeEntry;
 
	if (EntryType* abstractOrigin = dynamic_cast<EntryType*>(
			entry->AbstractOrigin())) {
		entry = abstractOrigin;
		if (DIEType* typeEntry = entry->GetType())
			return typeEntry;
	}
 
	if (EntryType* specification = dynamic_cast<EntryType*>(
			entry->Specification())) {
		entry = specification;
		if (DIEType* typeEntry = entry->GetType())
			return typeEntry;
	}
 
	return NULL;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTypeFactory.