290 lines
10 KiB
C++
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
|