asmjit::ErrorHandler Class Referenceabstract

Error handler can be used to override the default behavior of error handling.

It's available to all classes that inherit BaseEmitter. Override ErrorHandler::handleError() to implement your own error handler.

The following use-cases are supported:

  • Record the error and continue code generation. This is the simplest approach that can be used to at least log possible errors.
  • Throw an exception. AsmJit doesn't use exceptions and is completely exception-safe, but it's perfectly legal to throw an exception from the error handler.
  • Use plain old C's setjmp() and longjmp(). Asmjit always puts Assembler, Builder and Compiler to a consistent state before calling handleError(), so longjmp() can be used without issues to cancel the code-generation if an error occurred. This method can be used if exception handling in your project is turned off and you still want some comfort. In most cases it should be safe as AsmJit uses Zone memory and the ownership of memory it allocates always ends with the instance that allocated it. If using this approach please never jump outside the life-time of CodeHolder and BaseEmitter.

ErrorHandler can be attached to CodeHolder or BaseEmitter, which has a priority. The example below uses error handler that just prints the error, but lets AsmJit continue:

// Error Handling #1 - Logging and returing Error.
#include <asmjit/x86.h>
#include <stdio.h>
using namespace asmjit;
// Error handler that just prints the error and lets AsmJit ignore it.
class SimpleErrorHandler : public ErrorHandler {
public:
Error err;
inline SimpleErrorHandler() : err(kErrorOk) {}
void handleError(Error err, const char* message, BaseEmitter* origin) override {
this->err = err;
fprintf(stderr, "ERROR: %s\n", message);
}
};
int main() {
SimpleErrorHandler eh;
CodeHolder code;
code.init(rt.environment());
code.setErrorHandler(&eh);
// Try to emit instruction that doesn't exist.
x86::Assembler a(&code);
a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
if (eh.err) {
// Assembler failed!
return 1;
}
return 0;
}

If error happens during instruction emitting / encoding the assembler behaves transactionally - the output buffer won't advance if encoding failed, thus either a fully encoded instruction or nothing is emitted. The error handling shown above is useful, but it's still not the best way of dealing with errors in AsmJit. The following example shows how to use exception handling to handle errors in a more C++ way:

// Error Handling #2 - Throwing an exception.
#include <asmjit/x86.h>
#include <exception>
#include <string>
#include <stdio.h>
using namespace asmjit;
// Error handler that throws a user-defined `AsmJitException`.
class AsmJitException : public std::exception {
public:
Error err;
std::string message;
AsmJitException(Error err, const char* message) noexcept
: err(err),
message(message) {}
const char* what() const noexcept override { return message.c_str(); }
};
class ThrowableErrorHandler : public ErrorHandler {
public:
// Throw is possible, functions that use ErrorHandler are never 'noexcept'.
void handleError(Error err, const char* message, BaseEmitter* origin) override {
throw AsmJitException(err, message);
}
};
int main() {
ThrowableErrorHandler eh;
CodeHolder code;
code.init(rt.environment());
code.setErrorHandler(&eh);
x86::Assembler a(&code);
// Try to emit instruction that doesn't exist.
try {
a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
}
catch (const AsmJitException& ex) {
printf("EXCEPTION THROWN: %s\n", ex.what());
return 1;
}
return 0;
}

If C++ exceptions are not what you like or your project turns off them completely there is still a way of reducing the error handling to a minimum by using a standard setjmp/longjmp approach. AsmJit is exception-safe and cleans up everything before calling the ErrorHandler, so any approach is safe. You can simply jump from the error handler without causing any side-effects or memory leaks. The following example demonstrates how it could be done:

// Error Handling #3 - Using setjmp/longjmp if exceptions are not allowed.
#include <asmjit/x86.h>
#include <setjmp.h>
#include <stdio.h>
class LongJmpErrorHandler : public asmjit::ErrorHandler {
public:
inline LongJmpErrorHandler() : err(asmjit::kErrorOk) {}
void handleError(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override {
this->err = err;
longjmp(state, 1);
}
jmp_buf state;
};
int main(int argc, char* argv[]) {
using namespace asmjit;
LongJmpErrorHandler eh;
CodeHolder code;
code.init(rt.rt.environment());
code.setErrorHandler(&eh);
x86::Assembler a(&code);
if (!setjmp(eh.state)) {
// Try to emit instruction that doesn't exist.
a.emit(x86::Inst::kIdMov, x86::xmm0, x86::xmm1);
}
else {
Error err = eh.err;
printf("ASMJIT ERROR: 0x%08X [%s]\n", err, DebugUtils::errorAsString(err));
}
return 0;
}

Member Functions

Constructor & Destructor Documentation

ErrorHandler::ErrorHandler()noexcept

Creates a new ErrorHandler instance.

ErrorHandler::~ErrorHandler()virtualnoexcept

Destroys the ErrorHandler instance.

Member Function Documentation

void ErrorHandler::handleError(Error err, const char* message, BaseEmitter* origin)pure virtual

Error handler (must be reimplemented).

Error handler is called after an error happened and before it's propagated to the caller. There are multiple ways how the error handler can be used:

  1. User-based error handling without throwing exception or using C's longjmp(). This is for users that don't use exceptions and want customized error handling.
  2. Throwing an exception. AsmJit doesn't use exceptions and is completely exception-safe, but you can throw exception from your error handler if this way is the preferred way of handling errors in your project.
  3. Using plain old C's setjmp() and longjmp(). Asmjit always puts BaseEmitter to a consistent state before calling handleError() so longjmp() can be used without any issues to cancel the code generation if an error occurred. There is no difference between exceptions and longjmp() from AsmJit's perspective, however, never jump outside of CodeHolder and BaseEmitter scope as you would leak memory.