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 */