Browse Source

Merge pull request #781 from svaarala/remove-panic-concept

Remove fatal/panic distinction and use fatal errors only
pull/787/head
Sami Vaarala 9 years ago
parent
commit
2d7cb4bb8a
  1. 5
      Makefile
  2. 14
      RELEASES.rst
  3. 27
      config/config-options/DUK_USE_FATAL_HANDLER.yaml
  4. 1
      config/config-options/DUK_USE_PANIC_ABORT.yaml
  5. 1
      config/config-options/DUK_USE_PANIC_EXIT.yaml
  6. 1
      config/config-options/DUK_USE_PANIC_HANDLER.yaml
  7. 1
      config/config-options/DUK_USE_PANIC_SEGFAULT.yaml
  8. 6
      config/header-snippets/compiler_fillins.h.in
  9. 6
      dist-files/Makefile.cmdline
  10. 50
      doc/code-issues.rst
  11. 7
      doc/feature-options.rst
  12. 64
      doc/release-notes-v2-0.rst
  13. 59
      dukweb/dukweb.c
  14. 20
      examples/cmdline/duk_cmdline.c
  15. 4
      src/duk_api_public.h.in
  16. 20
      src/duk_api_stack.c
  17. 76
      src/duk_error.h
  18. 7
      src/duk_error_longjmp.c
  19. 67
      src/duk_error_macros.c
  20. 2
      src/duk_heap.h
  21. 8
      src/duk_heap_alloc.c
  22. 4
      src/duk_internal.h
  23. 162
      src/duk_selftest.c
  24. 2
      src/duk_selftest.h
  25. 4
      testrunner/client-simple-node/run_commit_test.py
  26. 2
      tests/api/test-all-public-symbols.c
  27. 10
      tests/api/test-fatal.c
  28. 1
      util/matrix_compile.py
  29. 2
      website/api/defines.html
  30. 17
      website/api/duk_create_heap.yaml
  31. 10
      website/api/duk_fatal.yaml
  32. 15
      website/guide/compiling.html
  33. 73
      website/guide/programming.html

5
Makefile

@ -189,9 +189,8 @@ CCOPTS_FEATURES =
#CCOPTS_FEATURES += -DDUK_OPT_NO_REFERENCE_COUNTING
#CCOPTS_FEATURES += -DDUK_OPT_NO_MARK_AND_SWEEP
#CCOPTS_FEATURES += -DDUK_OPT_NO_VOLUNTARY_GC
CCOPTS_FEATURES += -DDUK_OPT_SEGFAULT_ON_PANIC # segfault on panic allows valgrind to show stack trace on panic
#CCOPTS_FEATURES += -DDUK_OPT_NO_FILE_IO
#CCOPTS_FEATURES += '-DDUK_OPT_PANIC_HANDLER(code,msg)={printf("*** %d:%s\n",(code),(msg));abort();}'
CCOPTS_FEATURES += '-DDUK_OPT_FATAL_HANDLER(udata,msg)=do { const char *fatal_msg = (msg); fprintf(stderr, "*** FATAL ERROR: %s\n", fatal_msg ? fatal_msg : "no message"); *((unsigned int *) 0) = (unsigned int) 0xdeadbeefUL; abort(); } while(0)'
CCOPTS_FEATURES += -DDUK_OPT_SELF_TESTS
#CCOPTS_FEATURES += -DDUK_OPT_NO_TRACEBACKS
#CCOPTS_FEATURES += -DDUK_OPT_NO_PC2LINE
@ -756,7 +755,7 @@ emscriptenduktest: emscripten dist
# and providing an eval() facility from both sides. This is a placeholder now
# and doesn't do anything useful yet.
EMCCOPTS_DUKWEB_EXPORT=-s EXPORTED_FUNCTIONS='["_dukweb_is_open", "_dukweb_open","_dukweb_close","_dukweb_eval"]'
EMCCOPTS_DUKWEB_DEFINES='-DDUK_OPT_DECLARE=extern void dukweb_panic_handler(int code, const char *msg);' '-DDUK_OPT_PANIC_HANDLER(code,msg)={dukweb_panic_handler((code),(msg));abort();}'
EMCCOPTS_DUKWEB_DEFINES='-DDUK_OPT_DECLARE=extern void dukweb_panic_handler(int code, const char *msg); extern void dukweb_fatal_handler(void *udata, const char *msg);' '-DDUK_OPT_FATAL_HANDLER(udata,msg)={dukweb_fatal_handler((udata),(msg));abort();}'
#EMCCOPTS_DUKWEB_DEFINES+=-DDUK_OPT_ASSERTIONS
EMCCOPTS_DUKWEB_DEFINES+=-DDUK_OPT_SELF_TESTS

