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_REFERENCE_COUNTING
#CCOPTS_FEATURES += -DDUK_OPT_NO_MARK_AND_SWEEP #CCOPTS_FEATURES += -DDUK_OPT_NO_MARK_AND_SWEEP
#CCOPTS_FEATURES += -DDUK_OPT_NO_VOLUNTARY_GC #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_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_SELF_TESTS
#CCOPTS_FEATURES += -DDUK_OPT_NO_TRACEBACKS #CCOPTS_FEATURES += -DDUK_OPT_NO_TRACEBACKS
#CCOPTS_FEATURES += -DDUK_OPT_NO_PC2LINE #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 providing an eval() facility from both sides. This is a placeholder now
# and doesn't do anything useful yet. # and doesn't do anything useful yet.
EMCCOPTS_DUKWEB_EXPORT=-s EXPORTED_FUNCTIONS='["_dukweb_is_open", "_dukweb_open","_dukweb_close","_dukweb_eval"]' 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_ASSERTIONS
EMCCOPTS_DUKWEB_DEFINES+=-DDUK_OPT_SELF_TESTS 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 debug writes when DUK_USE_DEBUG is enabled; this avoids a platform I/O
dependency and allows debug log filtering and retargeting (GH-782) 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(), * 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 duk_components_to_time()) to allow C code to conveniently work with the
same time provider as seen by Ecmascript code (GH-771) 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 * Add an extra module providing Duktape 1.x compatible logging framework
(Duktape.Logger, duk_log(), duk_log_va()) (GH-746) (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 * Internal change: rework shared internal string handling so that shared
strings are plain string constants used in macro values, rather than 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 #define DUK_USE_PANIC_ABORT
#endif #endif
introduced: 1.0.0 introduced: 1.0.0
removed: 2.0.0
default: true default: true
tags: tags:
- portability - portability

1
config/config-options/DUK_USE_PANIC_EXIT.yaml

@ -1,5 +1,6 @@
define: DUK_USE_PANIC_EXIT define: DUK_USE_PANIC_EXIT
introduced: 1.0.0 introduced: 1.0.0
removed: 2.0.0
default: false default: false
tags: tags:
- portability - 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)) #define DUK_USE_PANIC_HANDLER(code,msg) DUK_OPT_PANIC_HANDLER((code),(msg))
#endif #endif
introduced: 1.0.0 introduced: 1.0.0
removed: 2.0.0
default: false default: false
tags: tags:
- portability - portability

1
config/config-options/DUK_USE_PANIC_SEGFAULT.yaml

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

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

@ -29,9 +29,9 @@
#endif #endif
#if !defined(DUK_CAUSE_SEGFAULT) #if !defined(DUK_CAUSE_SEGFAULT)
/* This is optionally used by panic handling to cause the program to segfault /* This is used by the default fatal error handling to cause the program to
* (instead of e.g. abort()) on panic. Valgrind will then indicate the C * intentionally segfault on a fatal error. Valgrind will then indicate the
* call stack leading to the panic. * C call stack leading to the error.
*/ */
#define DUK_CAUSE_SEGFAULT() do { *((volatile duk_uint32_t *) NULL) = (duk_uint32_t) 0xdeadbeefUL; } while (0) #define DUK_CAUSE_SEGFAULT() do { *((volatile duk_uint32_t *) NULL) = (duk_uint32_t) 0xdeadbeefUL; } while (0)
#endif #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. # Enable console object (console.log() etc) for command line.
CCOPTS += -DDUK_CMDLINE_CONSOLE_SUPPORT -I./extras/console 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. # Enable Duktape.Logger for command line.
CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extraslogging CCOPTS += -DDUK_CMDLINE_LOGGING_SUPPORT -I./extras/logging
DUKTAPE_CMDLINE_SOURCES += extras/logging/duk_logging.c CMDLINE_SOURCES += extras/logging/duk_logging.c
# If you want linenoise, you can enable these. At the moment linenoise # If you want linenoise, you can enable these. At the moment linenoise
# will cause some harmless compilation warnings. # will cause some harmless compilation warnings.

50
doc/code-issues.rst

