blob: 69b75f09a9635e8b29c01693979354605d3e726f [file] [log] [blame]
/*
* 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->handling_error flag; the recursion
* then stops at the second level.
*/
#ifdef 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
duk_context *ctx = (duk_context *) thr;
duk_bool_t double_error = thr->heap->handling_error;
#ifdef 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);
DUK_ASSERT(ctx != NULL);
thr->heap->handling_error = 1;
if (!double_error) {
/* Allow headroom for calls during error handling (see GH-191).
* We allow space for 10 additional recursions, with one extra
* for, e.g. a print() call at the deepest level.
*/
DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX);
thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11;
}
DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11); /* just making sure */
/* 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.
* If a "double error" occurs, use a fixed error instance
* to avoid further trouble.
*/
/* XXX: if attempt to push beyond allocated valstack, this double fault
* handling fails miserably. We should really write the double error
* directly to thr->heap->lj.value1 and avoid valstack use entirely.
*/
if (double_error) {
if (thr->builtins[DUK_BIDX_DOUBLE_ERROR]) {
DUK_D(DUK_DPRINT("double fault detected -> push built-in fixed 'double error' instance"));
duk_push_hobject_bidx(ctx, DUK_BIDX_DOUBLE_ERROR);
} else {
DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance "
"-> push the error code as a number"));
duk_push_int(ctx, (duk_int_t) code);
}
} else {
/* Error object is augmented at its creation here. */
duk_require_stack(ctx, 1);
/* XXX: unnecessary '%s' formatting here, but cannot use
* 'msg' as a format string directly.
*/
#ifdef DUK_USE_VERBOSE_ERRORS
duk_push_error_object_raw(ctx,
code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
filename,
line,
"%s",
(const char *) msg);
#else
duk_push_error_object_raw(ctx,
code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
NULL,
0,
NULL);
#endif
}
/*
* Augment error (throw time), unless alloc/double error
*/
if (double_error || code == DUK_ERR_ALLOC_ERROR) {
DUK_D(DUK_DPRINT("alloc or double error: skip throw augmenting to avoid further trouble"));
} else {
#if defined(DUK_USE_AUGMENT_ERROR_THROW)
DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)",
(duk_tval *) duk_get_tval(ctx, -1)));
duk_err_augment_error_throw(thr);
#endif
}
/*
* Finally, longjmp
*/
duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW);
thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; /* reset callstack limit */
thr->heap->handling_error = 0;
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_context *ctx = (duk_context *) thr;
const char *msg;
duk_errcode_t code;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(rc < 0);
/* XXX: this generates quite large code - perhaps select the error
* class based on the code and then just use the error 'name'?
*/
/* XXX: shared strings */
code = -rc;
switch (rc) {
case DUK_RET_UNIMPLEMENTED_ERROR: msg = "unimplemented"; break;
case DUK_RET_UNSUPPORTED_ERROR: msg = "unsupported"; break;
case DUK_RET_INTERNAL_ERROR: msg = "internal"; break;
case DUK_RET_ALLOC_ERROR: msg = "alloc"; break;
case DUK_RET_ASSERTION_ERROR: msg = "assertion"; break;
case DUK_RET_API_ERROR: msg = "api"; break;
case DUK_RET_UNCAUGHT_ERROR: msg = "uncaught"; break;
case DUK_RET_ERROR: msg = "error"; break;
case DUK_RET_EVAL_ERROR: msg = "eval"; break;
case DUK_RET_RANGE_ERROR: msg = "range"; break;
case DUK_RET_REFERENCE_ERROR: msg = "reference"; break;
case DUK_RET_SYNTAX_ERROR: msg = "syntax"; break;
case DUK_RET_TYPE_ERROR: msg = "type"; break;
case DUK_RET_URI_ERROR: msg = "uri"; break;
default: msg = "unknown"; break;
}
DUK_ASSERT(msg != NULL);
/*
* 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.
*/
duk_error_raw(ctx, code, NULL, 0, "%s error (rc %ld)", (const char *) msg, (long) rc);
DUK_UNREACHABLE();
}