14
RELEASES.rst

@ -1616,6 +1616,18 @@ Planned
debug writes when DUK_USE_DEBUG is enabled; this avoids a platform I/O
dependency and allows debug log filtering and retargeting (GH-782)
* Incompatible change: remove the distinction between panic and fatal errors,
and simplify the fatal error handler function signature to
``void my_fatal(void *udata, const char *msg);`` (GH-781)
* Incompatible change: remove error code argument from duk_fatal() API
call to match revised fatal error handler function signature (GH-781)
* Incompatible change: default fatal error handler (similar to panic handler
in Duktape 1.x) segfaults and infinite loops without calling e.g. abort()
(which may not be available); this behavior can be overridden by defining
DUK_USE_FATAL_HANDLER() in duk_config.h (GH-781)
* Add time functions to the C API (duk_get_now(), duk_time_to_components(),
duk_components_to_time()) to allow C code to conveniently work with the
same time provider as seen by Ecmascript code (GH-771)
@ -1632,7 +1644,7 @@ Planned
* Add an extra module providing Duktape 1.x compatible logging framework
(Duktape.Logger, duk_log(), duk_log_va()) (GH-746)
* Add an extra module with a minimal 'console' binding (GH-767)
* Add an extra module providing a minimal 'console' binding (GH-767)
* Internal change: rework shared internal string handling so that shared
strings are plain string constants used in macro values, rather than

27
config/config-options/DUK_USE_FATAL_HANDLER.yaml

@ -0,0 +1,27 @@
define: DUK_USE_FATAL_HANDLER
feature_snippet: |
#undef DUK_USE_FATAL_HANDLER
#if defined(DUK_OPT_FATAL_HANDLER)
#define DUK_USE_FATAL_HANDLER(udata,msg) DUK_OPT_FATAL_HANDLER((udata),(msg))
#endif
introduced: 2.0.0
default: false
tags:
- portability
description: >
Provide a custom default fatal error handler to replace the built-in one
(which causes an intentional segfault and forever loops). The default
fatal error gets called when (1) a fatal error occurs and application code
didn't register a fatal error handler in heap creation or (2) a context-free
fatal error happens, concretely e.g. an assertion failure.
The handler is called like a C function with the prototype
"void fatal_handler(void *udata, const char *msg)". The "msg" argument can
be NULL. The "udata" argument matches the heap-related userdata but is
NULL for fatal errors unrelated to a heap/thread context (this is the case
for e.g. assertions).
A custom default fatal error handler is recommended for any environment
where recover from fatal errors is important. A custom handler can take
appropriate action to recover, e.g. record the error and reboot the target
device.

1
config/config-options/DUK_USE_PANIC_ABORT.yaml

@ -5,6 +5,7 @@ feature_snippet: |
#define DUK_USE_PANIC_ABORT
#endif
introduced: 1.0.0
removed: 2.0.0
default: true
tags:
- portability

1
config/config-options/DUK_USE_PANIC_EXIT.yaml

@ -1,5 +1,6 @@
define: DUK_USE_PANIC_EXIT
introduced: 1.0.0
removed: 2.0.0
default: false
tags:
- portability

1
config/config-options/DUK_USE_PANIC_HANDLER.yaml

@ -5,6 +5,7 @@ feature_snippet: |
#define DUK_USE_PANIC_HANDLER(code,msg) DUK_OPT_PANIC_HANDLER((code),(msg))
#endif
introduced: 1.0.0
removed: 2.0.0
default: false
tags:
- portability

1
config/config-options/DUK_USE_PANIC_SEGFAULT.yaml

