| /* |
| * 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(); |
| } |