Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

290 lines
10 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <hilti/rt/backtrace.h>
#include <hilti/rt/extension-points.h>
#include <hilti/rt/fmt.h>
namespace hilti::rt {
/**
* HILTI's base exception type. All HILTI-side runtime exceptions are derived
* from this. Instantiate specialized derived classes, not the base class.
*/
class Exception : public std::runtime_error {
public:
/**
* @param desc message describing the situation
*/
Exception(std::string_view desc) : Exception(Internal(), "Exception", desc) {
#ifndef NDEBUG
_backtrace = Backtrace();
#endif
}
/**
* @param desc message describing the situation
* @param location string indicating the location of the operation that failed
*/
Exception(std::string_view desc, std::string_view location) : Exception(Internal(), "Exception", desc, location) {
#ifndef NDEBUG
_backtrace = Backtrace();
#endif
}
Exception();
Exception(const Exception& other)
: std::runtime_error(other),
_description(other._description),
_location(other._location),
_backtrace(other._backtrace) {}
Exception(Exception&&) noexcept = default;
Exception& operator=(const Exception& other) = default;
Exception& operator=(Exception&&) noexcept = default;
// Empty, but required to make exception handling work between library
// and host application. See:
// http://www.toptip.ca/2012/06/c-exceptions-thrown-from-shared-library.html
~Exception() override;
/** Returns the message associated with the exception. */
const auto& description() const { return _description; }
/** Returns the location associated with the exception. */
const auto& location() const { return _location; }
/**
* Returns a stack backtrace captured at the time the exception was
* thrown, if available. Returns null if unavailable.
*/
const Backtrace* backtrace() const {
if ( ! _backtrace )
return nullptr;
return &*_backtrace;
}
protected:
enum Internal {};
Exception(Internal, const char* type, std::string_view desc);
Exception(Internal, const char* type, std::string_view desc, std::string_view location);
private:
Exception(Internal, const char* type, std::string_view what, std::string_view desc, std::string_view location);
std::string _description;
std::string _location;
std::optional<Backtrace> _backtrace; // null if unavailable.
};
inline std::ostream& operator<<(std::ostream& stream, const Exception& e) { return stream << e.what(); }
#define HILTI_EXCEPTION(name, base) \
class name : public ::hilti::rt::base { \
public: \
name(std::string_view desc) : base(Internal(), #name, desc) {} \
name(std::string_view desc, std::string_view location) : base(Internal(), #name, desc, location) {} \
virtual ~name(); /* required to create vtable, see hilti::rt::Exception */ \
protected: \
using base::base; \
}; // namespace hilti::rt
#define HILTI_EXCEPTION_NS(name, ns, base) \
class name : public ns::base { \
public: \
name(std::string_view desc) : base(Internal(), #name, desc) {} \
name(std::string_view desc, std::string_view location) : base(Internal(), #name, desc, location) {} \
virtual ~name(); /* required to create vtable, see hilti::rt::Exception */ \
protected: \
using base::base; \
};
#define HILTI_EXCEPTION_IMPL(name) name::name::~name() = default;
/** Base class for exceptions thrown during runtime when encountering unexpected input/situations. */
HILTI_EXCEPTION(RuntimeError, Exception)
/** Base class for exceptions indicating non-recoverable misuse of some functionality. */
HILTI_EXCEPTION(UsageError, Exception)
/** Base class for exceptions which can be recovered. */
HILTI_EXCEPTION(RecoverableFailure, RuntimeError)
/** Thrown when an `assert` statement fails. */
HILTI_EXCEPTION(AssertionFailure, RuntimeError)
/*
* Exception triggered y the ".?" operator to signal to host applications that
* a struct attribute isn't set.
*/
HILTI_EXCEPTION(AttributeNotSet, RuntimeError)
/**
* Exception triggered when a division by zero is attempted.
*/
HILTI_EXCEPTION(DivisionByZero, RuntimeError)
/** Thrown for trouble encountered while managing the runtime environment. */
HILTI_EXCEPTION(EnvironmentError, UsageError)
/** Exception indicating access to an already expired weak reference. **/
HILTI_EXCEPTION(ExpiredReference, RuntimeError)
/**
* Exception reflecting an attempt to modify a stream object that's been frozen.
*/
HILTI_EXCEPTION(Frozen, RuntimeError)
/** Exception indicating an undefined use of a reference type. */
HILTI_EXCEPTION(IllegalReference, RuntimeError)
/** Thrown when an invalid container index is accessed. */
HILTI_EXCEPTION(IndexError, RuntimeError)
/** Exception flagging invalid arguments passed to a function. */
HILTI_EXCEPTION(InvalidArgument, RuntimeError);
/** Exception flagging access to an iterator that not, or no longer, valid. */
HILTI_EXCEPTION(InvalidIterator, RuntimeError)
/** Exception flagging incorrect use of type-info values. */
HILTI_EXCEPTION(InvalidValue, RuntimeError);
/** Exception indicating illegal reuse of MatchState. **/
HILTI_EXCEPTION(MatchStateReuse, RuntimeError)
/** Exception indicating that the request data is missing. **/
HILTI_EXCEPTION(MissingData, RecoverableFailure);
/** Exception indicating use of unsupported matching capabilities. */
HILTI_EXCEPTION(NotSupported, RuntimeError)
/** Exception indicating access to an unset (null) reference. **/
HILTI_EXCEPTION(NullReference, RuntimeError)
/** Thrown when a value is found to be outside of its permissible range. */
HILTI_EXCEPTION(OutOfRange, RuntimeError)
/**
* Exception triggered when a numerical operation causes an overflow.
*/
HILTI_EXCEPTION(Overflow, RuntimeError)
/** Exception indicating trouble when compiling a regular expression. */
HILTI_EXCEPTION(PatternError, RuntimeError)
/** * Thrown when a default-less `switch` statement hits case that's no covered. */
HILTI_EXCEPTION(UnhandledSwitchCase, RuntimeError)
/** Exception indicating problems with UTF-8 encodings. **/
HILTI_EXCEPTION(UnicodeError, RuntimeError)
/**
* Exception reflecting an access to an unset optional value.
*/
HILTI_EXCEPTION(UnsetOptional, RuntimeError)
/**
* Exception reflecting an access to an unset tuple element.
*/
HILTI_EXCEPTION(UnsetTupleElement, RuntimeError)
/**
* Exception triggered by member access to fields that don't hold the value.
*/
HILTI_EXCEPTION(UnsetUnionMember, RuntimeError)
/**
* Exception triggered by the fiber code when running out of stack space.
*/
HILTI_EXCEPTION(StackSizeExceeded, RuntimeError)
/** Thrown when fmt() reports a problem. */
class FormattingError : public RuntimeError {
public:
FormattingError(std::string desc) : RuntimeError(_sanitize(std::move(desc))) {}
private:
std::string _sanitize(std::string desc) {
if ( auto pos = desc.find("tinyformat: "); pos != std::string::npos )
desc.erase(pos, 12);
return desc;
}
};
/**
* Exception signaling that an operation could not complete due to lack of
* input or I/O delays. The operation should be retried when that situation
* may have changed.
*
* This is outside the standard exception hierarchy as it does not reflect an
* error condition.
*/
class WouldBlock : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
/**
* @param desc message describing the situation
* @param location string indicating the location of the operation that couldn't complete
*/
WouldBlock(std::string_view desc, std::string_view location);
};
namespace exception {
// Disables `Configuration::abort_on_exception` during its lifetime.
class DisableAbortOnExceptions {
public:
DisableAbortOnExceptions();
~DisableAbortOnExceptions();
DisableAbortOnExceptions(const DisableAbortOnExceptions&) = delete;
DisableAbortOnExceptions(DisableAbortOnExceptions&&) noexcept = delete;
DisableAbortOnExceptions& operator=(const DisableAbortOnExceptions&) = delete;
DisableAbortOnExceptions& operator=(DisableAbortOnExceptions&&) noexcept = delete;
};
/** Utility function printing out an uncaught exception to stderr. */
void printUncaught(const Exception& e);
/** Utility function printing out an uncaught exception to an output stream. */
void printUncaught(const Exception& e, std::ostream& out);
/**
* Returns the message associated with an exception. The returned message does
* not include the location.
*/
inline std::string what(const Exception& e) { return e.description(); }
/**
* Returns the message associated with an exception. This is a fallback for
* standard exceptions that aren't ours.
*/
inline std::string what(const std::exception& e) { return e.what(); }
/** Returns the location associated with an exception. */
inline std::string where(const Exception& e) { return e.location(); }
} // namespace exception
namespace detail::adl {
inline std::string to_string(const Exception& e, adl::tag /*unused*/) { return fmt("<exception: %s>", e.what()); }
inline std::string to_string(const WouldBlock& e, adl::tag /*unused*/) { return fmt("<exception: %s>", e.what()); }
} // namespace detail::adl
} // namespace hilti::rt