162 lines
5.6 KiB
C
162 lines
5.6 KiB
C
/*
|
|
* Create and throw an ECMAScript error object based on a code and a message.
|
|
*
|
|
* Used when we throw errors internally. ECMAScript generated error objects
|
|
* are created by ECMAScript code, and the throwing is handled by the bytecode
|
|
* executor.
|
|
*/
|
|
|
|
#include "duk_internal.h"
|
|
|
|
/*
|
|
* Create and throw an error (originating from Duktape internally)
|
|
*
|
|
* Push an error object on top of the stack, possibly throw augmenting
|
|
* the error, and finally longjmp.
|
|
*
|
|
* If an error occurs while we're dealing with the current error, we might
|
|
* enter an infinite recursion loop. This is prevented by detecting a
|
|
* "double fault" through the heap->creating_error flag; the recursion
|
|
* then stops at the second level.
|
|
*/
|
|
|
|
#if defined(DUK_USE_VERBOSE_ERRORS)
|
|
DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, const char *msg, const char *filename, duk_int_t line) {
|
|
#else
|
|
DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) {
|
|
#endif
|
|
#if defined(DUK_USE_VERBOSE_ERRORS)
|
|
DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld",
|
|
(long) code, (const char *) msg,
|
|
(const char *) filename, (long) line));
|
|
#else
|
|
DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld", (long) code));
|
|
#endif
|
|
|
|
DUK_ASSERT(thr != NULL);
|
|
|
|
/* Even though nested call is possible because we throw an error when
|
|
* trying to create an error, the potential errors must happen before
|
|
* the longjmp state is configured.
|
|
*/
|
|
DUK_ASSERT_LJSTATE_UNSET(thr->heap);
|
|
|
|
/* Sync so that augmentation sees up-to-date activations, NULL
|
|
* thr->ptr_curr_pc so that it's not used if side effects occur
|
|
* in augmentation or longjmp handling.
|
|
*/
|
|
duk_hthread_sync_and_null_currpc(thr);
|
|
|
|
/*
|
|
* Create and push an error object onto the top of stack.
|
|
* The error is potentially augmented before throwing.
|
|
*
|
|
* If a "double error" occurs, use a fixed error instance
|
|
* to avoid further trouble.
|
|
*/
|
|
|
|
if (thr->heap->creating_error) {
|
|
duk_tval tv_val;
|
|
duk_hobject *h_err;
|
|
|
|
thr->heap->creating_error = 0;
|
|
|
|
h_err = thr->builtins[DUK_BIDX_DOUBLE_ERROR];
|
|
if (h_err != NULL) {
|
|
DUK_D(DUK_DPRINT("double fault detected -> use built-in fixed 'double error' instance"));
|
|
DUK_TVAL_SET_OBJECT(&tv_val, h_err);
|
|
} else {
|
|
DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance "
|
|
"-> use the error code as a number"));
|
|
DUK_TVAL_SET_I32(&tv_val, (duk_int32_t) code);
|
|
}
|
|
|
|
duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, &tv_val);
|
|
|
|
/* No augmentation to avoid any allocations or side effects. */
|
|
} else {
|
|
/* Prevent infinite recursion. Extra call stack and C
|
|
* recursion headroom (see GH-191) is added for augmentation.
|
|
* That is now signalled by heap->augmenting error and taken
|
|
* into account in call handling without an explicit limit bump.
|
|
*/
|
|
thr->heap->creating_error = 1;
|
|
|
|
duk_require_stack(thr, 1);
|
|
|
|
/* XXX: usually unnecessary '%s' formatting here, but cannot
|
|
* use 'msg' as a format string directly.
|
|
*/
|
|
#if defined(DUK_USE_VERBOSE_ERRORS)
|
|
duk_push_error_object_raw(thr,
|
|
code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
|
|
filename,
|
|
line,
|
|
"%s",
|
|
(const char *) msg);
|
|
#else
|
|
duk_push_error_object_raw(thr,
|
|
code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
#endif
|
|
|
|
/* Note that an alloc error may happen during error augmentation.
|
|
* This may happen both when the original error is an alloc error
|
|
* and when it's something else. Because any error in augmentation
|
|
* must be handled correctly anyway, there's no special check for
|
|
* avoiding it for alloc errors (this differs from Duktape 1.x).
|
|
*/
|
|
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
|
|
DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)",
|
|
(duk_tval *) duk_get_tval(thr, -1)));
|
|
duk_err_augment_error_throw(thr);
|
|
#endif
|
|
|
|
duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1));
|
|
thr->heap->creating_error = 0;
|
|
|
|
/* Error is now created and we assume no errors can occur any
|
|
* more. Check for debugger Throw integration only when the
|
|
* error is complete. If we enter debugger message loop,
|
|
* creating_error must be 0 so that errors can be thrown in
|
|
* the paused state, e.g. in Eval commands.
|
|
*/
|
|
#if defined(DUK_USE_DEBUGGER_SUPPORT)
|
|
duk_err_check_debugger_integration(thr);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Finally, longjmp
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT, %!iT (after throw augment)",
|
|
(duk_tval *) &thr->heap->lj.value1, (duk_tval *) &thr->heap->lj.value2));
|
|
|
|
duk_err_longjmp(thr);
|
|
DUK_UNREACHABLE();
|
|
}
|
|
|
|
/*
|
|
* Helper for C function call negative return values.
|
|
*/
|
|
|
|
DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc) {
|
|
DUK_ASSERT(thr != NULL);
|
|
DUK_ASSERT(rc < 0);
|
|
|
|
/*
|
|
* The __FILE__ and __LINE__ information is intentionally not used in the
|
|
* creation of the error object, as it isn't useful in the tracedata. The
|
|
* tracedata still contains the function which returned the negative return
|
|
* code, and having the file/line of this function isn't very useful.
|
|
*
|
|
* The error messages for DUK_RET_xxx shorthand are intentionally very
|
|
* minimal: they're only really useful for low memory targets.
|
|
*/
|
|
|
|
duk_error_raw(thr, -rc, NULL, 0, "error (rc %ld)", (long) rc);
|
|
DUK_WO_NORETURN(return;);
|
|
}
|