Skip to content

Commit

Permalink
Refactor and document Error object
Browse files Browse the repository at this point in the history
  • Loading branch information
ZehMatt committed May 12, 2024
1 parent 4d21332 commit b46b76b
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 70 deletions.
42 changes: 31 additions & 11 deletions include/zasm/core/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

namespace zasm
{
namespace detail
{
struct ErrorData;
}

enum class ErrorCode : std::uint32_t
{
None = 0,
Expand Down Expand Up @@ -53,9 +48,7 @@ namespace zasm

public:
constexpr Error() noexcept = default;

Error(const Error& other);

Error(Error&& other) noexcept;

/// <summary>
Expand All @@ -74,15 +67,42 @@ namespace zasm

~Error() noexcept;

bool operator==(ErrorCode code) const noexcept;
bool operator!=(ErrorCode code) const noexcept;
Error& operator=(Error&& other) noexcept;

/// <summary>
/// Returns the assigned error code.
/// </summary>
/// <returns>Error code</returns>
ErrorCode getCode() const noexcept;

/// <summary>
/// Returns the error code name as a string.
/// The return value is never nullptr.
/// </summary>
/// <returns>Error name</returns>
const char* getErrorName() const noexcept;

/// <summary>
/// Get the error message if it was set. If the object was constructed without a message
/// this function will translate the error code into a generic message.
/// The return value is never nullptr.
/// </summary>
/// <returns>Error message</returns>
const char* getErrorMessage() const noexcept;

/// <summary>
/// Clears the error object which means that the error code will be set to ErrorCode::None.
/// If the object has an additional message it will be deallocated.
/// </summary>
void clear();

/// <summary>
/// Returns true if the error object is empty.
/// </summary>
bool empty() const noexcept;

bool operator==(ErrorCode code) const noexcept;
bool operator!=(ErrorCode code) const noexcept;
Error& operator=(Error&& other) noexcept;
Error& operator=(const Error& other) noexcept;
};

} // namespace zasm
169 changes: 110 additions & 59 deletions src/zasm/src/core/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,34 @@ namespace zasm
std::string message;
};

ErrorExt* toErrorExt(std::uint64_t data) noexcept
// TODO: This is not fully portable so we should check for systems where
// we have to use a different approach.
static ErrorExt* toErrorExt(std::uint64_t data) noexcept
{
assert((data & kErrorExtBit) != 0);

auto* ext = reinterpret_cast<ErrorExt*>(data & ~kErrorExtBit);
return ext;
}

static std::uint64_t toInternalErrorData(const ErrorExt* ext) noexcept
{
return static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(ext)) | kErrorExtBit;
}

static bool isExtError(std::uint64_t data) noexcept
{
return (data & kErrorExtBit) != 0;
}