@ -5,6 +5,7 @@ feature_snippet: |
#define DUK_USE_PANIC_SEGFAULT
#endif
introduced: 1.0.0
removed: 2.0.0
default: false
tags:
- portability

6
config/header-snippets/compiler_fillins.h.in

@ -29,9 +29,9 @@
#endif
#if !defined(DUK_CAUSE_SEGFAULT)
/* This is optionally used by panic handling to cause the program to segfault
* (instead of e.g. abort()) on panic. Valgrind will then indicate the C
* call stack leading to the panic.
/* This is used by the default fatal error handling to cause the program to
* intentionally segfault on a fatal error. Valgrind will then indicate the
* C call stack leading to the error.
*/
#define DUK_CAUSE_SEGFAULT() do { *((volatile duk_uint32_t *) NULL) = (duk_uint32_t) 0xdeadbeefUL; } while (0)
#endif

6
dist-files/Makefile.cmdline

@ -19,11 +19,11 @@ CMDLINE_SOURCES += extras/print-alert/duk_print_alert.c
# Enable console object (console.log() etc) for command line.
CCOPTS += -DDUK_CMDLINE_CONSOLE_SUPPORT -I./extras/console
DUKTAPE_CMDLINE_SOURCES += extras/console/duk_console.c
CMDLINE_SOURCES += extras/console/duk_console.c
# Enable Duktape.Logger for command line.
CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extraslogging
DUKTAPE_CMDLINE_SOURCES += extras/logging/duk_logging.c
CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extras/logging
CMDLINE_SOURCES += extras/logging/duk_logging.c
# If you want linenoise, you can enable these. At the moment linenoise
# will cause some harmless compilation warnings.

50
doc/code-issues.rst

@ -645,6 +645,26 @@ with exotic pointer models::
p++;
}
Argument order
==============
Having a consistent argument order makes it easier to read and maintain code.
Also when the argument position of functions match it often saves some move
instructions in the compiled code.
Current conventions:
* The ``ctx`` or ``heap`` argument, if present, is always first.
* For callbacks, a userdata argument follows ``ctx`` or ``heap``; if neither
is present, the userdata argument is first. Same applies to user-defined
macros which accept a userdata argument (e.g. pointer compression macros).
* When registering a callback, the userdata argument to be given in later
callbacks is immediately after the callback(s) related to the userdata.
* Flags fields are typically last.
Symbol visibility
=================
@ -1908,13 +1928,13 @@ Calling platform functions
==========================
All platform function calls (ANSI C and other) are wrapped through macros
defined in ``duk_features.h``. For example, ``fwrite()`` calls are made
using ``DUK_FWRITE()``.
defined in ``duk_config.h``. For example, ``fwrite()`` calls are made using
``DUK_FWRITE()``.
Many of these wrappers are not currently needed but some are, so it's simplest
to wrap just about everything in case something needs to be tweaked. As an
example, on some old uclibc versions ``memcpy()`` is broken and can be
replaced with ``memmove()`` in ``duk_features.h``.
replaced with ``memmove()`` in ``duk_config.h``.
The only exception is platform specific Date built-in code. As this code is
always platform specific and contained to the Date code, wrapping them is not
@ -1940,31 +1960,23 @@ is more space for code and fixed data.
Feature defines
===============
Almost all feature detection is concentrated into ``duk_features.h`` which
considers inputs from various sources:
All feature detection is concentrated into ``duk_config.h`` which considers
inputs from various sources:
* ``DUK_OPT_xxx`` defines, which allow a user to request a specific feature
or provide a specific value (such as traceback depth)
or provide a specific value (such as traceback depth).
* Compiler and platform specific defines and features
* Compiler and platform specific defines and features.
As a result, ``duk_features.h`` defines ``DUK_USE_xxx`` macros which enable
As a result, ``duk_config.h`` defines ``DUK_USE_xxx`` macros which enable
and disable specific features and provide parameter values (such as traceback
depth). These are the **only** feature defines which should be used in
internal Duktape code.
The only exception so far is ``DUK_PANIC_HANDLER()`` in ``duk_error.h`` which
can be directly overridden by the user if necessary.
This basic approach is complicated a bit by the fact that ``duktape.h`` must
do some minimal platform feature detection to ensure that the public API uses
the correct types, etc. These are coordinated with ``duk_features.h``;
``duk_features.h`` either uses whatever ``duktape.h`` ended up using, or does
its own checking and ensures the two are consistent.
internal Duktape code. The ``duk_config.h`` defines, especially typedefs,
are also visible for the public API header.
When adding specific hacks and workarounds which might not be of interest
to all users, add a ``DUK_OPT_xxx`` flag for them and translate it to a
``DUK_USE_xxx`` flag in ``duk_features.h``. If the ``DUK_OPT_xxx`` flag
``DUK_USE_xxx`` flag in ``duk_config.h``. If the ``DUK_OPT_xxx`` flag
is absent, the custom behavior MUST NOT be enabled.
Platforms and compilers