@ -645,6 +645,26 @@ with exotic pointer models::
p++; 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 Symbol visibility
================= =================
@ -1908,13 +1928,13 @@ Calling platform functions
========================== ==========================
All platform function calls (ANSI C and other) are wrapped through macros All platform function calls (ANSI C and other) are wrapped through macros
defined in ``duk_features.h``. For example, ``fwrite()`` calls are made defined in ``duk_config.h``. For example, ``fwrite()`` calls are made using
using ``DUK_FWRITE()``. ``DUK_FWRITE()``.
Many of these wrappers are not currently needed but some are, so it's simplest 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 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 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 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 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 Feature defines
=============== ===============
Almost all feature detection is concentrated into ``duk_features.h`` which All feature detection is concentrated into ``duk_config.h`` which considers
considers inputs from various sources: inputs from various sources:
* ``DUK_OPT_xxx`` defines, which allow a user to request a specific feature * ``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 and disable specific features and provide parameter values (such as traceback
depth). These are the **only** feature defines which should be used in depth). These are the **only** feature defines which should be used in
internal Duktape code. internal Duktape code. The ``duk_config.h`` defines, especially typedefs,
are also visible for the public API header.
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.
When adding specific hacks and workarounds which might not be of interest 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 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. is absent, the custom behavior MUST NOT be enabled.
Platforms and compilers Platforms and compilers

7
doc/feature-options.rst