Error::Error(const Error& other)
{
if (other._data & kErrorExtBit)
if (isExtError(other._data))
{
const auto* ext = toErrorExt(other._data);

auto* newExt = new ErrorExt(*ext);
_data = reinterpret_cast<std::uint64_t>(newExt) | kErrorExtBit;
_data = toInternalErrorData(newExt);
}
else
{
Expand Down Expand Up @@ -55,19 +67,29 @@ namespace zasm
ext->code = code;
ext->message = message;

_data = reinterpret_cast<std::uint64_t>(ext) | kErrorExtBit;
_data = toInternalErrorData(ext);
}

Error::~Error() noexcept
{
if (_data & kErrorExtBit)
clear();
}

void Error::clear()
{
if (isExtError(_data))
{
const auto* ext = toErrorExt(_data);
delete ext;
}
_data = 0;
}

bool Error::empty() const noexcept
{
return _data == 0;
}

bool Error::operator==(ErrorCode code) const noexcept
{
return getCode() == code;
Expand All @@ -80,26 +102,41 @@ namespace zasm

Error& Error::operator=(Error&& other) noexcept
{
if (_data & kErrorExtBit)
{
const auto* ext = toErrorExt(_data);
delete ext;
}
clear();

_data = other._data;
other._data = 0;

return *this;
}

Error& Error::operator=(const Error& other) noexcept
{
clear();

if (isExtError(other._data))
{
const auto* ext = toErrorExt(other._data);

auto* newExt = new ErrorExt(*ext);
_data = toInternalErrorData(newExt);
}
else
{
_data = other._data;
}

return *this;
}

static ErrorCode getErrorCode(std::uint64_t data) noexcept
{
if (data == 0)
{
return ErrorCode::None;
}

if (data & kErrorExtBit)
if (isExtError(data))
{
const auto* ext = toErrorExt(data);
return ext->code;
Expand Down Expand Up @@ -143,6 +180,9 @@ namespace zasm
ERROR_STRING(ErrorCode::ImpossibleInstruction);
ERROR_STRING(ErrorCode::EmptyState);
ERROR_STRING(ErrorCode::ImpossibleRelocation);
ERROR_STRING(ErrorCode::AddressOutOfRange);
ERROR_STRING(ErrorCode::InstructionTooLong);
ERROR_STRING(ErrorCode::ExternalLabelNotBindable);
default:
assert(false);
break;
Expand All @@ -156,63 +196,74 @@ namespace zasm
return getErrorCodeName(getCode());
}

static const char* getErrorCodeMessage(ErrorCode err) noexcept
{
// Give a default message for known error codes.
switch (err)
{
case ErrorCode::None:
return "No error";
case ErrorCode::InvalidMode:
return "Invalid mode";
case ErrorCode::NotInitialized:
return "Not initialized";
case ErrorCode::InvalidOperation:
return "Invalid operation";
case ErrorCode::InvalidParameter:
return "Invalid parameter";
case ErrorCode::FileNotFound:
return "File not found";
case ErrorCode::AccessDenied:
return "Access denied";
case ErrorCode::OutOfMemory:
return "Out of memory";
case ErrorCode::LabelNotFound:
return "Label not found";
case ErrorCode::UnresolvedLabel:
return "Unresolved label";
case ErrorCode::InvalidLabel:
return "Invalid label";
case ErrorCode::LabelAlreadyBound:
return "Label already bound";
case ErrorCode::SectionNotFound:
return "Section not found";
case ErrorCode::SectionAlreadyBound:
return "Section already bound";
case ErrorCode::SignatureMismatch:
return "Signature mismatch";
case ErrorCode::InvalidInstruction:
return "Invalid instruction";
case ErrorCode::OutOfBounds:
return "Out of bounds";
case ErrorCode::ImpossibleInstruction:
return "Impossible instruction";
case ErrorCode::EmptyState:
return "Empty state";
case ErrorCode::ImpossibleRelocation:
return "Impossible relocation";
case ErrorCode::ExternalLabelNotBindable:
return "External label can not be bound";
case ErrorCode::InstructionTooLong:
return "Instruction too long";
case ErrorCode::AddressOutOfRange:
return "Address out of range";
default:
break;
}
return "<invalid error code>";
}

const char* Error::getErrorMessage() const noexcept
{
if (_data & kErrorExtBit)
if (isExtError(_data))
{
const auto* ext = toErrorExt(_data);
return ext->message.c_str();
}
else
{
// Give a default message for known error codes.
switch (getCode())
{
case ErrorCode::None:
return "No error";
case ErrorCode::InvalidMode:
return "Invalid mode";
case ErrorCode::NotInitialized:
return "Not initialized";
case ErrorCode::InvalidOperation:
return "Invalid operation";
case ErrorCode::InvalidParameter:
return "Invalid parameter";
case ErrorCode::FileNotFound:
return "File not found";
case ErrorCode::AccessDenied:
return "Access denied";
case ErrorCode::OutOfMemory:
return "Out of memory";
case ErrorCode::LabelNotFound:
return "Label not found";
case ErrorCode::UnresolvedLabel:
return "Unresolved label";
case ErrorCode::InvalidLabel:
return "Invalid label";
case ErrorCode::LabelAlreadyBound:
return "Label already bound";
case ErrorCode::SectionNotFound:
return "Section not found";
case ErrorCode::SectionAlreadyBound:
return "Section already bound";
case ErrorCode::SignatureMismatch:
return "Signature mismatch";
case ErrorCode::InvalidInstruction:
return "Invalid instruction";
case ErrorCode::OutOfBounds:
return "Out of bounds";
case ErrorCode::ImpossibleInstruction:
return "Impossible instruction";
case ErrorCode::EmptyState:
return "Empty state";
case ErrorCode::ImpossibleRelocation:
return "Impossible relocation";
default:
break;
}
return getErrorCodeMessage(getCode());
}
return "";
}

} // namespace zasm

0 comments on commit b46b76b

Please sign in to comment.