http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/ede68a10/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_call.c
----------------------------------------------------------------------
diff --git 
a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_call.c
 
b/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_call.c
deleted file mode 100644
index 7dc04ed..0000000
--- 
a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_call.c
+++ /dev/null
@@ -1,2727 +0,0 @@
-/*
- *  Call handling.
- *
- *  Main functions are:
- *
- *    - duk_handle_call_unprotected(): unprotected call to Ecmascript or
- *      Duktape/C function
- *    - duk_handle_call_protected(): protected call to Ecmascript or
- *      Duktape/C function
- *    - duk_handle_safe_call(): make a protected C call within current
- *      activation
- *    - duk_handle_ecma_call_setup(): Ecmascript-to-Ecmascript calls
- *      (not always possible), including tail calls and coroutine resume
- *
- *  See 'execution.rst'.
- *
- *  Note: setjmp() and local variables have a nasty interaction,
- *  see execution.rst; non-volatile locals modified after setjmp()
- *  call are not guaranteed to keep their value.
- */
-
-#include "duk_internal.h"
-
-/*
- *  Forward declarations.
- */
-
-DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
-                                      duk_idx_t num_stack_args,
-                                      duk_small_uint_t call_flags,
-                                      duk_idx_t idx_func);
-DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
-                                      duk_size_t entry_valstack_bottom_index,
-                                      duk_size_t entry_valstack_end,
-                                      duk_size_t entry_catchstack_top,
-                                      duk_size_t entry_callstack_top,
-                                      duk_int_t entry_call_recursion_depth,
-                                      duk_hthread *entry_curr_thread,
-                                      duk_uint_fast8_t entry_thread_state,
-                                      duk_instr_t **entry_ptr_curr_pc,
-                                      duk_idx_t idx_func,
-                                      duk_jmpbuf *old_jmpbuf_ptr);
-DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
-                                           duk_safe_call_function func,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t 
entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top);
-DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t 
entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top,
-                                           duk_jmpbuf *old_jmpbuf_ptr);
-DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
-                                            duk_idx_t idx_retbase,
-                                            duk_idx_t num_stack_rets,
-                                            duk_int_t 
entry_call_recursion_depth,
-                                            duk_hthread *entry_curr_thread,
-                                            duk_uint_fast8_t 
entry_thread_state,
-                                            duk_instr_t **entry_ptr_curr_pc);
-
-/*
- *  Interrupt counter fixup (for development only).
- */
-
-#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
-DUK_LOCAL void duk__interrupt_fixup(duk_hthread *thr, duk_hthread 
*entry_curr_thread) {
-       /* Currently the bytecode executor and executor interrupt
-        * instruction counts are off because we don't execute the
-        * interrupt handler when we're about to exit from the initial
-        * user call into Duktape.
-        *
-        * If we were to execute the interrupt handler here, the counts
-        * would match.  You can enable this block manually to check
-        * that this is the case.
-        */
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(thr->heap != NULL);
-
-#if defined(DUK_USE_INTERRUPT_DEBUG_FIXUP)
-       if (entry_curr_thread == NULL) {
-               thr->interrupt_init = thr->interrupt_init - 
thr->interrupt_counter;
-               thr->heap->inst_count_interrupt += thr->interrupt_init;
-               DUK_DD(DUK_DDPRINT("debug test: updated interrupt count on exit 
to "
-                                  "user code, instruction counts: 
executor=%ld, interrupt=%ld",
-                                  (long) thr->heap->inst_count_exec, (long) 
thr->heap->inst_count_interrupt));
-               DUK_ASSERT(thr->heap->inst_count_exec == 
thr->heap->inst_count_interrupt);
-       }
-#else
-       DUK_UNREF(thr);
-       DUK_UNREF(entry_curr_thread);
-#endif
-}
-#endif
-
-/*
- *  Arguments object creation.
- *
- *  Creating arguments objects involves many small details, see E5 Section
- *  10.6 for the specific requirements.  Much of the arguments object exotic
- *  behavior is implemented in duk_hobject_props.c, and is enabled by the
- *  object flag DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS.
- */
-
-DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr,
-                                            duk_hobject *func,
-                                            duk_hobject *varenv,
-                                            duk_idx_t idx_argbase,        /* 
idx of first argument on stack */
-                                            duk_idx_t num_stack_args) {   /* 
num args starting from idx_argbase */
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *arg;          /* 'arguments' */
-       duk_hobject *formals;      /* formals for 'func' (may be NULL if func 
is a C function) */
-       duk_idx_t i_arg;
-       duk_idx_t i_map;
-       duk_idx_t i_mappednames;
-       duk_idx_t i_formals;
-       duk_idx_t i_argbase;
-       duk_idx_t n_formals;
-       duk_idx_t idx;
-       duk_bool_t need_map;
-
-       DUK_DDD(DUK_DDDPRINT("creating arguments object for func=%!iO, 
varenv=%!iO, "
-                            "idx_argbase=%ld, num_stack_args=%ld",
-                            (duk_heaphdr *) func, (duk_heaphdr *) varenv,
-                            (long) idx_argbase, (long) num_stack_args));
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(func != NULL);
-       DUK_ASSERT(DUK_HOBJECT_IS_NONBOUND_FUNCTION(func));
-       DUK_ASSERT(varenv != NULL);
-       DUK_ASSERT(idx_argbase >= 0);  /* assumed to bottom relative */
-       DUK_ASSERT(num_stack_args >= 0);
-
-       need_map = 0;
-
-       i_argbase = idx_argbase;
-       DUK_ASSERT(i_argbase >= 0);
-
-       duk_push_hobject(ctx, func);
-       duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_FORMALS);
-       formals = duk_get_hobject(ctx, -1);
-       n_formals = 0;
-       if (formals) {
-               duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH);
-               n_formals = (duk_idx_t) duk_require_int(ctx, -1);
-               duk_pop(ctx);
-       }
-       duk_remove(ctx, -2);  /* leave formals on stack for later use */
-       i_formals = duk_require_top_index(ctx);
-
-       DUK_ASSERT(n_formals >= 0);
-       DUK_ASSERT(formals != NULL || n_formals == 0);
-
-       DUK_DDD(DUK_DDDPRINT("func=%!O, formals=%!O, n_formals=%ld",
-                            (duk_heaphdr *) func, (duk_heaphdr *) formals,
-                            (long) n_formals));
-
-       /* [ ... formals ] */
-
-       /*
-        *  Create required objects:
-        *    - 'arguments' object: array-like, but not an array
-        *    - 'map' object: internal object, tied to 'arguments'
-        *    - 'mappedNames' object: temporary value used during construction
-        */
-
-       i_arg = duk_push_object_helper(ctx,
-                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
-                                      DUK_HOBJECT_FLAG_ARRAY_PART |
-                                      
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARGUMENTS),
-                                      DUK_BIDX_OBJECT_PROTOTYPE);
-       DUK_ASSERT(i_arg >= 0);
-       arg = duk_require_hobject(ctx, -1);
-       DUK_ASSERT(arg != NULL);
-
-       i_map = duk_push_object_helper(ctx,
-                                      DUK_HOBJECT_FLAG_EXTENSIBLE |
-                                      
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
-                                      -1);  /* no prototype */
-       DUK_ASSERT(i_map >= 0);
-
-       i_mappednames = duk_push_object_helper(ctx,
-                                              DUK_HOBJECT_FLAG_EXTENSIBLE |
-                                              
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
-                                              -1);  /* no prototype */
-       DUK_ASSERT(i_mappednames >= 0);
-
-       /* [ ... formals arguments map mappedNames ] */
-
-       DUK_DDD(DUK_DDDPRINT("created arguments related objects: "
-                            "arguments at index %ld -> %!O "
-                            "map at index %ld -> %!O "
-                            "mappednames at index %ld -> %!O",
-                            (long) i_arg, (duk_heaphdr *) duk_get_hobject(ctx, 
i_arg),
-                            (long) i_map, (duk_heaphdr *) duk_get_hobject(ctx, 
i_map),
-                            (long) i_mappednames, (duk_heaphdr *) 
duk_get_hobject(ctx, i_mappednames)));
-
-       /*
-        *  Init arguments properties, map, etc.
-        */
-
-       duk_push_int(ctx, num_stack_args);
-       duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_LENGTH, 
DUK_PROPDESC_FLAGS_WC);
-
-       /*
-        *  Init argument related properties
-        */
-
-       /* step 11 */
-       idx = num_stack_args - 1;
-       while (idx >= 0) {
-               DUK_DDD(DUK_DDDPRINT("arg idx %ld, argbase=%ld, argidx=%ld",
-                                    (long) idx, (long) i_argbase, (long) 
(i_argbase + idx)));
-
-               DUK_DDD(DUK_DDDPRINT("define arguments[%ld]=arg", (long) idx));
-               duk_dup(ctx, i_argbase + idx);
-               duk_xdef_prop_index_wec(ctx, i_arg, (duk_uarridx_t) idx);
-               DUK_DDD(DUK_DDDPRINT("defined arguments[%ld]=arg", (long) idx));
-
-               /* step 11.c is relevant only if non-strict (checked in 
11.c.ii) */
-               if (!DUK_HOBJECT_HAS_STRICT(func) && idx < n_formals) {
-                       DUK_ASSERT(formals != NULL);
-
-                       DUK_DDD(DUK_DDDPRINT("strict function, index within 
formals (%ld < %ld)",
-                                            (long) idx, (long) n_formals));
-
-                       duk_get_prop_index(ctx, i_formals, idx);
-                       DUK_ASSERT(duk_is_string(ctx, -1));
-
-                       duk_dup(ctx, -1);  /* [ ... name name ] */
-
-                       if (!duk_has_prop(ctx, i_mappednames)) {
-                               /* steps 11.c.ii.1 - 11.c.ii.4, but our 
internal book-keeping
-                                * differs from the reference model
-                                */
-
-                               /* [ ... name ] */
-
-                               need_map = 1;
-
-                               DUK_DDD(DUK_DDDPRINT("set mappednames[%s]=%ld",
-                                                    (const char *) 
duk_get_string(ctx, -1),
-                                                    (long) idx));
-                               duk_dup(ctx, -1);                      /* name 
*/
-                               duk_push_uint(ctx, (duk_uint_t) idx);  /* index 
*/
-                               duk_to_string(ctx, -1);
-                               duk_xdef_prop_wec(ctx, i_mappednames);  /* out 
of spec, must be configurable */
-
-                               DUK_DDD(DUK_DDDPRINT("set map[%ld]=%s",
-                                                    (long) idx,
-                                                    duk_get_string(ctx, -1)));
-                               duk_dup(ctx, -1);         /* name */
-                               duk_xdef_prop_index_wec(ctx, i_map, 
(duk_uarridx_t) idx);  /* out of spec, must be configurable */
-                       } else {
-                               /* duk_has_prop() popped the second 'name' */
-                       }
-
-                       /* [ ... name ] */
-                       duk_pop(ctx);  /* pop 'name' */
-               }
-
-               idx--;
-       }
-
-       DUK_DDD(DUK_DDDPRINT("actual arguments processed"));
-
-       /* step 12 */
-       if (need_map) {
-               DUK_DDD(DUK_DDDPRINT("adding 'map' and 'varenv' to arguments 
object"));
-
-               /* should never happen for a strict callee */
-               DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func));
-
-               duk_dup(ctx, i_map);
-               duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_INT_MAP, 
DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
-
-               /* The variable environment for magic variable bindings needs 
to be
-                * given by the caller and recorded in the arguments object.
-                *
-                * See E5 Section 10.6, the creation of setters/getters.
-                *
-                * The variable environment also provides access to the callee, 
so
-                * an explicit (internal) callee property is not needed.
-                */
-
-               duk_push_hobject(ctx, varenv);
-               duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_INT_VARENV, 
DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
-       }
-
-       /* steps 13-14 */
-       if (DUK_HOBJECT_HAS_STRICT(func)) {
-               /* Callee/caller are throwers and are not deletable etc.  They
-                * could be implemented as virtual properties, but currently
-                * there is no support for virtual properties which are 
accessors
-                * (only plain virtual properties).  This would not be difficult
-                * to change in duk_hobject_props, but we can make the throwers
-                * normal, concrete properties just as easily.
-                *
-                * Note that the specification requires that the *same* thrower
-                * built-in object is used here!  See E5 Section 10.6 main
-                * algoritm, step 14, and Section 13.2.3 which describes the
-                * thrower.  See test case test-arguments-throwers.js.
-                */
-
-               DUK_DDD(DUK_DDDPRINT("strict function, setting caller/callee to 
throwers"));
-
-               duk_xdef_prop_stridx_thrower(ctx, i_arg, DUK_STRIDX_CALLER, 
DUK_PROPDESC_FLAGS_NONE);
-               duk_xdef_prop_stridx_thrower(ctx, i_arg, DUK_STRIDX_CALLEE, 
DUK_PROPDESC_FLAGS_NONE);
-       } else {
-               DUK_DDD(DUK_DDDPRINT("non-strict function, setting callee to 
actual value"));
-               duk_push_hobject(ctx, func);
-               duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_CALLEE, 
DUK_PROPDESC_FLAGS_WC);
-       }
-
-       /* set exotic behavior only after we're done */
-       if (need_map) {
-               /* Exotic behaviors are only enabled for arguments objects
-                * which have a parameter map (see E5 Section 10.6 main
-                * algorithm, step 12).
-                *
-                * In particular, a non-strict arguments object with no
-                * mapped formals does *NOT* get exotic behavior, even
-                * for e.g. "caller" property.  This seems counterintuitive
-                * but seems to be the case.
-                */
-
-               /* cannot be strict (never mapped variables) */
-               DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func));
-
-               DUK_DDD(DUK_DDDPRINT("enabling exotic behavior for arguments 
object"));
-               DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(arg);
-       } else {
-               DUK_DDD(DUK_DDDPRINT("not enabling exotic behavior for 
arguments object"));
-       }
-
-       DUK_DDD(DUK_DDDPRINT("final arguments related objects: "
-                            "arguments at index %ld -> %!O "
-                            "map at index %ld -> %!O "
-                            "mappednames at index %ld -> %!O",
-                            (long) i_arg, (duk_heaphdr *) duk_get_hobject(ctx, 
i_arg),
-                            (long) i_map, (duk_heaphdr *) duk_get_hobject(ctx, 
i_map),
-                            (long) i_mappednames, (duk_heaphdr *) 
duk_get_hobject(ctx, i_mappednames)));
-
-       /* [ args(n) [crud] formals arguments map mappednames ] */
-
-       duk_pop_2(ctx);
-       duk_remove(ctx, -2);
-
-       /* [ args [crud] arguments ] */
-}
-
-/* Helper for creating the arguments object and adding it to the env record
- * on top of the value stack.  This helper has a very strict dependency on
- * the shape of the input stack.
- */
-DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr,
-                                               duk_hobject *func,
-                                               duk_hobject *env,
-                                               duk_idx_t num_stack_args) {
-       duk_context *ctx = (duk_context *) thr;
-
-       DUK_DDD(DUK_DDDPRINT("creating arguments object for function call"));
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(func != NULL);
-       DUK_ASSERT(env != NULL);
-       DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
-       DUK_ASSERT(duk_get_top(ctx) >= num_stack_args + 1);
-
-       /* [ ... arg1 ... argN envobj ] */
-
-       duk__create_arguments_object(thr,
-                                    func,
-                                    env,
-                                    duk_get_top(ctx) - num_stack_args - 1,    
/* idx_argbase */
-                                    num_stack_args);
-
-       /* [ ... arg1 ... argN envobj argobj ] */
-
-       duk_xdef_prop_stridx(ctx,
-                            -2,
-                            DUK_STRIDX_LC_ARGUMENTS,
-                            DUK_HOBJECT_HAS_STRICT(func) ? 
DUK_PROPDESC_FLAGS_E :   /* strict: non-deletable, non-writable */
-                                                           
DUK_PROPDESC_FLAGS_WE);  /* non-strict: non-deletable, writable */
-       /* [ ... arg1 ... argN envobj ] */
-}
-
-/*
- *  Helper for handling a "bound function" chain when a call is being made.
- *
- *  Follows the bound function chain until a non-bound function is found.
- *  Prepends the bound arguments to the value stack (at idx_func + 2),
- *  updating 'num_stack_args' in the process.  The 'this' binding is also
- *  updated if necessary (at idx_func + 1).  Note that for constructor calls
- *  the 'this' binding is never updated by [[BoundThis]].
- *
- *  XXX: bound function chains could be collapsed at bound function creation
- *  time so that each bound function would point directly to a non-bound
- *  function.  This would make call time handling much easier.
- */
-
-DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
-                                                duk_idx_t idx_func,
-                                                duk_idx_t *p_num_stack_args,   
/* may be changed by call */
-                                                duk_bool_t 
is_constructor_call) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_idx_t num_stack_args;
-       duk_tval *tv_func;
-       duk_hobject *func;
-       duk_uint_t sanity;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(p_num_stack_args != NULL);
-
-       /* On entry, item at idx_func is a bound, non-lightweight function,
-        * but we don't rely on that below.
-        */
-
-       num_stack_args = *p_num_stack_args;
-
-       sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
-       do {
-               duk_idx_t i, len;
-
-               tv_func = duk_require_tval(ctx, idx_func);
-               DUK_ASSERT(tv_func != NULL);
-
-               if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
-                       /* Lightweight function: never bound, so terminate. */
-                       break;
-               } else if (DUK_TVAL_IS_OBJECT(tv_func)) {
-                       func = DUK_TVAL_GET_OBJECT(tv_func);
-                       if (!DUK_HOBJECT_HAS_BOUND(func)) {
-                               /* Normal non-bound function. */
-                               break;
-                       }
-               } else {
-                       /* Function.prototype.bind() should never let this 
happen,
-                        * ugly error message is enough.
-                        */
-                       DUK_ERROR_INTERNAL_DEFMSG(thr);
-               }
-               DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv_func) != NULL);
-
-               /* XXX: this could be more compact by accessing the internal 
properties
-                * directly as own properties (they cannot be inherited, and 
are not
-                * externally visible).
-                */
-
-               DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p, 
num_stack_args=%ld: %!T",
-                                    (void *) DUK_TVAL_GET_OBJECT(tv_func), 
(long) num_stack_args, tv_func));
-
-               /* [ ... func this arg1 ... argN ] */
-
-               if (is_constructor_call) {
-                       /* See: tests/ecmascript/test-spec-bound-constructor.js 
*/
-                       DUK_DDD(DUK_DDDPRINT("constructor call: don't update 
this binding"));
-               } else {
-                       duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_THIS);
-                       duk_replace(ctx, idx_func + 1);  /* idx_this = idx_func 
+ 1 */
-               }
-
-               /* [ ... func this arg1 ... argN ] */
-
-               /* XXX: duk_get_length? */
-               duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_ARGS);  /* -> 
[ ... func this arg1 ... argN _Args ] */
-               duk_get_prop_stridx(ctx, -1, DUK_STRIDX_LENGTH);          /* -> 
[ ... func this arg1 ... argN _Args length ] */
-               len = (duk_idx_t) duk_require_int(ctx, -1);
-               duk_pop(ctx);
-               for (i = 0; i < len; i++) {
-                       /* XXX: very slow - better to bulk allocate a gap, and 
copy
-                        * from args_array directly (we know it has a compact 
array
-                        * part, etc).
-                        */
-
-                       /* [ ... func this <some bound args> arg1 ... argN 
_Args ] */
-                       duk_get_prop_index(ctx, -1, i);
-                       duk_insert(ctx, idx_func + 2 + i);  /* idx_args = 
idx_func + 2 */
-               }
-               num_stack_args += len;  /* must be updated to work properly 
(e.g. creation of 'arguments') */
-               duk_pop(ctx);
-
-               /* [ ... func this <bound args> arg1 ... argN ] */
-
-               duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_TARGET);
-               duk_replace(ctx, idx_func);  /* replace in stack */
-
-               DUK_DDD(DUK_DDDPRINT("bound function handled, 
num_stack_args=%ld, idx_func=%ld, curr func=%!T",
-                                    (long) num_stack_args, (long) idx_func, 
duk_get_tval(ctx, idx_func)));
-       } while (--sanity > 0);
-
-       if (sanity == 0) {
-               DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
-       }
-
-       DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", 
duk_get_tval(ctx, idx_func)));
-
-#if defined(DUK_USE_ASSERTIONS)
-       tv_func = duk_require_tval(ctx, idx_func);
-       DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || 
DUK_TVAL_IS_OBJECT(tv_func));
-       if (DUK_TVAL_IS_OBJECT(tv_func)) {
-               func = DUK_TVAL_GET_OBJECT(tv_func);
-               DUK_ASSERT(func != NULL);
-               DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
-               DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func) ||
-                          DUK_HOBJECT_HAS_NATIVEFUNCTION(func));
-       }
-#endif
-
-       /* write back */
-       *p_num_stack_args = num_stack_args;
-}
-
-/*
- *  Helper for setting up var_env and lex_env of an activation,
- *  assuming it does NOT have the DUK_HOBJECT_FLAG_NEWENV flag.
- */
-
-DUK_LOCAL void duk__handle_oldenv_for_call(duk_hthread *thr,
-                                           duk_hobject *func,
-                                           duk_activation *act) {
-       duk_tval *tv;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(func != NULL);
-       DUK_ASSERT(act != NULL);
-       DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV(func));
-       DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));
-
-       tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, 
DUK_HTHREAD_STRING_INT_LEXENV(thr));
-       if (tv) {
-               DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
-               DUK_ASSERT(DUK_HOBJECT_IS_ENV(DUK_TVAL_GET_OBJECT(tv)));
-               act->lex_env = DUK_TVAL_GET_OBJECT(tv);
-
-               tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, 
DUK_HTHREAD_STRING_INT_VARENV(thr));
-               if (tv) {
-                       DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
-                       DUK_ASSERT(DUK_HOBJECT_IS_ENV(DUK_TVAL_GET_OBJECT(tv)));
-                       act->var_env = DUK_TVAL_GET_OBJECT(tv);
-               } else {
-                       act->var_env = act->lex_env;
-               }
-       } else {
-               act->lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
-               act->var_env = act->lex_env;
-       }
-
-       DUK_HOBJECT_INCREF_ALLOWNULL(thr, act->lex_env);
-       DUK_HOBJECT_INCREF_ALLOWNULL(thr, act->var_env);
-}
-
-/*
- *  Helper for updating callee 'caller' property.
- */
-
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject 
*func) {
-       duk_tval *tv_caller;
-       duk_hobject *h_tmp;
-       duk_activation *act_callee;
-       duk_activation *act_caller;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(func != NULL);
-       DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));  /* bound chain resolved */
-       DUK_ASSERT(thr->callstack_top >= 1);
-
-       if (DUK_HOBJECT_HAS_STRICT(func)) {
-               /* Strict functions don't get their 'caller' updated. */
-               return;
-       }
-
-       act_callee = thr->callstack + thr->callstack_top - 1;
-       act_caller = (thr->callstack_top >= 2 ? act_callee - 1 : NULL);
-
-       /* XXX: check .caller writability? */
-
-       /* Backup 'caller' property and update its value. */
-       tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, 
DUK_HTHREAD_STRING_CALLER(thr));
-       if (tv_caller) {
-               /* If caller is global/eval code, 'caller' should be set to
-                * 'null'.
-                *
-                * XXX: there is no exotic flag to infer this correctly now.
-                * The NEWENV flag is used now which works as intended for
-                * everything (global code, non-strict eval code, and functions)
-                * except strict eval code.  Bound functions are never an issue
-                * because 'func' has been resolved to a non-bound function.
-                */
-
-               if (act_caller) {
-                       /* act_caller->func may be NULL in some finalization 
cases,
-                        * just treat like we don't know the caller.
-                        */
-                       if (act_caller->func && 
!DUK_HOBJECT_HAS_NEWENV(act_caller->func)) {
-                               /* Setting to NULL causes 'caller' to be set to
-                                * 'null' as desired.
-                                */
-                               act_caller = NULL;
-                       }
-               }
-
-               if (DUK_TVAL_IS_OBJECT(tv_caller)) {
-                       h_tmp = DUK_TVAL_GET_OBJECT(tv_caller);
-                       DUK_ASSERT(h_tmp != NULL);
-                       act_callee->prev_caller = h_tmp;
-
-                       /* Previous value doesn't need refcount changes because 
its ownership
-                        * is transferred to prev_caller.
-                        */
-
-                       if (act_caller) {
-                               DUK_ASSERT(act_caller->func != NULL);
-                               DUK_TVAL_SET_OBJECT(tv_caller, 
act_caller->func);
-                               DUK_TVAL_INCREF(thr, tv_caller);
-                       } else {
-                               DUK_TVAL_SET_NULL(tv_caller);  /* no incref */
-                       }
-               } else {
-                       /* 'caller' must only take on 'null' or function value 
*/
-                       DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_caller));
-                       DUK_ASSERT(act_callee->prev_caller == NULL);
-                       if (act_caller && act_caller->func) {
-                               /* Tolerate act_caller->func == NULL which 
happens in
-                                * some finalization cases; treat like unknown 
caller.
-                                */
-                               DUK_TVAL_SET_OBJECT(tv_caller, 
act_caller->func);
-                               DUK_TVAL_INCREF(thr, tv_caller);
-                       } else {
-                               DUK_TVAL_SET_NULL(tv_caller);  /* no incref */
-                       }
-               }
-       }
-}
-#endif  /* DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */
-
-/*
- *  Determine the effective 'this' binding and coerce the current value
- *  on the valstack to the effective one (in-place, at idx_this).
- *
- *  The current this value in the valstack (at idx_this) represents either:
- *    - the caller's requested 'this' binding; or
- *    - a 'this' binding accumulated from the bound function chain
- *
- *  The final 'this' binding for the target function may still be
- *  different, and is determined as described in E5 Section 10.4.3.
- *
- *  For global and eval code (E5 Sections 10.4.1 and 10.4.2), we assume
- *  that the caller has provided the correct 'this' binding explicitly
- *  when calling, i.e.:
- *
- *    - global code: this=global object
- *    - direct eval: this=copy from eval() caller's this binding
- *    - other eval:  this=global object
- *
- *  Note: this function may cause a recursive function call with arbitrary
- *  side effects, because ToObject() may be called.
- */
-
-DUK_LOCAL void duk__coerce_effective_this_binding(duk_hthread *thr,
-                                                  duk_hobject *func,
-                                                  duk_idx_t idx_this) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_tval *tv_this;
-       duk_hobject *obj_global;
-
-       if (func == NULL || DUK_HOBJECT_HAS_STRICT(func)) {
-               /* Lightfuncs are always considered strict. */
-               DUK_DDD(DUK_DDDPRINT("this binding: strict -> use directly"));
-               return;
-       }
-
-       /* XXX: byte offset */
-       tv_this = thr->valstack_bottom + idx_this;
-       switch (DUK_TVAL_GET_TAG(tv_this)) {
-       case DUK_TAG_OBJECT:
-       case DUK_TAG_LIGHTFUNC:  /* lightfuncs are treated like objects and not 
coerced */
-               DUK_DDD(DUK_DDDPRINT("this binding: non-strict, object -> use 
directly"));
-               break;
-       case DUK_TAG_UNDEFINED:
-       case DUK_TAG_NULL:
-               DUK_DDD(DUK_DDDPRINT("this binding: non-strict, undefined/null 
-> use global object"));
-               obj_global = thr->builtins[DUK_BIDX_GLOBAL];
-               /* XXX: avoid this check somehow */
-               if (DUK_LIKELY(obj_global != NULL)) {
-                       DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this));  /* 
no need to decref previous value */
-                       DUK_TVAL_SET_OBJECT(tv_this, obj_global);
-                       DUK_HOBJECT_INCREF(thr, obj_global);
-               } else {
-                       /* This may only happen if built-ins are being "torn 
down".
-                        * This behavior is out of specification scope.
-                        */
-                       DUK_D(DUK_DPRINT("this binding: wanted to use global 
object, but it is NULL -> using undefined instead"));
-                       DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_this));  /* 
no need to decref previous value */
-                       DUK_TVAL_SET_UNDEFINED(tv_this);  /* nothing to incref 
*/
-               }
-               break;
-       default:
-               DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_this));
-               DUK_DDD(DUK_DDDPRINT("this binding: non-strict, not 
object/undefined/null -> use ToObject(value)"));
-               duk_to_object(ctx, idx_this);  /* may have side effects */
-               break;
-       }
-}
-
-/*
- *  Shared helper for non-bound func lookup.
- *
- *  Returns duk_hobject * to the final non-bound function (NULL for lightfunc).
- */
-
-DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx,
-                                                 duk_idx_t idx_func,
-                                                 duk_idx_t *out_num_stack_args,
-                                                 duk_tval **out_tv_func,
-                                                 duk_small_uint_t call_flags) {
-       duk_hthread *thr = (duk_hthread *) ctx;
-       duk_tval *tv_func;
-       duk_hobject *func;
-
-       for (;;) {
-               /* Use loop to minimize code size of relookup after bound 
function case */
-               tv_func = DUK_GET_TVAL_POSIDX(ctx, idx_func);
-               DUK_ASSERT(tv_func != NULL);
-
-               if (DUK_TVAL_IS_OBJECT(tv_func)) {
-                       func = DUK_TVAL_GET_OBJECT(tv_func);
-                       if (!DUK_HOBJECT_IS_CALLABLE(func)) {
-                               goto not_callable_error;
-                       }
-                       if (DUK_HOBJECT_HAS_BOUND(func)) {
-                               duk__handle_bound_chain_for_call(thr, idx_func, 
out_num_stack_args, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
-
-                               /* The final object may be a normal function or 
a lightfunc.
-                                * We need to re-lookup tv_func because it may 
have changed
-                                * (also value stack may have been resized).  
Loop again to
-                                * do that; we're guaranteed not to come here 
again.
-                                */
-                               
DUK_ASSERT(DUK_TVAL_IS_OBJECT(duk_require_tval(ctx, idx_func)) ||
-                                          
DUK_TVAL_IS_LIGHTFUNC(duk_require_tval(ctx, idx_func)));
-                               continue;
-                       }
-               } else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
-                       func = NULL;
-               } else {
-                       goto not_callable_error;
-               }
-               break;
-       }
-
-       DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_func) && 
DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_func))) ||
-                  DUK_TVAL_IS_LIGHTFUNC(tv_func));
-       DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
-       DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPILEDFUNCTION(func) ||
-                                   DUK_HOBJECT_IS_NATIVEFUNCTION(func)));
-
-       *out_tv_func = tv_func;
-       return func;
-
- not_callable_error:
-       DUK_ASSERT(tv_func != NULL);
-#if defined(DUK_USE_PARANOID_ERRORS)
-       DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
-#else
-       DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", 
duk_push_string_tval_readable(ctx, tv_func));
-#endif
-       DUK_UNREACHABLE();
-       return NULL;  /* never executed */
-}
-
-/*
- *  Value stack resize and stack top adjustment helper.
- *
- *  XXX: This should all be merged to duk_valstack_resize_raw().
- */
-
-DUK_LOCAL void duk__adjust_valstack_and_top(duk_hthread *thr,
-                                            duk_idx_t num_stack_args,
-                                            duk_idx_t idx_args,
-                                            duk_idx_t nregs,
-                                            duk_idx_t nargs,
-                                            duk_hobject *func) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_size_t vs_min_size;
-       duk_bool_t adjusted_top = 0;
-
-       vs_min_size = (thr->valstack_bottom - thr->valstack) +  /* bottom of 
current func */
-                     idx_args;                                 /* bottom of 
new func */
-
-       if (nregs >= 0) {
-               DUK_ASSERT(nargs >= 0);
-               DUK_ASSERT(nregs >= nargs);
-               vs_min_size += nregs;
-       } else {
-               /* 'func' wants stack "as is" */
-               vs_min_size += num_stack_args;  /* num entries of new func at 
entry */
-       }
-       if (func == NULL || DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
-               vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM;  /* Duktape/C 
API guaranteed entries (on top of args) */
-       }
-       vs_min_size += DUK_VALSTACK_INTERNAL_EXTRA;             /* + spare */
-
-       /* XXX: We can't resize the value stack to a size smaller than the
-        * current top, so the order of the resize and adjusting the stack
-        * top depends on the current vs. final size of the value stack.
-        * The operations could be combined to avoid this, but the proper
-        * fix is to only grow the value stack on a function call, and only
-        * shrink it (without throwing if the shrink fails) on function
-        * return.
-        */
-
-       if (vs_min_size < (duk_size_t) (thr->valstack_top  - thr->valstack)) {
-               DUK_DDD(DUK_DDDPRINT(("final size smaller, set top before 
resize")));
-
-               DUK_ASSERT(nregs >= 0);  /* can't happen when keeping current 
stack size */
-               duk_set_top(ctx, idx_args + nargs);  /* clamp anything above 
nargs */
-               duk_set_top(ctx, idx_args + nregs);  /* extend with undefined */
-               adjusted_top = 1;
-       }
-
-       (void) duk_valstack_resize_raw((duk_context *) thr,
-                                      vs_min_size,
-                                      DUK_VSRESIZE_FLAG_SHRINK |      /* flags 
*/
-                                      0 /* no compact */ |
-                                      DUK_VSRESIZE_FLAG_THROW);
-
-       if (!adjusted_top) {
-               if (nregs >= 0) {
-                       DUK_ASSERT(nregs >= nargs);
-                       duk_set_top(ctx, idx_args + nargs);  /* clamp anything 
above nargs */
-                       duk_set_top(ctx, idx_args + nregs);  /* extend with 
undefined */
-               }
-       }
-}
-
-/*
- *  Manipulate value stack so that exactly 'num_stack_rets' return
- *  values are at 'idx_retbase' in every case, assuming there are
- *  'rc' return values on top of stack.
- *
- *  This is a bit tricky, because the called C function operates in
- *  the same activation record and may have e.g. popped the stack
- *  empty (below idx_retbase).
- */
-
-DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t 
idx_retbase, duk_idx_t num_stack_rets, duk_idx_t num_actual_rets) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_idx_t idx_rcbase;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(idx_retbase >= 0);
-       DUK_ASSERT(num_stack_rets >= 0);
-       DUK_ASSERT(num_actual_rets >= 0);
-
-       idx_rcbase = duk_get_top(ctx) - num_actual_rets;  /* base of known 
return values */
-
-       DUK_DDD(DUK_DDDPRINT("adjust valstack after func call: "
-                            "num_stack_rets=%ld, num_actual_rets=%ld, 
stack_top=%ld, idx_retbase=%ld, idx_rcbase=%ld",
-                            (long) num_stack_rets, (long) num_actual_rets, 
(long) duk_get_top(ctx),
-                            (long) idx_retbase, (long) idx_rcbase));
-
-       DUK_ASSERT(idx_rcbase >= 0);  /* caller must check */
-
-       /* Ensure space for final configuration (idx_retbase + num_stack_rets)
-        * and intermediate configurations.
-        */
-       duk_require_stack_top(ctx,
-                             (idx_rcbase > idx_retbase ? idx_rcbase : 
idx_retbase) +
-                             num_stack_rets);
-
-       /* Chop extra retvals away / extend with undefined. */
-       duk_set_top(ctx, idx_rcbase + num_stack_rets);
-
-       if (idx_rcbase >= idx_retbase) {
-               duk_idx_t count = idx_rcbase - idx_retbase;
-               duk_idx_t i;
-
-               DUK_DDD(DUK_DDDPRINT("elements at/after idx_retbase have enough 
to cover func retvals "
-                                    "(idx_retbase=%ld, idx_rcbase=%ld)", 
(long) idx_retbase, (long) idx_rcbase));
-
-               /* nuke values at idx_retbase to get the first retval (initially
-                * at idx_rcbase) to idx_retbase
-                */
-
-               DUK_ASSERT(count >= 0);
-
-               for (i = 0; i < count; i++) {
-                       /* XXX: inefficient; block remove primitive */
-                       duk_remove(ctx, idx_retbase);
-               }
-       } else {
-               duk_idx_t count = idx_retbase - idx_rcbase;
-               duk_idx_t i;
-
-               DUK_DDD(DUK_DDDPRINT("not enough elements at/after idx_retbase 
to cover func retvals "
-                                    "(idx_retbase=%ld, idx_rcbase=%ld)", 
(long) idx_retbase, (long) idx_rcbase));
-
-               /* insert 'undefined' values at idx_rcbase to get the
-                * return values to idx_retbase
-                */
-
-               DUK_ASSERT(count > 0);
-
-               for (i = 0; i < count; i++) {
-                       /* XXX: inefficient; block insert primitive */
-                       duk_push_undefined(ctx);
-                       duk_insert(ctx, idx_rcbase);
-               }
-       }
-}
-
-/*
- *  Misc shared helpers.
- */
-
-/* Get valstack index for the func argument or throw if insane stack. */
-DUK_LOCAL duk_idx_t duk__get_idx_func(duk_hthread *thr, duk_idx_t 
num_stack_args) {
-       duk_size_t off_stack_top;
-       duk_size_t off_stack_args;
-       duk_size_t off_stack_all;
-       duk_idx_t idx_func;         /* valstack index of 'func' and retval 
(relative to entry valstack_bottom) */
-
-       /* Argument validation and func/args offset. */
-       off_stack_top = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - 
(duk_uint8_t *) thr->valstack_bottom);
-       off_stack_args = (duk_size_t) ((duk_size_t) num_stack_args * 
sizeof(duk_tval));
-       off_stack_all = off_stack_args + 2 * sizeof(duk_tval);
-       if (DUK_UNLIKELY(off_stack_all > off_stack_top)) {
-               /* Since stack indices are not reliable, we can't do anything 
useful
-                * here.  Invoke the existing setjmp catcher, or if it doesn't 
exist,
-                * call the fatal error handler.
-                */
-               DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
-               return 0;
-       }
-       idx_func = (duk_idx_t) ((off_stack_top - off_stack_all) / 
sizeof(duk_tval));
-       return idx_func;
-}
-
-/*
- *  duk_handle_call_protected() and duk_handle_call_unprotected():
- *  call into a Duktape/C or an Ecmascript function from any state.
- *
- *  Input stack (thr):
- *
- *    [ func this arg1 ... argN ]
- *
- *  Output stack (thr):
- *
- *    [ retval ]         (DUK_EXEC_SUCCESS)
- *    [ errobj ]         (DUK_EXEC_ERROR (normal error), protected call)
- *
- *  Even when executing a protected call an error may be thrown in rare cases
- *  such as an insane num_stack_args argument.  If there is no catchpoint for
- *  such errors, the fatal error handler is called.
- *
- *  The error handling path should be error free, even for out-of-memory
- *  errors, to ensure safe sandboxing.  (As of Duktape 1.4.0 this is not
- *  yet the case, see XXX notes below.)
- */
-
-DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
-                                                 duk_idx_t num_stack_args,
-                                                 duk_small_uint_t call_flags) {
-       duk_context *ctx;
-       duk_size_t entry_valstack_bottom_index;
-       duk_size_t entry_valstack_end;
-       duk_size_t entry_callstack_top;
-       duk_size_t entry_catchstack_top;
-       duk_int_t entry_call_recursion_depth;
-       duk_hthread *entry_curr_thread;
-       duk_uint_fast8_t entry_thread_state;
-       duk_instr_t **entry_ptr_curr_pc;
-       duk_jmpbuf *old_jmpbuf_ptr = NULL;
-       duk_jmpbuf our_jmpbuf;
-       duk_idx_t idx_func;  /* valstack index of 'func' and retval (relative 
to entry valstack_bottom) */
-
-       /* XXX: Multiple tv_func lookups are now avoided by making a local
-        * copy of tv_func.  Another approach would be to compute an offset
-        * for tv_func from valstack bottom and recomputing the tv_func
-        * pointer quickly as valstack + offset instead of calling 
duk_get_tval().
-        */
-
-       ctx = (duk_context *) thr;
-       DUK_UNREF(ctx);
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT_CTX_VALID(ctx);
-       DUK_ASSERT(num_stack_args >= 0);
-       /* XXX: currently NULL allocations are not supported; remove if later 
allowed */
-       DUK_ASSERT(thr->valstack != NULL);
-       DUK_ASSERT(thr->callstack != NULL);
-       DUK_ASSERT(thr->catchstack != NULL);
-
-       /* Argument validation and func/args offset. */
-       idx_func = duk__get_idx_func(thr, num_stack_args);
-
-       /* Preliminaries, required by setjmp() handler.  Must be careful not
-        * to throw an unintended error here.
-        */
-
-       entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - 
thr->valstack);
-#if defined(DUK_USE_PREFER_SIZE)
-       entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-       DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == 
thr->valstack_size);
-       entry_valstack_end = thr->valstack_size;
-#endif
-       entry_callstack_top = thr->callstack_top;
-       entry_catchstack_top = thr->catchstack_top;
-       entry_call_recursion_depth = thr->heap->call_recursion_depth;
-       entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if 
first call */
-       entry_thread_state = thr->state;
-       entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-
-       DUK_DD(DUK_DDPRINT("duk_handle_call_protected: thr=%p, 
num_stack_args=%ld, "
-                          "call_flags=0x%08lx (ignorerec=%ld, 
constructor=%ld), "
-                          "valstack_top=%ld, idx_func=%ld, idx_args=%ld, 
rec_depth=%ld/%ld, "
-                          "entry_valstack_bottom_index=%ld, 
entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-                          "entry_call_recursion_depth=%ld, 
entry_curr_thread=%p, entry_thread_state=%ld",
-                          (void *) thr,
-                          (long) num_stack_args,
-                          (unsigned long) call_flags,
-                          (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) 
!= 0 ? 1 : 0),
-                          (long) ((call_flags & 
DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0),
-                          (long) duk_get_top(ctx),
-                          (long) idx_func,
-                          (long) (idx_func + 2),
-                          (long) thr->heap->call_recursion_depth,
-                          (long) thr->heap->call_recursion_limit,
-                          (long) entry_valstack_bottom_index,
-                          (long) entry_callstack_top,
-                          (long) entry_catchstack_top,
-                          (long) entry_call_recursion_depth,
-                          (void *) entry_curr_thread,
-                          (long) entry_thread_state));
-
-       old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
-       thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       try {
-#else
-       DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
-       if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
-#endif
-               /* Call handling and success path.  Success path exit cleans
-                * up almost all state.
-                */
-               duk__handle_call_inner(thr, num_stack_args, call_flags, 
idx_func);
-
-               /* Success path handles */
-               DUK_ASSERT(thr->heap->call_recursion_depth == 
entry_call_recursion_depth);
-               DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
-
-               /* Longjmp state is kept clean in success path */
-               DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
-               DUK_ASSERT(thr->heap->lj.iserror == 0);
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
-               thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-               return DUK_EXEC_SUCCESS;
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       } catch (duk_internal_exception &exc) {
-#else
-       } else {
-#endif
-               /* Error; error value is in heap->lj.value1. */
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-               DUK_UNREF(exc);
-#endif
-
-               duk__handle_call_error(thr,
-                                      entry_valstack_bottom_index,
-                                      entry_valstack_end,
-                                      entry_catchstack_top,
-                                      entry_callstack_top,
-                                      entry_call_recursion_depth,
-                                      entry_curr_thread,
-                                      entry_thread_state,
-                                      entry_ptr_curr_pc,
-                                      idx_func,
-                                      old_jmpbuf_ptr);
-
-               /* Longjmp state is cleaned up by error handling */
-               DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
-               DUK_ASSERT(thr->heap->lj.iserror == 0);
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-               return DUK_EXEC_ERROR;
-       }
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       catch (std::exception &exc) {
-               const char *what = exc.what();
-               if (!what) {
-                       what = "unknown";
-               }
-               DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown 
by user code)"));
-               try {
-                       DUK_ERROR_FMT1(thr, DUK_ERR_API_ERROR, "caught invalid 
c++ std::exception '%s' (perhaps thrown by user code)", what);
-               } catch (duk_internal_exception exc) {
-                       DUK_D(DUK_DPRINT("caught api error thrown from 
unexpected c++ std::exception"));
-                       DUK_UNREF(exc);
-                       duk__handle_call_error(thr,
-                                              entry_valstack_bottom_index,
-                                              entry_valstack_end,
-                                              entry_catchstack_top,
-                                              entry_callstack_top,
-                                              entry_call_recursion_depth,
-                                              entry_curr_thread,
-                                              entry_thread_state,
-                                              entry_ptr_curr_pc,
-                                              idx_func,
-                                              old_jmpbuf_ptr);
-                       return DUK_EXEC_ERROR;
-               }
-       } catch (...) {
-               DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by 
user code)"));
-               try {
-                       DUK_ERROR_API(thr, "caught invalid c++ exception 
(perhaps thrown by user code)");
-               } catch (duk_internal_exception exc) {
-                       DUK_D(DUK_DPRINT("caught api error thrown from 
unexpected c++ exception"));
-                       DUK_UNREF(exc);
-                       duk__handle_call_error(thr,
-                                              entry_valstack_bottom_index,
-                                              entry_valstack_end,
-                                              entry_catchstack_top,
-                                              entry_callstack_top,
-                                              entry_call_recursion_depth,
-                                              entry_curr_thread,
-                                              entry_thread_state,
-                                              entry_ptr_curr_pc,
-                                              idx_func,
-                                              old_jmpbuf_ptr);
-                       return DUK_EXEC_ERROR;
-               }
-       }
-#endif
-}
-
-DUK_INTERNAL void duk_handle_call_unprotected(duk_hthread *thr,
-                                              duk_idx_t num_stack_args,
-                                              duk_small_uint_t call_flags) {
-       duk_idx_t idx_func;         /* valstack index of 'func' and retval 
(relative to entry valstack_bottom) */
-
-       /* Argument validation and func/args offset. */
-       idx_func = duk__get_idx_func(thr, num_stack_args);
-
-       duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func);
-}
-
-DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
-                                      duk_idx_t num_stack_args,
-                                      duk_small_uint_t call_flags,
-                                      duk_idx_t idx_func) {
-       duk_context *ctx;
-       duk_size_t entry_valstack_bottom_index;
-       duk_size_t entry_valstack_end;
-       duk_size_t entry_callstack_top;
-       duk_size_t entry_catchstack_top;
-       duk_int_t entry_call_recursion_depth;
-       duk_hthread *entry_curr_thread;
-       duk_uint_fast8_t entry_thread_state;
-       duk_instr_t **entry_ptr_curr_pc;
-       duk_idx_t nargs;            /* # argument registers target function 
wants (< 0 => "as is") */
-       duk_idx_t nregs;            /* # total registers target function wants 
on entry (< 0 => "as is") */
-       duk_hobject *func;          /* 'func' on stack (borrowed reference) */
-       duk_tval *tv_func;          /* duk_tval ptr for 'func' on stack 
(borrowed reference) or tv_func_copy */
-       duk_tval tv_func_copy;      /* to avoid relookups */
-       duk_activation *act;
-       duk_hobject *env;
-       duk_ret_t rc;
-
-       ctx = (duk_context *) thr;
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT_CTX_VALID(ctx);
-       DUK_ASSERT(ctx != NULL);
-       DUK_ASSERT(num_stack_args >= 0);
-       /* XXX: currently NULL allocations are not supported; remove if later 
allowed */
-       DUK_ASSERT(thr->valstack != NULL);
-       DUK_ASSERT(thr->callstack != NULL);
-       DUK_ASSERT(thr->catchstack != NULL);
-
-       DUK_DD(DUK_DDPRINT("duk__handle_call_inner: num_stack_args=%ld, 
call_flags=0x%08lx, top=%ld",
-                          (long) num_stack_args, (long) call_flags, (long) 
duk_get_top(ctx)));
-
-       /*
-        *  Store entry state.
-        */
-
-       entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - 
thr->valstack);
-#if defined(DUK_USE_PREFER_SIZE)
-       entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-       DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == 
thr->valstack_size);
-       entry_valstack_end = thr->valstack_size;
-#endif
-       entry_callstack_top = thr->callstack_top;
-       entry_catchstack_top = thr->catchstack_top;
-       entry_call_recursion_depth = thr->heap->call_recursion_depth;
-       entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if 
first call */
-       entry_thread_state = thr->state;
-       entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-
-       /* If thr->ptr_curr_pc is set, sync curr_pc to act->pc.  Then NULL
-        * thr->ptr_curr_pc so that it's not accidentally used with an incorrect
-        * activation when side effects occur.
-        */
-       duk_hthread_sync_and_null_currpc(thr);
-
-       DUK_DD(DUK_DDPRINT("duk__handle_call_inner: thr=%p, num_stack_args=%ld, 
"
-                          "call_flags=0x%08lx (ignorerec=%ld, 
constructor=%ld), "
-                          "valstack_top=%ld, idx_func=%ld, idx_args=%ld, 
rec_depth=%ld/%ld, "
-                          "entry_valstack_bottom_index=%ld, 
entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-                          "entry_call_recursion_depth=%ld, 
entry_curr_thread=%p, entry_thread_state=%ld",
-                          (void *) thr,
-                          (long) num_stack_args,
-                          (unsigned long) call_flags,
-                          (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) 
!= 0 ? 1 : 0),
-                          (long) ((call_flags & 
DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0),
-                          (long) duk_get_top(ctx),
-                          (long) idx_func,
-                          (long) (idx_func + 2),
-                          (long) thr->heap->call_recursion_depth,
-                          (long) thr->heap->call_recursion_limit,
-                          (long) entry_valstack_bottom_index,
-                          (long) entry_callstack_top,
-                          (long) entry_catchstack_top,
-                          (long) entry_call_recursion_depth,
-                          (void *) entry_curr_thread,
-                          (long) entry_thread_state));
-
-
-       /*
-        *  Thread state check and book-keeping.
-        */
-
-       if (thr == thr->heap->curr_thread) {
-               /* same thread */
-               if (thr->state != DUK_HTHREAD_STATE_RUNNING) {
-                       /* should actually never happen, but check anyway */
-                       goto thread_state_error;
-               }
-       } else {
-               /* different thread */
-               DUK_ASSERT(thr->heap->curr_thread == NULL ||
-                          thr->heap->curr_thread->state == 
DUK_HTHREAD_STATE_RUNNING);
-               if (thr->state != DUK_HTHREAD_STATE_INACTIVE) {
-                       goto thread_state_error;
-               }
-               DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
-               thr->state = DUK_HTHREAD_STATE_RUNNING;
-
-               /* Note: multiple threads may be simultaneously in the RUNNING
-                * state, but not in the same "resume chain".
-                */
-       }
-       DUK_ASSERT(thr->heap->curr_thread == thr);
-       DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
-
-       /*
-        *  C call recursion depth check, which provides a reasonable upper
-        *  bound on maximum C stack size (arbitrary C stack growth is only
-        *  possible by recursive handle_call / handle_safe_call calls).
-        */
-
-       /* XXX: remove DUK_CALL_FLAG_IGNORE_RECLIMIT flag: there's now the
-        * reclimit bump?
-        */
-
-       DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
-       DUK_ASSERT(thr->heap->call_recursion_depth <= 
thr->heap->call_recursion_limit);
-       if (call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) {
-               DUK_DD(DUK_DDPRINT("ignoring reclimit for this call (probably 
an errhandler call)"));
-       } else {
-               if (thr->heap->call_recursion_depth >= 
thr->heap->call_recursion_limit) {
-                       /* XXX: error message is a bit misleading: we reached a 
recursion
-                        * limit which is also essentially the same as a C 
callstack limit
-                        * (except perhaps with some relaxed threading 
assumptions).
-                        */
-                       DUK_ERROR_RANGE(thr, DUK_STR_C_CALLSTACK_LIMIT);
-               }
-               thr->heap->call_recursion_depth++;
-       }
-
-       /*
-        *  Check the function type, handle bound function chains, and prepare
-        *  parameters for the rest of the call handling.  Also figure out the
-        *  effective 'this' binding, which replaces the current value at
-        *  idx_func + 1.
-        *
-        *  If the target function is a 'bound' one, follow the chain of 'bound'
-        *  functions until a non-bound function is found.  During this process,
-        *  bound arguments are 'prepended' to existing ones, and the "this"
-        *  binding is overridden.  See E5 Section 15.3.4.5.1.
-        *
-        *  Lightfunc detection happens here too.  Note that lightweight 
functions
-        *  can be wrapped by (non-lightweight) bound functions so we must 
resolve
-        *  the bound function chain first.
-        */
-
-       func = duk__nonbound_func_lookup(ctx, idx_func, &num_stack_args, 
&tv_func, call_flags);
-       DUK_TVAL_SET_TVAL(&tv_func_copy, tv_func);
-       tv_func = &tv_func_copy;  /* local copy to avoid relookups */
-
-       DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
-       DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPILEDFUNCTION(func) ||
-                                   DUK_HOBJECT_IS_NATIVEFUNCTION(func)));
-
-       duk__coerce_effective_this_binding(thr, func, idx_func + 1);
-       DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T",
-                            (duk_tval *) duk_get_tval(ctx, idx_func + 1)));
-
-       /* [ ... func this arg1 ... argN ] */
-
-       /*
-        *  Setup a preliminary activation and figure out nargs/nregs.
-        *
-        *  Don't touch valstack_bottom or valstack_top yet so that Duktape API
-        *  calls work normally.
-        */
-
-       duk_hthread_callstack_grow(thr);
-
-       if (thr->callstack_top > 0) {
-               /*
-                *  Update idx_retval of current activation.
-                *
-                *  Although it might seem this is not necessary (bytecode 
executor
-                *  does this for Ecmascript-to-Ecmascript calls; other calls 
are
-                *  handled here), this turns out to be necessary for handling 
yield
-                *  and resume.  For them, an Ecmascript-to-native call 
happens, and
-                *  the Ecmascript call's idx_retval must be set for things to 
work.
-                */
-
-               (thr->callstack + thr->callstack_top - 1)->idx_retval = 
entry_valstack_bottom_index + idx_func;
-       }
-
-       DUK_ASSERT(thr->callstack_top < thr->callstack_size);
-       act = thr->callstack + thr->callstack_top;
-       thr->callstack_top++;
-       DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
-       DUK_ASSERT(thr->valstack_top > thr->valstack_bottom);  /* at least 
effective 'this' */
-       DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));
-
-       act->flags = 0;
-
-       /* For now all calls except Ecma-to-Ecma calls prevent a yield. */
-       act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
-       if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) {
-               act->flags |= DUK_ACT_FLAG_CONSTRUCT;
-       }
-       if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) {
-               act->flags |= DUK_ACT_FLAG_DIRECT_EVAL;
-       }
-
-       /* These base values are never used, but if the compiler doesn't know
-        * that DUK_ERROR() won't return, these are needed to silence warnings.
-        * On the other hand, scan-build will warn about the values not being
-        * used, so add a DUK_UNREF.
-        */
-       nargs = 0; DUK_UNREF(nargs);
-       nregs = 0; DUK_UNREF(nregs);
-
-       if (DUK_LIKELY(func != NULL)) {
-               if (DUK_HOBJECT_HAS_STRICT(func)) {
-                       act->flags |= DUK_ACT_FLAG_STRICT;
-               }
-               if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
-                       nargs = ((duk_hcompiledfunction *) func)->nargs;
-                       nregs = ((duk_hcompiledfunction *) func)->nregs;
-                       DUK_ASSERT(nregs >= nargs);
-               } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(func)) {
-                       /* Note: nargs (and nregs) may be negative for a native,
-                        * function, which indicates that the function wants the
-                        * input stack "as is" (i.e. handles "vararg" 
arguments).
-                        */
-                       nargs = ((duk_hnativefunction *) func)->nargs;
-                       nregs = nargs;
-               } else {
-                       /* XXX: this should be an assert */
-                       DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
-               }
-       } else {
-               duk_small_uint_t lf_flags;
-
-               DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
-               lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func);
-               nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
-               if (nargs == DUK_LFUNC_NARGS_VARARGS) {
-                       nargs = -1;  /* vararg */
-               }
-               nregs = nargs;
-
-               act->flags |= DUK_ACT_FLAG_STRICT;
-       }
-
-       act->func = func;  /* NULL for lightfunc */
-       act->var_env = NULL;
-       act->lex_env = NULL;
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-       act->prev_caller = NULL;
-#endif
-       act->curr_pc = NULL;
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-       act->prev_line = 0;
-#endif
-       act->idx_bottom = entry_valstack_bottom_index + idx_func + 2;
-#if 0  /* topmost activation idx_retval is considered garbage, no need to init 
*/
-       act->idx_retval = 0;
-#endif
-       DUK_TVAL_SET_TVAL(&act->tv_func, tv_func);  /* borrowed, no refcount */
-
-       /* XXX: remove the preventcount and make yield walk the callstack?
-        * Or perhaps just use a single flag, not a counter, faster to just
-        * set and restore?
-        */
-       if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
-               /* duk_hthread_callstack_unwind() will decrease this on unwind 
*/
-               thr->callstack_preventcount++;
-       }
-
-       /* XXX: Is this INCREF necessary? 'func' is always a borrowed
-        * reference reachable through the value stack?  If changed, stack
-        * unwind code also needs to be fixed to match.
-        */
-       DUK_HOBJECT_INCREF_ALLOWNULL(thr, func);  /* act->func */
-
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-       if (func) {
-               duk__update_func_caller_prop(thr, func);
-       }
-       act = thr->callstack + thr->callstack_top - 1;
-#endif
-
-       /* [ ... func this arg1 ... argN ] */
-
-       /*
-        *  Environment record creation and 'arguments' object creation.
-        *  Named function expression name binding is handled by the
-        *  compiler; the compiled function's parent env will contain
-        *  the (immutable) binding already.
-        *
-        *  This handling is now identical for C and Ecmascript functions.
-        *  C functions always have the 'NEWENV' flag set, so their
-        *  environment record initialization is delayed (which is good).
-        *
-        *  Delayed creation (on demand) is handled in duk_js_var.c.
-        */
-
-       DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUND(func));  /* bound 
function chain has already been resolved */
-
-       if (DUK_LIKELY(func != NULL)) {
-               if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) {
-                       if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) {
-                               /* Use a new environment but there's no 
'arguments' object;
-                                * delayed environment initialization.  This is 
the most
-                                * common case.
-                                */
-                               DUK_ASSERT(act->lex_env == NULL);
-                               DUK_ASSERT(act->var_env == NULL);
-                       } else {
-                               /* Use a new environment and there's an 
'arguments' object.
-                                * We need to initialize it right now.
-                                */
-
-                               /* third arg: absolute index (to entire 
valstack) of idx_bottom of new activation */
-                               env = 
duk_create_activation_environment_record(thr, func, act->idx_bottom);
-                               DUK_ASSERT(env != NULL);
-
-                               /* [ ... func this arg1 ... argN envobj ] */
-
-                               DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
-                               duk__handle_createargs_for_call(thr, func, env, 
num_stack_args);
-
-                               /* [ ... func this arg1 ... argN envobj ] */
-
-                               act = thr->callstack + thr->callstack_top - 1;
-                               act->lex_env = env;
-                               act->var_env = env;
-                               DUK_HOBJECT_INCREF(thr, env);
-                               DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref 
by count (2) directly */
-                               duk_pop(ctx);
-                       }
-               } else {
-                       /* Use existing env (e.g. for non-strict eval); cannot 
have
-                        * an own 'arguments' object (but can refer to an 
existing one).
-                        */
-
-                       DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));
-
-                       duk__handle_oldenv_for_call(thr, func, act);
-
-                       DUK_ASSERT(act->lex_env != NULL);
-                       DUK_ASSERT(act->var_env != NULL);
-               }
-       } else {
-               /* Lightfuncs are always native functions and have "newenv". */
-               DUK_ASSERT(act->lex_env == NULL);
-               DUK_ASSERT(act->var_env == NULL);
-       }
-
-       /* [ ... func this arg1 ... argN ] */
-
-       /*
-        *  Setup value stack: clamp to 'nargs', fill up to 'nregs'
-        *
-        *  Value stack may either grow or shrink, depending on the
-        *  number of func registers and the number of actual arguments.
-        *  If nregs >= 0, func wants args clamped to 'nargs'; else it
-        *  wants all args (= 'num_stack_args').
-        */
-
-       /* XXX: optimize value stack operation */
-       /* XXX: don't want to shrink allocation here */
-
-       duk__adjust_valstack_and_top(thr,
-                                    num_stack_args,
-                                    idx_func + 2,
-                                    nregs,
-                                    nargs,
-                                    func);
-
-       /*
-        *  Determine call type, then finalize activation, shift to
-        *  new value stack bottom, and call the target.
-        */
-
-       if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
-               /*
-                *  Ecmascript call
-                */
-
-               duk_tval *tv_ret;
-               duk_tval *tv_funret;
-
-               DUK_ASSERT(func != NULL);
-               DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(func));
-               act->curr_pc = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, 
(duk_hcompiledfunction *) func);
-
-               thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
-               /* keep current valstack_top */
-               DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-               DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-               DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-               /* [ ... func this | arg1 ... argN ] ('this' must precede new 
bottom) */
-
-               /*
-                *  Bytecode executor call.
-                *
-                *  Execute bytecode, handling any recursive function calls and
-                *  thread resumptions.  Returns when execution would return 
from
-                *  the entry level activation.  When the executor returns, a
-                *  single return value is left on the stack top.
-                *
-                *  The only possible longjmp() is an error (DUK_LJ_TYPE_THROW),
-                *  other types are handled internally by the executor.
-                */
-
-               /* thr->ptr_curr_pc is set by bytecode executor early on entry 
*/
-               DUK_ASSERT(thr->ptr_curr_pc == NULL);
-               DUK_DDD(DUK_DDDPRINT("entering bytecode execution"));
-               duk_js_execute_bytecode(thr);
-               DUK_DDD(DUK_DDDPRINT("returned from bytecode execution"));
-
-               /* Unwind. */
-
-               DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);  /* 
may need unwind */
-               DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
-               DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
-               duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
-               duk_hthread_catchstack_shrink_check(thr);
-               duk_hthread_callstack_unwind(thr, entry_callstack_top);
-               duk_hthread_callstack_shrink_check(thr);
-
-               thr->valstack_bottom = thr->valstack + 
entry_valstack_bottom_index;
-               /* keep current valstack_top */
-               DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-               DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-               DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-               DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func 
+ 1);
-
-               /* Return value handling. */
-
-               /* [ ... func this (crud) retval ] */
-
-               tv_ret = thr->valstack_bottom + idx_func;
-               tv_funret = thr->valstack_top - 1;
-#if defined(DUK_USE_FASTINT)
-               /* Explicit check for fastint downgrade. */
-               DUK_TVAL_CHKFAST_INPLACE(tv_funret);
-#endif
-               DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret);  /* side 
effects */
-       } else {
-               /*
-                *  Native call.
-                */
-
-               duk_tval *tv_ret;
-               duk_tval *tv_funret;
-
-               thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
-               /* keep current valstack_top */
-               DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-               DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-               DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-               DUK_ASSERT(func == NULL || ((duk_hnativefunction *) func)->func 
!= NULL);
-
-               /* [ ... func this | arg1 ... argN ] ('this' must precede new 
bottom) */
-
-               /* For native calls must be NULL so we don't sync back */
-               DUK_ASSERT(thr->ptr_curr_pc == NULL);
-
-               if (func) {
-                       rc = ((duk_hnativefunction *) func)->func((duk_context 
*) thr);
-               } else {
-                       duk_c_function funcptr = 
DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func);
-                       rc = funcptr((duk_context *) thr);
-               }
-
-               /* Automatic error throwing, retval check. */
-
-               if (rc < 0) {
-                       duk_error_throw_from_negative_rc(thr, rc);
-                       DUK_UNREACHABLE();
-               } else if (rc > 1) {
-                       DUK_ERROR_API(thr, "c function returned invalid rc");
-               }
-               DUK_ASSERT(rc == 0 || rc == 1);
-
-               /* Unwind. */
-
-               DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);  /* no 
need to unwind */
-               DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
-               duk_hthread_callstack_unwind(thr, entry_callstack_top);
-               duk_hthread_callstack_shrink_check(thr);
-
-               thr->valstack_bottom = thr->valstack + 
entry_valstack_bottom_index;
-               /* keep current valstack_top */
-               DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-               DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-               DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-               DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func 
+ 1);
-
-               /* Return value handling. */
-
-               /* XXX: should this happen in the callee's activation or after 
unwinding? */
-               tv_ret = thr->valstack_bottom + idx_func;
-               if (rc == 0) {
-                       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv_ret);  /* side 
effects */
-               } else {
-                       /* [ ... func this (crud) retval ] */
-                       tv_funret = thr->valstack_top - 1;
-#if defined(DUK_USE_FASTINT)
-                       /* Explicit check for fastint downgrade. */
-                       DUK_TVAL_CHKFAST_INPLACE(tv_funret);
-#endif
-                       DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret);  /* 
side effects */
-               }
-       }
-
-       duk_set_top(ctx, idx_func + 1);  /* XXX: unnecessary, handle in adjust 
*/
-
-       /* [ ... retval ] */
-
-       /* Ensure there is internal valstack spare before we exit; this may
-        * throw an alloc error.  The same guaranteed size must be available
-        * as before the call.  This is not optimal now: we store the valstack
-        * allocated size during entry; this value may be higher than the
-        * minimal guarantee for an application.
-        */
-
-       /* XXX: we should never shrink here; when we error out later, we'd
-        * need to potentially grow the value stack in error unwind which could
-        * cause another error.
-        */
-
-       (void) duk_valstack_resize_raw((duk_context *) thr,
-                                      entry_valstack_end,                    
/* same as during entry */
-                                      DUK_VSRESIZE_FLAG_SHRINK |             
/* flags */
-                                      DUK_VSRESIZE_FLAG_COMPACT |
-                                      DUK_VSRESIZE_FLAG_THROW);
-
-       /* Restore entry thread executor curr_pc stack frame pointer. */
-       thr->ptr_curr_pc = entry_ptr_curr_pc;
-
-       DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
-       thr->state = (duk_uint8_t) entry_thread_state;
-
-       DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread == NULL) ||  /* first call */
-                  (thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread != NULL) ||  /* other call */
-                  (thr->state == DUK_HTHREAD_STATE_RUNNING && 
thr->heap->curr_thread == thr));     /* current thread */
-
-       thr->heap->call_recursion_depth = entry_call_recursion_depth;
-
-       /* If the debugger is active we need to force an interrupt so that
-        * debugger breakpoints are rechecked.  This is important for function
-        * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see
-        * GH-303.  Only needed for success path, error path always causes a
-        * breakpoint recheck in the executor.  It would be enough to set this
-        * only when returning to an Ecmascript activation, but setting the flag
-        * on every return should have no ill effect.
-        */
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-       if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
-               DUK_DD(DUK_DDPRINT("returning with debugger enabled, force 
interrupt"));
-               DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
-               thr->interrupt_init -= thr->interrupt_counter;
-               thr->interrupt_counter = 0;
-               thr->heap->dbg_force_restart = 1;
-       }
-#endif
-
-#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
-       duk__interrupt_fixup(thr, entry_curr_thread);
-#endif
-
-       return;
-
- thread_state_error:
-       DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for call 
(%ld)", (long) thr->state);
-       DUK_UNREACHABLE();
-       return;  /* never executed */
-}
-
-DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
-                                      duk_size_t entry_valstack_bottom_index,
-                                      duk_size_t entry_valstack_end,
-                                      duk_size_t entry_catchstack_top,
-                                      duk_size_t entry_callstack_top,
-                                      duk_int_t entry_call_recursion_depth,
-                                      duk_hthread *entry_curr_thread,
-                                      duk_uint_fast8_t entry_thread_state,
-                                      duk_instr_t **entry_ptr_curr_pc,
-                                      duk_idx_t idx_func,
-                                      duk_jmpbuf *old_jmpbuf_ptr) {
-       duk_context *ctx;
-       duk_tval *tv_ret;
-
-       ctx = (duk_context *) thr;
-
-       DUK_DDD(DUK_DDDPRINT("error caught during duk__handle_call_inner(): 
%!T",
-                            (duk_tval *) &thr->heap->lj.value1));
-
-       /* Other longjmp types are handled by executor before propagating
-        * the error here.
-        */
-       DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
-       DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-       DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
-
-       /* We don't need to sync back thr->ptr_curr_pc here because
-        * the bytecode executor always has a setjmp catchpoint which
-        * does that before errors propagate to here.
-        */
-       DUK_ASSERT(thr->ptr_curr_pc == NULL);
-
-       /* Restore the previous setjmp catcher so that any error in
-        * error handling will propagate outwards rather than re-enter
-        * the same handler.  However, the error handling path must be
-        * designed to be error free so that sandboxing guarantees are
-        * reliable, see e.g. https://github.com/svaarala/duktape/issues/476.
-        */
-       thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-       /* XXX: callstack unwind may now throw an error when closing
-        * scopes; this is a sandboxing issue, described in:
-        * https://github.com/svaarala/duktape/issues/476
-        */
-       duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
-       duk_hthread_catchstack_shrink_check(thr);
-       duk_hthread_callstack_unwind(thr, entry_callstack_top);
-       duk_hthread_callstack_shrink_check(thr);
-
-       thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
-       tv_ret = thr->valstack_bottom + idx_func;  /* XXX: byte offset? */
-       DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, &thr->heap->lj.value1);  /* side 
effects */
-#if defined(DUK_USE_FASTINT)
-       /* Explicit check for fastint downgrade. */
-       DUK_TVAL_CHKFAST_INPLACE(tv_ret);
-#endif
-       duk_set_top(ctx, idx_func + 1);  /* XXX: could be eliminated with 
valstack adjust */
-
-       /* [ ... errobj ] */
-
-       /* Ensure there is internal valstack spare before we exit; this may
-        * throw an alloc error.  The same guaranteed size must be available
-        * as before the call.  This is not optimal now: we store the valstack
-        * allocated size during entry; this value may be higher than the
-        * minimal guarantee for an application.
-        */
-
-       /* XXX: this needs to be reworked so that we never shrink the value
-        * stack on function entry so that we never need to grow it here.
-        * Needing to grow here is a sandboxing issue because we need to
-        * allocate which may cause an error in the error handling path
-        * and thus propagate an error out of a protected call.
-        */
-
-       (void) duk_valstack_resize_raw((duk_context *) thr,
-                                      entry_valstack_end,                    
/* same as during entry */
-                                      DUK_VSRESIZE_FLAG_SHRINK |             
/* flags */
-                                      DUK_VSRESIZE_FLAG_COMPACT |
-                                      DUK_VSRESIZE_FLAG_THROW);
-
-
-       /* These are just convenience "wiping" of state.  Side effects should
-        * not be an issue here: thr->heap and thr->heap->lj have a stable
-        * pointer.  Finalizer runs etc capture even out-of-memory errors so
-        * nothing should throw here.
-        */
-       thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
-       thr->heap->lj.iserror = 0;
-       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1);  /* side 
effects */
-       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2);  /* side 
effects */
-
-       /* Restore entry thread executor curr_pc stack frame pointer. */
-       thr->ptr_curr_pc = entry_ptr_curr_pc;
-
-       DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
-       thr->state = (duk_uint8_t) entry_thread_state;
-
-       DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread == NULL) ||  /* first call */
-                  (thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread != NULL) ||  /* other call */
-                  (thr->state == DUK_HTHREAD_STATE_RUNNING && 
thr->heap->curr_thread == thr));     /* current thread */
-
-       thr->heap->call_recursion_depth = entry_call_recursion_depth;
-
-       /* If the debugger is active we need to force an interrupt so that
-        * debugger breakpoints are rechecked.  This is important for function
-        * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see
-        * GH-303.  Only needed for success path, error path always causes a
-        * breakpoint recheck in the executor.  It would be enough to set this
-        * only when returning to an Ecmascript activation, but setting the flag
-        * on every return should have no ill effect.
-        */
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-       if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
-               DUK_DD(DUK_DDPRINT("returning with debugger enabled, force 
interrupt"));
-               DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
-               thr->interrupt_init -= thr->interrupt_counter;
-               thr->interrupt_counter = 0;
-               thr->heap->dbg_force_restart = 1;
-       }
-#endif
-
-#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
-       duk__interrupt_fixup(thr, entry_curr_thread);
-#endif
-}
-
-/*
- *  duk_handle_safe_call(): make a "C protected call" within the
- *  current activation.
- *
- *  The allowed thread states for making a call are the same as for
- *  duk_handle_call_xxx().
- *
- *  Error handling is similar to duk_handle_call_xxx(); errors may be thrown
- *  (and result in a fatal error) for insane arguments.
- */
-
-/* XXX: bump preventcount by one for the duration of this call? */
-
-DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
-                                            duk_safe_call_function func,
-                                            duk_idx_t num_stack_args,
-                                            duk_idx_t num_stack_rets) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_size_t entry_valstack_bottom_index;
-       duk_size_t entry_callstack_top;
-       duk_size_t entry_catchstack_top;
-       duk_int_t entry_call_recursion_depth;
-       duk_hthread *entry_curr_thread;
-       duk_uint_fast8_t entry_thread_state;
-       duk_instr_t **entry_ptr_curr_pc;
-       duk_jmpbuf *old_jmpbuf_ptr = NULL;
-       duk_jmpbuf our_jmpbuf;
-       duk_idx_t idx_retbase;
-       duk_int_t retval;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(ctx != NULL);
-
-       /* Note: careful with indices like '-x'; if 'x' is zero, it refers to 
bottom */
-       entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - 
thr->valstack);
-       entry_callstack_top = thr->callstack_top;
-       entry_catchstack_top = thr->catchstack_top;
-       entry_call_recursion_depth = thr->heap->call_recursion_depth;
-       entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if 
first call */
-       entry_thread_state = thr->state;
-       entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-       idx_retbase = duk_get_top(ctx) - num_stack_args;  /* Note: not a valid 
stack index if num_stack_args == 0 */
-
-       /* Note: cannot portably debug print a function pointer, hence 'func' 
not printed! */
-       DUK_DD(DUK_DDPRINT("duk_handle_safe_call: thr=%p, num_stack_args=%ld, 
num_stack_rets=%ld, "
-                          "valstack_top=%ld, idx_retbase=%ld, 
rec_depth=%ld/%ld, "
-                          "entry_valstack_bottom_index=%ld, 
entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-                          "entry_call_recursion_depth=%ld, 
entry_curr_thread=%p, entry_thread_state=%ld",
-                          (void *) thr,
-                          (long) num_stack_args,
-                          (long) num_stack_rets,
-                          (long) duk_get_top(ctx),
-                          (long) idx_retbase,
-                          (long) thr->heap->call_recursion_depth,
-                          (long) thr->heap->call_recursion_limit,
-                          (long) entry_valstack_bottom_index,
-                          (long) entry_callstack_top,
-                          (long) entry_catchstack_top,
-                          (long) entry_call_recursion_depth,
-                          (void *) entry_curr_thread,
-                          (long) entry_thread_state));
-
-       if (idx_retbase < 0) {
-               /* Since stack indices are not reliable, we can't do anything 
useful
-                * here.  Invoke the existing setjmp catcher, or if it doesn't 
exist,
-                * call the fatal error handler.
-                */
-
-               DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
-       }
-
-       /* setjmp catchpoint setup */
-
-       old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
-       thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       try {
-#else
-       DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
-       if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
-               /* Success path. */
-#endif
-               DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup 
complete"));
-
-               duk__handle_safe_call_inner(thr,
-                                           func,
-                                           idx_retbase,
-                                           num_stack_rets,
-                                           entry_valstack_bottom_index,
-                                           entry_callstack_top,
-                                           entry_catchstack_top);
-
-               /* Longjmp state is kept clean in success path */
-               DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
-               DUK_ASSERT(thr->heap->lj.iserror == 0);
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
-               /* Note: either pointer may be NULL (at entry), so don't assert 
*/
-               thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-               retval = DUK_EXEC_SUCCESS;
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       } catch (duk_internal_exception &exc) {
-               DUK_UNREF(exc);
-#else
-       } else {
-               /* Error path. */
-#endif
-               duk__handle_safe_call_error(thr,
-                                           idx_retbase,
-                                           num_stack_rets,
-                                           entry_valstack_bottom_index,
-                                           entry_callstack_top,
-                                           entry_catchstack_top,
-                                           old_jmpbuf_ptr);
-
-               /* Longjmp state is cleaned up by error handling */
-               DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
-               DUK_ASSERT(thr->heap->lj.iserror == 0);
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
-               DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
-               retval = DUK_EXEC_ERROR;
-       }
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-       catch (std::exception &exc) {
-               const char *what = exc.what();
-               if (!what) {
-                       what = "unknown";
-               }
-               DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown 
by user code)"));
-               try {
-                       DUK_ERROR_FMT1(thr, DUK_ERR_API_ERROR, "caught invalid 
c++ std::exception '%s' (perhaps thrown by user code)", what);
-               } catch (duk_internal_exception exc) {
-                       DUK_D(DUK_DPRINT("caught api error thrown from 
unexpected c++ std::exception"));
-                       DUK_UNREF(exc);
-                       duk__handle_safe_call_error(thr,
-                                                   idx_retbase,
-                                                   num_stack_rets,
-                                                   entry_valstack_bottom_index,
-                                                   entry_callstack_top,
-                                                   entry_catchstack_top,
-                                                   old_jmpbuf_ptr);
-                       retval = DUK_EXEC_ERROR;
-               }
-       } catch (...) {
-               DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by 
user code)"));
-               try {
-                       DUK_ERROR_API(thr, "caught invalid c++ exception 
(perhaps thrown by user code)");
-               } catch (duk_internal_exception exc) {
-                       DUK_D(DUK_DPRINT("caught api error thrown from 
unexpected c++ exception"));
-                       DUK_UNREF(exc);
-                       duk__handle_safe_call_error(thr,
-                                                   idx_retbase,
-                                                   num_stack_rets,
-                                                   entry_valstack_bottom_index,
-                                                   entry_callstack_top,
-                                                   entry_catchstack_top,
-                                                   old_jmpbuf_ptr);
-                       retval = DUK_EXEC_ERROR;
-               }
-       }
-#endif
-
-       DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr);  /* 
success/error path both do this */
-
-       duk__handle_safe_call_shared(thr,
-                                    idx_retbase,
-                                    num_stack_rets,
-                                    entry_call_recursion_depth,
-                                    entry_curr_thread,
-                                    entry_thread_state,
-                                    entry_ptr_curr_pc);
-
-       return retval;
-}
-
-DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
-                                           duk_safe_call_function func,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t 
entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top) {
-       duk_context *ctx;
-       duk_ret_t rc;
-
-       DUK_ASSERT(thr != NULL);
-       ctx = (duk_context *) thr;
-       DUK_ASSERT_CTX_VALID(ctx);
-       DUK_UNREF(entry_valstack_bottom_index);
-       DUK_UNREF(entry_callstack_top);
-       DUK_UNREF(entry_catchstack_top);
-
-       /*
-        *  Thread state check and book-keeping.
-        */
-
-       if (thr == thr->heap->curr_thread) {
-               /* same thread */
-               if (thr->state != DUK_HTHREAD_STATE_RUNNING) {
-                       /* should actually never happen, but check anyway */
-                       goto thread_state_error;
-               }
-       } else {
-               /* different thread */
-               DUK_ASSERT(thr->heap->curr_thread == NULL ||
-                          thr->heap->curr_thread->state == 
DUK_HTHREAD_STATE_RUNNING);
-               if (thr->state != DUK_HTHREAD_STATE_INACTIVE) {
-                       goto thread_state_error;
-               }
-               DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
-               thr->state = DUK_HTHREAD_STATE_RUNNING;
-
-               /* Note: multiple threads may be simultaneously in the RUNNING
-                * state, but not in the same "resume chain".
-                */
-       }
-
-       DUK_ASSERT(thr->heap->curr_thread == thr);
-       DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
-
-       /*
-        *  Recursion limit check.
-        *
-        *  Note: there is no need for an "ignore recursion limit" flag
-        *  for duk_handle_safe_call now.
-        */
-
-       DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
-       DUK_ASSERT(thr->heap->call_recursion_depth <= 
thr->heap->call_recursion_limit);
-       if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit) 
{
-               /* XXX: error message is a bit misleading: we reached a 
recursion
-                * limit which is also essentially the same as a C callstack 
limit
-                * (except perhaps with some relaxed threading assumptions).
-                */
-               DUK_ERROR_RANGE(thr, DUK_STR_C_CALLSTACK_LIMIT);
-       }
-       thr->heap->call_recursion_depth++;
-
-       /*
-        *  Valstack spare check
-        */
-
-       duk_require_stack(ctx, 0);  /* internal spare */
-
-       /*
-        *  Make the C call
-        */
-
-       rc = func(ctx);
-
-       DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc));
-
-       /*
-        *  Valstack manipulation for results.
-        */
-
-       /* we're running inside the caller's activation, so no change in 
call/catch stack or valstack bottom */
-       DUK_ASSERT(thr->callstack_top == entry_callstack_top);
-       DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);
-       DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-       DUK_ASSERT((duk_size_t) (thr->valstack_bottom - thr->valstack) == 
entry_valstack_bottom_index);
-       DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-       DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-       if (rc < 0) {
-               duk_error_throw_from_negative_rc(thr, rc);
-       }
-       DUK_ASSERT(rc >= 0);
-
-       if (duk_get_top(ctx) < rc) {
-               DUK_ERROR_API(thr, "not enough stack values for safe_call rc");
-       }
-
-       DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);  /* no need to 
unwind */
-       DUK_ASSERT(thr->callstack_top == entry_callstack_top);
-
-       duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc);
-       return;
-
- thread_state_error:
-       DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for 
safe_call (%ld)", (long) thr->state);
-       DUK_UNREACHABLE();
-}
-
-DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t 
entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top,
-                                           duk_jmpbuf *old_jmpbuf_ptr) {
-       duk_context *ctx;
-
-       DUK_ASSERT(thr != NULL);
-       ctx = (duk_context *) thr;
-       DUK_ASSERT_CTX_VALID(ctx);
-
-       /*
-        *  Error during call.  The error value is at heap->lj.value1.
-        *
-        *  The very first thing we do is restore the previous setjmp catcher.
-        *  This means that any error in error handling will propagate outwards
-        *  instead of causing a setjmp() re-entry above.
-        */
-
-       DUK_DDD(DUK_DDDPRINT("error caught during protected 
duk_handle_safe_call()"));
-
-       /* Other longjmp types are handled by executor before propagating
-        * the error here.
-        */
-       DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
-       DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-       DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
-
-       /* Note: either pointer may be NULL (at entry), so don't assert. */
-       thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-       DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
-       DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-       duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
-       duk_hthread_catchstack_shrink_check(thr);
-       duk_hthread_callstack_unwind(thr, entry_callstack_top);
-       duk_hthread_callstack_shrink_check(thr);
-       thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
-
-       /* [ ... | (crud) ] */
-
-       /* XXX: space in valstack?  see discussion in duk_handle_call_xxx(). */
-       duk_push_tval(ctx, &thr->heap->lj.value1);
-
-       /* [ ... | (crud) errobj ] */
-
-       DUK_ASSERT(duk_get_top(ctx) >= 1);  /* at least errobj must be on stack 
*/
-
-       /* check that the valstack has space for the final amount and any
-        * intermediate space needed; this is unoptimal but should be safe
-        */
-       duk_require_stack_top(ctx, idx_retbase + num_stack_rets);  /* final 
configuration */
-       duk_require_stack(ctx, num_stack_rets);
-
-       duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1);  
/* 1 = num actual 'return values' */
-
-       /* [ ... | ] or [ ... | errobj (M * undefined)] where M = 
num_stack_rets - 1 */
-
-       /* These are just convenience "wiping" of state.  Side effects should
-        * not be an issue here: thr->heap and thr->heap->lj have a stable
-        * pointer.  Finalizer runs etc capture even out-of-memory errors so
-        * nothing should throw here.
-        */
-       thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
-       thr->heap->lj.iserror = 0;
-       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1);  /* side 
effects */
-       DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2);  /* side 
effects */
-}
-
-DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
-                                            duk_idx_t idx_retbase,
-                                            duk_idx_t num_stack_rets,
-                                            duk_int_t 
entry_call_recursion_depth,
-                                            duk_hthread *entry_curr_thread,
-                                            duk_uint_fast8_t 
entry_thread_state,
-                                            duk_instr_t **entry_ptr_curr_pc) {
-       duk_context *ctx;
-
-       DUK_ASSERT(thr != NULL);
-       ctx = (duk_context *) thr;
-       DUK_ASSERT_CTX_VALID(ctx);
-       DUK_UNREF(ctx);
-       DUK_UNREF(idx_retbase);
-       DUK_UNREF(num_stack_rets);
-
-       /* Restore entry thread executor curr_pc stack frame pointer. */
-       thr->ptr_curr_pc = entry_ptr_curr_pc;
-
-       /* XXX: because we unwind stacks above, thr->heap->curr_thread is at
-        * risk of pointing to an already freed thread.  This was indeed the
-        * case in test-bug-multithread-valgrind.c, until duk_handle_call()
-        * was fixed to restore thr->heap->curr_thread before rethrowing an
-        * uncaught error.
-        */
-       DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
-       thr->state = (duk_uint8_t) entry_thread_state;
-
-       DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread == NULL) ||  /* first call */
-                  (thr->state == DUK_HTHREAD_STATE_INACTIVE && 
thr->heap->curr_thread != NULL) ||  /* other call */
-                  (thr->state == DUK_HTHREAD_STATE_RUNNING && 
thr->heap->curr_thread == thr));     /* current thread */
-
-       thr->heap->call_recursion_depth = entry_call_recursion_depth;
-
-       /* stack discipline consistency check */
-       DUK_ASSERT(duk_get_top(ctx) == idx_retbase + num_stack_rets);
-
-       /* A debugger forced interrupt check is not needed here, as
-        * problematic safe calls are not caused by side effects.
-        */
-
-#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
-       duk__interrupt_fixup(thr, entry_curr_thread);
-#endif
-}
-
-/*
- *  Helper for handling an Ecmascript-to-Ecmascript call or an Ecmascript
- *  function (initial) Duktape.Thread.resume().
- *
- *  Compared to normal calls handled by duk_handle_call(), there are a
- *  bunch of differences:
- *
- *    - the call is never protected
- *    - there is no C recursion depth increase (hence an "ignore recursion
- *      limit" flag is not applicable)
- *    - instead of making the call, this helper just performs the thread
- *      setup and returns; the bytecode executor 

<TRUNCATED>

Reply via email to