@ -5,8 +5,7 @@ Duktape feature options
Overview Overview
======== ========
The effective set of Duktape features is resolved in three steps in Duktape 1.x The effective set of Duktape features is resolved in three steps in Duktape 1.x:
(this process will change in Duktape 2.x):
* User defines ``DUK_OPT_xxx`` feature options. These are essentially * User defines ``DUK_OPT_xxx`` feature options. These are essentially
requests to enable/disable some feature. (These will be removed in 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 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 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 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. 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 Known issues
============ ============

59
dukweb/dukweb.c

@ -22,23 +22,48 @@ static int dukweb__emscripten_run_script(duk_context *ctx) {
return 0; return 0;
} }
void dukweb_panic_handler(int code, const char *msg) { /* XXX: extract shared helper for string compatible escaping */
printf("dukweb_panic_handler(), code=%d, msg=%s\n", code, msg); static void dukweb_fatal_helper(const char *msg, const char *prefix) {
fflush(stdout); char buf[4096];
/* FIXME: add code/msg to alert */ char *p;
EM_ASM( const char *q;
alert('Duktape panic handler called');
); memset((void *) buf, 0, sizeof(buf));
abort(); 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) { void dukweb_fatal_handler(void *udata, const char *msg) {
printf("dukweb_fatal_handler(), code=%d, msg=%s\n", (int) code, msg); (void) udata;
fflush(stdout);
/* FIXME: add code/msg to alert */ if (!msg) {
EM_ASM( msg = "no message";
alert('Duktape fatal handler called'); }
); dukweb_fatal_helper(msg, "alert");
dukweb_fatal_helper(msg, "throw new Error");
abort(); abort();
} }
@ -123,6 +148,10 @@ const char *dukweb_eval(const char *code) {
} }
duk_set_top(ctx, 0); 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; 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 */ #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) { static int get_stack_raw(duk_context *ctx, void *udata) {
(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_realloc_logging,
duk_free_logging, duk_free_logging,
(void *) 0xdeadbeef, (void *) 0xdeadbeef,
NULL); cmdline_fatal_handler);
#else #else
fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n"); fprintf(stderr, "Warning: option --alloc-logging ignored, no logging allocator support\n");
fflush(stderr); fflush(stderr);
@ -1047,7 +1056,7 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
duk_realloc_torture, duk_realloc_torture,
duk_free_torture, duk_free_torture,
(void *) 0xdeadbeef, (void *) 0xdeadbeef,
NULL); cmdline_fatal_handler);
#else #else
fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n"); fprintf(stderr, "Warning: option --alloc-torture ignored, no torture allocator support\n");
fflush(stderr); fflush(stderr);
@ -1064,7 +1073,7 @@ static duk_context *create_duktape_heap(int alloc_provider, int debugger, int aj
duk_realloc_hybrid, duk_realloc_hybrid,
duk_free_hybrid, duk_free_hybrid,
udata, udata,
NULL); cmdline_fatal_handler);
} }
#else #else
fprintf(stderr, "Warning: option --alloc-hybrid ignored, no hybrid allocator support\n"); 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_realloc_wrapped : AJS_Realloc,
ajsheap_log ? ajsheap_free_wrapped : AJS_Free, ajsheap_log ? ajsheap_free_wrapped : AJS_Free,
(void *) 0xdeadbeef, /* heap_udata: ignored by AjsHeap, use as marker */ (void *) 0xdeadbeef, /* heap_udata: ignored by AjsHeap, use as marker */
NULL cmdline_fatal_handler);
); /* fatal_handler */
#else #else
fprintf(stderr, "Warning: option --alloc-ajsheap ignored, no ajsheap allocator support\n"); fprintf(stderr, "Warning: option --alloc-ajsheap ignored, no ajsheap allocator support\n");
fflush(stderr); fflush(stderr);
#endif #endif
} }
if (!ctx && alloc_provider == ALLOC_DEFAULT) { if (!ctx && alloc_provider == ALLOC_DEFAULT) {
ctx = duk_create_heap_default(); ctx = duk_create_heap(NULL, NULL, NULL, NULL, cmdline_fatal_handler);
} }
if (!ctx) { 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_alloc_function) (void *udata, duk_size_t size);
typedef void *(*duk_realloc_function) (void *udata, void *ptr, 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_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 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_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); 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_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, ...)); 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); 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 /* 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 * need to check that here. If the value is NULL, a fatal error occurs
* we can't return. * because we can't return.
*/ */
duk_err_longjmp(thr); duk_err_longjmp(thr);
DUK_UNREACHABLE(); 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_hthread *thr = (duk_hthread *) ctx;
DUK_ASSERT_CTX_VALID(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 != NULL);
DUK_ASSERT(thr->heap->fatal_func != NULL); DUK_ASSERT(thr->heap->fatal_func != NULL);
DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s", DUK_D(DUK_DPRINT("fatal error occurred: %s", err_msg ? err_msg : "NULL"));
(long) err_code, (const char *) err_msg));
/* fatal_func should be noreturn, but noreturn declarations on function /* fatal_func should be noreturn, but noreturn declarations on function
* pointers has a very spotty support apparently so it's not currently * pointers has a very spotty support apparently so it's not currently
* done. * 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) { 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. * 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. * 1. Ordinary errors relative to a thread, cause a longjmp, catchable.
* 2. Fatal errors, relative to a heap, cause fatal handler to be called. * 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. * 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 * Fatal errors without context are used by debug code such as assertions.
* such as assertions. By providing a proper fatal error handler, user * By providing a fatal error handler for a Duktape heap, user code can
* code can avoid panics in non-debug builds. * avoid fatal errors without context in non-debug builds.
*/ */
#ifndef DUK_ERROR_H_INCLUDED #ifndef DUK_ERROR_H_INCLUDED
@ -127,66 +128,41 @@
#endif /* DUK_USE_VERBOSE_ERRORS */ #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 * The macro is an expression to make it compatible with DUK_ASSERT_EXPR().
* sites that the fatal error handler is called directly.
*/ */
/* #define DUK_FATAL_WITHOUT_CONTEXT(msg) \
* Panic error duk_default_fatal_handler(NULL, (msg))
*
* 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 */
/* /*
* 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) #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 * we don't care about assertion text size because they're not used in production
* builds. * builds.
*/ */
#define DUK_ASSERT(x) do { \ #define DUK_ASSERT(x) do { \
if (!(x)) { \ if (!(x)) { \
DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \ DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \
"assertion failed: " #x \
" (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \ " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"); \
} \ } \
} while (0) } while (0)
/* Assertion compatible inside a comma expression, evaluates to void. /* 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
#define DUK_ASSERT_EXPR(x) \ #define DUK_ASSERT_EXPR(x) \
((void) ((x) ? 0 : (DUK_PANIC(DUK_ERR_ASSERTION_ERROR, \ ((void) ((x) ? 0 : (DUK_FATAL_WITHOUT_CONTEXT("assertion failed: " #x \
"assertion failed: " #x \
" (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), 0))) " (" DUK_FILE_MACRO ":" DUK_MACRO_STRINGIFY(DUK_LINE_MACRO) ")"), 0)))
#endif
#else /* DUK_USE_ASSERTIONS */ #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_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)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, 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_INTERNAL_DECL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type); 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)); &thr->heap->lj.value1, &thr->heap->lj.value2));
#if !defined(DUK_USE_CPP_EXCEPTIONS) #if !defined(DUK_USE_CPP_EXCEPTIONS)
/* If we don't have a jmpbuf_ptr, there is little we can do /* If we don't have a jmpbuf_ptr, there is little we can do except
* except panic. The caller's expectation is that we never * cause a fatal error. The caller's expectation is that we never
* return. * return.
* *
* With C++ exceptions we now just propagate an uncaught error * 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. * a dummy jmpbuf for C++ exceptions now, this could be changed.
*/ */
if (!thr->heap->lj.jmpbuf_ptr) { if (!thr->heap->lj.jmpbuf_ptr) {
DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T", DUK_D(DUK_DPRINT("uncaught error: type=%d iserror=%d value1=%!T value2=%!T",
(int) thr->heap->lj.type, (int) thr->heap->lj.iserror, (int) thr->heap->lj.type, (int) thr->heap->lj.iserror,
&thr->heap->lj.value1, &thr->heap->lj.value2)); &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(); DUK_UNREACHABLE();
} }
#endif /* DUK_USE_CPP_EXCEPTIONS */ #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" #include "duk_internal.h"
@ -104,58 +104,29 @@ DUK_INTERNAL void duk_err_alloc(duk_hthread *thr) {
* Default fatal error handler * Default fatal error handler
*/ */
DUK_INTERNAL void duk_default_fatal_handler(duk_context *ctx, duk_errcode_t code, const char *msg) { DUK_INTERNAL void duk_default_fatal_handler(void *udata, const char *msg) {
DUK_UNREF(ctx); DUK_UNREF(udata);
#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_UNREF(msg); DUK_UNREF(msg);
#endif
#if defined(DUK_USE_PANIC_ABORT) #if defined(DUK_USE_FATAL_HANDLER)
DUK_ABORT(); /* duk_config.h provided a custom default fatal handler. */
#elif defined(DUK_USE_PANIC_EXIT) DUK_D(DUK_DPRINT("custom default fatal error handler called: %s", msg ? msg : "NULL"));
DUK_EXIT(-1); DUK_USE_FATAL_HANDLER(udata, msg);
#elif defined(DUK_USE_PANIC_SEGFAULT)
/* exit() afterwards to satisfy "noreturn" */
DUK_CAUSE_SEGFAULT(); /* SCANBUILD: "Dereference of null pointer", normal */
DUK_EXIT(-1);
#else #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 #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 #undef DUK__ERRFMT_BUFSIZE

2
src/duk_heap.h

@ -351,7 +351,7 @@ struct duk_heap {
duk_free_function free_func; duk_free_function free_func;
/* Heap udata, used for allocator functions but also for other heap /* 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; void *heap_udata;

8
src/duk_heap_alloc.c

@ -748,12 +748,12 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
#endif #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) #if defined(DUK_USE_SELF_TESTS)
DUK_D(DUK_DPRINT("running self tests")); if (duk_selftest_run_tests() > 0) {
duk_selftest_run_tests(); fatal_func(heap_udata, "self test(s) failed");
DUK_D(DUK_DPRINT("self tests passed")); }
#endif #endif
/* /*

4
src/duk_internal.h

@ -27,9 +27,7 @@
/* /*
* User declarations, e.g. prototypes for user functions used by Duktape * User declarations, e.g. prototypes for user functions used by Duktape
* macros. Concretely, if DUK_USE_PANIC_HANDLER is used and the macro * macros.
* value calls a user function, it needs to be declared for Duktape
* compilation to avoid warnings.
*/ */
DUK_USE_USER_DECLARE() DUK_USE_USER_DECLARE()

162
src/duk_selftest.c

@ -16,15 +16,21 @@ typedef union {
duk_uint8_t c[8]; duk_uint8_t c[8];
} duk__test_double_union; } 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 { \ #define DUK__DBLUNION_CMP_TRUE(a,b) do { \
if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) != 0) { \ 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) } while (0)
#define DUK__DBLUNION_CMP_FALSE(a,b) do { \ #define DUK__DBLUNION_CMP_FALSE(a,b) do { \
if (DUK_MEMCMP((const void *) (a), (const void *) (b), sizeof(duk__test_double_union)) == 0) { \ 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) } while (0)
@ -37,19 +43,21 @@ typedef union {
* Various sanity checks for typing * 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 && if (!(sizeof(duk_int8_t) == 1 &&
sizeof(duk_uint8_t) == 1 && sizeof(duk_uint8_t) == 1 &&
sizeof(duk_int16_t) == 2 && sizeof(duk_int16_t) == 2 &&
sizeof(duk_uint16_t) == 2 && sizeof(duk_uint16_t) == 2 &&
sizeof(duk_int32_t) == 4 && sizeof(duk_int32_t) == 4 &&
sizeof(duk_uint32_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 defined(DUK_USE_64BIT_OPS)
if (!(sizeof(duk_int64_t) == 8 && if (!(sizeof(duk_int64_t) == 8 &&
sizeof(duk_uint64_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 #endif
@ -57,30 +65,37 @@ DUK_LOCAL void duk__selftest_types(void) {
/* Some internal code now assumes that all duk_uint_t values /* Some internal code now assumes that all duk_uint_t values
* can be expressed with a duk_size_t. * 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)) { 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 * 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 defined(DUK_USE_PACKED_TVAL)
if (sizeof(void *) > 4) { 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 #endif
return error_count;
} }
/* /*
* Two's complement arithmetic. * 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; volatile int test;
test = -1; test = -1;
@ -88,8 +103,10 @@ DUK_LOCAL void duk__selftest_twos_complement(void) {
* 'test' will be 0xFF for two's complement. * 'test' will be 0xFF for two's complement.
*/ */
if (((volatile duk_uint8_t *) &test)[0] != (duk_uint8_t) 0xff) { 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. * 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_u32_union u1;
duk__test_double_union u2; duk__test_double_union u2;
@ -130,19 +148,22 @@ DUK_LOCAL void duk__selftest_byte_order(void) {
#endif #endif
if (u1.i != (duk_uint32_t) 0xdeadbeefUL) { 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) { 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_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_uint32_t x32;
duk_uint16_t x16; duk_uint16_t x16;
duk_double_union du; duk_double_union du;
@ -151,13 +172,13 @@ DUK_LOCAL void duk__selftest_bswap_macros(void) {
x16 = 0xbeefUL; x16 = 0xbeefUL;
x16 = DUK_BSWAP16(x16); x16 = DUK_BSWAP16(x16);
if (x16 != (duk_uint16_t) 0xefbeUL) { if (x16 != (duk_uint16_t) 0xefbeUL) {
DUK_PANIC(DUK_ERR_INTERNAL_ERROR, "self test failed: DUK_BSWAP16"); DUK__FAILED("DUK_BSWAP16");
} }
x32 = 0xdeadbeefUL; x32 = 0xdeadbeefUL;
x32 = DUK_BSWAP32(x32); x32 = DUK_BSWAP32(x32);
if (x32 != (duk_uint32_t) 0xefbeaddeUL) { 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')) /* >>> struct.unpack('>d', '4000112233445566'.decode('hex'))
@ -169,7 +190,7 @@ DUK_LOCAL void duk__selftest_bswap_macros(void) {
DUK_DBLUNION_DOUBLE_NTOH(&du); DUK_DBLUNION_DOUBLE_NTOH(&du);
du_diff = du.d - 2.008366013071895; du_diff = du.d - 2.008366013071895;
#if 0 #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 #endif
if (du_diff > 1e-15) { if (du_diff > 1e-15) {
/* Allow very small lenience because some compilers won't parse /* 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). * Linux gcc-4.8 -m32 at least).
*/ */
#if 0 #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[0], (unsigned int) du.uc[1],
(unsigned int) du.uc[2], (unsigned int) du.uc[3], (unsigned int) du.uc[2], (unsigned int) du.uc[3],
(unsigned int) du.uc[4], (unsigned int) du.uc[5], (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 #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. * 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) { 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. * Union aliasing, see misc/clang_aliasing.c.
*/ */
DUK_LOCAL void duk__selftest_double_aliasing(void) { DUK_LOCAL duk_uint_t duk__selftest_double_aliasing(void) {
duk__test_double_union a, b;
/* This testcase fails when Emscripten-generated code runs on Firefox. /* This testcase fails when Emscripten-generated code runs on Firefox.
* It's not an issue because the failure should only affect packed * It's not an issue because the failure should only affect packed
* duk_tval representation, which is not used with Emscripten. * duk_tval representation, which is not used with Emscripten.
*/ */
#if !defined(DUK_USE_PACKED_TVAL) #if defined(DUK_USE_PACKED_TVAL)
DUK_D(DUK_DPRINT("skip double aliasing self test when duk_tval is not packed")); duk_uint_t error_count = 0;
return; duk__test_double_union a, b;
#endif
/* Test signaling NaN and alias assignment in all endianness combinations. /* 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; a.c[4] = 0x11; a.c[5] = 0x22; a.c[6] = 0x33; a.c[7] = 0x44;
b = a; b = a;
DUK__DBLUNION_CMP_TRUE(&a, &b); 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. * 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; duk__test_double_union a, b;
a.d = 0.0; a.d = 0.0;
b.d = -a.d; b.d = -a.d;
DUK__DBLUNION_CMP_FALSE(&a, &b); 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. * 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 (DUK_USE_ALIGN_BY == 4)
if ((sizeof(duk_hbuffer_fixed) % 4) != 0) { 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) #elif (DUK_USE_ALIGN_BY == 8)
if ((sizeof(duk_hbuffer_fixed) % 8) != 0) { 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) #elif (DUK_USE_ALIGN_BY == 1)
/* no check */ /* no check */
#else #else
#error invalid DUK_USE_ALIGN_BY #error invalid DUK_USE_ALIGN_BY
#endif #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. * 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) #if defined(DUK_USE_64BIT_OPS)
volatile duk_int64_t i; volatile duk_int64_t i;
volatile duk_double_t d; volatile duk_double_t d;
@ -286,22 +323,25 @@ DUK_LOCAL void duk__selftest_64bit_arithmetic(void) {
d = 2147483648.0; d = 2147483648.0;
i = (duk_int64_t) d; i = (duk_int64_t) d;
if (i != 0x80000000LL) { 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 #else
/* nop */ /* nop */
#endif #endif
return error_count;
} }
/* /*
* Casting * 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 * https://github.com/svaarala/duktape/issues/127#issuecomment-77863473
*/ */
duk_uint_t error_count = 0;
duk_double_t d1, d2; duk_double_t d1, d2;
duk_small_uint_t u; 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; d2 = (duk_double_t) u;
if (!(d1 == 1.0 && u == 1 && d2 == 1.0 && d1 == d2)) { 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 */ /* Same test with volatiles */
@ -325,11 +365,13 @@ DUK_LOCAL void duk__selftest_cast_double_to_small_uint(void) {
d2v = (duk_double_t) uv; d2v = (duk_double_t) uv;
if (!(d1v == 1.0 && uv == 1 && d2v == 1.0 && d1v == d2v)) { 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 * This test fails on an exotic ARM target; double-to-uint
* cast is incorrectly clamped to -signed- int highest value. * 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 * https://github.com/svaarala/duktape/issues/336
*/ */
duk_uint_t error_count = 0;
duk_double_t dv; duk_double_t dv;
duk_uint32_t uv; duk_uint32_t uv;
@ -344,29 +387,40 @@ DUK_LOCAL void duk__selftest_cast_double_to_uint32(void) {
uv = (duk_uint32_t) dv; uv = (duk_uint32_t) dv;
if (uv != 0xdeadbeefUL) { 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 * Self test main
*/ */
DUK_INTERNAL void duk_selftest_run_tests(void) { DUK_INTERNAL duk_uint_t duk_selftest_run_tests(void) {
duk__selftest_types(); duk_uint_t error_count = 0;
duk__selftest_packed_tval();
duk__selftest_twos_complement(); DUK_D(DUK_DPRINT("self test starting"));
duk__selftest_byte_order();
duk__selftest_bswap_macros(); error_count += duk__selftest_types();
duk__selftest_double_union_size(); error_count += duk__selftest_packed_tval();
duk__selftest_double_aliasing(); error_count += duk__selftest_twos_complement();
duk__selftest_double_zero_sign(); error_count += duk__selftest_byte_order();
duk__selftest_struct_align(); error_count += duk__selftest_bswap_macros();
duk__selftest_64bit_arithmetic(); error_count += duk__selftest_double_union_size();
duk__selftest_cast_double_to_small_uint(); error_count += duk__selftest_double_aliasing();
duk__selftest_cast_double_to_uint32(); 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_TRUE
#undef DUK__DBLUNION_CMP_FALSE #undef DUK__DBLUNION_CMP_FALSE

2
src/duk_selftest.h

@ -6,7 +6,7 @@
#define DUK_SELFTEST_H_INCLUDED #define DUK_SELFTEST_H_INCLUDED
#if defined(DUK_USE_SELF_TESTS) #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
#endif /* DUK_SELFTEST_H_INCLUDED */ #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 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-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' ]: for i in [ 'duktape.c', 'duktape.h', 'duk_config.h' ]:
execute([ execute([
@ -732,7 +732,7 @@ def context_linux_x64_dukluv():
# Unpack dukluv snapshot and symlink dukluv/lib/duktape to dist. # 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-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([ execute([
'mv', '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_noresult(ctx, "dummy");
(void) duk_eval_string(ctx, "dummy"); (void) duk_eval_string(ctx, "dummy");
(void) duk_eval(ctx); (void) duk_eval(ctx);
(void) duk_fatal(ctx, 0, "dummy"); (void) duk_fatal(ctx, "dummy");
(void) duk_free_raw(ctx, NULL); (void) duk_free_raw(ctx, NULL);
(void) duk_free(ctx, NULL); (void) duk_free(ctx, NULL);
(void) duk_gc(ctx, 0); (void) duk_gc(ctx, 0);

10
tests/api/test-fatal.c

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

1
util/matrix_compile.py

@ -322,7 +322,6 @@ def create_matrix(fn_duk):
'-DDUK_OPT_GC_TORTURE' ]), '-DDUK_OPT_GC_TORTURE' ]),
'-DDUK_OPT_SHUFFLE_TORTURE', '-DDUK_OPT_SHUFFLE_TORTURE',
'-DDUK_OPT_NO_VOLUNTARY_GC', '-DDUK_OPT_NO_VOLUNTARY_GC',
'-DDUK_OPT_SEGFAULT_ON_PANIC',
'-DDUK_OPT_NO_PACKED_TVAL', '-DDUK_OPT_NO_PACKED_TVAL',
Select([ '', '-DDUK_OPT_FORCE_ALIGN=4', '-DDUK_OPT_FORCE_ALIGN=8' ]), Select([ '', '-DDUK_OPT_FORCE_ALIGN=4', '-DDUK_OPT_FORCE_ALIGN=8' ]),
'-DDUK_OPT_NO_TRACEBACKS', '-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_alloc_function) (void *udata, duk_size_t size);
typedef void *(*duk_realloc_function) (void *udata, void *ptr, 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_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 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_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); 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> memory management functions (ANSI C <code>malloc()</code>, <code>realloc()</code>
, and <code>free()</code>) are used. The memory management functions , and <code>free()</code>) are used. The memory management functions
share the same opaque userdata pointer, <code>heap_udata</code>. This share the same opaque userdata pointer, <code>heap_udata</code>. This
userdata pointer is also used for other Duktape features (like low memory userdata pointer is also used for other Duktape features like fatal error
pointer compression macros).</p> handling and low memory pointer compression macros.</p>
<p>A fatal error handler is provided in <code>fatal_handler</code>. This <p>A fatal error handler is provided in <code>fatal_handler</code>. This
handler is called in unrecoverable error situations such as uncaught handler is called in unrecoverable error situations such as uncaught
errors, out-of-memory errors not resolved by garbage collection, etc. errors, out-of-memory errors not resolved by garbage collection, self test
A caller SHOULD implement a fatal error handler in most applications. errors, etc. A caller SHOULD implement a fatal error handler in most
If not given, a default fatal error handler is used. The default applications. If not given, a default fatal error handler built into
handler ultimately calls ANSI C <code>abort()</code>, which may not always Duktape is used instead. <b>Note that the default fatal error handler (unless
be the preferred action.</p> 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 <p>To create a Duktape heap with default settings, use
<code><a href="#duk_create_heap_default">duk_create_heap_default()</a></code>.</p> <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 name: duk_fatal
proto: | 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: | summary: |
<p>Call fatal error handler with a specified error code and an optional <p>Call fatal error handler with an optional message (<code>err_msg</code>
message (<code>err_msg</code> may be <code>NULL</code>). The valid range may be <code>NULL</code>). Like all strings in Duktape, the error message
for user error codes is [1,16777215].</p> 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 <p>A fatal error handler never returns and may e.g. exit the current
process. Error catching points (like <code>try-catch</code> statements process. Error catching points (like <code>try-catch</code> statements
@ -15,7 +15,7 @@ summary: |
has occurred.</p> has occurred.</p>
example: | example: |
duk_fatal(ctx, DUK_ERR_INTERNAL_ERROR, "assumption failed"); duk_fatal(ctx, "assumption failed");
tags: tags:
- error - 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> <a href="https://github.com/svaarala/duktape/blob/master/doc/performance-sensitive.rst">performance-sensitive.rst</a>
for suggested feature options.</p> 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 <p>The built-in default fatal error handler will write a debug log message
disabled by <code>DUK_OPT_NO_FILE_IO</code>. It will then call <code>abort()</code> (but <b>won't</b> write anything to <code>stdout</code> to <code>stderr</code>),
or cause a segfault if <code>DUK_OPT_SEGFAULT_ON_PANIC</code> is defined.</p> 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 <p>This is usually not the best behavior for production applications which may
already have better panic recovery mechanisms. To replace the default panic already have better fatal error recovery mechanisms. To replace the default fatal
handler, see 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> <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 Duktape API calls to establish points in C code where errors can be caught
and handled. and handled.
An uncaught error causes the fatal error handler to be called, which is An uncaught error causes the fatal error handler to be called, which is
considered an unrecoverable situation and should ordinarily be avoided considered an unrecoverable situation and should ordinarily be avoided,
(see <a href="#error-fatal-panic">Error, fatal, and panic</a>).</p> 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 <p>To avoid fatal errors, typical application code should establish an error
catch point before making other Duktape API calls. This is done using 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 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> 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 <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> <code><a href="api.html#duk_throw">duk_throw()</a></code>
API call (or similar), or by an internal, API call (or similar), or by an internal, recoverable Duktape error.
recoverable Duktape error. Ordinary errors can be caught with a Ordinary errors can be caught with a <code>try-catch</code> in Ecmascript
<code>try-catch</code> in Ecmascript code or e.g. code or e.g. <code><a href="api.html#duk_pcall">duk_pcall()</a></code>
<code><a href="api.html#duk_pcall">duk_pcall()</a></code>
(see API calls tagged (see API calls tagged
<code><a href="api.html#taglist-protected">protected</a></code>) <code><a href="api.html#taglist-protected">protected</a></code>)
in C code.</p> in C code.</p>
<p>An uncaught error or an explicit call to <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> <code><a href="api.html#duk_fatal">duk_fatal()</a></code>, or an
causes a <b>fatal error</b> handler to be called. A fatal error handler is unrecoverable error inside Duktape. Each Duktape heap has a heap-wide fatal
associated with every Duktape heap upon creation. There is no reasonable way error handler registered in
to resume execution after a fatal error, so the fatal error handler must not <code><a href="api.html#duk_create_heap">duk_create_heap()</a></code>; if no
return. The default fatal error handler writes an error message to <code>stderr</code> handler is given a built-in default handler is used. There's no safe way to
and then escalates the fatal error to a panic (which, by default, resume execution after a fatal error, so that a fatal error handler must not
<code>abort()</code>s the process). You can provide your own fatal error return or call into the Duktape API. Instead the handler should e.g. write
handler to deal with fatal errors. The most appropriate recovery action is, out the message to the console or a log file and then exit the process (or
of course, platform and application specific. The handler could, for instance, restart an embedded device).</p>
write a diagnostic file detailing the situation and then restart the application
to recover.</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
<p>A <b>panic</b> is caused by Duktape assertion code (if included in the build) always call the built-in default fatal error handler (with the handler
or by the default fatal error handler. There is no way to induce a panic from userdata argument set to NULL). Fatal errors handled this way are currently
user code. The default panic handler writes an error message to <code>stderr</code> limited to assertion failures, so that if you don't enable assertions, no such
and <code>abort()</code>s the process. You can use the errors will currently occur. All fatal error handling goes through the
<code>DUK_OPT_SEGFAULT_ON_PANIC</code> feature option to cause a deliberate heap-associated fatal error handler which is in direct application control.</p>
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 <div class="note">
handler entirely with the feature option <code>DUK_OPT_PANIC_HANDLER</code>. The built-in default fatal error handler will write a debug log message
The panic handler is decided during build, while the fatal error handler is (but <b>won't</b> write anything to <code>stdout</code> to <code>stderr</code>),
decided at runtime by the calling application.</p> 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
<p>If assertions are turned off and the application provides a fatal error <b>strongly recommended</b> to both (1) provide a custom fatal error handler
handler, no panics will be caused by Duktape code. All errors will then be in heap creation and (2) replace the built-in default fatal error handler using
either ordinary errors or fatal errors, both under application control.</p> 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