mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
10 KiB
277 lines
10 KiB
/*
|
|
* Error handling macros, assertion macro, error codes.
|
|
*/
|
|
|
|
#ifndef DUK_ERROR_H_INCLUDED
|
|
#define DUK_ERROR_H_INCLUDED
|
|
|
|
/*
|
|
* Error codes (defined in duktape.h)
|
|
*
|
|
* Error codes are used as a shorthand to throw exceptions from inside
|
|
* the implementation. The appropriate Ecmascript object is constructed
|
|
* based on the code. Ecmascript code throws objects directly.
|
|
*
|
|
* The error codes are now defined in the public API header because they
|
|
* are also used by calling code.
|
|
*/
|
|
|
|
/* for function return codes */
|
|
#define DUK_ERR_OK 0 /* call successful */
|
|
#define DUK_ERR_FAIL 1 /* call failed */
|
|
|
|
/*
|
|
* Normal error is thrown with a longjmp() through the current setjmp()
|
|
* catchpoint record in the duk_heap. The 'curr_thread' of the duk_heap
|
|
* identifies the throwing thread.
|
|
*
|
|
* Panic is thrown without a heap/thread context and cannot be caught.
|
|
* All bets are off, and the default implementation exits the process.
|
|
*
|
|
* FIXME: panic should map to the fatal error handler.
|
|
*
|
|
* Error formatting is not always necessary but there are no separate calls
|
|
* (to minimize code size). Error object creation will consume a considerable
|
|
* amount of time, compared to which formatting is probably trivial. Note
|
|
* that special formatting (provided by DUK_DEBUG macros) is NOT available.
|
|
*
|
|
* The _RAW variants allow the caller to specify file and line. This makes
|
|
* it easier to write checked calls which want to use the call site of the
|
|
* checked function, not the error macro call inside the checked function.
|
|
*
|
|
* We prefer the standard variadic macros; if they are not available, we
|
|
* fall back to awkward hacks.
|
|
*/
|
|
|
|
#ifdef DUK_USE_VERBOSE_ERRORS
|
|
|
|
#ifdef DUK_USE_VARIADIC_MACROS
|
|
|
|
/* __VA_ARGS__ has comma issues for empty lists, so we mandate at least 1 argument for '...' (format string) */
|
|
#define DUK_ERROR(thr,err,...) duk_err_handle_error(DUK_FILE_MACRO, (int) DUK_LINE_MACRO, (thr), (err), __VA_ARGS__)
|
|
#define DUK_ERROR_RAW(file,line,thr,err,...) duk_err_handle_error((file), (line), (thr), (err), __VA_ARGS__)
|
|
#define DUK_PANIC(err,...) duk_err_handle_panic(DUK_FILE_MACRO, DUK_LINE_MACRO, (err), __VA_ARGS__)
|
|
#define DUK_PANIC_RAW(file,line,err,...) duk_err_handle_panic((file), (line), (err), __VA_ARGS__)
|
|
|
|
#else /* DUK_USE_VARIADIC_MACROS */
|
|
|
|
/* Parameter passing here is not thread safe. We rely on the __FILE__
|
|
* pointer being a constant which can be passed through a global.
|
|
*/
|
|
|
|
#define DUK_ERROR \
|
|
duk_err_file_stash = (const char *) DUK_FILE_MACRO, \
|
|
duk_err_line_stash = (int) DUK_LINE_MACRO, \
|
|
(void) duk_err_handle_error_stash /* arguments follow */
|
|
#define DUK_ERROR_RAW duk_err_handle_error
|
|
#define DUK_PANIC \
|
|
duk_err_file_stash = (const char *) DUK_FILE_MACRO, \
|
|
duk_err_line_stash = (int) DUK_LINE_MACRO, \
|
|
(void) duk_err_handle_panic_stash /* arguments follow */
|
|
#define DUK_PANIC_RAW duk_err_handle_panic
|
|
|
|
#endif /* DUK_USE_VARIADIC_MACROS */
|
|
|
|
#else /* DUK_USE_VERBOSE_ERRORS */
|
|
|
|
#ifdef DUK_USE_VARIADIC_MACROS
|
|
|
|
#define DUK_ERROR(thr,err,...) duk_err_handle_error((thr), (err))
|
|
#define DUK_ERROR_RAW(file,line,thr,err,...) duk_err_handle_error((thr), (err))
|
|
#define DUK_PANIC(err,...) duk_err_handle_panic((err))
|
|
#define DUK_PANIC_RAW(err,...) duk_err_handle_panic((err))
|
|
|
|
#else /* DUK_USE_VARIADIC_MACROS */
|
|
|
|
/* This is sub-optimal because arguments will be passed but ignored, and the strings
|
|
* will go into the object file. Can't think of how to do this portably and still
|
|
* relatively conveniently.
|
|
*/
|
|
#define DUK_ERROR duk_err_handle_error_nonverbose1
|
|
#define DUK_ERROR_RAW duk_err_handle_error_nonverbose2
|
|
#define DUK_PANIC duk_err_handle_panic_nonverbose1
|
|
#define DUK_PANIC_RAW duk_err_handle_panic_nonverbose2
|
|
|
|
#endif /* DUK_USE_VARIADIC_MACROS */
|
|
|
|
#endif /* DUK_USE_VERBOSE_ERRORS */
|
|
|
|
/*
|
|
* Assert macro: failure cause DUK_PANIC().
|
|
*/
|
|
|
|
#ifdef DUK_USE_ASSERTIONS
|
|
|
|
/* the message should be a compile time constant without formatting (less risk);
|
|
* we don't care about assertion text size because they're not used in production
|
|
* builds.
|
|
*/
|
|
#define DUK_ASSERT(x) do { \
|
|
if (!(x)) { \
|
|
DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \
|
|
"assertion failed: " #x \
|
|
" (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \
|
|
} \
|
|
} while (0)
|
|
|
|
#else /* DUK_USE_ASSERTIONS */
|
|
|
|
#define DUK_ASSERT(x) do { /* assertion omitted */ } while(0)
|
|
|
|
#endif /* DUK_USE_ASSERTIONS */
|
|
|
|
/*
|
|
* Final panic handler macro (unless defined already)
|
|
*/
|
|
|
|
/* FIXME: Change this is so that if DUK_USER_PANIC_HANDLER defined, map
|
|
* DUK_PANIC_HANDLER to it? Cleaner than allowing user to define directly.
|
|
* In any case, panics should map do fatal error handler in the public API.
|
|
*/
|
|
|
|
#if defined(DUK_PANIC_HANDLER)
|
|
/* already defined, good */
|
|
#else
|
|
#if defined(DUK_USE_PANIC_ABORT)
|
|
#define DUK_PANIC_EXIT() abort()
|
|
#elif defined(DUK_USE_PANIC_EXIT)
|
|
#define DUK_PANIC_EXIT() exit(-1)
|
|
#elif defined(DUK_USE_PANIC_SEGFAULT)
|
|
#define DUK_PANIC_EXIT() do { \
|
|
/* exit() afterwards to satisfy "noreturn" */ \
|
|
DUK_CAUSE_SEGFAULT(); \
|
|
exit(-1); \
|
|
} while (0)
|
|
#else
|
|
#error no DUK_USE_PANIC_xxx macro defined
|
|
#endif
|
|
|
|
#ifdef DUK_USE_FILE_IO
|
|
#define DUK_PANIC_PRINTMSG(code,msg) do { \
|
|
fprintf(stderr, "PANIC %d: %s\n", code, msg ? msg : "null"); \
|
|
fflush(stderr); \
|
|
} while (0)
|
|
#else
|
|
#define DUK_PANIC_PRINTMSG(code,msg)
|
|
#endif
|
|
|
|
#ifdef DUK_USE_GCC_PRAGMAS
|
|
#define DUK_PANIC_HANDLER(code,msg) do { \
|
|
/* GCC pragmas to suppress: warning: the address of 'xxx' will always evaluate as 'true' [-Waddress]' */ \
|
|
_Pragma("GCC diagnostic push"); \
|
|
_Pragma("GCC diagnostic ignored \"-Waddress\""); \
|
|
DUK_PANIC_PRINTMSG((code),(msg)); \
|
|
DUK_PANIC_EXIT(); \
|
|
DUK_UNREACHABLE(); \
|
|
_Pragma("GCC diagnostic pop"); \
|
|
} while (0)
|
|
#else
|
|
#define DUK_PANIC_HANDLER(code,msg) do { \
|
|
/* No pragmas to suppress warning, causes unclean build */ \
|
|
DUK_PANIC_PRINTMSG((code),(msg)); \
|
|
DUK_PANIC_EXIT(); \
|
|
DUK_UNREACHABLE(); \
|
|
} while (0)
|
|
#endif /* DUK_USE_GCC_PRAGMAS */
|
|
#endif /* DUK_PANIC_HANDLER */
|
|
|
|
/*
|
|
* Assertion helpers
|
|
*/
|
|
|
|
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
|
|
#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h) do { \
|
|
DUK_ASSERT((h) == NULL || DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) (h)) > 0); \
|
|
} while (0)
|
|
#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv) do { \
|
|
if ((tv) != NULL && DUK_TVAL_IS_HEAP_ALLOCATED((tv))) { \
|
|
DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(DUK_TVAL_GET_HEAPHDR((tv))) > 0); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(h) /* no refcount check */
|
|
#define DUK_ASSERT_REFCOUNT_NONZERO_TVAL(tv) /* no refcount check */
|
|
#endif
|
|
|
|
#define DUK_ASSERT_TOP(ctx,n) DUK_ASSERT(duk_get_top((ctx)) == (n))
|
|
|
|
#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_PACKED_TVAL)
|
|
#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval) do { \
|
|
duk_double_union assert_tmp_du; \
|
|
assert_tmp_du.d = (dval); \
|
|
DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&assert_tmp_du)); \
|
|
} while (0)
|
|
#else
|
|
#define DUK_ASSERT_DOUBLE_IS_NORMALIZED(dval) /* nop */
|
|
#endif
|
|
|
|
/*
|
|
* Helper for valstack space
|
|
*
|
|
* Caller of ASSERT_VALSTACK_SPACE() estimates the number of free stack entries
|
|
* required for its own use, and any child calls which are not (a) Duktape API calls
|
|
* or (b) Duktape calls which involve extending the valstack (e.g. getter call).
|
|
*/
|
|
|
|
#define DUK_VALSTACK_ASSERT_EXTRA 5 /* this is added to checks to allow for Duktape
|
|
* API calls in addition to function's own use
|
|
*/
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
#define ASSERT_VALSTACK_SPACE(thr,n) do { \
|
|
DUK_ASSERT((thr) != NULL); \
|
|
DUK_ASSERT((thr)->valstack_end - (thr)->valstack_top >= (n) + DUK_VALSTACK_ASSERT_EXTRA); \
|
|
} while (0)
|
|
#else
|
|
#define ASSERT_VALSTACK_SPACE(thr,n) /* no valstack space check */
|
|
#endif
|
|
|
|
/*
|
|
* Prototypes
|
|
*/
|
|
|
|
#ifdef DUK_USE_VERBOSE_ERRORS
|
|
#ifdef DUK_USE_VARIADIC_MACROS
|
|
DUK_NORETURN(void duk_err_handle_error(const char *filename, int line, duk_hthread *thr, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_panic(const char *filename, int line, int code, const char *fmt, ...));
|
|
#else /* DUK_USE_VARIADIC_MACROS */
|
|
extern const char *duk_err_file_stash;
|
|
extern int duk_err_line_stash;
|
|
DUK_NORETURN(void duk_err_handle_error(const char *filename, int line, duk_hthread *thr, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_error_stash(duk_hthread *thr, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_panic(const char *filename, int line, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_panic_stash(int code, const char *fmt, ...));
|
|
#endif /* DUK_USE_VARIADIC_MACROS */
|
|
#else /* DUK_USE_VERBOSE_ERRORS */
|
|
#ifdef DUK_USE_VARIADIC_MACROS
|
|
DUK_NORETURN(void duk_err_handle_error(duk_hthread *thr, int code));
|
|
DUK_NORETURN(void duk_err_handle_panic(int code));
|
|
#else /* DUK_USE_VARIADIC_MACROS */
|
|
DUK_NORETURN(void duk_err_handle_error_nonverbose1(duk_hthread *thr, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_error_nonverbose2(const char *filename, int line, duk_hthread *thr, int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_panic_nonverbose1(int code, const char *fmt, ...));
|
|
DUK_NORETURN(void duk_err_handle_panic_nonverbose2(const char *filename, int line, int code, const char *fmt, ...));
|
|
#endif /* DUK_USE_VARIADIC_MACROS */
|
|
#endif /* DUK_USE_VERBOSE_ERRORS */
|
|
|
|
#ifdef DUK_USE_VERBOSE_ERRORS
|
|
DUK_NORETURN(void duk_err_create_and_throw(duk_hthread *thr, duk_uint32_t code, const char *msg, const char *filename, int line));
|
|
#else
|
|
DUK_NORETURN(void duk_err_create_and_throw(duk_hthread *thr, duk_uint32_t code));
|
|
#endif
|
|
|
|
DUK_NORETURN(void duk_error_throw_from_negative_rc(duk_hthread *thr, int rc));
|
|
|
|
#ifdef DUK_USE_AUGMENT_ERRORS
|
|
void duk_err_augment_error(duk_hthread *thr, duk_hthread *thr_callstack, int err_index, const char *filename, int line, int noblame_fileline);
|
|
#endif
|
|
|
|
DUK_NORETURN(void duk_err_longjmp(duk_hthread *thr));
|
|
|
|
DUK_NORETURN(void duk_default_fatal_handler(duk_context *ctx, int code));
|
|
|
|
void duk_err_setup_heap_ljstate(duk_hthread *thr, int lj_type);
|
|
|
|
duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, int err_code);
|
|
|
|
#endif /* DUK_ERROR_H_INCLUDED */
|
|
|
|
|