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 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
/*
* Error helpers
*/
#include "duk_internal.h"
/*
* Helper to walk the thread chain and see if there is an active error
* catcher. Protected calls or finally blocks aren't considered catching.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_LOCAL duk_bool_t duk__have_active_catcher(duk_hthread *thr) {
/* As noted above, a protected API call won't be counted as a
* catcher. This is usually convenient, e.g. in the case of a top-
* level duk_pcall(), but may not always be desirable. Perhaps add
* an argument to treat them as catchers?
*/
duk_activation *act;
duk_catcher *cat;
DUK_ASSERT(thr != NULL);
for (; thr != NULL; thr = thr->resumer) {
for (act = thr->callstack_curr; act != NULL; act = act->parent) {
for (cat = act->cat; cat != NULL; cat = cat->parent) {
if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
return 1; /* all we need to know */
}
}
}
}
return 0;
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
/*
* Get prototype object for an integer error code.
*/
DUK_INTERNAL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t code) {
switch (code) {
case DUK_ERR_EVAL_ERROR:
return thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE];
case DUK_ERR_RANGE_ERROR:
return thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE];
case DUK_ERR_REFERENCE_ERROR:
return thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE];
case DUK_ERR_SYNTAX_ERROR:
return thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE];
case DUK_ERR_TYPE_ERROR:
return thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE];
case DUK_ERR_URI_ERROR:
return thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE];
case DUK_ERR_ERROR:
default:
return thr->builtins[DUK_BIDX_ERROR_PROTOTYPE];
}
}
/*
* Helper for debugger throw notify and pause-on-uncaught integration.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) {
duk_bool_t uncaught;
duk_tval *tv_obj;
/* If something is thrown with the debugger attached and nobody will
* catch it, execution is paused before the longjmp, turning over
* control to the debug client. This allows local state to be examined
* before the stack is unwound. Errors are not intercepted when debug
* message loop is active (e.g. for Eval).
*/
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
/* XXX: Allow customizing the pause and notify behavior at runtime
* using debugger runtime flags. For now the behavior is fixed using
* config options.
*/
if (!duk_debug_is_attached(thr->heap) || thr->heap->dbg_processing || thr->heap->lj.type != DUK_LJ_TYPE_THROW ||
thr->heap->creating_error) {
DUK_D(DUK_DPRINT("skip debugger error integration; not attached, debugger processing, not THROW, or error thrown "
"while creating error"));
return;
}
/* Don't intercept a DoubleError, we may have caused the initial double
* fault and attempting to intercept it will cause us to be called
* recursively and exhaust the C stack. (This should no longer happen
* for the initial throw because DoubleError path doesn't do a debugger
* integration check, but it might happen for rethrows.)
*/
tv_obj = &thr->heap->lj.value1;
if (DUK_TVAL_IS_OBJECT(tv_obj) && DUK_TVAL_GET_OBJECT(tv_obj) == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) {
DUK_D(DUK_DPRINT("built-in DoubleError instance (re)thrown, not intercepting"));
return;
}
uncaught = !duk__have_active_catcher(thr);
/* Debugger code expects the value at stack top. This also serves
* as a backup: we need to store/restore the longjmp state because
* when the debugger is paused Eval commands may be executed and
* they can arbitrarily clobber the longjmp state.
*/
duk_push_tval(thr, tv_obj);
/* Store and reset longjmp state. */
DUK_ASSERT_LJSTATE_SET(thr->heap);
DUK_TVAL_DECREF_NORZ(thr, tv_obj);
DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); /* Always for THROW type. */
DUK_TVAL_SET_UNDEFINED(tv_obj);
thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
DUK_ASSERT_LJSTATE_UNSET(thr->heap);
#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
/* Report it to the debug client */
DUK_D(DUK_DPRINT("throw with debugger attached, report to client"));
duk_debug_send_throw(thr, uncaught);
#endif
if (uncaught) {
if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_UNCAUGHT_ERROR) {
DUK_D(DUK_DPRINT("PAUSE TRIGGERED by uncaught error"));
duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
}
} else {
if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_CAUGHT_ERROR) {
DUK_D(DUK_DPRINT("PAUSE TRIGGERED by caught error"));
duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
}
}
/* Restore longjmp state. */
DUK_ASSERT_LJSTATE_UNSET(thr->heap);
thr->heap->lj.type = DUK_LJ_TYPE_THROW;
tv_obj = DUK_GET_TVAL_NEGIDX(thr, -1);
DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, tv_obj);
DUK_TVAL_INCREF(thr, tv_obj);
DUK_ASSERT_LJSTATE_SET(thr->heap);
duk_pop(thr);
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
/*
* Helpers for setting up heap longjmp state.
*/
DUK_INTERNAL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val) {
duk_heap *heap;
DUK_ASSERT(thr != NULL);
heap = thr->heap;
DUK_ASSERT(heap != NULL);
DUK_ASSERT(tv_val != NULL);
DUK_ASSERT_LJSTATE_UNSET(heap);
heap->lj.type = lj_type;
DUK_TVAL_SET_TVAL(&heap->lj.value1, tv_val);
DUK_TVAL_INCREF(thr, tv_val);
DUK_ASSERT_LJSTATE_SET(heap);
}
|