diff --git a/src-input/duk_error_longjmp.c b/src-input/duk_error_longjmp.c index fef32a1a..f347d1cd 100644 --- a/src-input/duk_error_longjmp.c +++ b/src-input/duk_error_longjmp.c @@ -76,14 +76,9 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { DUK_DD(DUK_DDPRINT("about to longjmp, pf_prevent_count=%ld", (long) thr->heap->pf_prevent_count)); -#if !defined(DUK_USE_CPP_EXCEPTIONS) /* 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 - * instead of invoking the fatal error handler. Because there's - * 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", @@ -97,16 +92,12 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { #endif DUK_UNREACHABLE(); } -#endif /* DUK_USE_CPP_EXCEPTIONS */ #if defined(DUK_USE_CPP_EXCEPTIONS) - { - duk_internal_exception exc; /* dummy */ - throw exc; - } -#else /* DUK_USE_CPP_EXCEPTIONS */ + throw duk_internal_exception(); /* dummy */ +#else DUK_LONGJMP(thr->heap->lj.jmpbuf_ptr->jb); -#endif /* DUK_USE_CPP_EXCEPTIONS */ +#endif DUK_UNREACHABLE(); } diff --git a/src-input/duk_error_macros.c b/src-input/duk_error_macros.c index ebb304a3..428593c9 100644 --- a/src-input/duk_error_macros.c +++ b/src-input/duk_error_macros.c @@ -113,10 +113,18 @@ DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *ms DUK_UNREF(udata); DUK_UNREF(msg); + msg = msg ? msg : "NULL"; + #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_D(DUK_DPRINT("custom default fatal error handler called: %s", msg)); DUK_USE_FATAL_HANDLER(udata, msg); +#elif defined(DUK_USE_CPP_EXCEPTIONS) + /* With C++ use a duk_fatal_exception which user code can catch in + * a natural way. + */ + DUK_D(DUK_DPRINT("built-in default C++ fatal error handler called: %s", msg)); + throw duk_fatal_exception(msg); #else /* Default behavior is to abort() on error. There's no printout * which makes this awkward, so it's always recommended to use an @@ -133,7 +141,7 @@ DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *ms * - http://duktape.org/api.html#taglist-protected * ==================================================================== */ - DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg ? msg : "NULL")); + DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg)); DUK_ABORT(); #endif diff --git a/src-input/duk_exception.h b/src-input/duk_exception.h index 846573e4..580e9d7e 100644 --- a/src-input/duk_exception.h +++ b/src-input/duk_exception.h @@ -1,18 +1,30 @@ /* - * Exception for Duktape internal throws when C++ exceptions are used + * Exceptions for Duktape internal throws when C++ exceptions are used * for long control transfers. - * - * Doesn't inherit from any exception base class to minimize the chance - * that user code would accidentally catch this exception. */ #if !defined(DUK_EXCEPTION_H_INCLUDED) #define DUK_EXCEPTION_H_INCLUDED #if defined(DUK_USE_CPP_EXCEPTIONS) +/* Internal exception used as a setjmp-longjmp replacement. User code should + * NEVER see or catch this exception, so it doesn't inherit from any base + * class which should minimize the chance of user code accidentally catching + * the exception. + */ class duk_internal_exception { /* intentionally empty */ }; + +/* Fatal error, thrown as a specific C++ exception with C++ exceptions + * enabled. It is unsafe to continue; doing so may cause crashes or memory + * leaks. This is intended to be either uncaught, or caught by user code + * aware of the "unsafe to continue" semantics. + */ +class duk_fatal_exception : public virtual std::runtime_error { + public: + duk_fatal_exception(const char *message) : std::runtime_error(message) {} +}; #endif #endif /* DUK_EXCEPTION_H_INCLUDED */ diff --git a/src-input/duk_js_call.c b/src-input/duk_js_call.c index da1fe6ce..183ad58c 100644 --- a/src-input/duk_js_call.c +++ b/src-input/duk_js_call.c @@ -2724,7 +2724,10 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, retval = DUK_EXEC_ERROR; } #if defined(DUK_USE_CPP_EXCEPTIONS) - catch (std::exception &exc) { + catch (duk_fatal_exception &exc) { + DUK_D(DUK_DPRINT("rethrow duk_fatal_exception")); + throw; + } catch (std::exception &exc) { const char *what = exc.what(); DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff); DUK_STATS_INC(thr->heap, stats_safecall_throw); diff --git a/src-input/duk_js_executor.c b/src-input/duk_js_executor.c index eeb64158..727b327f 100644 --- a/src-input/duk_js_executor.c +++ b/src-input/duk_js_executor.c @@ -2937,7 +2937,10 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { entry_jmpbuf_ptr); } #if defined(DUK_USE_CPP_EXCEPTIONS) - catch (std::exception &exc) { + catch (duk_fatal_exception &exc) { + DUK_D(DUK_DPRINT("rethrow duk_fatal_exception")); + throw; + } catch (std::exception &exc) { const char *what = exc.what(); if (!what) { what = "unknown";