1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
/*
* 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;);
}
|