7
doc/feature-options.rst

@ -5,8 +5,7 @@ Duktape feature options
Overview
========
The effective set of Duktape features is resolved in three steps in Duktape 1.x
(this process will change in Duktape 2.x):
The effective set of Duktape features is resolved in three steps in Duktape 1.x:
* User defines ``DUK_OPT_xxx`` feature options. These are essentially
requests to enable/disable some feature. (These will be removed in
@ -34,7 +33,9 @@ The effective set of Duktape features is resolved in three steps in Duktape 1.x
Starting from Duktape 1.3 an external ``duk_config.h`` is required; it may
be a prebuilt multi-platform header or a user-modified one. Duktape 2.x
will remove support for ``DUK_OPT_xxx`` feature options entirely.
will remove support for ``DUK_OPT_xxx`` feature options entirely so that
all config changes are made by editing ``duk_config.h`` or by regenerating
it e.g. using genconfig.
Feature option naming
=====================

64
doc/release-notes-v2-0.rst

@ -272,6 +272,70 @@ Debug print related config options were reworked as follows:
See http://wiki.duktape.org/HowtoDebugPrints.html for more information.
Fatal error and panic handling reworked
---------------------------------------
The following changes were made to fatal error and panic handling:
* Fatal error function signature was simplied from::
/* Duktape 1.x */
void func(duk_context *ctx, duk_errcode_t code, const char *msg);
to::
/* Duktape 2.x */
void func(void *udata, const char *msg);
where the ``udata`` argument is the userdata argument given in heap creation.
* ``duk_fatal()`` error code argument was removed to match the signature
change.
* The entire concept of "panic errors" was removed and replaced with calls to
the fatal error mechanism. There's a user-registered (optional) fatal error
handler in heap creation, and a built-in default fatal error handler which
is called if user code doesn't provide a fatal error handler.
Some fatal errors, currently assertion failures, happen without a Duktape
heap/thread context so that a user-registered handler cannot be called
(there's no heap reference to look it up). For these errors the default
fatal error handler is always called, with the userdata argument as ``NULL``.
The default fatal error handler can be replaced by editing ``duk_config.h``.
To upgrade:
* If you're not providing a fatal error handler nor using a custom panic
handler, no action is needed -- however, providing a fatal error handler
in heap creation is **strongly recommended**. The default fatal error
handler will by default cause an intentional segfault; to improve this
behavior define ``DUK_USE_FATAL_HANDLER()`` in your ``duk_config.h``.
* If you have a fatal error handler, update its signature::
/* Duktape 1.x */
void my_fatal(duk_context *ctx, duk_errcode_t error_code, const char *msg) {
/* ... */
}
/* Duktape 2.x */
void my_fatal(void *udata, const char *msg) {
/* ... */
}
* If you're using ``duk_fatal()`` API calls, remove the error code argument::
/* Duktape 1.x */
duk_fatal(ctx, DUK_ERR_INTERNAL_ERROR, "assumption failed");
/* Duktape 2.x */
duk_fatal(ctx, "assumption failed");
* If you have a custom panic handler in your ``duk_config.h``, convert it to
a default fatal error handler, also provided by ``duk_config.h``. Both
Duktape 1.x panic handler and Duktape 2.x default fatal error handler apply
to all Duktape heaps (rather than a specific Duktape heap).
Known issues
============

59
dukweb/dukweb.c

@ -22,23 +22,48 @@ static int dukweb__emscripten_run_script(duk_context *ctx) {
return 0;
}
void dukweb_panic_handler(int code, const char *msg) {
printf("dukweb_panic_handler(), code=%d, msg=%s\n", code, msg);
fflush(stdout);
/* FIXME: add code/msg to alert */
EM_ASM(
alert('Duktape panic handler called');
);
abort();
/* XXX: extract shared helper for string compatible escaping */
static void dukweb_fatal_helper(const char *msg, const char *prefix) {
char buf[4096];
char *p;
const char *q;
memset((void *) buf, 0, sizeof(buf));
p = buf;
p += sprintf(p, "%s('Duktape fatal error: ", prefix);
for (q = msg; *q; q++) {
size_t space = sizeof(buf) - (size_t) (q - buf);
unsigned char ch;
if (space < 8) {
break;
}
ch = (unsigned char) *q;
if (ch < 0x20 || ch >= 0x7e || ch == (unsigned char) '\'' || ch == (unsigned char) '"') {
/* Escape to remain compatible with a string literal.
* No UTF-8 handling now; shouldn't be needed as fatal
* error messages are typically ASCII.
*/
p += sprintf(p, "\\u%04x", (unsigned int) ch);
} else {
*p++ = (char) ch;
}
}
p += sprintf(p, "');");
*p++ = (char) 0;
emscripten_run_script((const char *) buf);
}
void dukweb_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) {
printf("dukweb_fatal_handler(), code=%d, msg=%s\n", (int) code, msg);
fflush(stdout);
/* FIXME: add code/msg to alert */
EM_ASM(
alert('Duktape fatal handler called');
);
void dukweb_fatal_handler(void *udata, const char *msg) {
(void) udata;
if (!msg) {
msg = "no message";
}
dukweb_fatal_helper(msg, "alert");
dukweb_fatal_helper(msg, "throw new Error");
abort();
}
@ -123,6 +148,10 @@ const char *dukweb_eval(const char *code) {
}
duk_set_top(ctx, 0);
#if 0
/* Test fatal error handling and its escaping */
duk_fatal(ctx, "aiee! {} ' + ' \" \n \xc0");
#endif
return (const char *) duk__evalbuf;
}

20
examples/cmdline/duk_cmdline.c

@ -167,6 +167,15 @@ static void set_sigint_handler(void) {
}
#endif /* DUK_CMDLINE_SIGNAL */
static void cmdline_fatal_handler(void *udata, const char *msg) {
(void) udata;
fprintf(stderr, "*** FATAL ERROR: %s\n", msg ? msg : "no message");
fprintf(stderr, "Causing intentional segfault...\n");
fflush(stderr);
*((unsigned int *) 0) = (unsigned int) 0xdeadbeefUL;
abort();
}
static int get_stack_raw(duk_context *ctx, void *udata) {
(void) udata;
@ -1035,7 +1044,7 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
duk_realloc_logging,
duk_free_logging,
(void *) 0xdeadbeef,
NULL);
cmdline_fatal_handler);
#else
fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n");
fflush(stderr);
@ -1047,7 +1056,7 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
duk_realloc_torture,
duk_free_torture,
(void *) 0xdeadbeef,
NULL);
cmdline_fatal_handler);
#else
fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n");
fflush(stderr);
@ -1064,7 +1073,7 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
duk_realloc_hybrid,
duk_free_hybrid,
udata,
NULL);
cmdline_fatal_handler);
}
#else
fprintf(stderr, "Warning: option --alloc-hybrid ignored, no hybrid allocator support\n");
@ -1080,15 +1089,14 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
ajsheap_log ? ajsheap_realloc_wrapped : AJS_Realloc,
ajsheap_log ? ajsheap_free_wrapped : AJS_Free,
(void *) 0xdeadbeef, /* heap_udata: ignored by AjsHeap, use as marker */
NULL
); /* fatal_handler */
cmdline_fatal_handler);
#else
fprintf(stderr, "Warning: option --alloc-ajsheap ignored, no ajsheap allocator support\n");
fflush(stderr);
#endif
}
if (!ctx && alloc_provider == ALLOC_DEFAULT) {
ctx = duk_create_heap_default();
ctx = duk_create_heap(NULL, NULL, NULL, NULL, cmdline_fatal_handler);
}
if (!ctx) {

4
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, ...));

