// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include #include #include #include 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; // 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("", e.what()); } inline std::string to_string(const WouldBlock& e, adl::tag /*unused*/) { return fmt("", e.what()); } } // namespace detail::adl } // namespace hilti::rt