JavaScript-Duktape
view release on metacpan or search on metacpan
lib/JavaScript/Duktape/C/lib/duktape.c view on Meta::CPAN
}
}
DUK_INTERNAL void duk_debug_clear_pause_state(duk_heap *heap) {
heap->dbg_pause_flags = 0;
heap->dbg_pause_act = NULL;
heap->dbg_pause_startline = 0;
}
#else /* DUK_USE_DEBUGGER_SUPPORT */
/* No debugger support. */
#endif /* DUK_USE_DEBUGGER_SUPPORT */
/* automatic undefs */
#undef DUK__DBG_TPORT_ENTER
#undef DUK__DBG_TPORT_EXIT
#undef DUK__SET_CONN_BROKEN
#line 1 "duk_error_augment.c"
/*
* Augmenting errors at their creation site and their throw site.
*
* When errors are created, traceback data is added by built-in code
* and a user error handler (if defined) can process or replace the
* error. Similarly, when errors are thrown, a user error handler
* (if defined) can process or replace the error.
*
* Augmentation and other processing at error creation time is nice
* because an error is only created once, but it may be thrown and
* rethrown multiple times. User error handler registered for processing
* an error at its throw site must be careful to handle rethrowing in
* a useful manner.
*
* Error augmentation may throw an internal error (e.g. alloc error).
*
* ECMAScript allows throwing any values, so all values cannot be
* augmented. Currently, the built-in augmentation at error creation
* only augments error values which are Error instances (= have the
* built-in Error.prototype in their prototype chain) and are also
* extensible. User error handlers have no limitations in this respect.
*/
/* #include duk_internal.h -> already included */
/*
* Helper for calling a user error handler.
*
* 'thr' must be the currently active thread; the error handler is called
* in its context. The valstack of 'thr' must have the error value on
* top, and will be replaced by another error value based on the return
* value of the error handler.
*
* The helper calls duk_handle_call() recursively in protected mode.
* Before that call happens, no longjmps should happen; as a consequence,
* we must assume that the valstack contains enough temporary space for
* arguments and such.
*
* While the error handler runs, any errors thrown will not trigger a
* recursive error handler call (this is implemented using a heap level
* flag which will "follow" through any coroutines resumed inside the
* error handler). If the error handler is not callable or throws an
* error, the resulting error replaces the original error (for Duktape
* internal errors, duk_error_throw.c further substitutes this error with
* a DoubleError which is not ideal). This would be easy to change and
* even signal to the caller.
*
* The user error handler is stored in 'Duktape.errCreate' or
* 'Duktape.errThrow' depending on whether we're augmenting the error at
* creation or throw time. There are several alternatives to this approach,
* see doc/error-objects.rst for discussion.
*
* Note: since further longjmp()s may occur while calling the error handler
* (for many reasons, e.g. a labeled 'break' inside the handler), the
* caller can make no assumptions on the thr->heap->lj state after the
* call (this affects especially duk_error_throw.c). This is not an issue
* as long as the caller writes to the lj state only after the error handler
* finishes.
*/
#if defined(DUK_USE_ERRTHROW) || defined(DUK_USE_ERRCREATE)
DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) {
duk_tval *tv_hnd;
duk_int_t rc;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->heap != NULL);
DUK_ASSERT_STRIDX_VALID(stridx_cb);
if (thr->heap->augmenting_error) {
DUK_D(DUK_DPRINT("recursive call to error augmentation, ignore"));
return;
}
/*
* Check whether or not we have an error handler.
*
* We must be careful of not triggering an error when looking up the
* property. For instance, if the property is a getter, we don't want
* to call it, only plain values are allowed. The value, if it exists,
* is not checked. If the value is not a function, a TypeError happens
* when it is called and that error replaces the original one.
*/
DUK_ASSERT_VALSTACK_SPACE(thr, 4); /* 3 entries actually needed below */
/* [ ... errval ] */
if (thr->builtins[DUK_BIDX_DUKTAPE] == NULL) {
/* When creating built-ins, some of the built-ins may not be set
* and we want to tolerate that when throwing errors.
*/
DUK_DD(DUK_DDPRINT("error occurred when DUK_BIDX_DUKTAPE is NULL, ignoring"));
return;
}
tv_hnd = duk_hobject_find_existing_entry_tval_ptr(thr->heap,
thr->builtins[DUK_BIDX_DUKTAPE],
DUK_HTHREAD_GET_STRING(thr, stridx_cb));
if (tv_hnd == NULL) {
DUK_DD(DUK_DDPRINT("error handler does not exist or is not a plain value: %!T",
(duk_tval *) tv_hnd));
lib/JavaScript/Duktape/C/lib/duktape.c view on Meta::CPAN
DUK_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller"));
return DUK__RETHAND_RESTART;
}
#if defined(DUK_USE_COROUTINE_SUPPORT)
DUK_DD(DUK_DDPRINT("no calling activation, thread finishes (similar to yield)"));
DUK_ASSERT(thr->resumer != NULL);
DUK_ASSERT(thr->resumer->callstack_top >= 2); /* ECMAScript activation + Duktape.Thread.resume() activation */
DUK_ASSERT(thr->resumer->callstack_curr != NULL);
DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL);
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL &&
DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent))); /* an ECMAScript function */
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
resumer = thr->resumer;
/* Share yield longjmp handler.
*
* This sequence of steps is a bit fragile (see GH-1845):
* - We need the return value from 'thr' (resumed thread) value stack.
* The termination unwinds its value stack, losing the value.
* - We need a refcounted reference for 'thr', which may only exist
* in the caller value stack. We can't unwind or reconfigure the
* caller's value stack without potentially freeing 'thr'.
*
* Current approach is to capture the 'thr' return value and store
* a reference to 'thr' in the caller value stack temporarily. This
* keeps 'thr' reachable until final yield/return handling which
* removes the references atomatically.
*/
DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
duk_hthread_activation_unwind_norz(resumer); /* May remove last reference to 'thr', but is NORZ. */
duk_push_tval(resumer, thr->valstack_top - 1); /* Capture return value, side effect free. */
duk_push_hthread(resumer, thr); /* Make 'thr' reachable again, before side effects. */
duk_hthread_terminate(thr); /* Updates thread state, minimizes its allocations. */
thr->resumer = NULL;
DUK_HTHREAD_DECREF(thr, resumer);
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED);
resumer->state = DUK_HTHREAD_STATE_RUNNING;
DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);
DUK_ASSERT(resumer->valstack_top - 2 >= resumer->valstack_bottom);
duk__handle_yield(thr, resumer, resumer->valstack_top - 2);
thr = NULL; /* 'thr' invalidated by call */
#if 0
thr = resumer; /* not needed */
#endif
DUK_DD(DUK_DDPRINT("-> return not caught, thread terminated; handle like yield, restart execution in resumer"));
return DUK__RETHAND_RESTART;
#else
/* Without coroutine support this case should never happen. */
DUK_ERROR_INTERNAL(thr);
DUK_WO_NORETURN(return 0;);
#endif
}
/*
* Executor interrupt handling
*
* The handler is called whenever the interrupt countdown reaches zero
* (or below). The handler must perform whatever checks are activated,
* e.g. check for cumulative step count to impose an execution step
* limit or check for breakpoints or other debugger interaction.
*
* When the actions are done, the handler must reinit the interrupt
* init and counter values. The 'init' value must indicate how many
* bytecode instructions are executed before the next interrupt. The
* counter must interface with the bytecode executor loop. Concretely,
* the new init value is normally one higher than the new counter value.
* For instance, to execute exactly one bytecode instruction the init
* value is set to 1 and the counter to 0. If an error is thrown by the
* interrupt handler, the counters are set to the same value (e.g. both
* to 0 to cause an interrupt when the next bytecode instruction is about
* to be executed after error handling).
*
* Maintaining the init/counter value properly is important for accurate
* behavior. For instance, executor step limit needs a cumulative step
* count which is simply computed as a sum of 'init' values. This must
* work accurately even when single stepping.
*/
#if defined(DUK_USE_INTERRUPT_COUNTER)
#define DUK__INT_NOACTION 0 /* no specific action, resume normal execution */
#define DUK__INT_RESTART 1 /* must "goto restart_execution", e.g. breakpoints changed */
#if defined(DUK_USE_DEBUGGER_SUPPORT)
DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_immediate, duk_small_uint_t *out_interrupt_retval) {
duk_activation *act;
duk_breakpoint *bp;
duk_breakpoint **bp_active;
duk_uint_fast32_t line = 0;
duk_bool_t process_messages;
duk_bool_t processed_messages = 0;
DUK_ASSERT(thr->heap->dbg_processing == 0); /* don't re-enter e.g. during Eval */
act = thr->callstack_curr;
DUK_ASSERT(act != NULL);
/* It might seem that replacing 'thr->heap' with just 'heap' below
* might be a good idea, but it increases code size slightly
* (probably due to unnecessary spilling) at least on x64.
*/
/*
* Single opcode step check
*/
if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE) {
DUK_D(DUK_DPRINT("PAUSE TRIGGERED by one opcode step"));
lib/JavaScript/Duktape/C/lib/duktape.c view on Meta::CPAN
/* [ ... enum ] -> [ ... next_key ] */
DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ",
(duk_tval *) duk_get_tval(thr, -1)));
pc_skip = 1;
} else {
/* [ ... enum ] -> [ ... ] */
DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */
thr->valstack_top++;
}
duk_replace(thr, (duk_idx_t) b);
} else {
/* 'null' enumerator case -> behave as with an empty enumerator */
DUK_ASSERT(duk_is_null(thr, (duk_idx_t) c));
DUK_DDD(DUK_DDDPRINT("enum is null, execute jump slot"));
}
return pc_skip;
}
/*
* Call handling helpers.
*/
DUK_LOCAL duk_bool_t duk__executor_handle_call(duk_hthread *thr, duk_idx_t idx, duk_idx_t nargs, duk_small_uint_t call_flags) {
duk_bool_t rc;
duk_set_top_unsafe(thr, (duk_idx_t) (idx + nargs + 2)); /* [ ... func this arg1 ... argN ] */
/* Attempt an Ecma-to-Ecma call setup. If the call
* target is (directly or indirectly) Reflect.construct(),
* the call may change into a constructor call on the fly.
*/
rc = (duk_bool_t) duk_handle_call_unprotected(thr, idx, call_flags);
if (rc != 0) {
/* Ecma-to-ecma call possible, may or may not
* be a tail call. Avoid C recursion by
* reusing current executor instance.
*/
DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
/* curr_pc synced by duk_handle_call_unprotected() */
DUK_ASSERT(thr->ptr_curr_pc == NULL);
return rc;
} else {
/* Call was handled inline. */
}
DUK_ASSERT(thr->ptr_curr_pc != NULL);
return rc;
}
/*
* ECMAScript bytecode executor.
*
* Resume execution for the current thread from its current activation.
* Returns when execution would return from the entry level activation,
* leaving a single return value on top of the stack. Function calls
* and thread resumptions are handled internally. If an error occurs,
* a longjmp() with type DUK_LJ_TYPE_THROW is called on the entry level
* setjmp() jmpbuf.
*
* ECMAScript function calls and coroutine resumptions are handled
* internally (by the outer executor function) without recursive C calls.
* Other function calls are handled using duk_handle_call(), increasing
* C recursion depth.
*
* Abrupt completions (= long control tranfers) are handled either
* directly by reconfiguring relevant stacks and restarting execution,
* or via a longjmp. Longjmp-free handling is preferable for performance
* (especially Emscripten performance), and is used for: break, continue,
* and return.
*
* For more detailed notes, see doc/execution.rst.
*
* Also see doc/code-issues.rst for discussion of setjmp(), longjmp(),
* and volatile.
*/
/* Presence of 'fun' is config based, there's a marginal performance
* difference and the best option is architecture dependent.
*/
#if defined(DUK_USE_EXEC_FUN_LOCAL)
#define DUK__FUN() fun
#else
#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr))
#endif
/* Strict flag. */
#define DUK__STRICT() ((duk_small_uint_t) DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN()))
/* Reg/const access macros: these are very footprint and performance sensitive
* so modify with care. Arguments are sometimes evaluated multiple times which
* is not ideal.
*/
#define DUK__REG(x) (*(thr->valstack_bottom + (x)))
#define DUK__REGP(x) (thr->valstack_bottom + (x))
#define DUK__CONST(x) (*(consts + (x)))
#define DUK__CONSTP(x) (consts + (x))
/* Reg/const access macros which take the 32-bit instruction and avoid an
* explicit field decoding step by using shifts and masks. These must be
* kept in sync with duk_js_bytecode.h. The shift/mask values are chosen
* so that 'ins' can be shifted and masked and used as a -byte- offset
* instead of a duk_tval offset which needs further shifting (which is an
* issue on some, but not all, CPUs).
*/
#define DUK__RCBIT_B DUK_BC_REGCONST_B
#define DUK__RCBIT_C DUK_BC_REGCONST_C
#if defined(DUK_USE_EXEC_REGCONST_OPTIMIZE)
#if defined(DUK_USE_PACKED_TVAL)
#define DUK__TVAL_SHIFT 3 /* sizeof(duk_tval) == 8 */
#else
#define DUK__TVAL_SHIFT 4 /* sizeof(duk_tval) == 16; not always the case so also asserted for */
#endif
#define DUK__SHIFT_A (DUK_BC_SHIFT_A - DUK__TVAL_SHIFT)
#define DUK__SHIFT_B (DUK_BC_SHIFT_B - DUK__TVAL_SHIFT)
#define DUK__SHIFT_C (DUK_BC_SHIFT_C - DUK__TVAL_SHIFT)
#define DUK__SHIFT_BC (DUK_BC_SHIFT_BC - DUK__TVAL_SHIFT)
#define DUK__MASK_A (DUK_BC_UNSHIFTED_MASK_A << DUK__TVAL_SHIFT)
#define DUK__MASK_B (DUK_BC_UNSHIFTED_MASK_B << DUK__TVAL_SHIFT)
#define DUK__MASK_C (DUK_BC_UNSHIFTED_MASK_C << DUK__TVAL_SHIFT)
#define DUK__MASK_BC (DUK_BC_UNSHIFTED_MASK_BC << DUK__TVAL_SHIFT)
( run in 1.289 second using v1.01-cache-2.11-cpan-ceb78f64989 )