20
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) {

76
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);

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

67
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

2
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;

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

4
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()

162
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

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

4
testrunner/client-simple-node/run_commit_test.py

@ -712,7 +712,7 @@ def context_linux_x64_minisphere():
# Unpack minisphere snapshot and copy Duktape files over.
#unpack_targz(os.path.join(repo_snapshot_dir, 'minisphere-20160328.tar.gz'))
unpack_targz(os.path.join(repo_snapshot_dir, 'minisphere-20160507.tar.gz'))
unpack_targz(os.path.join(repo_snapshot_dir, 'minisphere-20160515.tar.gz'))
for i in [ 'duktape.c', 'duktape.h', 'duk_config.h' ]:
execute([
@ -732,7 +732,7 @@ def context_linux_x64_dukluv():
# Unpack dukluv snapshot and symlink dukluv/lib/duktape to dist.
#unpack_targz(os.path.join(repo_snapshot_dir, 'dukluv-20160328.tar.gz'))
unpack_targz(os.path.join(repo_snapshot_dir, 'dukluv-20160508.tar.gz'))
unpack_targz(os.path.join(repo_snapshot_dir, 'dukluv-20160515.tar.gz'))
execute([
'mv',

2
tests/api/test-all-public-symbols.c

@ -77,7 +77,7 @@ static duk_ret_t test_func(duk_context *ctx, void *udata) {
(void) duk_eval_string_noresult(ctx, "dummy");
(void) duk_eval_string(ctx, "dummy");
(void) duk_eval(ctx);
(void) duk_fatal(ctx, 0, "dummy");
(void) duk_fatal(ctx, "dummy");
(void) duk_free_raw(ctx, NULL);
(void) duk_free(ctx, NULL);
(void) duk_gc(ctx, 0);

10
tests/api/test-fatal.c

@ -1,10 +1,12 @@
/*===
my_func
fatal error: 123456 (reason)
fatal error: my reason
===*/
void my_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) {
printf("fatal error: %d (%s)\n", (int) code, msg);
void my_fatal_handler(void *udata, const char *msg) {
(void) udata;
printf("fatal error: %s\n", msg);
/* A fatal error handler must not return, so exit here */
exit(0);
@ -14,7 +16,7 @@ static duk_ret_t my_func(duk_context *ctx, void *udata) {
(void) udata;
printf("my_func\n");
duk_fatal(ctx, 123456, "reason");
duk_fatal(ctx, "my reason");
printf("never here\n");
return 0;
}

1
util/matrix_compile.py

@ -322,7 +322,6 @@ def create_matrix(fn_duk):
'-DDUK_OPT_GC_TORTURE' ]),
'-DDUK_OPT_SHUFFLE_TORTURE',
'-DDUK_OPT_NO_VOLUNTARY_GC',
'-DDUK_OPT_SEGFAULT_ON_PANIC',
'-DDUK_OPT_NO_PACKED_TVAL',
Select([ '', '-DDUK_OPT_FORCE_ALIGN=4', '-DDUK_OPT_FORCE_ALIGN=8' ]),
'-DDUK_OPT_NO_TRACEBACKS',

2
website/api/defines.html

@ -52,7 +52,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);

17
website/api/duk_create_heap.yaml

@ -19,16 +19,19 @@ summary: |
memory management functions (ANSI C <code>malloc()</code>, <code>realloc()</code>
, and <code>free()</code>) are used. The memory management functions
share the same opaque userdata pointer, <code>heap_udata</code>. This
userdata pointer is also used for other Duktape features (like low memory
pointer compression macros).</p>
userdata pointer is also used for other Duktape features like fatal error
handling and low memory pointer compression macros.</p>
<p>A fatal error handler is provided in <code>fatal_handler</code>. This
handler is called in unrecoverable error situations such as uncaught
errors, out-of-memory errors not resolved by garbage collection, etc.
A caller SHOULD implement a fatal error handler in most applications.
If not given, a default fatal error handler is used. The default
handler ultimately calls ANSI C <code>abort()</code>, which may not always
be the preferred action.</p>
errors, out-of-memory errors not resolved by garbage collection, self test
errors, etc. A caller SHOULD implement a fatal error handler in most
applications. If not given, a default fatal error handler built into
Duktape is used instead. <b>Note that the default fatal error handler (unless
overridden by <code>duk_config.h</code>) causes an intentional segfault to
exit a process to avoid relying on platform API calls like abort().</b> See
<a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>
for more detail and examples.</p>
<p>To create a Duktape heap with default settings, use
<code><a href="#duk_create_heap_default">duk_create_heap_default()</a></code>.</p>

10
website/api/duk_fatal.yaml

@ -1,12 +1,12 @@
name: duk_fatal
proto: |
void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg);
void duk_fatal(duk_context *ctx, const char *err_msg);
summary: |
<p>Call fatal error handler with a specified error code and an optional
message (<code>err_msg</code> may be <code>NULL</code>). The valid range
for user error codes is [1,16777215].</p>
<p>Call fatal error handler with an optional message (<code>err_msg</code>
may be <code>NULL</code>). Like all strings in Duktape, the error message
should be an UTF-8 string, although pure ASCII is strongly recommended.</p>
<p>A fatal error handler never returns and may e.g. exit the current
process. Error catching points (like <code>try-catch</code> statements
@ -15,7 +15,7 @@ summary: |
has occurred.</p>
example: |
duk_fatal(ctx, DUK_ERR_INTERNAL_ERROR, "assumption failed");
duk_fatal(ctx, "assumption failed");
tags:
- error

15
website/guide/compiling.html

@ -160,16 +160,17 @@ may be used to improve performance by e.g. enabling fast paths, enabling
<a href="https://github.com/svaarala/duktape/blob/master/doc/performance-sensitive.rst">performance-sensitive.rst</a>
for suggested feature options.</p>
<h2>DUK_OPT_PANIC_HANDLER</h2>
<h2>DUK_USE_FATAL_HANDLER</h2>
<p>The default panic handler will print an error message to stdout unless I/O is
disabled by <code>DUK_OPT_NO_FILE_IO</code>. It will then call <code>abort()</code>
or cause a segfault if <code>DUK_OPT_SEGFAULT_ON_PANIC</code> is defined.</p>
<p>The built-in default fatal error handler will write a debug log message
(but <b>won't</b> write anything to <code>stdout</code> to <code>stderr</code>),
and will then cause an <b>intentional segfault</b>; if that fails, it enters an
infinite loop to ensure execution doesn't resume after a fatal error.</p>
<p>This is not always the best behavior for production applications which may
already have better panic recovery mechanisms. To replace the default panic
<p>This is usually not the best behavior for production applications which may
already have better fatal error recovery mechanisms. To replace the default fatal
handler, see
<a href="http://wiki.duktape.org/FeatureOptions.html">Duktape feature options</a>.</p>
<a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>.</p>
<h2>Memory management alternatives</h2>

73
website/guide/programming.html

@ -602,8 +602,9 @@ handled. However, instead of a try-catch statement application code uses
Duktape API calls to establish points in C code where errors can be caught
and handled.
An uncaught error causes the fatal error handler to be called, which is
considered an unrecoverable situation and should ordinarily be avoided
(see <a href="#error-fatal-panic">Error, fatal, and panic</a>).</p>
considered an unrecoverable situation and should ordinarily be avoided,
see <a href="#normal-and-fatal-errors">Normal and fatal errors</a> and
<a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>.</p>
<p>To avoid fatal errors, typical application code should establish an error
catch point before making other Duktape API calls. This is done using
@ -688,42 +689,46 @@ applications would typically simply exit when a fatal error occurs. Even
without an actual recovery strategy, a fatal error handler should be used to
e.g. write fatal error information to <code>stderr</code> before process exit.</p>
<h2 id="error-fatal-panic">Error, fatal, and panic</h2>
<a name="error-fatal-panic"></a> <!-- for old links -->
<h2 id="normal-and-fatal-errors">Normal and fatal errors</h2>
<p>An <b>ordinary error</b> is caused by a <code>throw</code> statement, a
<code><a href="api.html#duk_throw">duk_throw()</a></code>
API call (or similar), or by an internal,
recoverable Duktape error. Ordinary errors can be caught with a
<code>try-catch</code> in Ecmascript code or e.g.
<code><a href="api.html#duk_pcall">duk_pcall()</a></code>
API call (or similar), or by an internal, recoverable Duktape error.
Ordinary errors can be caught with a <code>try-catch</code> in Ecmascript
code or e.g. <code><a href="api.html#duk_pcall">duk_pcall()</a></code>
(see API calls tagged
<code><a href="api.html#taglist-protected">protected</a></code>)
in C code.</p>
<p>An uncaught error or an explicit call to
<code><a href="api.html#duk_fatal">duk_fatal()</a></code>
causes a <b>fatal error</b> handler to be called. A fatal error handler is
associated with every Duktape heap upon creation. There is no reasonable way
to resume execution after a fatal error, so the fatal error handler must not
return. The default fatal error handler writes an error message to <code>stderr</code>
and then escalates the fatal error to a panic (which, by default,
<code>abort()</code>s the process). You can provide your own fatal error
handler to deal with fatal errors. The most appropriate recovery action is,
of course, platform and application specific. The handler could, for instance,
write a diagnostic file detailing the situation and then restart the application
to recover.</p>
<p>A <b>panic</b> is caused by Duktape assertion code (if included in the build)
or by the default fatal error handler. There is no way to induce a panic from
user code. The default panic handler writes an error message to <code>stderr</code>
and <code>abort()</code>s the process. You can use the
<code>DUK_OPT_SEGFAULT_ON_PANIC</code> feature option to cause a deliberate
segfault instead of an <code>abort()</code>, which may be useful to get a stack
trace from some debugging tools. You can also override the default panic
handler entirely with the feature option <code>DUK_OPT_PANIC_HANDLER</code>.
The panic handler is decided during build, while the fatal error handler is
decided at runtime by the calling application.</p>
<p>If assertions are turned off and the application provides a fatal error
handler, no panics will be caused by Duktape code. All errors will then be
either ordinary errors or fatal errors, both under application control.</p>
<p>A <b>fatal error</b> is caused by an uncaught error, an explicit call to
<code><a href="api.html#duk_fatal">duk_fatal()</a></code>, or an
unrecoverable error inside Duktape. Each Duktape heap has a heap-wide fatal
error handler registered in
<code><a href="api.html#duk_create_heap">duk_create_heap()</a></code>; if no
handler is given a built-in default handler is used. There's no safe way to
resume execution after a fatal error, so that a fatal error handler must not
return or call into the Duktape API. Instead the handler should e.g. write
out the message to the console or a log file and then exit the process (or
restart an embedded device).</p>
<p>Fatal errors may also happen without any heap context, so that Duktape
can't look up a possible heap-specific fatal error handler. Duktape will then
always call the built-in default fatal error handler (with the handler
userdata argument set to NULL). Fatal errors handled this way are currently
limited to assertion failures, so that if you don't enable assertions, no such
errors will currently occur. All fatal error handling goes through the
heap-associated fatal error handler which is in direct application control.</p>
<div class="note">
The built-in default fatal error handler will write a debug log message
(but <b>won't</b> write anything to <code>stdout</code> to <code>stderr</code>),
and will then cause an <b>intentional segfault</b>; if that fails, it enters an
infinite loop to ensure execution doesn't resume after a fatal error. It is
<b>strongly recommended</b> to both (1) provide a custom fatal error handler
in heap creation and (2) replace the built-in default fatal error handler using
the <code>DUK_USE_FATAL_HANDLER()</code> option in <code>duk_config.h</code>.
</div>
<p>See <a href="http://wiki.duktape.org/HowtoFatalErrors.html">How to handle fatal errors</a>
for more detail and examples.</p>

Loading…
Cancel
Save