/* * 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 duk_context *ctx = (duk_context *) thr; #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); DUK_ASSERT(ctx != 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; #if 0 /* XXX: not always true because the second throw may come from a different coroutine */ DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11); #endif thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; 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 { /* 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. */ #if 0 /* XXX: not always true, second throw may come from a different coroutine */ DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX); #endif thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11; thr->heap->creating_error = 1; duk_require_stack(ctx, 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(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 /* 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(ctx, -1))); duk_err_augment_error_throw(thr); #endif duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1)); thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; 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_context *ctx = (duk_context *) thr; 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(ctx, -rc, NULL, 0, "error (rc %ld)", (long) rc); DUK_UNREACHABLE(); }