errorhandler.h (7893B)
1 // This file is part of AsmJit project <https://asmjit.com> 2 // 3 // See <asmjit/core.h> or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #ifndef ASMJIT_CORE_ERRORHANDLER_H_INCLUDED 7 #define ASMJIT_CORE_ERRORHANDLER_H_INCLUDED 8 9 #include "../core/globals.h" 10 11 ASMJIT_BEGIN_NAMESPACE 12 13 //! \addtogroup asmjit_error_handling 14 //! \{ 15 16 class BaseEmitter; 17 18 //! Error handler can be used to override the default behavior of error handling. 19 //! 20 //! It's available to all classes that inherit `BaseEmitter`. Override \ref ErrorHandler::handle_error() to implement 21 //! your own error handler. 22 //! 23 //! The following use-cases are supported: 24 //! 25 //! - Record the error and continue code generation. This is the simplest approach that can be used to at least log 26 //! possible errors. 27 //! - Throw an exception. AsmJit doesn't use exceptions and is completely exception-safe, but it's perfectly legal 28 //! to throw an exception from the error handler. 29 //! - Use plain old C's `setjmp()` and `longjmp()`. Asmjit always puts Assembler, Builder and Compiler to 30 //! a consistent state before calling \ref handle_error(), so `longjmp()` can be used without issues to cancel the 31 //! code generation if an error occurred. This method can be used if exception handling in your project is turned 32 //! off and you still want some comfort. In most cases it should be safe as AsmJit uses \ref Arena allocator and 33 //! the ownership of allocated memory it allocates always ends with the instance that allocated it. If using this 34 //! approach please never jump outside the life-time of \ref CodeHolder and \ref BaseEmitter. 35 //! 36 //! \ref ErrorHandler can be attached to \ref CodeHolder or \ref BaseEmitter, which has a priority. The example below 37 //! uses error handler that just prints the error, but lets AsmJit continue: 38 //! 39 //! ``` 40 //! // Error Handling #1 - Logging and returning Error. 41 //! #include <asmjit/x86.h> 42 //! #include <stdio.h> 43 //! 44 //! using namespace asmjit; 45 //! 46 //! // Error handler that just prints the error and lets AsmJit ignore it. 47 //! class SimpleErrorHandler : public ErrorHandler { 48 //! public: 49 //! Error err; 50 //! 51 //! inline SimpleErrorHandler() : err(Error::kOk) {} 52 //! 53 //! void handle_error(Error err, const char* message, BaseEmitter* origin) override { 54 //! this->err = err; 55 //! fprintf(stderr, "ERROR: %s\n", message); 56 //! } 57 //! }; 58 //! 59 //! int main() { 60 //! JitRuntime rt; 61 //! SimpleErrorHandler eh; 62 //! 63 //! CodeHolder code; 64 //! code.init(rt.environment(), rt.cpu_features()); 65 //! code.set_error_handler(&eh); 66 //! 67 //! // Try to emit instruction that doesn't exist. 68 //! x86::Assembler a(&code); 69 //! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1); 70 //! 71 //! if (eh.err) { 72 //! // Assembler failed! 73 //! return 1; 74 //! } 75 //! 76 //! return 0; 77 //! } 78 //! ``` 79 //! 80 //! If error happens during instruction emitting / encoding the assembler behaves transactionally - the output buffer 81 //! won't advance if encoding failed, thus either a fully encoded instruction or nothing is emitted. The error handling 82 //! shown above is useful, but it's still not the best way of dealing with errors in AsmJit. The following example 83 //! shows how to use exception handling to handle errors in a more C++ way: 84 //! 85 //! ``` 86 //! // Error Handling #2 - Throwing an exception. 87 //! #include <asmjit/x86.h> 88 //! #include <exception> 89 //! #include <string> 90 //! #include <stdio.h> 91 //! 92 //! using namespace asmjit; 93 //! 94 //! // Error handler that throws a user-defined `AsmJitException`. 95 //! class AsmJitException : public std::exception { 96 //! public: 97 //! Error err; 98 //! std::string message; 99 //! 100 //! AsmJitException(Error err, const char* message) noexcept 101 //! : err(err), 102 //! message(message) {} 103 //! 104 //! const char* what() const noexcept override { return message.c_str(); } 105 //! }; 106 //! 107 //! class ThrowableErrorHandler : public ErrorHandler { 108 //! public: 109 //! // Throw is possible, functions that use ErrorHandler are never 'noexcept'. 110 //! void handle_error(Error err, const char* message, BaseEmitter* origin) override { 111 //! throw AsmJitException(err, message); 112 //! } 113 //! }; 114 //! 115 //! int main() { 116 //! JitRuntime rt; 117 //! ThrowableErrorHandler eh; 118 //! 119 //! CodeHolder code; 120 //! code.init(rt.environment(), rt.cpu_features()); 121 //! code.set_error_handler(&eh); 122 //! 123 //! x86::Assembler a(&code); 124 //! 125 //! // Try to emit instruction that doesn't exist. 126 //! try { 127 //! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1); 128 //! } 129 //! catch (const AsmJitException& ex) { 130 //! printf("EXCEPTION THROWN: %s\n", ex.what()); 131 //! return 1; 132 //! } 133 //! 134 //! return 0; 135 //! } 136 //! ``` 137 //! 138 //! If C++ exceptions are not what you like or your project turns off them completely there is still a way of reducing 139 //! the error handling to a minimum by using a standard setjmp/longjmp approach. AsmJit is exception-safe and cleans 140 //! up everything before calling the ErrorHandler, so any approach is safe. You can simply jump from the error handler 141 //! without causing any side-effects or memory leaks. The following example demonstrates how it could be done: 142 //! 143 //! ``` 144 //! // Error Handling #3 - Using setjmp/longjmp if exceptions are not allowed. 145 //! #include <asmjit/x86.h> 146 //! #include <setjmp.h> 147 //! #include <stdio.h> 148 //! 149 //! class LongJmpErrorHandler : public asmjit::ErrorHandler { 150 //! public: 151 //! inline LongJmpErrorHandler() : err(asmjit::Error::kOk) {} 152 //! 153 //! void handle_error(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override { 154 //! this->err = err; 155 //! longjmp(state, 1); 156 //! } 157 //! 158 //! jmp_buf state; 159 //! asmjit::Error err; 160 //! }; 161 //! 162 //! int main(int argc, char* argv[]) { 163 //! using namespace asmjit; 164 //! 165 //! JitRuntime rt; 166 //! LongJmpErrorHandler eh; 167 //! 168 //! CodeHolder code; 169 //! code.init(rt.environment(), rt.cpu_features()); 170 //! code.set_error_handler(&eh); 171 //! 172 //! x86::Assembler a(&code); 173 //! 174 //! if (!setjmp(eh.state)) { 175 //! // Try to emit instruction that doesn't exist. 176 //! a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1); 177 //! } 178 //! else { 179 //! Error err = eh.err; 180 //! printf("ASMJIT ERROR: 0x%08X [%s]\n", err, DebugUtils::error_as_string(err)); 181 //! } 182 //! 183 //! return 0; 184 //! } 185 //! ``` 186 class ASMJIT_VIRTAPI ErrorHandler { 187 public: 188 ASMJIT_BASE_CLASS(ErrorHandler) 189 190 //! \name Construction & Destruction 191 //! \{ 192 193 //! Creates a new `ErrorHandler` instance. 194 ASMJIT_API ErrorHandler() noexcept; 195 //! Destroys the `ErrorHandler` instance. 196 ASMJIT_API virtual ~ErrorHandler() noexcept; 197 198 //! \} 199 200 //! \name Interface 201 //! \{ 202 203 //! Error handler (must be reimplemented). 204 //! 205 //! Error handler is called after an error happened and before it's propagated to the caller. There are multiple 206 //! ways how the error handler can be used: 207 //! 208 //! 1. User-based error handling without throwing exception or using C's`longjmp()`. This is for users that don't 209 //! use exceptions and want customized error handling. 210 //! 211 //! 2. Throwing an exception. AsmJit doesn't use exceptions and is completely exception-safe, but you can throw 212 //! exception from your error handler if this way is the preferred way of handling errors in your project. 213 //! 214 //! 3. Using plain old C's `setjmp()` and `longjmp()`. Asmjit always puts `BaseEmitter` to a consistent state before 215 //! calling `handle_error()` so `longjmp()` can be used without any issues to cancel the code generation if an 216 //! error occurred. There is no difference between exceptions and `longjmp()` from AsmJit's perspective, however, 217 //! never jump outside of `CodeHolder` and `BaseEmitter` scope as you would leak memory. 218 ASMJIT_API virtual void handle_error(Error err, const char* message, BaseEmitter* origin); 219 220 //! \} 221 }; 222 223 //! \} 224 225 ASMJIT_END_NAMESPACE 226 227 #endif // ASMJIT_CORE_ERRORHANDLER_H_INCLUDED 228