// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.

// [Guard]
#ifndef _ASMJIT_BASE_ERROR_H
#define _ASMJIT_BASE_ERROR_H

// [Dependencies - AsmJit]
#include "../base/globals.h"

// [Api-Begin]
#include "../apibegin.h"

namespace asmjit {

//! \addtogroup asmjit_base_general
//! \{

// ============================================================================
// [asmjit::ErrorCode]
// ============================================================================

//! AsmJit error codes.
ASMJIT_ENUM(ErrorCode) {
  //! No error (success).
  //!
  //! This is default state and state you want.
  kErrorOk = 0,

  //! Heap memory allocation failed.
  kErrorNoHeapMemory = 1,

  //! Virtual memory allocation failed.
  kErrorNoVirtualMemory = 2,

  //! Invalid argument.
  kErrorInvalidArgument = 3,

  //! Invalid state.
  kErrorInvalidState = 4,

  //! No code generated.
  //!
  //! Returned by runtime if the code-generator contains no code.
  kErrorNoCodeGenerated = 5,

  //! Code generated is too large to fit in memory reserved.
  //!
  //! Returned by `StaticRuntime` in case that the code generated is too large
  //! to fit in the memory already reserved for it.
  kErrorCodeTooLarge = 6,

  //! Label is already bound.
  kErrorLabelAlreadyBound = 7,

  //! Unknown instruction (an instruction ID is out of bounds or instruction
  //! name is invalid).
  kErrorUnknownInst = 8,

  //! Illegal instruction.
  //!
  //! This status code can also be returned in X64 mode if AH, BH, CH or DH
  //! registers have been used together with a REX prefix. The instruction
  //! is not encodable in such case.
  //!
  //! Example of raising `kErrorIllegalInst` error.
  //!
  //! ~~~
  //! // Invalid address size.
  //! a.mov(dword_ptr(eax), al);
  //!
  //! // Undecodable instruction - AH used with R10, however R10 can only be
  //! // encoded by using REX prefix, which conflicts with AH.
  //! a.mov(byte_ptr(r10), ah);
  //! ~~~
  //!
  //! \note In debug mode assertion is raised instead of returning an error.
  kErrorIllegalInst = 9,

  //! Illegal (unencodable) addressing used.
  kErrorIllegalAddresing = 10,

  //! Illegal (unencodable) displacement used.
  //!
  //! X86/X64
  //! -------
  //!
  //! Short form of jump instruction has been used, but the displacement is out
  //! of bounds.
  kErrorIllegalDisplacement = 11,

  //! A variable has been assigned more than once to a function argument (Compiler).
  kErrorOverlappedArgs = 12,

  //! Count of AsmJit error codes.
  kErrorCount = 13
};

// ============================================================================
// [asmjit::Error]
// ============================================================================

//! AsmJit error type (unsigned integer).
typedef uint32_t Error;

// ============================================================================
// [asmjit::ErrorHandler]
// ============================================================================

//! Error handler.
//!
//! Error handler can be used to override the default behavior of `CodeGen`
//! error handling and propagation. See `handleError` on how to override it.
//!
//! Please note that `addRef` and `release` functions are used, but there is
//! no reference counting implemented by default, reimplement to change the
//! default behavior.
struct ASMJIT_VCLASS ErrorHandler {
  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! Create a new `ErrorHandler` instance.
  ASMJIT_API ErrorHandler();
  //! Destroy the `ErrorHandler` instance.
  ASMJIT_API virtual ~ErrorHandler();

  // --------------------------------------------------------------------------
  // [Interface]
  // --------------------------------------------------------------------------

  //! Reference this error handler.
  //!
  //! \note This member function is provided for convenience. The default
  //! implementation does nothing. If you are working in environment where
  //! multiple `ErrorHandler` instances are used by a different code generators
  //! you may provide your own functionality for reference counting. In that
  //! case `addRef()` and `release()` functions should be overridden.
  ASMJIT_API virtual ErrorHandler* addRef() const;

  //! Release this error handler.
  //!
  //! \note This member function is provided for convenience. See `addRef()`
  //! for more detailed information related to reference counting.
  ASMJIT_API virtual void release();

  //! Error handler (pure).
  //!
  //! Error handler is called when an error happened. An error can happen in
  //! many places, but error handler is mostly used by `Assembler` and
  //! `Compiler` classes to report anything that may cause incorrect code
  //! generation. There are multiple ways how the error handler can be used
  //! and each has it's pros/cons.
  //!
  //! AsmJit library doesn't use exceptions and can be compiled with or without
  //! exception handling support. Even if the AsmJit library is compiled without
  //! exceptions it is exception-safe and handleError() can report an incoming
  //! error by throwing an exception of any type. It's guaranteed that the
  //! exception won't be catched by AsmJit and will be propagated to the code
  //! calling AsmJit `Assembler` or `Compiler` methods. Alternative to
  //! throwing an exception is using `setjmp()` and `longjmp()` pair available
  //! in the standard C library.
  //!
  //! If the exception or setjmp() / longjmp() mechanism is used, the state of
  //! the `BaseAssember` or `Compiler` is unchanged and if it's possible the
  //! execution (instruction serialization) can continue. However if the error
  //! happened during any phase that translates or modifies the stored code
  //! (for example relocation done by `Assembler` or analysis/translation
  //! done by `Compiler`) the execution can't continue and the error will
  //! be also stored in `Assembler` or `Compiler`.
  //!
  //! Finally, if no exceptions nor setjmp() / longjmp() mechanisms were used,
  //! you can still implement a compatible handling by returning from your
  //! error handler. Returning `true` means that error was reported and AsmJit
  //! should continue execution, but `false` sets the rror immediately to the
  //! `Assembler` or `Compiler` and execution shouldn't continue (this
  //! is the default behavior in case no error handler is used).
  virtual bool handleError(Error code, const char* message) = 0;
};

// ============================================================================
// [asmjit::ErrorUtil]
// ============================================================================

//! Error utilities.
struct ErrorUtil {
#if !defined(ASMJIT_DISABLE_NAMES)
  //! Get a printable version of AsmJit `Error` code.
  static ASMJIT_API const char* asString(Error code);
#endif // ASMJIT_DISABLE_NAMES
};

//! \}

// ============================================================================
// [ASMJIT_PROPAGATE_ERROR]
// ============================================================================

//! \internal
//!
//! Used by AsmJit to return the `_Exp_` result if it's an error.
#define ASMJIT_PROPAGATE_ERROR(_Exp_) \
  do { \
    ::asmjit::Error errval_ = (_Exp_); \
    if (errval_ != ::asmjit::kErrorOk) \
      return errval_; \
  } while (0)

} // asmjit namespace

// [Api-End]
#include "../apiend.h"

// [Guard]
#endif // _ASMJIT_BASE_ERROR_H
