From 407ce44d556a04a7eb628031f2a3f1d2f17c0de4 Mon Sep 17 00:00:00 2001 From: Sami Vaarala Date: Sat, 14 May 2016 01:05:10 +0300 Subject: [PATCH] Remove panic handling, use fatal errors instead Instead of having a separate panic concept which previously differed from fatal error handling in that there was no context attached to the error, use fatal errors also for call sites which previously used the panic handler. Because these call sites are context-free (DUK_ASSERT() failures) simply call the Duktape-wide default fatal error handler instead of the user fatal error handler. For heap creation errors (self test failures) the udata is available; for assertion it isn't and NULL is used instead. Add a config option to replace the Duktape-wide fatal error handler; the default one just segfaults on purpose, to avoid creating postability issues by depending on e.g. abort(). Remove the error code from the fatal error function signature (it's mostly pointless) and change the "ctx" argument to "udata" (heap userdata) which is less confusing than an arbitrary context related to the heap (especially because it's unsafe to actually use the "ctx" to e.g. call into the Duktape API). The fatal error signature change also affects the duk_fatal() API call, which loses the error code argument. --- src/duk_api_public.h.in | 4 +- src/duk_api_stack.c | 20 +++-- src/duk_error.h | 76 ++++++------------- src/duk_error_longjmp.c | 7 +- src/duk_error_macros.c | 67 +++++------------ src/duk_heap.h | 2 +- src/duk_heap_alloc.c | 8 +- src/duk_internal.h | 4 +- src/duk_selftest.c | 162 ++++++++++++++++++++++++++-------------- src/duk_selftest.h | 2 +- 10 files changed, 176 insertions(+), 176 deletions(-) diff --git a/src/duk_api_public.h.in b/src/duk_api_public.h.in index 67577e52..61001122 100644 --- a/src/duk_api_public.h.in +++ b/src/duk_api_public.h.in @@ -49,7 +49,7 @@ typedef duk_ret_t (*duk_c_function)(duk_context *ctx); typedef void *(*duk_alloc_function) (void *udata, duk_size_t size); typedef void *(*duk_realloc_function) (void *udata, void *ptr, duk_size_t size); typedef void (*duk_free_function) (void *udata, void *ptr); -typedef void (*duk_fatal_function) (duk_context *ctx, duk_errcode_t code, const char *msg); +typedef void (*duk_fatal_function) (void *udata, const char *msg); typedef void (*duk_decode_char_function) (void *udata, duk_codepoint_t codepoint); typedef duk_codepoint_t (*duk_map_char_function) (void *udata, duk_codepoint_t codepoint); typedef duk_ret_t (*duk_safe_call_function) (duk_context *ctx, void *udata); @@ -297,7 +297,7 @@ DUK_EXTERNAL_DECL void duk_gc(duk_context *ctx, duk_uint_t flags); */ DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_throw(duk_context *ctx)); -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg)); +DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_fatal(duk_context *ctx, const char *err_msg)); DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...)); diff --git a/src/duk_api_stack.c b/src/duk_api_stack.c index cb379a1e..532e80e8 100644 --- a/src/duk_api_stack.c +++ b/src/duk_api_stack.c @@ -4337,15 +4337,15 @@ DUK_EXTERNAL void duk_throw(duk_context *ctx) { duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't - * need to check that here. If the value is NULL, a panic occurs because - * we can't return. + * need to check that here. If the value is NULL, a fatal error occurs + * because we can't return. */ duk_err_longjmp(thr); DUK_UNREACHABLE(); } -DUK_EXTERNAL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg) { +DUK_EXTERNAL void duk_fatal(duk_context *ctx, const char *err_msg) { duk_hthread *thr = (duk_hthread *) ctx; DUK_ASSERT_CTX_VALID(ctx); @@ -4353,16 +4353,22 @@ DUK_EXTERNAL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(thr->heap->fatal_func != NULL); - DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s", - (long) err_code, (const char *) err_msg)); + DUK_D(DUK_DPRINT("fatal error occurred: %s", err_msg ? err_msg : "NULL")); /* fatal_func should be noreturn, but noreturn declarations on function * pointers has a very spotty support apparently so it's not currently * done. */ - thr->heap->fatal_func(ctx, err_code, err_msg); + thr->heap->fatal_func(thr->heap->heap_udata, err_msg); - DUK_PANIC(DUK_ERR_API_ERROR, "fatal handler returned"); + /* If the fatal handler returns, all bets are off. It'd be nice to + * print something here but since we don't want to depend on stdio, + * there's no way to do so portably. + */ + DUK_D(DUK_DPRINT("fatal error handler returned, all bets are off!")); + for (;;) { + /* loop forever, don't return (function marked noreturn) */ + } } DUK_EXTERNAL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) { diff --git a/src/duk_error.h b/src/duk_error.h index b5083021..4cbcc093 100644 --- a/src/duk_error.h +++ b/src/duk_error.h @@ -1,15 +1,16 @@ /* * Error handling macros, assertion macro, error codes. * - * There are three level of 'errors': + * There are three types of 'errors': * - * 1. Ordinary errors, relative to a thread, cause a longjmp, catchable. - * 2. Fatal errors, relative to a heap, cause fatal handler to be called. - * 3. Panic errors, unrelated to a heap and cause a process exit. + * 1. Ordinary errors relative to a thread, cause a longjmp, catchable. + * 2. Fatal errors relative to a heap, cause fatal handler to be called. + * 3. Fatal errors without context, cause the default (not heap specific) + * fatal handler to be called. * - * Panics are used by the default fatal error handler and by debug code - * such as assertions. By providing a proper fatal error handler, user - * code can avoid panics in non-debug builds. + * Fatal errors without context are used by debug code such as assertions. + * By providing a fatal error handler for a Duktape heap, user code can + * avoid fatal errors without context in non-debug builds. */ #ifndef DUK_ERROR_H_INCLUDED @@ -127,66 +128,41 @@ #endif /* DUK_USE_VERBOSE_ERRORS */ /* - * Fatal error + * Fatal error without context * - * There are no fatal error macros at the moment. There are so few call - * sites that the fatal error handler is called directly. + * The macro is an expression to make it compatible with DUK_ASSERT_EXPR(). */ -/* - * Panic error - * - * Panic errors are not relative to either a heap or a thread, and cause - * DUK_PANIC() macro to be invoked. Unless a user provides DUK_USE_PANIC_HANDLER, - * DUK_PANIC() calls a helper which prints out the error and causes a process - * exit. - * - * The user can override the macro to provide custom handling. A macro is - * used to allow the user to have inline panic handling if desired (without - * causing a potentially risky function call). - * - * Panics are only used in debug code such as assertions, and by the default - * fatal error handler. - */ - -#if defined(DUK_USE_PANIC_HANDLER) -/* already defined, good */ -#define DUK_PANIC(code,msg) DUK_USE_PANIC_HANDLER((code),(msg)) -#else -#define DUK_PANIC(code,msg) duk_default_panic_handler((code),(msg)) -#endif /* DUK_USE_PANIC_HANDLER */ +#define DUK_FATAL_WITHOUT_CONTEXT(msg) \ + duk_default_fatal_handler(NULL, (msg)) /* - * Assert macro: failure causes panic. + * Assert macro: failure causes a fatal error. + * + * NOTE: since the assert macro doesn't take a heap/context argument, there's + * no way to look up a heap/context specific fatal error handler which may have + * been given by the application. Instead, assertion failures always use the + * internal default fatal error handler; it can be replaced via duk_config.h + * and then applies to all Duktape heaps. */ #if defined(DUK_USE_ASSERTIONS) -/* the message should be a compile time constant without formatting (less risk); +/* 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_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \ " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \ } \ } while (0) -/* Assertion compatible inside a comma expression, evaluates to void. - * Currently not compatible with DUK_USE_PANIC_HANDLER() which may have - * a statement block. - */ -#if defined(DUK_USE_PANIC_HANDLER) -/* XXX: resolve macro definition issue or call through a helper function? */ -#define DUK_ASSERT_EXPR(x) ((void) 0) -#else +/* Assertion compatible inside a comma expression, evaluates to void. */ #define DUK_ASSERT_EXPR(x) \ - ((void) ((x) ? 0 : (DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \ - "assertion failed: " #x \ + ((void) ((x) ? 0 : (DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \ " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), 0))) -#endif #else /* DUK_USE_ASSERTIONS */ @@ -422,11 +398,7 @@ DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_alloc(duk_hthread *thr)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_longjmp(duk_hthread *thr)); -DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg)); - -#if !defined(DUK_USE_PANIC_HANDLER) -DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_panic_handler(duk_errcode_t code, const char *msg)); -#endif +DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, const char *msg)); DUK_INTERNAL_DECL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type); diff --git a/src/duk_error_longjmp.c b/src/duk_error_longjmp.c index bfb1b46f..2b891be3 100644 --- a/src/duk_error_longjmp.c +++ b/src/duk_error_longjmp.c @@ -13,8 +13,8 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { &thr->heap->lj.value1, &thr->heap->lj.value2)); #if !defined(DUK_USE_CPP_EXCEPTIONS) - /* If we don't have a jmpbuf_ptr, there is little we can do - * except panic. The caller's expectation is that we never + /* If we don't have a jmpbuf_ptr, there is little we can do except + * cause a fatal error. The caller's expectation is that we never * return. * * With C++ exceptions we now just propagate an uncaught error @@ -22,12 +22,11 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { * a dummy jmpbuf for C++ exceptions now, this could be changed. */ if (!thr->heap->lj.jmpbuf_ptr) { - DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T", (int) thr->heap->lj.type, (int) thr->heap->lj.iserror, &thr->heap->lj.value1, &thr->heap->lj.value2)); - duk_fatal((duk_context *) thr, DUK_ERR_UNCAUGHT_ERROR, "uncaught error"); + duk_fatal((duk_context *) thr, "uncaught error"); DUK_UNREACHABLE(); } #endif /* DUK_USE_CPP_EXCEPTIONS */ diff --git a/src/duk_error_macros.c b/src/duk_error_macros.c index d665dbbf..8511ceac 100644 --- a/src/duk_error_macros.c +++ b/src/duk_error_macros.c @@ -1,5 +1,5 @@ /* - * Error, fatal, and panic handling. + * Error and fatal handling. */ #include "duk_internal.h" @@ -104,58 +104,29 @@ DUK_INTERNAL void duk_err_alloc(duk_hthread *thr) { * Default fatal error handler */ -DUK_INTERNAL void duk_default_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) { - DUK_UNREF(ctx); -#if defined(DUK_USE_FILE_IO) - DUK_FPRINTF(DUK_STDERR, "FATAL %ld: %s\n", (long) code, (const char *) (msg ? msg : "null")); - DUK_FFLUSH(DUK_STDERR); -#else - /* omit print */ -#endif - DUK_D(DUK_DPRINT("default fatal handler called, code %ld -> calling DUK_PANIC()", (long) code)); - DUK_PANIC(code, msg); - DUK_UNREACHABLE(); -} - -/* - * Default panic handler - */ - -#if !defined(DUK_USE_PANIC_HANDLER) -DUK_INTERNAL void duk_default_panic_handler(duk_errcode_t code, const char *msg) { -#if defined(DUK_USE_FILE_IO) - DUK_FPRINTF(DUK_STDERR, "PANIC %ld: %s (" -#if defined(DUK_USE_PANIC_ABORT) - "calling abort" -#elif defined(DUK_USE_PANIC_EXIT) - "calling exit" -#elif defined(DUK_USE_PANIC_SEGFAULT) - "segfaulting on purpose" -#else -#error no DUK_USE_PANIC_xxx macro defined -#endif - ")\n", (long) code, (const char *) (msg ? msg : "null")); - DUK_FFLUSH(DUK_STDERR); -#else - /* omit print */ - DUK_UNREF(code); +DUK_INTERNAL void duk_default_fatal_handler(void *udata, const char *msg) { + DUK_UNREF(udata); DUK_UNREF(msg); -#endif -#if defined(DUK_USE_PANIC_ABORT) - DUK_ABORT(); -#elif defined(DUK_USE_PANIC_EXIT) - DUK_EXIT(-1); -#elif defined(DUK_USE_PANIC_SEGFAULT) - /* exit() afterwards to satisfy "noreturn" */ - DUK_CAUSE_SEGFAULT(); /* SCANBUILD: "Dereference of null pointer", normal */ - DUK_EXIT(-1); +#if defined(DUK_USE_FATAL_HANDLER) + /* duk_config.h provided a custom default fatal handler. */ + DUK_D(DUK_DPRINT("custom default fatal error handler called: %s", msg ? msg : "NULL")); + DUK_USE_FATAL_HANDLER(udata, msg); #else -#error no DUK_USE_PANIC_xxx macro defined + /* Since we don't want to rely on stdio being available, we'll just + * cause a segfault on purpose: it's a portable way to usually cause + * a process to finish, and when used with valgrind it also gives us + * a nice stack trace. For better behavior application code should + * provide a fatal error handler. + */ + DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg ? msg : "NULL")); + DUK_CAUSE_SEGFAULT(); /* SCANBUILD: "Dereference of null pointer", normal */ #endif - DUK_UNREACHABLE(); + DUK_D(DUK_DPRINT("fatal error handler returned or segfault didn't succeed, enter forever loop")); + for (;;) { + /* Loop forever to ensure we don't return. */ + } } -#endif /* !DUK_USE_PANIC_HANDLER */ #undef DUK__ERRFMT_BUFSIZE diff --git a/src/duk_heap.h b/src/duk_heap.h index 5f96b1af..75156e62 100644 --- a/src/duk_heap.h +++ b/src/duk_heap.h @@ -351,7 +351,7 @@ struct duk_heap { duk_free_function free_func; /* Heap udata, used for allocator functions but also for other heap - * level callbacks like pointer compression, etc. + * level callbacks like fatal function, pointer compression, etc. */ void *heap_udata; diff --git a/src/duk_heap_alloc.c b/src/duk_heap_alloc.c index 4ef443c3..abae044c 100644 --- a/src/duk_heap_alloc.c +++ b/src/duk_heap_alloc.c @@ -748,12 +748,12 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #endif /* - * If selftests enabled, run them as early as possible + * If selftests enabled, run them as early as possible. */ #if defined(DUK_USE_SELF_TESTS) - DUK_D(DUK_DPRINT("running self tests")); - duk_selftest_run_tests(); - DUK_D(DUK_DPRINT("self tests passed")); + if (duk_selftest_run_tests() > 0) { + fatal_func(heap_udata, "self test(s) failed"); + } #endif /* diff --git a/src/duk_internal.h b/src/duk_internal.h index 7c8c7afc..3369261b 100644 --- a/src/duk_internal.h +++ b/src/duk_internal.h @@ -27,9 +27,7 @@ /* * User declarations, e.g. prototypes for user functions used by Duktape - * macros. Concretely, if DUK_USE_PANIC_HANDLER is used and the macro - * value calls a user function, it needs to be declared for Duktape - * compilation to avoid warnings. + * macros. */ DUK_USE_USER_DECLARE() diff --git a/src/duk_selftest.c b/src/duk_selftest.c index 6208490f..ee952f10 100644 --- a/src/duk_selftest.c +++ b/src/duk_selftest.c @@ -16,15 +16,21 @@ typedef union { duk_uint8_t c[8]; } duk__test_double_union; +/* Self test failed. Expects a local variable 'error_count' to exist. */ +#define DUK__FAILED(msg) do { \ + DUK_D(DUK_DPRINT("self test failed: " #msg)); \ + error_count++; \ + } while (0) + #define DUK__DBLUNION_CMP_TRUE(a,b) do { \ if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \ - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares false (expected true)"); \ + DUK__FAILED("double union compares false (expected true)"); \ } \ } while (0) #define DUK__DBLUNION_CMP_FALSE(a,b) do { \ if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \ - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double union compares true (expected false)"); \ + DUK__FAILED("double union compares true (expected false)"); \ } \ } while (0) @@ -37,19 +43,21 @@ typedef union { * Various sanity checks for typing */ -DUK_LOCAL void duk__selftest_types(void) { +DUK_LOCAL duk_uint_t duk__selftest_types(void) { + duk_uint_t error_count = 0; + if (!(sizeof(duk_int8_t) == 1 && sizeof(duk_uint8_t) == 1 && sizeof(duk_int16_t) == 2 && sizeof(duk_uint16_t) == 2 && sizeof(duk_int32_t) == 4 && sizeof(duk_uint32_t) == 4)) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_(u)int{8,16,32}_t size"); + DUK__FAILED("duk_(u)int{8,16,32}_t size"); } #if defined(DUK_USE_64BIT_OPS) if (!(sizeof(duk_int64_t) == 8 && sizeof(duk_uint64_t) == 8)) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_(u)int64_t size"); + DUK__FAILED("duk_(u)int64_t size"); } #endif @@ -57,30 +65,37 @@ DUK_LOCAL void duk__selftest_types(void) { /* Some internal code now assumes that all duk_uint_t values * can be expressed with a duk_size_t. */ - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_size_t is smaller than duk_uint_t"); + DUK__FAILED("duk_size_t is smaller than duk_uint_t"); } if (!(sizeof(duk_int_t) >= 4)) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_int_t is not 32 bits"); + DUK__FAILED("duk_int_t is not 32 bits"); } + + return error_count; } /* * Packed tval sanity */ -DUK_LOCAL void duk__selftest_packed_tval(void) { +DUK_LOCAL duk_uint_t duk__selftest_packed_tval(void) { + duk_uint_t error_count = 0; + #if defined(DUK_USE_PACKED_TVAL) if (sizeof(void *) > 4) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: packed duk_tval in use but sizeof(void *) > 4"); + DUK__FAILED("packed duk_tval in use but sizeof(void *) > 4"); } #endif + + return error_count; } /* * Two's complement arithmetic. */ -DUK_LOCAL void duk__selftest_twos_complement(void) { +DUK_LOCAL duk_uint_t duk__selftest_twos_complement(void) { + duk_uint_t error_count = 0; volatile int test; test = -1; @@ -88,8 +103,10 @@ DUK_LOCAL void duk__selftest_twos_complement(void) { * 'test' will be 0xFF for two's complement. */ if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: two's complement arithmetic"); + DUK__FAILED("two's complement arithmetic"); } + + return error_count; } /* @@ -98,7 +115,8 @@ DUK_LOCAL void duk__selftest_twos_complement(void) { * defines. */ -DUK_LOCAL void duk__selftest_byte_order(void) { +DUK_LOCAL duk_uint_t duk__selftest_byte_order(void) { + duk_uint_t error_count = 0; duk__test_u32_union u1; duk__test_double_union u2; @@ -130,19 +148,22 @@ DUK_LOCAL void duk__selftest_byte_order(void) { #endif if (u1.i != (duk_uint32_t) 0xdeadbeefUL) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: duk_uint32_t byte order"); + DUK__FAILED("duk_uint32_t byte order"); } if (u2.d != (double) 102030405060.0) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double byte order"); + DUK__FAILED("double byte order"); } + + return error_count; } /* * DUK_BSWAP macros */ -DUK_LOCAL void duk__selftest_bswap_macros(void) { +DUK_LOCAL duk_uint_t duk__selftest_bswap_macros(void) { + duk_uint_t error_count = 0; duk_uint32_t x32; duk_uint16_t x16; duk_double_union du; @@ -151,13 +172,13 @@ DUK_LOCAL void duk__selftest_bswap_macros(void) { x16 = 0xbeefUL; x16 = DUK_BSWAP16(x16); if (x16 != (duk_uint16_t) 0xefbeUL) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_BSWAP16"); + DUK__FAILED("DUK_BSWAP16"); } x32 = 0xdeadbeefUL; x32 = DUK_BSWAP32(x32); if (x32 != (duk_uint32_t) 0xefbeaddeUL) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_BSWAP32"); + DUK__FAILED("DUK_BSWAP32"); } /* >>> struct.unpack('>d', '4000112233445566'.decode('hex')) @@ -169,7 +190,7 @@ DUK_LOCAL void duk__selftest_bswap_macros(void) { DUK_DBLUNION_DOUBLE_NTOH(&du); du_diff = du.d - 2.008366013071895; #if 0 - DUK_FPRINTF(DUK_STDERR, "du_diff: %lg\n", (double) du_diff); + DUK_D(DUK_DPRINT("du_diff: %lg\n", (double) du_diff)); #endif if (du_diff > 1e-15) { /* Allow very small lenience because some compilers won't parse @@ -177,41 +198,44 @@ DUK_LOCAL void duk__selftest_bswap_macros(void) { * Linux gcc-4.8 -m32 at least). */ #if 0 - DUK_FPRINTF(DUK_STDERR, "Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n", + DUK_D(DUK_DPRINT("Result of DUK_DBLUNION_DOUBLE_NTOH: %02x %02x %02x %02x %02x %02x %02x %02x\n", (unsigned int) du.uc[0], (unsigned int) du.uc[1], (unsigned int) du.uc[2], (unsigned int) du.uc[3], (unsigned int) du.uc[4], (unsigned int) du.uc[5], - (unsigned int) du.uc[6], (unsigned int) du.uc[7]); + (unsigned int) du.uc[6], (unsigned int) du.uc[7])); #endif - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_DBLUNION_DOUBLE_NTOH"); + DUK__FAILED("DUK_DBLUNION_DOUBLE_NTOH"); } + + return error_count; } /* * Basic double / byte union memory layout. */ -DUK_LOCAL void duk__selftest_double_union_size(void) { +DUK_LOCAL duk_uint_t duk__selftest_double_union_size(void) { + duk_uint_t error_count = 0; + if (sizeof(duk__test_double_union) != 8) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: invalid union size"); + DUK__FAILED("invalid union size"); } + + return error_count; } /* * Union aliasing, see misc/clang_aliasing.c. */ -DUK_LOCAL void duk__selftest_double_aliasing(void) { - duk__test_double_union a, b; - +DUK_LOCAL duk_uint_t duk__selftest_double_aliasing(void) { /* This testcase fails when Emscripten-generated code runs on Firefox. * It's not an issue because the failure should only affect packed * duk_tval representation, which is not used with Emscripten. */ -#if !defined(DUK_USE_PACKED_TVAL) - DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed")); - return; -#endif +#if defined(DUK_USE_PACKED_TVAL) + duk_uint_t error_count = 0; + duk__test_double_union a, b; /* Test signaling NaN and alias assignment in all endianness combinations. */ @@ -233,18 +257,27 @@ DUK_LOCAL void duk__selftest_double_aliasing(void) { a.c[4] = 0x11; a.c[5] = 0x22; a.c[6] = 0x33; a.c[7] = 0x44; b = a; DUK__DBLUNION_CMP_TRUE(&a, &b); + + return error_count; +#else + DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed")); + return 0; +#endif } /* * Zero sign, see misc/tcc_zerosign2.c. */ -DUK_LOCAL void duk__selftest_double_zero_sign(void) { +DUK_LOCAL duk_uint_t duk__selftest_double_zero_sign(void) { + duk_uint_t error_count = 0; duk__test_double_union a, b; a.d = 0.0; b.d = -a.d; DUK__DBLUNION_CMP_FALSE(&a, &b); + + return error_count; } /* @@ -254,20 +287,23 @@ DUK_LOCAL void duk__selftest_double_zero_sign(void) { * selftest ensures they're correctly detected and used. */ -DUK_LOCAL void duk__selftest_struct_align(void) { +DUK_LOCAL duk_uint_t duk__selftest_struct_align(void) { + duk_uint_t error_count = 0; + #if (DUK_USE_ALIGN_BY == 4) if ((sizeof(duk_hbuffer_fixed) % 4) != 0) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 4"); + DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 4"); } #elif (DUK_USE_ALIGN_BY == 8) if ((sizeof(duk_hbuffer_fixed) % 8) != 0) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: sizeof(duk_hbuffer_fixed) not aligned to 8"); + DUK__FAILED("sizeof(duk_hbuffer_fixed) not aligned to 8"); } #elif (DUK_USE_ALIGN_BY == 1) /* no check */ #else #error invalid DUK_USE_ALIGN_BY #endif + return error_count; } /* @@ -277,7 +313,8 @@ DUK_LOCAL void duk__selftest_struct_align(void) { * but don't work correctly. Test for known cases. */ -DUK_LOCAL void duk__selftest_64bit_arithmetic(void) { +DUK_LOCAL duk_uint_t duk__selftest_64bit_arithmetic(void) { + duk_uint_t error_count = 0; #if defined(DUK_USE_64BIT_OPS) volatile duk_int64_t i; volatile duk_double_t d; @@ -286,22 +323,25 @@ DUK_LOCAL void duk__selftest_64bit_arithmetic(void) { d = 2147483648.0; i = (duk_int64_t) d; if (i != 0x80000000LL) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: casting 2147483648.0 to duk_int64_t failed"); + DUK__FAILED("casting 2147483648.0 to duk_int64_t failed"); } #else /* nop */ #endif + return error_count; } /* * Casting */ -DUK_LOCAL void duk__selftest_cast_double_to_small_uint(void) { +DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_small_uint(void) { /* * https://github.com/svaarala/duktape/issues/127#issuecomment-77863473 */ + duk_uint_t error_count = 0; + duk_double_t d1, d2; duk_small_uint_t u; @@ -315,7 +355,7 @@ DUK_LOCAL void duk__selftest_cast_double_to_small_uint(void) { d2 = (duk_double_t) u; if (!(d1 == 1.0 && u == 1 && d2 == 1.0 && d1 == d2)) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_small_uint_t cast failed"); + DUK__FAILED("double to duk_small_uint_t cast failed"); } /* Same test with volatiles */ @@ -325,11 +365,13 @@ DUK_LOCAL void duk__selftest_cast_double_to_small_uint(void) { d2v = (duk_double_t) uv; if (!(d1v == 1.0 && uv == 1 && d2v == 1.0 && d1v == d2v)) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_small_uint_t cast failed"); + DUK__FAILED("double to duk_small_uint_t cast failed"); } + + return error_count; } -DUK_LOCAL void duk__selftest_cast_double_to_uint32(void) { +DUK_LOCAL duk_uint_t duk__selftest_cast_double_to_uint32(void) { /* * This test fails on an exotic ARM target; double-to-uint * cast is incorrectly clamped to -signed- int highest value. @@ -337,6 +379,7 @@ DUK_LOCAL void duk__selftest_cast_double_to_uint32(void) { * https://github.com/svaarala/duktape/issues/336 */ + duk_uint_t error_count = 0; duk_double_t dv; duk_uint32_t uv; @@ -344,29 +387,40 @@ DUK_LOCAL void duk__selftest_cast_double_to_uint32(void) { uv = (duk_uint32_t) dv; if (uv != 0xdeadbeefUL) { - DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: double to duk_uint32_t cast failed"); + DUK__FAILED("double to duk_uint32_t cast failed"); } + + return error_count; } /* * Self test main */ -DUK_INTERNAL void duk_selftest_run_tests(void) { - duk__selftest_types(); - duk__selftest_packed_tval(); - duk__selftest_twos_complement(); - duk__selftest_byte_order(); - duk__selftest_bswap_macros(); - duk__selftest_double_union_size(); - duk__selftest_double_aliasing(); - duk__selftest_double_zero_sign(); - duk__selftest_struct_align(); - duk__selftest_64bit_arithmetic(); - duk__selftest_cast_double_to_small_uint(); - duk__selftest_cast_double_to_uint32(); +DUK_INTERNAL duk_uint_t duk_selftest_run_tests(void) { + duk_uint_t error_count = 0; + + DUK_D(DUK_DPRINT("self test starting")); + + error_count += duk__selftest_types(); + error_count += duk__selftest_packed_tval(); + error_count += duk__selftest_twos_complement(); + error_count += duk__selftest_byte_order(); + error_count += duk__selftest_bswap_macros(); + error_count += duk__selftest_double_union_size(); + error_count += duk__selftest_double_aliasing(); + error_count += duk__selftest_double_zero_sign(); + error_count += duk__selftest_struct_align(); + error_count += duk__selftest_64bit_arithmetic(); + error_count += duk__selftest_cast_double_to_small_uint(); + error_count += duk__selftest_cast_double_to_uint32(); + + DUK_D(DUK_DPRINT("self test complete, total error count: %ld", (long) error_count)); + + return error_count; } +#undef DUK__FAILED #undef DUK__DBLUNION_CMP_TRUE #undef DUK__DBLUNION_CMP_FALSE diff --git a/src/duk_selftest.h b/src/duk_selftest.h index 90527975..a8ce22f7 100644 --- a/src/duk_selftest.h +++ b/src/duk_selftest.h @@ -6,7 +6,7 @@ #define DUK_SELFTEST_H_INCLUDED #if defined(DUK_USE_SELF_TESTS) -DUK_INTERNAL_DECL void duk_selftest_run_tests(void); +DUK_INTERNAL_DECL duk_uint_t duk_selftest_run_tests(void); #endif #endif /* DUK_SELFTEST_H_INCLUDED */