http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/b8103f03/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_hobject_props.c
----------------------------------------------------------------------
diff --git 
a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_hobject_props.c
 
b/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_hobject_props.c
deleted file mode 100644
index f7510df..0000000
--- 
a/thirdparty/civetweb-1.9.1/src/third_party/duktape-1.5.2/src-separate/duk_hobject_props.c
+++ /dev/null
@@ -1,5999 +0,0 @@
-/*
- *  Hobject property set/get functionality.
- *
- *  This is very central functionality for size, performance, and compliance.
- *  It is also rather intricate; see hobject-algorithms.rst for discussion on
- *  the algorithms and memory-management.rst for discussion on refcounts and
- *  side effect issues.
- *
- *  Notes:
- *
- *    - It might be tempting to assert "refcount nonzero" for objects
- *      being operated on, but that's not always correct: objects with
- *      a zero refcount may be operated on by the refcount implementation
- *      (finalization) for instance.  Hence, no refcount assertions are made.
- *
- *    - Many operations (memory allocation, identifier operations, etc)
- *      may cause arbitrary side effects (e.g. through GC and finalization).
- *      These side effects may invalidate duk_tval pointers which point to
- *      areas subject to reallocation (like value stack).  Heap objects
- *      themselves have stable pointers.  Holding heap object pointers or
- *      duk_tval copies is not problematic with respect to side effects;
- *      care must be taken when holding and using argument duk_tval pointers.
- *
- *    - If a finalizer is executed, it may operate on the the same object
- *      we're currently dealing with.  For instance, the finalizer might
- *      delete a certain property which has already been looked up and
- *      confirmed to exist.  Ideally finalizers would be disabled if GC
- *      happens during property access.  At the moment property table realloc
- *      disables finalizers, and all DECREFs may cause arbitrary changes so
- *      handle DECREF carefully.
- *
- *    - The order of operations for a DECREF matters.  When DECREF is executed,
- *      the entire object graph must be consistent; note that a refzero may
- *      lead to a mark-and-sweep through a refcount finalizer.
- */
-
-/*
- *  XXX: array indices are mostly typed as duk_uint32_t here; duk_uarridx_t
- *  might be more appropriate.
- */
-
-/*
- *  XXX: duk_uint_fast32_t should probably be used in many places here.
- */
-
-#include "duk_internal.h"
-
-/*
- *  Local defines
- */
-
-#define DUK__NO_ARRAY_INDEX             DUK_HSTRING_NO_ARRAY_INDEX
-
-/* hash probe sequence */
-#define DUK__HASH_INITIAL(hash,h_size)  
DUK_HOBJECT_HASH_INITIAL((hash),(h_size))
-#define DUK__HASH_PROBE_STEP(hash)      DUK_HOBJECT_HASH_PROBE_STEP((hash))
-
-/* marker values for hash part */
-#define DUK__HASH_UNUSED                DUK_HOBJECT_HASHIDX_UNUSED
-#define DUK__HASH_DELETED               DUK_HOBJECT_HASHIDX_DELETED
-
-/* valstack space that suffices for all local calls, including recursion
- * of other than Duktape calls (getters etc)
- */
-#define DUK__VALSTACK_SPACE             10
-
-/* valstack space allocated especially for proxy lookup which does a
- * recursive property lookup
- */
-#define DUK__VALSTACK_PROXY_LOOKUP      20
-
-/*
- *  Local prototypes
- */
-
-DUK_LOCAL_DECL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc);
-DUK_LOCAL_DECL void duk__check_arguments_map_for_put(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc, duk_bool_t 
throw_flag);
-DUK_LOCAL_DECL void duk__check_arguments_map_for_delete(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc);
-
-DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length_smaller(duk_hthread 
*thr, duk_hobject *obj, duk_uint32_t old_len, duk_uint32_t new_len, duk_bool_t 
force_flag, duk_uint32_t *out_result_len);
-DUK_LOCAL_DECL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, 
duk_hobject *obj);
-
-DUK_LOCAL_DECL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject 
*obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags);
-DUK_LOCAL_DECL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_uint32_t arr_idx, duk_propdesc 
*out_desc, duk_small_uint_t flags);
-DUK_LOCAL duk_uint32_t duk__get_old_array_length(duk_hthread *thr, duk_hobject 
*obj, duk_propdesc *temp_desc);
-
-/*
- *  Misc helpers
- */
-
-/* Convert a duk_tval number (caller checks) to a 32-bit index.  Returns
- * DUK__NO_ARRAY_INDEX if the number is not whole or not a valid array
- * index.
- */
-/* XXX: for fastints, could use a variant which assumes a double duk_tval
- * (and doesn't need to check for fastint again).
- */
-DUK_LOCAL duk_uint32_t duk__tval_number_to_arr_idx(duk_tval *tv) {
-       duk_double_t dbl;
-       duk_uint32_t idx;
-
-       DUK_ASSERT(tv != NULL);
-       DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-
-       /* -0 is accepted here as index 0 because ToString(-0) == "0" which is
-        * in canonical form and thus an array index.
-        */
-       dbl = DUK_TVAL_GET_NUMBER(tv);
-       idx = (duk_uint32_t) dbl;
-       if ((duk_double_t) idx == dbl) {
-               /* Is whole and within 32 bit range.  If the value happens to 
be 0xFFFFFFFF,
-                * it's not a valid array index but will then match 
DUK__NO_ARRAY_INDEX.
-                */
-               return idx;
-       }
-       return DUK__NO_ARRAY_INDEX;
-}
-
-#if defined(DUK_USE_FASTINT)
-/* Convert a duk_tval fastint (caller checks) to a 32-bit index. */
-DUK_LOCAL duk_uint32_t duk__tval_fastint_to_arr_idx(duk_tval *tv) {
-       duk_int64_t t;
-
-       DUK_ASSERT(tv != NULL);
-       DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
-
-       t = DUK_TVAL_GET_FASTINT(tv);
-       if ((t & ~0xffffffffULL) != 0) {
-               /* Catches >0x100000000 and negative values. */
-               return DUK__NO_ARRAY_INDEX;
-       }
-
-       /* If the value happens to be 0xFFFFFFFF, it's not a valid array index
-        * but will then match DUK__NO_ARRAY_INDEX.
-        */
-       return (duk_uint32_t) t;
-}
-#endif  /* DUK_USE_FASTINT */
-
-/* Push an arbitrary duk_tval to the stack, coerce it to string, and return
- * both a duk_hstring pointer and an array index (or DUK__NO_ARRAY_INDEX).
- */
-DUK_LOCAL duk_uint32_t duk__push_tval_to_hstring_arr_idx(duk_context *ctx, 
duk_tval *tv, duk_hstring **out_h) {
-       duk_uint32_t arr_idx;
-       duk_hstring *h;
-
-       DUK_ASSERT(ctx != NULL);
-       DUK_ASSERT(tv != NULL);
-       DUK_ASSERT(out_h != NULL);
-
-       duk_push_tval(ctx, tv);
-       duk_to_string(ctx, -1);
-       h = duk_get_hstring(ctx, -1);
-       DUK_ASSERT(h != NULL);
-       *out_h = h;
-
-       arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(h);
-       return arr_idx;
-}
-
-/* String is an own (virtual) property of a lightfunc. */
-DUK_LOCAL duk_bool_t duk__key_is_lightfunc_ownprop(duk_hthread *thr, 
duk_hstring *key) {
-       DUK_UNREF(thr);
-       return (key == DUK_HTHREAD_STRING_LENGTH(thr) ||
-               key == DUK_HTHREAD_STRING_NAME(thr));
-}
-
-/*
- *  Helpers for managing property storage size
- */
-
-/* Get default hash part size for a certain entry part size. */
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-DUK_LOCAL duk_uint32_t duk__get_default_h_size(duk_uint32_t e_size) {
-       DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES);
-
-       if (e_size >= DUK_HOBJECT_E_USE_HASH_LIMIT) {
-               duk_uint32_t res;
-
-               /* result: hash_prime(floor(1.2 * e_size)) */
-               res = duk_util_get_hash_prime(e_size + e_size / 
DUK_HOBJECT_H_SIZE_DIVISOR);
-
-               /* if fails, e_size will be zero = not an issue, except 
performance-wise */
-               DUK_ASSERT(res == 0 || res > e_size);
-               return res;
-       } else {
-               return 0;
-       }
-}
-#endif  /* USE_PROP_HASH_PART */
-
-/* Get minimum entry part growth for a certain size. */
-DUK_LOCAL duk_uint32_t duk__get_min_grow_e(duk_uint32_t e_size) {
-       duk_uint32_t res;
-
-       DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES);
-
-       res = (e_size + DUK_HOBJECT_E_MIN_GROW_ADD) / 
DUK_HOBJECT_E_MIN_GROW_DIVISOR;
-       DUK_ASSERT(res >= 1);  /* important for callers */
-       return res;
-}
-
-/* Get minimum array part growth for a certain size. */
-DUK_LOCAL duk_uint32_t duk__get_min_grow_a(duk_uint32_t a_size) {
-       duk_uint32_t res;
-
-       DUK_ASSERT((duk_size_t) a_size <= DUK_HOBJECT_MAX_PROPERTIES);
-
-       res = (a_size + DUK_HOBJECT_A_MIN_GROW_ADD) / 
DUK_HOBJECT_A_MIN_GROW_DIVISOR;
-       DUK_ASSERT(res >= 1);  /* important for callers */
-       return res;
-}
-
-/* Count actually used entry part entries (non-NULL keys). */
-DUK_LOCAL duk_uint32_t duk__count_used_e_keys(duk_hthread *thr, duk_hobject 
*obj) {
-       duk_uint_fast32_t i;
-       duk_uint_fast32_t n = 0;
-       duk_hstring **e;
-
-       DUK_ASSERT(obj != NULL);
-       DUK_UNREF(thr);
-
-       e = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, obj);
-       for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
-               if (*e++) {
-                       n++;
-               }
-       }
-       return (duk_uint32_t) n;
-}
-
-/* Count actually used array part entries and array minimum size.
- * NOTE: 'out_min_size' can be computed much faster by starting from the
- * end and breaking out early when finding first used entry, but this is
- * not needed now.
- */
-DUK_LOCAL void duk__compute_a_stats(duk_hthread *thr, duk_hobject *obj, 
duk_uint32_t *out_used, duk_uint32_t *out_min_size) {
-       duk_uint_fast32_t i;
-       duk_uint_fast32_t used = 0;
-       duk_uint_fast32_t highest_idx = (duk_uint_fast32_t) -1;  /* see below */
-       duk_tval *a;
-
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(out_used != NULL);
-       DUK_ASSERT(out_min_size != NULL);
-       DUK_UNREF(thr);
-
-       a = DUK_HOBJECT_A_GET_BASE(thr->heap, obj);
-       for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
-               duk_tval *tv = a++;
-               if (!DUK_TVAL_IS_UNUSED(tv)) {
-                       used++;
-                       highest_idx = i;
-               }
-       }
-
-       /* Initial value for highest_idx is -1 coerced to unsigned.  This
-        * is a bit odd, but (highest_idx + 1) will then wrap to 0 below
-        * for out_min_size as intended.
-        */
-
-       *out_used = used;
-       *out_min_size = highest_idx + 1;  /* 0 if no used entries */
-}
-
-/* Check array density and indicate whether or not the array part should be 
abandoned. */
-DUK_LOCAL duk_bool_t duk__abandon_array_density_check(duk_uint32_t a_used, 
duk_uint32_t a_size) {
-       /*
-        *  Array abandon check; abandon if:
-        *
-        *    new_used / new_size < limit
-        *    new_used < limit * new_size        || limit is 3 bits fixed point
-        *    new_used < limit' / 8 * new_size   || *8
-        *    8*new_used < limit' * new_size     || :8
-        *    new_used < limit' * (new_size / 8)
-        *
-        *  Here, new_used = a_used, new_size = a_size.
-        *
-        *  Note: some callers use approximate values for a_used and/or a_size
-        *  (e.g. dropping a '+1' term).  This doesn't affect the usefulness
-        *  of the check, but may confuse debugging.
-        */
-
-       return (a_used < DUK_HOBJECT_A_ABANDON_LIMIT * (a_size >> 3));
-}
-
-/* Fast check for extending array: check whether or not a slow density check 
is required. */
-DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t 
arr_idx, duk_uint32_t old_size) {
-       /*
-        *  In a fast check we assume old_size equals old_used (i.e., existing
-        *  array is fully dense).
-        *
-        *  Slow check if:
-        *
-        *    (new_size - old_size) / old_size > limit
-        *    new_size - old_size > limit * old_size
-        *    new_size > (1 + limit) * old_size        || limit' is 3 bits 
fixed point
-        *    new_size > (1 + (limit' / 8)) * old_size || * 8
-        *    8 * new_size > (8 + limit') * old_size   || : 8
-        *    new_size > (8 + limit') * (old_size / 8)
-        *    new_size > limit'' * (old_size / 8)      || limit'' = 9 -> max 
25% increase
-        *    arr_idx + 1 > limit'' * (old_size / 8)
-        *
-        *  This check doesn't work well for small values, so old_size is 
rounded
-        *  up for the check (and the '+ 1' of arr_idx can be ignored in 
practice):
-        *
-        *    arr_idx > limit'' * ((old_size + 7) / 8)
-        */
-
-       return (arr_idx > DUK_HOBJECT_A_FAST_RESIZE_LIMIT * ((old_size + 7) >> 
3));
-}
-
-/*
- *  Proxy helpers
- */
-
-#if defined(DUK_USE_ES6_PROXY)
-DUK_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hthread *thr, duk_hobject 
*obj, duk_hobject **out_target, duk_hobject **out_handler) {
-       duk_tval *tv_target;
-       duk_tval *tv_handler;
-       duk_hobject *h_target;
-       duk_hobject *h_handler;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(out_target != NULL);
-       DUK_ASSERT(out_handler != NULL);
-
-       /* Caller doesn't need to check exotic proxy behavior (but does so for
-        * some fast paths).
-        */
-       if (DUK_LIKELY(!DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
-               return 0;
-       }
-
-       tv_handler = duk_hobject_find_existing_entry_tval_ptr(thr->heap, obj, 
DUK_HTHREAD_STRING_INT_HANDLER(thr));
-       if (!tv_handler) {
-               DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REVOKED);
-               return 0;
-       }
-       DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_handler));
-       h_handler = DUK_TVAL_GET_OBJECT(tv_handler);
-       DUK_ASSERT(h_handler != NULL);
-       *out_handler = h_handler;
-       tv_handler = NULL;  /* avoid issues with relocation */
-
-       tv_target = duk_hobject_find_existing_entry_tval_ptr(thr->heap, obj, 
DUK_HTHREAD_STRING_INT_TARGET(thr));
-       if (!tv_target) {
-               DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REVOKED);
-               return 0;
-       }
-       DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target));
-       h_target = DUK_TVAL_GET_OBJECT(tv_target);
-       DUK_ASSERT(h_target != NULL);
-       *out_target = h_target;
-       tv_target = NULL;  /* avoid issues with relocation */
-
-       return 1;
-}
-#endif  /* DUK_USE_ES6_PROXY */
-
-/* Get Proxy target object.  If the argument is not a Proxy, return it as is.
- * If a Proxy is revoked, an error is thrown.
- */
-#if defined(DUK_USE_ES6_PROXY)
-DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hthread *thr, 
duk_hobject *obj) {
-       duk_hobject *h_target;
-       duk_hobject *h_handler;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-
-       /* Resolve Proxy targets until Proxy chain ends.  No explicit check for
-        * a Proxy loop: user code cannot create such a loop without tweaking
-        * internal properties directly.
-        */
-
-       while (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
-               if (duk_hobject_proxy_check(thr, obj, &h_target, &h_handler)) {
-                       DUK_ASSERT(h_target != NULL);
-                       obj = h_target;
-               } else {
-                       break;
-               }
-       }
-
-       DUK_ASSERT(obj != NULL);
-       return obj;
-}
-#endif  /* DUK_USE_ES6_PROXY */
-
-#if defined(DUK_USE_ES6_PROXY)
-DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, 
duk_small_uint_t stridx_trap, duk_tval *tv_key, duk_hobject **out_target) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *h_handler;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(tv_key != NULL);
-       DUK_ASSERT(out_target != NULL);
-
-       if (!duk_hobject_proxy_check(thr, obj, out_target, &h_handler)) {
-               return 0;
-       }
-       DUK_ASSERT(*out_target != NULL);
-       DUK_ASSERT(h_handler != NULL);
-
-       /* XXX: At the moment Duktape accesses internal keys like _Finalizer 
using a
-        * normal property set/get which would allow a proxy handler to 
interfere with
-        * such behavior and to get access to internal key strings.  This is 
not a problem
-        * as such because internal key strings can be created in other ways 
too (e.g.
-        * through buffers).  The best fix is to change Duktape internal 
lookups to
-        * skip proxy behavior.  Until that, internal property accesses bypass 
the
-        * proxy and are applied to the target (as if the handler did not 
exist).
-        * This has some side effects, see test-bi-proxy-internal-keys.js.
-        */
-
-       if (DUK_TVAL_IS_STRING(tv_key)) {
-               duk_hstring *h_key = (duk_hstring *) 
DUK_TVAL_GET_STRING(tv_key);
-               DUK_ASSERT(h_key != NULL);
-               if (DUK_HSTRING_HAS_INTERNAL(h_key)) {
-                       DUK_DDD(DUK_DDDPRINT("internal key, skip proxy handler 
and apply to target"));
-                       return 0;
-               }
-       }
-
-       /* The handler is looked up with a normal property lookup; it may be an
-        * accessor or the handler object itself may be a proxy object.  If the
-        * handler is a proxy, we need to extend the valstack as we make a
-        * recursive proxy check without a function call in between (in fact
-        * there is no limit to the potential recursion here).
-        *
-        * (For sanity, proxy creation rejects another proxy object as either
-        * the handler or the target at the moment so recursive proxy cases
-        * are not realized now.)
-        */
-
-       /* XXX: C recursion limit if proxies are allowed as handler/target 
values */
-
-       duk_require_stack(ctx, DUK__VALSTACK_PROXY_LOOKUP);
-       duk_push_hobject(ctx, h_handler);
-       if (duk_get_prop_stridx(ctx, -1, stridx_trap)) {
-               /* -> [ ... handler trap ] */
-               duk_insert(ctx, -2);  /* -> [ ... trap handler ] */
-
-               /* stack prepped for func call: [ ... trap handler ] */
-               return 1;
-       } else {
-               duk_pop_2(ctx);
-               return 0;
-       }
-}
-#endif  /* DUK_USE_ES6_PROXY */
-
-/*
- *  Reallocate property allocation, moving properties to the new allocation.
- *
- *  Includes key compaction, rehashing, and can also optionally abandoning
- *  the array part, 'migrating' array entries into the beginning of the
- *  new entry part.  Arguments are not validated here, so e.g. new_h_size
- *  MUST be a valid prime.
- *
- *  There is no support for in-place reallocation or just compacting keys
- *  without resizing the property allocation.  This is intentional to keep
- *  code size minimal.
- *
- *  The implementation is relatively straightforward, except for the array
- *  abandonment process.  Array abandonment requires that new string keys
- *  are interned, which may trigger GC.  All keys interned so far must be
- *  reachable for GC at all times; valstack is used for that now.
- *
- *  Also, a GC triggered during this reallocation process must not interfere
- *  with the object being resized.  This is currently controlled by using
- *  heap->mark_and_sweep_base_flags to indicate that no finalizers will be
- *  executed (as they can affect ANY object) and no objects are compacted
- *  (it would suffice to protect this particular object only, though).
- *
- *  Note: a non-checked variant would be nice but is a bit tricky to
- *  implement for the array abandonment process.  It's easy for
- *  everything else.
- *
- *  Note: because we need to potentially resize the valstack (as part
- *  of abandoning the array part), any tval pointers to the valstack
- *  will become invalid after this call.
- */
-
-DUK_LOCAL
-void duk__realloc_props(duk_hthread *thr,
-                        duk_hobject *obj,
-                        duk_uint32_t new_e_size,
-                        duk_uint32_t new_a_size,
-                        duk_uint32_t new_h_size,
-                        duk_bool_t abandon_array) {
-       duk_context *ctx = (duk_context *) thr;
-#ifdef DUK_USE_MARK_AND_SWEEP
-       duk_small_uint_t prev_mark_and_sweep_base_flags;
-#endif
-       duk_uint32_t new_alloc_size;
-       duk_uint32_t new_e_size_adjusted;
-       duk_uint8_t *new_p;
-       duk_hstring **new_e_k;
-       duk_propvalue *new_e_pv;
-       duk_uint8_t *new_e_f;
-       duk_tval *new_a;
-       duk_uint32_t *new_h;
-       duk_uint32_t new_e_next;
-       duk_uint_fast32_t i;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(ctx != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(!abandon_array || new_a_size == 0);  /* if abandon_array, 
new_a_size must be 0 */
-       DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL || 
(DUK_HOBJECT_GET_ESIZE(obj) == 0 && DUK_HOBJECT_GET_ASIZE(obj) == 0));
-       DUK_ASSERT(new_h_size == 0 || new_h_size >= new_e_size);  /* required 
to guarantee success of rehashing,
-                                                                  * 
intentionally use unadjusted new_e_size
-                                                                  */
-       DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       /*
-        *  Pre resize assertions.
-        */
-
-#ifdef DUK_USE_ASSERTIONS
-       /* XXX: pre-checks (such as no duplicate keys) */
-#endif
-
-       /*
-        *  For property layout 1, tweak e_size to ensure that the whole entry
-        *  part (key + val + flags) is a suitable multiple for alignment
-        *  (platform specific).
-        *
-        *  Property layout 2 does not require this tweaking and is preferred
-        *  on low RAM platforms requiring alignment.
-        */
-
-#if defined(DUK_USE_HOBJECT_LAYOUT_2) || defined(DUK_USE_HOBJECT_LAYOUT_3)
-       DUK_DDD(DUK_DDDPRINT("using layout 2 or 3, no need to pad e_size: %ld", 
(long) new_e_size));
-       new_e_size_adjusted = new_e_size;
-#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && (DUK_HOBJECT_ALIGN_TARGET == 1)
-       DUK_DDD(DUK_DDDPRINT("using layout 1, but no need to pad e_size: %ld", 
(long) new_e_size));
-       new_e_size_adjusted = new_e_size;
-#elif defined(DUK_USE_HOBJECT_LAYOUT_1) && ((DUK_HOBJECT_ALIGN_TARGET == 4) || 
(DUK_HOBJECT_ALIGN_TARGET == 8))
-       new_e_size_adjusted = (new_e_size + DUK_HOBJECT_ALIGN_TARGET - 1) & 
(~(DUK_HOBJECT_ALIGN_TARGET - 1));
-       DUK_DDD(DUK_DDDPRINT("using layout 1, and alignment target is %ld, 
adjusted e_size: %ld -> %ld",
-                            (long) DUK_HOBJECT_ALIGN_TARGET, (long) 
new_e_size, (long) new_e_size_adjusted));
-       DUK_ASSERT(new_e_size_adjusted >= new_e_size);
-#else
-#error invalid hobject layout defines
-#endif
-
-       /*
-        *  Debug logging after adjustment.
-        */
-
-       DUK_DDD(DUK_DDDPRINT("attempt to resize hobject %p props (%ld -> %ld 
bytes), from {p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to "
-                            "{e_size=%ld,a_size=%ld,h_size=%ld}, 
abandon_array=%ld, unadjusted new_e_size=%ld",
-                            (void *) obj,
-                            (long) 
DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
-                                                              
DUK_HOBJECT_GET_ASIZE(obj),
-                                                              
DUK_HOBJECT_GET_HSIZE(obj)),
-                            (long) 
DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size),
-                            (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj),
-                            (long) DUK_HOBJECT_GET_ESIZE(obj),
-                            (long) DUK_HOBJECT_GET_ENEXT(obj),
-                            (long) DUK_HOBJECT_GET_ASIZE(obj),
-                            (long) DUK_HOBJECT_GET_HSIZE(obj),
-                            (long) new_e_size_adjusted,
-                            (long) new_a_size,
-                            (long) new_h_size,
-                            (long) abandon_array,
-                            (long) new_e_size));
-
-       /*
-        *  Property count check.  This is the only point where we ensure that
-        *  we don't get more (allocated) property space that we can handle.
-        *  There aren't hard limits as such, but some algorithms fail (e.g.
-        *  finding next higher prime, selecting hash part size) if we get too
-        *  close to the 4G property limit.
-        *
-        *  Since this works based on allocation size (not actually used size),
-        *  the limit is a bit approximate but good enough in practice.
-        */
-
-       if (new_e_size_adjusted + new_a_size > DUK_HOBJECT_MAX_PROPERTIES) {
-               DUK_ERROR_ALLOC_DEFMSG(thr);
-       }
-
-       /*
-        *  Compute new alloc size and alloc new area.
-        *
-        *  The new area is allocated as a dynamic buffer and placed into the
-        *  valstack for reachability.  The actual buffer is then detached at
-        *  the end.
-        *
-        *  Note: heap_mark_and_sweep_base_flags are altered here to ensure
-        *  no-one touches this object while we're resizing and rehashing it.
-        *  The flags must be reset on every exit path after it.  Finalizers
-        *  and compaction is prevented currently for all objects while it
-        *  would be enough to restrict it only for the current object.
-        */
-
-#ifdef DUK_USE_MARK_AND_SWEEP
-       prev_mark_and_sweep_base_flags = thr->heap->mark_and_sweep_base_flags;
-       thr->heap->mark_and_sweep_base_flags |=
-               DUK_MS_FLAG_NO_FINALIZERS |         /* avoid attempts to 
add/remove object keys */
-               DUK_MS_FLAG_NO_OBJECT_COMPACTION;   /* avoid attempt to compact 
the current object */
-#endif
-
-       new_alloc_size = DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, 
new_a_size, new_h_size);
-       DUK_DDD(DUK_DDDPRINT("new hobject allocation size is %ld", (long) 
new_alloc_size));
-       if (new_alloc_size == 0) {
-               /* for zero size, don't push anything on valstack */
-               DUK_ASSERT(new_e_size_adjusted == 0);
-               DUK_ASSERT(new_a_size == 0);
-               DUK_ASSERT(new_h_size == 0);
-               new_p = NULL;
-       } else {
-               /* This may trigger mark-and-sweep with arbitrary side effects,
-                * including an attempted resize of the object we're resizing,
-                * executing a finalizer which may add or remove properties of
-                * the object we're resizing etc.
-                */
-
-               /* Note: buffer is dynamic so that we can 'steal' the actual
-                * allocation later.
-                */
-
-               new_p = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, 
new_alloc_size);  /* errors out if out of memory */
-               DUK_ASSERT(new_p != NULL);  /* since new_alloc_size > 0 */
-       }
-
-       /* Set up pointers to the new property area: this is hidden behind a 
macro
-        * because it is memory layout specific.
-        */
-       DUK_HOBJECT_P_SET_REALLOC_PTRS(new_p, new_e_k, new_e_pv, new_e_f, 
new_a, new_h,
-                                      new_e_size_adjusted, new_a_size, 
new_h_size);
-       DUK_UNREF(new_h);  /* happens when hash part dropped */
-       new_e_next = 0;
-
-       /* if new_p == NULL, all of these pointers are NULL */
-       DUK_ASSERT((new_p != NULL) ||
-                  (new_e_k == NULL && new_e_pv == NULL && new_e_f == NULL &&
-                   new_a == NULL && new_h == NULL));
-
-       DUK_DDD(DUK_DDDPRINT("new alloc size %ld, new_e_k=%p, new_e_pv=%p, 
new_e_f=%p, new_a=%p, new_h=%p",
-                            (long) new_alloc_size, (void *) new_e_k, (void *) 
new_e_pv, (void *) new_e_f,
-                            (void *) new_a, (void *) new_h));
-
-       /*
-        *  Migrate array to start of entries if requested.
-        *
-        *  Note: from an enumeration perspective the order of entry keys 
matters.
-        *  Array keys should appear wherever they appeared before the array 
abandon
-        *  operation.
-        */
-
-       if (abandon_array) {
-               /*
-                *  Note: assuming new_a_size == 0, and that entry part contains
-                *  no conflicting keys, refcounts do not need to be adjusted 
for
-                *  the values, as they remain exactly the same.
-                *
-                *  The keys, however, need to be interned, incref'd, and be
-                *  reachable for GC.  Any intern attempt may trigger a GC and
-                *  claim any non-reachable strings, so every key must be 
reachable
-                *  at all times.
-                *
-                *  A longjmp must not occur here, as the new_p allocation would
-                *  be freed without these keys being decref'd, hence the messy
-                *  decref handling if intern fails.
-                */
-               DUK_ASSERT(new_a_size == 0);
-
-               for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
-                       duk_tval *tv1;
-                       duk_tval *tv2;
-                       duk_hstring *key;
-
-                       DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != 
NULL);
-
-                       tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i);
-                       if (DUK_TVAL_IS_UNUSED(tv1)) {
-                               continue;
-                       }
-
-                       DUK_ASSERT(new_p != NULL && new_e_k != NULL &&
-                                  new_e_pv != NULL && new_e_f != NULL);
-
-                       /*
-                        *  Intern key via the valstack to ensure reachability 
behaves
-                        *  properly.  We must avoid longjmp's here so use 
non-checked
-                        *  primitives.
-                        *
-                        *  Note: duk_check_stack() potentially reallocs the 
valstack,
-                        *  invalidating any duk_tval pointers to valstack.  
Callers
-                        *  must be careful.
-                        */
-
-                       /* never shrinks; auto-adds 
DUK_VALSTACK_INTERNAL_EXTRA, which is generous */
-                       if (!duk_check_stack(ctx, 1)) {
-                               goto abandon_error;
-                       }
-                       DUK_ASSERT_VALSTACK_SPACE(thr, 1);
-                       key = duk_heap_string_intern_u32(thr->heap, i);
-                       if (!key) {
-                               goto abandon_error;
-                       }
-                       duk_push_hstring(ctx, key);  /* keep key reachable for 
GC etc; guaranteed not to fail */
-
-                       /* key is now reachable in the valstack */
-
-                       DUK_HSTRING_INCREF(thr, key);   /* second incref for 
the entry reference */
-                       new_e_k[new_e_next] = key;
-                       tv2 = &new_e_pv[new_e_next].v;  /* array entries are 
all plain values */
-                       DUK_TVAL_SET_TVAL(tv2, tv1);
-                       new_e_f[new_e_next] = DUK_PROPDESC_FLAG_WRITABLE |
-                                             DUK_PROPDESC_FLAG_ENUMERABLE |
-                                             DUK_PROPDESC_FLAG_CONFIGURABLE;
-                       new_e_next++;
-
-                       /* Note: new_e_next matches pushed temp key count, and 
nothing can
-                        * fail above between the push and this point.
-                        */
-               }
-
-               DUK_DDD(DUK_DDDPRINT("abandon array: pop %ld key temps from 
valstack", (long) new_e_next));
-               duk_pop_n(ctx, new_e_next);
-       }
-
-       /*
-        *  Copy keys and values in the entry part (compacting them at the same 
time).
-        */
-
-       for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
-               duk_hstring *key;
-
-               DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL);
-
-               key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i);
-               if (!key) {
-                       continue;
-               }
-
-               DUK_ASSERT(new_p != NULL && new_e_k != NULL &&
-                          new_e_pv != NULL && new_e_f != NULL);
-
-               new_e_k[new_e_next] = key;
-               new_e_pv[new_e_next] = DUK_HOBJECT_E_GET_VALUE(thr->heap, obj, 
i);
-               new_e_f[new_e_next] = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, 
i);
-               new_e_next++;
-       }
-       /* the entries [new_e_next, new_e_size_adjusted[ are left uninitialized 
on purpose (ok, not gc reachable) */
-
-       /*
-        *  Copy array elements to new array part.
-        */
-
-       if (new_a_size > DUK_HOBJECT_GET_ASIZE(obj)) {
-               /* copy existing entries as is */
-               DUK_ASSERT(new_p != NULL && new_a != NULL);
-               if (DUK_HOBJECT_GET_ASIZE(obj) > 0) {
-                       /* Avoid zero copy with an invalid pointer.  If obj->p 
is NULL,
-                        * the 'new_a' pointer will be invalid which is not 
allowed even
-                        * when copy size is zero.
-                        */
-                       DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != 
NULL);
-                       DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0);
-                       DUK_MEMCPY((void *) new_a, (void *) 
DUK_HOBJECT_A_GET_BASE(thr->heap, obj), sizeof(duk_tval) * 
DUK_HOBJECT_GET_ASIZE(obj));
-               }
-
-               /* fill new entries with -unused- (required, gc reachable) */
-               for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) {
-                       duk_tval *tv = &new_a[i];
-                       DUK_TVAL_SET_UNUSED(tv);
-               }
-       } else {
-#ifdef DUK_USE_ASSERTIONS
-               /* caller must have decref'd values above new_a_size (if that 
is necessary) */
-               if (!abandon_array) {
-                       for (i = new_a_size; i < DUK_HOBJECT_GET_ASIZE(obj); 
i++) {
-                               duk_tval *tv;
-                               tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, 
obj, i);
-
-                               /* current assertion is quite strong: decref's 
and set to unused */
-                               DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv));
-                       }
-               }
-#endif
-               if (new_a_size > 0) {
-                       /* Avoid zero copy with an invalid pointer.  If obj->p 
is NULL,
-                        * the 'new_a' pointer will be invalid which is not 
allowed even
-                        * when copy size is zero.
-                        */
-                       DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != 
NULL);
-                       DUK_ASSERT(new_a_size > 0);
-                       DUK_MEMCPY((void *) new_a, (void *) 
DUK_HOBJECT_A_GET_BASE(thr->heap, obj), sizeof(duk_tval) * new_a_size);
-               }
-       }
-
-       /*
-        *  Rebuild the hash part always from scratch (guaranteed to finish).
-        *
-        *  Any resize of hash part requires rehashing.  In addition, by 
rehashing
-        *  get rid of any elements marked deleted (DUK__HASH_DELETED) which is 
critical
-        *  to ensuring the hash part never fills up.
-        */
-
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       if (DUK_UNLIKELY(new_h_size > 0)) {
-               DUK_ASSERT(new_h != NULL);
-
-               /* fill new_h with u32 0xff = UNUSED */
-               DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL);
-               DUK_ASSERT(new_h_size > 0);
-               DUK_MEMSET(new_h, 0xff, sizeof(duk_uint32_t) * new_h_size);
-
-               DUK_ASSERT(new_e_next <= new_h_size);  /* equality not actually 
possible */
-               for (i = 0; i < new_e_next; i++) {
-                       duk_hstring *key = new_e_k[i];
-                       duk_uint32_t j, step;
-
-                       DUK_ASSERT(key != NULL);
-                       j = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), 
new_h_size);
-                       step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key));
-
-                       for (;;) {
-                               DUK_ASSERT(new_h[j] != DUK__HASH_DELETED);  /* 
should never happen */
-                               if (new_h[j] == DUK__HASH_UNUSED) {
-                                       DUK_DDD(DUK_DDDPRINT("rebuild hit %ld 
-> %ld", (long) j, (long) i));
-                                       new_h[j] = i;
-                                       break;
-                               }
-                               DUK_DDD(DUK_DDDPRINT("rebuild miss %ld, step 
%ld", (long) j, (long) step));
-                               j = (j + step) % new_h_size;
-
-                               /* guaranteed to finish */
-                               DUK_ASSERT(j != (duk_uint32_t) 
DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), new_h_size));
-                       }
-               }
-       } else {
-               DUK_DDD(DUK_DDDPRINT("no hash part, no rehash"));
-       }
-#endif  /* DUK_USE_HOBJECT_HASH_PART */
-
-       /*
-        *  Nice debug log.
-        */
-
-       DUK_DD(DUK_DDPRINT("resized hobject %p props (%ld -> %ld bytes), from 
{p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld} to "
-                          "{p=%p,e_size=%ld,e_next=%ld,a_size=%ld,h_size=%ld}, 
abandon_array=%ld, unadjusted new_e_size=%ld",
-                          (void *) obj,
-                          (long) 
DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
-                                                            
DUK_HOBJECT_GET_ASIZE(obj),
-                                                            
DUK_HOBJECT_GET_HSIZE(obj)),
-                          (long) new_alloc_size,
-                          (void *) DUK_HOBJECT_GET_PROPS(thr->heap, obj),
-                          (long) DUK_HOBJECT_GET_ESIZE(obj),
-                          (long) DUK_HOBJECT_GET_ENEXT(obj),
-                          (long) DUK_HOBJECT_GET_ASIZE(obj),
-                          (long) DUK_HOBJECT_GET_HSIZE(obj),
-                          (void *) new_p,
-                          (long) new_e_size_adjusted,
-                          (long) new_e_next,
-                          (long) new_a_size,
-                          (long) new_h_size,
-                          (long) abandon_array,
-                          (long) new_e_size));
-
-       /*
-        *  All done, switch properties ('p') allocation to new one.
-        */
-
-       DUK_FREE(thr->heap, DUK_HOBJECT_GET_PROPS(thr->heap, obj));  /* NULL 
obj->p is OK */
-       DUK_HOBJECT_SET_PROPS(thr->heap, obj, new_p);
-       DUK_HOBJECT_SET_ESIZE(obj, new_e_size_adjusted);
-       DUK_HOBJECT_SET_ENEXT(obj, new_e_next);
-       DUK_HOBJECT_SET_ASIZE(obj, new_a_size);
-       DUK_HOBJECT_SET_HSIZE(obj, new_h_size);
-
-       if (new_p) {
-               /*
-                *  Detach actual buffer from dynamic buffer in valstack, and
-                *  pop it from the stack.
-                *
-                *  XXX: the buffer object is certainly not reachable at this 
point,
-                *  so it would be nice to free it forcibly even with only
-                *  mark-and-sweep enabled.  Not a big issue though.
-                */
-               (void) duk_steal_buffer(ctx, -1, NULL);
-               duk_pop(ctx);
-       } else {
-               DUK_ASSERT(new_alloc_size == 0);
-               /* no need to pop, nothing was pushed */
-       }
-
-       /* clear array part flag only after switching */
-       if (abandon_array) {
-               DUK_HOBJECT_CLEAR_ARRAY_PART(obj);
-       }
-
-       DUK_DDD(DUK_DDDPRINT("resize result: %!O", (duk_heaphdr *) obj));
-
-#ifdef DUK_USE_MARK_AND_SWEEP
-       thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
-#endif
-
-       /*
-        *  Post resize assertions.
-        */
-
-#ifdef DUK_USE_ASSERTIONS
-       /* XXX: post-checks (such as no duplicate keys) */
-#endif
-       return;
-
-       /*
-        *  Abandon array failed, need to decref keys already inserted
-        *  into the beginning of new_e_k before unwinding valstack.
-        */
-
- abandon_error:
-       DUK_D(DUK_DPRINT("hobject resize failed during abandon array, decref 
keys"));
-       i = new_e_next;
-       while (i > 0) {
-               i--;
-               DUK_ASSERT(new_e_k != NULL);
-               DUK_ASSERT(new_e_k[i] != NULL);
-               DUK_HSTRING_DECREF(thr, new_e_k[i]);  /* side effects */
-       }
-
-#ifdef DUK_USE_MARK_AND_SWEEP
-       thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
-#endif
-
-       DUK_ERROR_ALLOC_DEFMSG(thr);
-}
-
-/*
- *  Helpers to resize properties allocation on specific needs.
- */
-
-/* Grow entry part allocation for one additional entry. */
-DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, 
duk_hobject *obj) {
-       duk_uint32_t old_e_used;  /* actually used, non-NULL entries */
-       duk_uint32_t new_e_size;
-       duk_uint32_t new_a_size;
-       duk_uint32_t new_h_size;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-
-       /* Duktape 0.11.0 and prior tried to optimize the resize by not
-        * counting the number of actually used keys prior to the resize.
-        * This worked mostly well but also caused weird leak-like behavior
-        * as in: test-bug-object-prop-alloc-unbounded.js.  So, now we count
-        * the keys explicitly to compute the new entry part size.
-        */
-
-       old_e_used = duk__count_used_e_keys(thr, obj);
-       new_e_size = old_e_used + duk__get_min_grow_e(old_e_used);
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       new_h_size = duk__get_default_h_size(new_e_size);
-#else
-       new_h_size = 0;
-#endif
-       new_a_size = DUK_HOBJECT_GET_ASIZE(obj);
-       DUK_ASSERT(new_e_size >= old_e_used + 1);  /* duk__get_min_grow_e() is 
always >= 1 */
-
-       duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
-}
-
-/* Grow array part for a new highest array index. */
-DUK_LOCAL void duk__grow_props_for_array_item(duk_hthread *thr, duk_hobject 
*obj, duk_uint32_t highest_arr_idx) {
-       duk_uint32_t new_e_size;
-       duk_uint32_t new_a_size;
-       duk_uint32_t new_h_size;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(highest_arr_idx >= DUK_HOBJECT_GET_ASIZE(obj));
-
-       /* minimum new length is highest_arr_idx + 1 */
-
-       new_e_size = DUK_HOBJECT_GET_ESIZE(obj);
-       new_h_size = DUK_HOBJECT_GET_HSIZE(obj);
-       new_a_size = highest_arr_idx + duk__get_min_grow_a(highest_arr_idx);
-       DUK_ASSERT(new_a_size >= highest_arr_idx + 1);  /* 
duk__get_min_grow_a() is always >= 1 */
-
-       duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
-}
-
-/* Abandon array part, moving array entries into entries part.
- * This requires a props resize, which is a heavy operation.
- * We also compact the entries part while we're at it, although
- * this is not strictly required.
- */
-DUK_LOCAL void duk__abandon_array_checked(duk_hthread *thr, duk_hobject *obj) {
-       duk_uint32_t new_e_size;
-       duk_uint32_t new_a_size;
-       duk_uint32_t new_h_size;
-       duk_uint32_t e_used;  /* actually used, non-NULL keys */
-       duk_uint32_t a_used;
-       duk_uint32_t a_size;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-
-       e_used = duk__count_used_e_keys(thr, obj);
-       duk__compute_a_stats(thr, obj, &a_used, &a_size);
-
-       /*
-        *  Must guarantee all actually used array entries will fit into
-        *  new entry part.  Add one growth step to ensure we don't run out
-        *  of space right away.
-        */
-
-       new_e_size = e_used + a_used;
-       new_e_size = new_e_size + duk__get_min_grow_e(new_e_size);
-       new_a_size = 0;
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       new_h_size = duk__get_default_h_size(new_e_size);
-#else
-       new_h_size = 0;
-#endif
-
-       DUK_DD(DUK_DDPRINT("abandon array part for hobject %p, "
-                          "array stats before: e_used=%ld, a_used=%ld, 
a_size=%ld; "
-                          "resize to e_size=%ld, a_size=%ld, h_size=%ld",
-                          (void *) obj, (long) e_used, (long) a_used, (long) 
a_size,
-                          (long) new_e_size, (long) new_a_size, (long) 
new_h_size));
-
-       duk__realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 1);
-}
-
-/*
- *  Compact an object.  Minimizes allocation size for objects which are
- *  not likely to be extended.  This is useful for internal and non-
- *  extensible objects, but can also be called for non-extensible objects.
- *  May abandon the array part if it is computed to be too sparse.
- *
- *  This call is relatively expensive, as it needs to scan both the
- *  entries and the array part.
- *
- *  The call may fail due to allocation error.
- */
-
-DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject 
*obj) {
-       duk_uint32_t e_size;       /* currently used -> new size */
-       duk_uint32_t a_size;       /* currently required */
-       duk_uint32_t a_used;       /* actually used */
-       duk_uint32_t h_size;
-       duk_bool_t abandon_array;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-
-#if defined(DUK_USE_ROM_OBJECTS)
-       if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) {
-               DUK_DD(DUK_DDPRINT("ignore attempt to compact a rom object"));
-               return;
-       }
-#endif
-
-       e_size = duk__count_used_e_keys(thr, obj);
-       duk__compute_a_stats(thr, obj, &a_used, &a_size);
-
-       DUK_DD(DUK_DDPRINT("compacting hobject, used e keys %ld, used a keys 
%ld, min a size %ld, "
-                          "resized array density would be: %ld/%ld = %lf",
-                          (long) e_size, (long) a_used, (long) a_size,
-                          (long) a_used, (long) a_size,
-                          (double) a_used / (double) a_size));
-
-       if (duk__abandon_array_density_check(a_used, a_size)) {
-               DUK_DD(DUK_DDPRINT("decided to abandon array during compaction, 
a_used=%ld, a_size=%ld",
-                                  (long) a_used, (long) a_size));
-               abandon_array = 1;
-               e_size += a_used;
-               a_size = 0;
-       } else {
-               DUK_DD(DUK_DDPRINT("decided to keep array during compaction"));
-               abandon_array = 0;
-       }
-
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       if (e_size >= DUK_HOBJECT_E_USE_HASH_LIMIT) {
-               h_size = duk__get_default_h_size(e_size);
-       } else {
-               h_size = 0;
-       }
-#else
-       h_size = 0;
-#endif
-
-       DUK_DD(DUK_DDPRINT("compacting hobject -> new e_size %ld, new 
a_size=%ld, new h_size=%ld, abandon_array=%ld",
-                          (long) e_size, (long) a_size, (long) h_size, (long) 
abandon_array));
-
-       duk__realloc_props(thr, obj, e_size, a_size, h_size, abandon_array);
-}
-
-/*
- *  Find an existing key from entry part either by linear scan or by
- *  using the hash index (if it exists).
- *
- *  Sets entry index (and possibly the hash index) to output variables,
- *  which allows the caller to update the entry and hash entries in-place.
- *  If entry is not found, both values are set to -1.  If entry is found
- *  but there is no hash part, h_idx is set to -1.
- */
-
-DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject 
*obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx) {
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(e_idx != NULL);
-       DUK_ASSERT(h_idx != NULL);
-       DUK_UNREF(heap);
-
-       if (DUK_LIKELY(DUK_HOBJECT_GET_HSIZE(obj) == 0))
-       {
-               /* Linear scan: more likely because most objects are small.
-                * This is an important fast path.
-                *
-                * XXX: this might be worth inlining for property lookups.
-                */
-               duk_uint_fast32_t i;
-               duk_uint_fast32_t n;
-               duk_hstring **h_keys_base;
-               DUK_DDD(DUK_DDDPRINT("duk_hobject_find_existing_entry() using 
linear scan for lookup"));
-
-               h_keys_base = DUK_HOBJECT_E_GET_KEY_BASE(heap, obj);
-               n = DUK_HOBJECT_GET_ENEXT(obj);
-               for (i = 0; i < n; i++) {
-                       if (h_keys_base[i] == key) {
-                               *e_idx = i;
-                               *h_idx = -1;
-                               return;
-                       }
-               }
-       }
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       else
-       {
-               /* hash lookup */
-               duk_uint32_t n;
-               duk_uint32_t i, step;
-               duk_uint32_t *h_base;
-
-               DUK_DDD(DUK_DDDPRINT("duk_hobject_find_existing_entry() using 
hash part for lookup"));
-
-               h_base = DUK_HOBJECT_H_GET_BASE(heap, obj);
-               n = DUK_HOBJECT_GET_HSIZE(obj);
-               i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n);
-               step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key));
-
-               for (;;) {
-                       duk_uint32_t t;
-
-                       DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
-                       DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj));
-                       t = h_base[i];
-                       DUK_ASSERT(t == DUK__HASH_UNUSED || t == 
DUK__HASH_DELETED ||
-                                  (t < DUK_HOBJECT_GET_ESIZE(obj)));  /* t >= 
0 always true, unsigned */
-
-                       if (t == DUK__HASH_UNUSED) {
-                               break;
-                       } else if (t == DUK__HASH_DELETED) {
-                               DUK_DDD(DUK_DDDPRINT("lookup miss (deleted) 
i=%ld, t=%ld",
-                                                    (long) i, (long) t));
-                       } else {
-                               DUK_ASSERT(t < DUK_HOBJECT_GET_ESIZE(obj));
-                               if (DUK_HOBJECT_E_GET_KEY(heap, obj, t) == key) 
{
-                                       DUK_DDD(DUK_DDDPRINT("lookup hit i=%ld, 
t=%ld -> key %p",
-                                                            (long) i, (long) 
t, (void *) key));
-                                       *e_idx = t;
-                                       *h_idx = i;
-                                       return;
-                               }
-                               DUK_DDD(DUK_DDDPRINT("lookup miss i=%ld, t=%ld",
-                                                    (long) i, (long) t));
-                       }
-                       i = (i + step) % n;
-
-                       /* guaranteed to finish, as hash is never full */
-                       DUK_ASSERT(i != (duk_uint32_t) 
DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n));
-               }
-       }
-#endif  /* DUK_USE_HOBJECT_HASH_PART */
-
-       /* not found */
-       *e_idx = -1;
-       *h_idx = -1;
-}
-
-/* For internal use: get non-accessor entry value */
-DUK_INTERNAL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap 
*heap, duk_hobject *obj, duk_hstring *key) {
-       duk_int_t e_idx;
-       duk_int_t h_idx;
-
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_UNREF(heap);
-
-       duk_hobject_find_existing_entry(heap, obj, key, &e_idx, &h_idx);
-       if (e_idx >= 0 && !DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) {
-               return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx);
-       } else {
-               return NULL;
-       }
-}
-
-/* For internal use: get non-accessor entry value and attributes */
-DUK_INTERNAL duk_tval 
*duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject 
*obj, duk_hstring *key, duk_int_t *out_attrs) {
-       duk_int_t e_idx;
-       duk_int_t h_idx;
-
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(out_attrs != NULL);
-       DUK_UNREF(heap);
-
-       duk_hobject_find_existing_entry(heap, obj, key, &e_idx, &h_idx);
-       if (e_idx >= 0 && !DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) {
-               *out_attrs = DUK_HOBJECT_E_GET_FLAGS(heap, obj, e_idx);
-               return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx);
-       } else {
-               *out_attrs = 0;
-               return NULL;
-       }
-}
-
-/* For internal use: get array part value */
-DUK_INTERNAL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap 
*heap, duk_hobject *obj, duk_uarridx_t i) {
-       duk_tval *tv;
-
-       DUK_ASSERT(obj != NULL);
-       DUK_UNREF(heap);
-
-       if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
-               return NULL;
-       }
-       if (i >= DUK_HOBJECT_GET_ASIZE(obj)) {
-               return NULL;
-       }
-       tv = DUK_HOBJECT_A_GET_VALUE_PTR(heap, obj, i);
-       return tv;
-}
-
-/*
- *  Allocate and initialize a new entry, resizing the properties allocation
- *  if necessary.  Returns entry index (e_idx) or throws an error if alloc 
fails.
- *
- *  Sets the key of the entry (increasing the key's refcount), and updates
- *  the hash part if it exists.  Caller must set value and flags, and update
- *  the entry value refcount.  A decref for the previous value is not 
necessary.
- */
-
-DUK_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject 
*obj, duk_hstring *key) {
-       duk_uint32_t idx;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) <= DUK_HOBJECT_GET_ESIZE(obj));
-
-#ifdef DUK_USE_ASSERTIONS
-       /* key must not already exist in entry part */
-       {
-               duk_uint_fast32_t i;
-               for (i = 0; i < DUK_HOBJECT_GET_ENEXT(obj); i++) {
-                       DUK_ASSERT(DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i) != 
key);
-               }
-       }
-#endif
-
-       if (DUK_HOBJECT_GET_ENEXT(obj) >= DUK_HOBJECT_GET_ESIZE(obj)) {
-               /* only need to guarantee 1 more slot, but allocation growth is 
in chunks */
-               DUK_DDD(DUK_DDDPRINT("entry part full, allocate space for one 
more entry"));
-               duk__grow_props_for_new_entry_item(thr, obj);
-       }
-       DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(obj) < DUK_HOBJECT_GET_ESIZE(obj));
-       idx = DUK_HOBJECT_POSTINC_ENEXT(obj);
-
-       /* previous value is assumed to be garbage, so don't touch it */
-       DUK_HOBJECT_E_SET_KEY(thr->heap, obj, idx, key);
-       DUK_HSTRING_INCREF(thr, key);
-
-#if defined(DUK_USE_HOBJECT_HASH_PART)
-       if (DUK_UNLIKELY(DUK_HOBJECT_GET_HSIZE(obj) > 0)) {
-               duk_uint32_t n;
-               duk_uint32_t i, step;
-               duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj);
-
-               n = DUK_HOBJECT_GET_HSIZE(obj);
-               i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n);
-               step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key));
-
-               for (;;) {
-                       duk_uint32_t t = h_base[i];
-                       if (t == DUK__HASH_UNUSED || t == DUK__HASH_DELETED) {
-                               
DUK_DDD(DUK_DDDPRINT("duk__alloc_entry_checked() inserted key into hash part, 
%ld -> %ld",
-                                                    (long) i, (long) idx));
-                               DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
-                               DUK_ASSERT(i < DUK_HOBJECT_GET_HSIZE(obj));
-                               DUK_ASSERT_DISABLE(idx >= 0);
-                               DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj));
-                               h_base[i] = idx;
-                               break;
-                       }
-                       DUK_DDD(DUK_DDDPRINT("duk__alloc_entry_checked() miss 
%ld", (long) i));
-                       i = (i + step) % n;
-
-                       /* guaranteed to find an empty slot */
-                       DUK_ASSERT(i != (duk_uint32_t) 
DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), DUK_HOBJECT_GET_HSIZE(obj)));
-               }
-       }
-#endif  /* DUK_USE_HOBJECT_HASH_PART */
-
-       /* Note: we could return the hash index here too, but it's not
-        * needed right now.
-        */
-
-       DUK_ASSERT_DISABLE(idx >= 0);
-       DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj));
-       DUK_ASSERT(idx < DUK_HOBJECT_GET_ENEXT(obj));
-       return idx;
-}
-
-/*
- *  Object internal value
- *
- *  Returned value is guaranteed to be reachable / incref'd, caller does not 
need
- *  to incref OR decref.  No proxies or accessors are invoked, no prototype 
walk.
- */
-
-DUK_INTERNAL duk_bool_t duk_hobject_get_internal_value(duk_heap *heap, 
duk_hobject *obj, duk_tval *tv_out) {
-       duk_int_t e_idx;
-       duk_int_t h_idx;
-
-       DUK_ASSERT(heap != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(tv_out != NULL);
-
-       /* always in entry part, no need to look up parents etc */
-       duk_hobject_find_existing_entry(heap, obj, 
DUK_HEAP_STRING_INT_VALUE(heap), &e_idx, &h_idx);
-       if (e_idx >= 0) {
-               DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx));
-               DUK_TVAL_SET_TVAL(tv_out, 
DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx));
-               return 1;
-       }
-       DUK_TVAL_SET_UNDEFINED(tv_out);
-       return 0;
-}
-
-DUK_INTERNAL duk_hstring *duk_hobject_get_internal_value_string(duk_heap 
*heap, duk_hobject *obj) {
-       duk_tval tv;
-
-       DUK_ASSERT(heap != NULL);
-       DUK_ASSERT(obj != NULL);
-
-       /* This is not strictly necessary, but avoids compiler warnings; e.g.
-        * gcc won't reliably detect that no uninitialized data is read below.
-        */
-       DUK_MEMZERO((void *) &tv, sizeof(duk_tval));
-
-       if (duk_hobject_get_internal_value(heap, obj, &tv)) {
-               duk_hstring *h;
-               DUK_ASSERT(DUK_TVAL_IS_STRING(&tv));
-               h = DUK_TVAL_GET_STRING(&tv);
-               return h;
-       }
-
-       return NULL;
-}
-
-/*
- *  Arguments handling helpers (argument map mainly).
- *
- *  An arguments object has exotic behavior for some numeric indices.
- *  Accesses may translate to identifier operations which may have
- *  arbitrary side effects (potentially invalidating any duk_tval
- *  pointers).
- */
-
-/* Lookup 'key' from arguments internal 'map', perform a variable lookup
- * if mapped, and leave the result on top of stack (and return non-zero).
- * Used in E5 Section 10.6 algorithms [[Get]] and [[GetOwnProperty]].
- */
-DUK_LOCAL
-duk_bool_t duk__lookup_arguments_map(duk_hthread *thr,
-                                     duk_hobject *obj,
-                                     duk_hstring *key,
-                                     duk_propdesc *temp_desc,
-                                     duk_hobject **out_map,
-                                     duk_hobject **out_varenv) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *map;
-       duk_hobject *varenv;
-       duk_bool_t rc;
-
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       DUK_DDD(DUK_DDDPRINT("arguments map lookup: thr=%p, obj=%p, key=%p, 
temp_desc=%p "
-                            "(obj -> %!O, key -> %!O)",
-                            (void *) thr, (void *) obj, (void *) key, (void *) 
temp_desc,
-                            (duk_heaphdr *) obj, (duk_heaphdr *) key));
-
-       if (!duk_hobject_get_own_propdesc(thr, obj, 
DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
-               DUK_DDD(DUK_DDDPRINT("-> no 'map'"));
-               return 0;
-       }
-
-       map = duk_require_hobject(ctx, -1);
-       DUK_ASSERT(map != NULL);
-       duk_pop(ctx);  /* map is reachable through obj */
-
-       if (!duk_hobject_get_own_propdesc(thr, map, key, temp_desc, 
DUK_GETDESC_FLAG_PUSH_VALUE)) {
-               DUK_DDD(DUK_DDDPRINT("-> 'map' exists, but key not in map"));
-               return 0;
-       }
-
-       /* [... varname] */
-       DUK_DDD(DUK_DDDPRINT("-> 'map' exists, and contains key, key is mapped 
to argument/variable binding %!T",
-                            (duk_tval *) duk_get_tval(ctx, -1)));
-       DUK_ASSERT(duk_is_string(ctx, -1));  /* guaranteed when building 
arguments */
-
-       /* get varenv for varname (callee's declarative lexical environment) */
-       rc = duk_hobject_get_own_propdesc(thr, obj, 
DUK_HTHREAD_STRING_INT_VARENV(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE);
-       DUK_UNREF(rc);
-       DUK_ASSERT(rc != 0);  /* arguments MUST have an initialized lexical 
environment reference */
-       varenv = duk_require_hobject(ctx, -1);
-       DUK_ASSERT(varenv != NULL);
-       duk_pop(ctx);  /* varenv remains reachable through 'obj' */
-
-       DUK_DDD(DUK_DDDPRINT("arguments varenv is: %!dO", (duk_heaphdr *) 
varenv));
-
-       /* success: leave varname in stack */
-       *out_map = map;
-       *out_varenv = varenv;
-       return 1;  /* [... varname] */
-}
-
-/* Lookup 'key' from arguments internal 'map', and leave replacement value
- * on stack top if mapped (and return non-zero).
- * Used in E5 Section 10.6 algorithm for [[GetOwnProperty]] (used by [[Get]]).
- */
-DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *map;
-       duk_hobject *varenv;
-       duk_hstring *varname;
-
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, 
&varenv)) {
-               DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic get 
behavior"));
-               return 0;
-       }
-
-       /* [... varname] */
-
-       varname = duk_require_hstring(ctx, -1);
-       DUK_ASSERT(varname != NULL);
-       duk_pop(ctx);  /* varname is still reachable */
-
-       DUK_DDD(DUK_DDDPRINT("arguments object automatic getvar for a bound 
variable; "
-                            "key=%!O, varname=%!O",
-                            (duk_heaphdr *) key,
-                            (duk_heaphdr *) varname));
-
-       (void) duk_js_getvar_envrec(thr, varenv, varname, 1 /*throw*/);
-
-       /* [... value this_binding] */
-
-       duk_pop(ctx);
-
-       /* leave result on stack top */
-       return 1;
-}
-
-/* Lookup 'key' from arguments internal 'map', perform a variable write if 
mapped.
- * Used in E5 Section 10.6 algorithm for [[DefineOwnProperty]] (used by 
[[Put]]).
- * Assumes stack top contains 'put' value (which is NOT popped).
- */
-DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject 
*obj, duk_hstring *key, duk_propdesc *temp_desc, duk_bool_t throw_flag) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *map;
-       duk_hobject *varenv;
-       duk_hstring *varname;
-
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       if (!duk__lookup_arguments_map(thr, obj, key, temp_desc, &map, 
&varenv)) {
-               DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic put 
behavior"));
-               return;
-       }
-
-       /* [... put_value varname] */
-
-       varname = duk_require_hstring(ctx, -1);
-       DUK_ASSERT(varname != NULL);
-       duk_pop(ctx);  /* varname is still reachable */
-
-       DUK_DDD(DUK_DDDPRINT("arguments object automatic putvar for a bound 
variable; "
-                            "key=%!O, varname=%!O, value=%!T",
-                            (duk_heaphdr *) key,
-                            (duk_heaphdr *) varname,
-                            (duk_tval *) duk_require_tval(ctx, -1)));
-
-       /* [... put_value] */
-
-       /*
-        *  Note: although arguments object variable mappings are only 
established
-        *  for non-strict functions (and a call to a non-strict function 
created
-        *  the arguments object in question), an inner strict function may be 
doing
-        *  the actual property write.  Hence the throw_flag applied here comes 
from
-        *  the property write call.
-        */
-
-       duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(ctx, -1), 
throw_flag);
-
-       /* [... put_value] */
-}
-
-/* Lookup 'key' from arguments internal 'map', delete mapping if found.
- * Used in E5 Section 10.6 algorithm for [[Delete]].  Note that the
- * variable/argument itself (where the map points) is not deleted.
- */
-DUK_LOCAL void duk__check_arguments_map_for_delete(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *temp_desc) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_hobject *map;
-
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       if (!duk_hobject_get_own_propdesc(thr, obj, 
DUK_HTHREAD_STRING_INT_MAP(thr), temp_desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
-               DUK_DDD(DUK_DDDPRINT("arguments: key not mapped, no exotic 
delete behavior"));
-               return;
-       }
-
-       map = duk_require_hobject(ctx, -1);
-       DUK_ASSERT(map != NULL);
-       duk_pop(ctx);  /* map is reachable through obj */
-
-       DUK_DDD(DUK_DDDPRINT("-> have 'map', delete key %!O from map (if 
exists)); ignore result",
-                            (duk_heaphdr *) key));
-
-       /* Note: no recursion issue, we can trust 'map' to behave */
-       DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(map));
-       DUK_DDD(DUK_DDDPRINT("map before deletion: %!O", (duk_heaphdr *) map));
-       (void) duk_hobject_delprop_raw(thr, map, key, 0);  /* ignore result */
-       DUK_DDD(DUK_DDDPRINT("map after deletion: %!O", (duk_heaphdr *) map));
-}
-
-/*
- *  Ecmascript compliant [[GetOwnProperty]](P), for internal use only.
- *
- *  If property is found:
- *    - Fills descriptor fields to 'out_desc'
- *    - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the
- *      property onto the stack ('undefined' for accessor properties).
- *    - Returns non-zero
- *
- *  If property is not found:
- *    - 'out_desc' is left in untouched state (possibly garbage)
- *    - Nothing is pushed onto the stack (not even with 
DUK_GETDESC_FLAG_PUSH_VALUE
- *      set)
- *    - Returns zero
- *
- *  Notes:
- *
- *    - Getting a property descriptor may cause an allocation (and hence
- *      GC) to take place, hence reachability and refcount of all related
- *      values matter.  Reallocation of value stack, properties, etc may
- *      invalidate many duk_tval pointers (concretely, those which reside
- *      in memory areas subject to reallocation).  However, heap object
- *      pointers are never affected (heap objects have stable pointers).
- *
- *    - The value of a plain property is always reachable and has a non-zero
- *      reference count.
- *
- *    - The value of a virtual property is not necessarily reachable from
- *      elsewhere and may have a refcount of zero.  Hence we push it onto
- *      the valstack for the caller, which ensures it remains reachable
- *      while it is needed.
- *
- *    - There are no virtual accessor properties.  Hence, all getters and
- *      setters are always related to concretely stored properties, which
- *      ensures that the get/set functions in the resulting descriptor are
- *      reachable and have non-zero refcounts.  Should there be virtual
- *      accessor properties later, this would need to change.
- */
-
-DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject 
*obj, duk_hstring *key, duk_uint32_t arr_idx, duk_propdesc *out_desc, 
duk_small_uint_t flags) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_tval *tv;
-
-       DUK_DDD(DUK_DDDPRINT("duk_hobject_get_own_propdesc: thr=%p, obj=%p, 
key=%p, out_desc=%p, flags=%lx, "
-                            "arr_idx=%ld (obj -> %!O, key -> %!O)",
-                            (void *) thr, (void *) obj, (void *) key, (void *) 
out_desc,
-                            (long) flags, (long) arr_idx,
-                            (duk_heaphdr *) obj, (duk_heaphdr *) key));
-
-       DUK_ASSERT(ctx != NULL);
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(thr->heap != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(out_desc != NULL);
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       /* XXX: optimize this filling behavior later */
-       out_desc->flags = 0;
-       out_desc->get = NULL;
-       out_desc->set = NULL;
-       out_desc->e_idx = -1;
-       out_desc->h_idx = -1;
-       out_desc->a_idx = -1;
-
-       /*
-        *  Array part
-        */
-
-       if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) {
-               if (arr_idx < DUK_HOBJECT_GET_ASIZE(obj)) {
-                       tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, 
arr_idx);
-                       if (!DUK_TVAL_IS_UNUSED(tv)) {
-                               DUK_DDD(DUK_DDDPRINT("-> found in array part"));
-                               if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                                       duk_push_tval(ctx, tv);
-                               }
-                               /* implicit attributes */
-                               out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
-                                                 
DUK_PROPDESC_FLAG_CONFIGURABLE |
-                                                 DUK_PROPDESC_FLAG_ENUMERABLE;
-                               out_desc->a_idx = arr_idx;
-                               goto prop_found;
-                       }
-               }
-               /* assume array part is comprehensive (contains all array 
indexed elements
-                * or none of them); hence no need to check the entries part 
here.
-                */
-               DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property (has 
array part, "
-                                    "should be there if present)"));
-               goto prop_not_found_concrete;
-       }
-
-       /*
-        *  Entries part
-        */
-
-       duk_hobject_find_existing_entry(thr->heap, obj, key, &out_desc->e_idx, 
&out_desc->h_idx);
-       if (out_desc->e_idx >= 0) {
-               duk_int_t e_idx = out_desc->e_idx;
-               out_desc->flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, 
e_idx);
-               if (out_desc->flags & DUK_PROPDESC_FLAG_ACCESSOR) {
-                       DUK_DDD(DUK_DDDPRINT("-> found accessor property in 
entry part"));
-                       out_desc->get = 
DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, e_idx);
-                       out_desc->set = 
DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, e_idx);
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               /* a dummy undefined value is pushed to make 
valstack
-                                * behavior uniform for caller
-                                */
-                               duk_push_undefined(ctx);
-                       }
-               } else {
-                       DUK_DDD(DUK_DDDPRINT("-> found plain property in entry 
part"));
-                       tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
e_idx);
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_push_tval(ctx, tv);
-                       }
-               }
-               goto prop_found;
-       }
-
-       /*
-        *  Not found as a concrete property, check whether a String object
-        *  virtual property matches.
-        */
-
- prop_not_found_concrete:
-
-       if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) {
-               DUK_DDD(DUK_DDDPRINT("string object exotic property get for 
key: %!O, arr_idx: %ld",
-                                    (duk_heaphdr *) key, (long) arr_idx));
-
-               if (arr_idx != DUK__NO_ARRAY_INDEX) {
-                       duk_hstring *h_val;
-
-                       DUK_DDD(DUK_DDDPRINT("array index exists"));
-
-                       h_val = 
duk_hobject_get_internal_value_string(thr->heap, obj);
-                       DUK_ASSERT(h_val);
-                       if (arr_idx < DUK_HSTRING_GET_CHARLEN(h_val)) {
-                               DUK_DDD(DUK_DDDPRINT("-> found, array index 
inside string"));
-                               if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                                       duk_push_hstring(ctx, h_val);
-                                       duk_substring(ctx, -1, arr_idx, arr_idx 
+ 1);  /* [str] -> [substr] */
-                               }
-                               out_desc->flags = DUK_PROPDESC_FLAG_ENUMERABLE 
|  /* E5 Section 15.5.5.2 */
-                                                 DUK_PROPDESC_FLAG_VIRTUAL;
-
-                               
DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-                               return 1;  /* cannot be e.g. arguments exotic, 
since exotic 'traits' are mutually exclusive */
-                       } else {
-                               /* index is above internal string length -> 
property is fully normal */
-                               DUK_DDD(DUK_DDDPRINT("array index outside 
string -> normal property"));
-                       }
-               } else if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-                       duk_hstring *h_val;
-
-                       DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length 
exotic behavior"));
-
-                       h_val = 
duk_hobject_get_internal_value_string(thr->heap, obj);
-                       DUK_ASSERT(h_val != NULL);
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_push_uint(ctx, (duk_uint_t) 
DUK_HSTRING_GET_CHARLEN(h_val));
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;  /* E5 
Section 15.5.5.1 */
-
-                       DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-                       return 1;  /* cannot be arguments exotic */
-               }
-       } else if (DUK_HOBJECT_IS_BUFFEROBJECT(obj)) {
-               duk_hbufferobject *h_bufobj;
-               duk_uint_t byte_off;
-               duk_small_uint_t elem_size;
-
-               h_bufobj = (duk_hbufferobject *) obj;
-               DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
-               DUK_DDD(DUK_DDDPRINT("bufferobject property get for key: %!O, 
arr_idx: %ld",
-                                    (duk_heaphdr *) key, (long) arr_idx));
-
-               if (arr_idx != DUK__NO_ARRAY_INDEX) {
-                       DUK_DDD(DUK_DDDPRINT("array index exists"));
-
-                       /* Careful with wrapping: arr_idx upshift may easily 
wrap, whereas
-                        * length downshift won't.
-                        */
-                       if (arr_idx < (h_bufobj->length >> h_bufobj->shift)) {
-                               byte_off = arr_idx << h_bufobj->shift;  /* no 
wrap assuming h_bufobj->length is valid */
-                               elem_size = 1 << h_bufobj->shift;
-                               if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                                       duk_uint8_t *data;
-
-                                       if (h_bufobj->buf != NULL && 
DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
-                                               data = (duk_uint8_t *) 
DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf) + h_bufobj->offset + 
byte_off;
-                                               
duk_hbufferobject_push_validated_read(ctx, h_bufobj, data, elem_size);
-                                       } else {
-                                               DUK_D(DUK_DPRINT("bufferobject 
access out of underlying buffer, ignoring (read zero)"));
-                                               duk_push_uint(ctx, 0);
-                                       }
-                               }
-                               out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
-                                                 DUK_PROPDESC_FLAG_ENUMERABLE |
-                                                 DUK_PROPDESC_FLAG_VIRTUAL;
-
-                               
DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-                               return 1;  /* cannot be e.g. arguments exotic, 
since exotic 'traits' are mutually exclusive */
-                       } else {
-                               /* index is above internal buffer length -> 
property is fully normal */
-                               DUK_DDD(DUK_DDDPRINT("array index outside 
buffer -> normal property"));
-                       }
-               } else if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-                       DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length 
exotic behavior"));
-
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               /* Length in elements: take into account shift, 
but
-                                * intentionally don't check the underlying 
buffer here.
-                                */
-                               duk_push_uint(ctx, h_bufobj->length >> 
h_bufobj->shift);
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
-
-                       DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-                       return 1;  /* cannot be arguments exotic */
-               } else if (key == DUK_HTHREAD_STRING_BYTE_LENGTH(thr)) {
-                       /* If neutered must return 0; length is zeroed during
-                        * neutering.
-                        */
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_push_uint(ctx, h_bufobj->length);
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
-                       return 1;  /* cannot be arguments exotic */
-               } else if (key == DUK_HTHREAD_STRING_BYTE_OFFSET(thr)) {
-                       /* If neutered must return 0; offset is zeroed during
-                        * neutering.
-                        */
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_push_uint(ctx, h_bufobj->offset);
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
-                       return 1;  /* cannot be arguments exotic */
-               } else if (key == DUK_HTHREAD_STRING_BYTES_PER_ELEMENT(thr)) {
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_push_uint(ctx, 1 << h_bufobj->shift);
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
-                       return 1;  /* cannot be arguments exotic */
-               }
-       } else if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(obj)) {
-               DUK_DDD(DUK_DDDPRINT("duktape/c object exotic property get for 
key: %!O, arr_idx: %ld",
-                                    (duk_heaphdr *) key, (long) arr_idx));
-
-               if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-                       DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length 
exotic behavior"));
-
-                       if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-                               duk_int16_t func_nargs = ((duk_hnativefunction 
*) obj)->nargs;
-                               duk_push_int(ctx, func_nargs == 
DUK_HNATIVEFUNCTION_NARGS_VARARGS ? 0 : func_nargs);
-                       }
-                       out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;  /* not 
enumerable */
-
-                       DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-                       return 1;  /* cannot be arguments exotic */
-               }
-       }
-
-       /* Array properties have exotic behavior but they are concrete,
-        * so no special handling here.
-        *
-        * Arguments exotic behavior (E5 Section 10.6, [[GetOwnProperty]]
-        * is only relevant as a post-check implemented below; hence no
-        * check here.
-        */
-
-       /*
-        *  Not found as concrete or virtual
-        */
-
-       DUK_DDD(DUK_DDDPRINT("-> not found (virtual, entry part, or array 
part)"));
-       return 0;
-
-       /*
-        *  Found
-        *
-        *  Arguments object has exotic post-processing, see E5 Section 10.6,
-        *  description of [[GetOwnProperty]] variant for arguments.
-        */
-
- prop_found:
-       DUK_DDD(DUK_DDDPRINT("-> property found, checking for arguments exotic 
post-behavior"));
-
-       /* Notes:
-        *  - only numbered indices are relevant, so arr_idx fast reject is good
-        *    (this is valid unless there are more than 4**32-1 arguments).
-        *  - since variable lookup has no side effects, this can be skipped if
-        *    DUK_GETDESC_FLAG_PUSH_VALUE is not set.
-        */
-
-       if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) &&
-           arr_idx != DUK__NO_ARRAY_INDEX &&
-           (flags & DUK_GETDESC_FLAG_PUSH_VALUE)) {
-               duk_propdesc temp_desc;
-
-               /* Magically bound variable cannot be an accessor.  However,
-                * there may be an accessor property (or a plain property) in
-                * place with magic behavior removed.  This happens e.g. when
-                * a magic property is redefined with defineProperty().
-                * Cannot assert for "not accessor" here.
-                */
-
-               /* replaces top of stack with new value if necessary */
-               DUK_ASSERT((flags & DUK_GETDESC_FLAG_PUSH_VALUE) != 0);
-
-               if (duk__check_arguments_map_for_get(thr, obj, key, 
&temp_desc)) {
-                       DUK_DDD(DUK_DDDPRINT("-> arguments exotic behavior 
overrides result: %!T -> %!T",
-                                            (duk_tval *) duk_get_tval(ctx, -2),
-                                            (duk_tval *) duk_get_tval(ctx, 
-1)));
-                       /* [... old_result result] -> [... result] */
-                       duk_remove(ctx, -2);
-               }
-       }
-
-       return 1;
-}
-
-DUK_INTERNAL duk_bool_t duk_hobject_get_own_propdesc(duk_hthread *thr, 
duk_hobject *obj, duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t 
flags) {
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(out_desc != NULL);
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       return duk__get_own_propdesc_raw(thr, obj, key, 
DUK_HSTRING_GET_ARRIDX_SLOW(key), out_desc, flags);
-}
-
-/*
- *  Ecmascript compliant [[GetProperty]](P), for internal use only.
- *
- *  If property is found:
- *    - Fills descriptor fields to 'out_desc'
- *    - If DUK_GETDESC_FLAG_PUSH_VALUE is set, pushes a value related to the
- *      property onto the stack ('undefined' for accessor properties).
- *    - Returns non-zero
- *
- *  If property is not found:
- *    - 'out_desc' is left in untouched state (possibly garbage)
- *    - Nothing is pushed onto the stack (not even with 
DUK_GETDESC_FLAG_PUSH_VALUE
- *      set)
- *    - Returns zero
- *
- *  May cause arbitrary side effects and invalidate (most) duk_tval
- *  pointers.
- */
-
-DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, 
duk_hstring *key, duk_propdesc *out_desc, duk_small_uint_t flags) {
-       duk_hobject *curr;
-       duk_uint32_t arr_idx;
-       duk_uint_t sanity;
-
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(thr->heap != NULL);
-       DUK_ASSERT(obj != NULL);
-       DUK_ASSERT(key != NULL);
-       DUK_ASSERT(out_desc != NULL);
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       arr_idx = DUK_HSTRING_GET_ARRIDX_FAST(key);
-
-       DUK_DDD(DUK_DDDPRINT("duk__get_propdesc: thr=%p, obj=%p, key=%p, 
out_desc=%p, flags=%lx, "
-                            "arr_idx=%ld (obj -> %!O, key -> %!O)",
-                            (void *) thr, (void *) obj, (void *) key, (void *) 
out_desc,
-                            (long) flags, (long) arr_idx,
-                            (duk_heaphdr *) obj, (duk_heaphdr *) key));
-
-       curr = obj;
-       DUK_ASSERT(curr != NULL);
-       sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
-       do {
-               if (duk__get_own_propdesc_raw(thr, curr, key, arr_idx, 
out_desc, flags)) {
-                       /* stack contains value (if requested), 'out_desc' is 
set */
-                       return 1;
-               }
-
-               /* not found in 'curr', next in prototype chain; impose max 
depth */
-               if (sanity-- == 0) {
-                       if (flags & DUK_GETDESC_FLAG_IGNORE_PROTOLOOP) {
-                               /* treat like property not found */
-                               break;
-                       } else {
-                               DUK_ERROR_RANGE(thr, 
DUK_STR_PROTOTYPE_CHAIN_LIMIT);
-                       }
-               }
-               curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
-       } while (curr);
-
-       /* out_desc is left untouched (possibly garbage), caller must use return
-        * value to determine whether out_desc can be looked up
-        */
-
-       return 0;
-}
-
-/*
- *  Shallow fast path checks for accessing array elements with numeric
- *  indices.  The goal is to try to avoid coercing an array index to an
- *  (interned) string for the most common lookups, in particular, for
- *  standard Array objects.
- *
- *  Interning is avoided but only for a very narrow set of cases:
- *    - Object has array part, index is within array allocation, and
- *      value is not unused (= key exists)
- *    - Object has no interfering exotic behavior (e.g. arguments or
- *      string object exotic behaviors interfere, array exotic
- *      behavior does not).
- *
- *  Current shortcoming: if key does not exist (even if it is within
- *  the array allocation range) a slow path lookup with interning is
- *  always required.  This can probably be fixed so that there is a
- *  quick fast path for non-existent elements as well, at least for
- *  standard Array objects.
- */
-
-DUK_LOCAL duk_tval *duk__getprop_shallow_fastpath_array_tval(duk_hthread *thr, 
duk_hobject *obj, duk_tval *tv_key) {
-       duk_tval *tv;
-       duk_uint32_t idx;
-
-       DUK_UNREF(thr);
-
-       if (!(DUK_HOBJECT_HAS_ARRAY_PART(obj) &&
-            !DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) &&
-            !DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) &&
-            !DUK_HOBJECT_IS_BUFFEROBJECT(obj) &&
-            !DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
-               /* Must have array part and no conflicting exotic behaviors.
-                * Doesn't need to have array special behavior, e.g. Arguments
-                * object has array part.
-                */
-               return NULL;
-       }
-
-       /* Arrays never have other exotic behaviors. */
-
-       DUK_DDD(DUK_DDDPRINT("fast path attempt (no exotic 
string/arguments/buffer "
-                            "behavior, object has array part)"));
-
-#if defined(DUK_USE_FASTINT)
-       if (DUK_TVAL_IS_FASTINT(tv_key)) {
-               idx = duk__tval_fastint_to_arr_idx(tv_key);
-       } else
-#endif
-       if (DUK_TVAL_IS_DOUBLE(tv_key)) {
-               idx = duk__tval_number_to_arr_idx(tv_key);
-       } else {
-               DUK_DDD(DUK_DDDPRINT("key is not a number"));
-               return NULL;
-       }
-
-       /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
-        * is 0xffffffffUL.  We don't need to check for that explicitly
-        * because 0xffffffffUL will never be inside object 'a_size'.
-        */
-
-       if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) {
-               DUK_DDD(DUK_DDDPRINT("key is not an array index or outside 
array part"));
-               return NULL;
-       }
-       DUK_ASSERT(idx != 0xffffffffUL);
-       DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);
-
-       /* XXX: for array instances we could take a shortcut here and assume
-        * Array.prototype doesn't contain an array index property.
-        */
-
-       DUK_DDD(DUK_DDDPRINT("key is a valid array index and inside array 
part"));
-       tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx);
-       if (!DUK_TVAL_IS_UNUSED(tv)) {
-               DUK_DDD(DUK_DDDPRINT("-> fast path successful"));
-               return tv;
-       }
-
-       DUK_DDD(DUK_DDDPRINT("fast path attempt failed, fall back to slow 
path"));
-       return NULL;
-}
-
-DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread 
*thr, duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val, duk_propdesc 
*temp_desc) {
-       duk_tval *tv;
-       duk_uint32_t idx;
-       duk_uint32_t old_len, new_len;
-
-       if (!(DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) &&
-             DUK_HOBJECT_HAS_ARRAY_PART(obj) &&
-             DUK_HOBJECT_HAS_EXTENSIBLE(obj))) {
-               return 0;
-       }
-       DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));  /* caller 
ensures */
-
-#if defined(DUK_USE_FASTINT)
-       if (DUK_TVAL_IS_FASTINT(tv_key)) {
-               idx = duk__tval_fastint_to_arr_idx(tv_key);
-       } else
-#endif
-       if (DUK_TVAL_IS_DOUBLE(tv_key)) {
-               idx = duk__tval_number_to_arr_idx(tv_key);
-       } else {
-               DUK_DDD(DUK_DDDPRINT("key is not a number"));
-               return 0;
-       }
-
-       /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
-        * is 0xffffffffUL.  We don't need to check for that explicitly
-        * because 0xffffffffUL will never be inside object 'a_size'.
-        */
-
-       if (idx >= DUK_HOBJECT_GET_ASIZE(obj)) {  /* for resizing of array 
part, use slow path */
-               return 0;
-       }
-       DUK_ASSERT(idx != 0xffffffffUL);
-       DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);
-
-       old_len = duk__get_old_array_length(thr, obj, temp_desc);
-
-       if (idx >= old_len) {
-               DUK_DDD(DUK_DDDPRINT("write new array entry requires length 
update "
-                                    "(arr_idx=%ld, old_len=%ld)",
-                                    (long) idx, (long) old_len));
-               if (!(temp_desc->flags & DUK_PROPDESC_FLAG_WRITABLE)) {
-                       DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE);
-                       return 0;  /* not reachable */
-               }
-               new_len = idx + 1;
-
-               /* No resize has occurred so temp_desc->e_idx is still OK */
-               tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, 
temp_desc->e_idx);
-               DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-               DUK_TVAL_SET_FASTINT_U32(tv, new_len);  /* no need for 
decref/incref because value is a number */
-       } else {
-               ;
-       }
-
-       tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, idx);
-       DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv_val);  /* side effects */
-
-       DUK_DDD(DUK_DDDPRINT("array fast path success for index %ld", (long) 
idx));
-       return 1;
-}
-
-/*
- *  Fast path for bufferobject getprop/putprop
- */
-
-DUK_LOCAL duk_bool_t duk__getprop_fastpath_bufobj_tval(duk_hthread *thr, 
duk_hobject *obj, duk_tval *tv_key) {
-       duk_context *ctx;
-       duk_uint32_t idx;
-       duk_hbufferobject *h_bufobj;
-       duk_uint_t byte_off;
-       duk_small_uint_t elem_size;
-       duk_uint8_t *data;
-
-       ctx = (duk_context *) thr;
-
-       if (!DUK_HOBJECT_IS_BUFFEROBJECT(obj)) {
-               return 0;
-       }
-       h_bufobj = (duk_hbufferobject *) obj;
-
-#if defined(DUK_USE_FASTINT)
-       if (DUK_TVAL_IS_FASTINT(tv_key)) {
-               idx = duk__tval_fastint_to_arr_idx(tv_key);
-       } else
-#endif
-       if (DUK_TVAL_IS_DOUBLE(tv_key)) {
-               idx = duk__tval_number_to_arr_idx(tv_key);
-       } else {
-               return 0;
-       }
-
-       /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
-        * is 0xffffffffUL.  We don't need to check for that explicitly
-        * because 0xffffffffUL will never be inside bufferobject length.
-        */
-
-       /* Careful with wrapping (left shifting idx would be unsafe). */
-       if (idx >= (h_bufobj->length >> h_bufobj->shift)) {
-               return 0;
-       }
-       DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);
-
-       byte_off = idx << h_bufobj->shift;  /* no wrap assuming 
h_bufobj->length is valid */
-       elem_size = 1 << h_bufobj->shift;
-
-       if (h_bufobj->buf != NULL && 
DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
-               data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, 
h_bufobj->buf) + h_bufobj->offset + byte_off;
-               duk_hbufferobject_push_validated_read(ctx, h_bufobj, data, 
elem_size);
-       } else {
-               DUK_D(DUK_DPRINT("bufferobject access out of underlying buffer, 
ignoring (read zero)"));
-               duk_push_uint(ctx, 0);
-       }
-
-       return 1;
-}
-
-DUK_LOCAL duk_bool_t duk__putprop_fastpath_bufobj_tval(duk_hthread *thr, 
duk_hobject *obj, duk_tval *tv_key, duk_tval *tv_val) {
-       duk_context *ctx;
-       duk_uint32_t idx;
-       duk_hbufferobject *h_bufobj;
-       duk_uint_t byte_off;
-       duk_small_uint_t elem_size;
-       duk_uint8_t *data;
-
-       ctx = (duk_context *) thr;
-
-       if (!(DUK_HOBJECT_IS_BUFFEROBJECT(obj) &&
-             DUK_TVAL_IS_NUMBER(tv_val))) {
-               return 0;
-       }
-       DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));  /* caller 
ensures; rom objects are never bufferobjects now */
-
-       h_bufobj = (duk_hbufferobject *) obj;
-#if defined(DUK_USE_FASTINT)
-       if (DUK_TVAL_IS_FASTINT(tv_key)) {
-               idx = duk__tval_fastint_to_arr_idx(tv_key);
-       } else
-#endif
-       if (DUK_TVAL_IS_DOUBLE(tv_key)) {
-               idx = duk__tval_number_to_arr_idx(tv_key);
-       } else {
-               return 0;
-       }
-
-       /* If index is not valid, idx will be DUK__NO_ARRAY_INDEX which
-        * is 0xffffffffUL.  We don't need to check for that explicitly
-        * because 0xffffffffUL will never be inside bufferobject length.
-        */
-
-       /* Careful with wrapping (left shifting idx would be unsafe). */
-       if (idx >= (h_bufobj->length >> h_bufobj->shift)) {
-               return 0;
-       }
-       DUK_ASSERT(idx != DUK__NO_ARRAY_INDEX);
-
-       byte_off = idx << h_bufobj->shift;  /* no wrap assuming 
h_bufobj->length is valid */
-       elem_size = 1 << h_bufobj->shift;
-
-       /* Value is required to be a number in the fast path so there
-        * are no side effects in write coercion.
-        */
-       duk_push_tval(ctx, tv_val);
-       DUK_ASSERT(duk_is_number(ctx, -1));
-
-       if (h_bufobj->buf != NULL && 
DUK_HBUFFEROBJECT_VALID_BYTEOFFSET_EXCL(h_bufobj, byte_off + elem_size)) {
-               data = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, 
h_bufobj->buf) + h_bufobj->offset + byte_off;
-               duk_hbufferobject_validated_write(ctx, h_bufobj, data, 
elem_size);
-       } else {
-               DUK_D(DUK_DPRINT("bufferobject access out of underlying buffer, 
ignoring (write skipped)"));
-       }
-
-       duk_pop(ctx);
-       return 1;
-}
-
-/*
- *  GETPROP: Ecmascript property read.
- */
-
-DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval 
*tv_obj, duk_tval *tv_key) {
-       duk_context *ctx = (duk_context *) thr;
-       duk_tval tv_obj_copy;
-       duk_tval tv_key_copy;
-       duk_hobject *curr = NULL;
-       duk_hstring *key = NULL;
-       duk_uint32_t arr_idx = DUK__NO_ARRAY_INDEX;
-       duk_propdesc desc;
-       duk_uint_t sanity;
-
-       DUK_DDD(DUK_DDDPRINT("getprop: thr=%p, obj=%p, key=%p (obj -> %!T, key 
-> %!T)",
-                            (void *) thr, (void *) tv_obj, (void *) tv_key,
-                            (duk_tval *) tv_obj, (duk_tval *) tv_key));
-
-       DUK_ASSERT(ctx != NULL);
-       DUK_ASSERT(thr != NULL);
-       DUK_ASSERT(thr->heap != NULL);
-       DUK_ASSERT(tv_obj != NULL);
-       DUK_ASSERT(tv_key != NULL);
-
-       DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-
-       /*
-        *  Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of
-        *  them being invalidated by a valstack resize.
-        *
-        *  XXX: this is now an overkill for many fast paths.  Rework this
-        *  to be faster (although switching to a valstack discipline might
-        *  be a better solution overall).
-        */
-
-       DUK_TVAL_SET_TVAL(&tv_obj_copy, tv_obj);
-       DUK_TVAL_SET_TVAL(&tv_key_copy, tv_key);
-       tv_obj = &tv_obj_copy;
-       tv_key = &tv_key_copy;
-
-       /*
-        *  Coercion and fast path processing
-        */
-
-       switch (DUK_TVAL_GET_TAG(tv_obj)) {
-       case DUK_TAG_UNDEFINED:
-       case DUK_TAG_NULL: {
-               /* Note: unconditional throw */
-               DUK_DDD(DUK_DDDPRINT("base object is undefined or null -> 
reject"));
-#if defined(DUK_USE_PARANOID_ERRORS)
-               DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
-#else
-               DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot read property 
%s of %s",
-                              duk_push_string_tval_readable(ctx, tv_key), 
duk_push_string_tval_readable(ctx, tv_obj));
-#endif
-               return 0;
-       }
-
-       case DUK_TAG_BOOLEAN: {
-               DUK_DDD(DUK_DDDPRINT("base object is a boolean, start lookup 
from boolean prototype"));
-               curr = thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE];
-               break;
-       }
-
-       case DUK_TAG_STRING: {
-               duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
-               duk_int_t pop_count;
-
-#if defined(DUK_USE_FASTINT)
-               if (DUK_TVAL_IS_FASTINT(tv_key)) {
-                       arr_idx = duk__tval_fastint_to_arr_idx(tv_key);
-                       DUK_DDD(DUK_DDDPRINT("base object string, key is a 
fast-path fastint; arr_idx %ld", (long) arr_idx));
-                       pop_count = 0;
-               } else
-#endif
-               if (DUK_TVAL_IS_NUMBER(tv_key)) {
-                       arr_idx = duk__tval_number_to_arr_idx(tv_key);
-                       DUK_DDD(DUK_DDDPRINT("base object string, key is a 
fast-path number; arr_idx %ld", (long) arr_idx));
-                       pop_count = 0;
-               } else {
-                       arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, 
tv_key, &key);
-                       DUK_ASSERT(key != NULL);
-                       DUK_DDD(DUK_DDDPRINT("base object string, key is a 
non-fast-path number; after "
-                                            "coercion key is %!T, arr_idx %ld",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1), (long) arr_idx));
-                       pop_count = 1;
-               }
-
-               if (arr_idx != DUK__NO_ARRAY_INDEX &&
-                   arr_idx < DUK_HSTRING_GET_CHARLEN(h)) {
-                       duk_pop_n(ctx, pop_count);
-                       duk_push_hstring(ctx, h);
-                       duk_substring(ctx, -1, arr_idx, arr_idx + 1);  /* [str] 
-> [substr] */
-
-                       DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is an 
index inside string length "
-                                            "after coercion -> return char)",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1)));
-                       return 1;
-               }
-
-               if (pop_count == 0) {
-                       /* This is a pretty awkward control flow, but we need 
to recheck the
-                        * key coercion here.
-                        */
-                       arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, 
tv_key, &key);
-                       DUK_ASSERT(key != NULL);
-                       DUK_DDD(DUK_DDDPRINT("base object string, key is a 
non-fast-path number; after "
-                                            "coercion key is %!T, arr_idx %ld",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1), (long) arr_idx));
-               }
-
-               if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-                       duk_pop(ctx);  /* [key] -> [] */
-                       duk_push_uint(ctx, (duk_uint_t) 
DUK_HSTRING_GET_CHARLEN(h));  /* [] -> [res] */
-
-                       DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is 
'length' after coercion -> "
-                                            "return string length)",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1)));
-                       return 1;
-               }
-               DUK_DDD(DUK_DDDPRINT("base object is a string, start lookup 
from string prototype"));
-               curr = thr->builtins[DUK_BIDX_STRING_PROTOTYPE];
-               goto lookup;  /* avoid double coercion */
-       }
-
-       case DUK_TAG_OBJECT: {
-               duk_tval *tmp;
-
-               curr = DUK_TVAL_GET_OBJECT(tv_obj);
-               DUK_ASSERT(curr != NULL);
-
-               tmp = duk__getprop_shallow_fastpath_array_tval(thr, curr, 
tv_key);
-               if (tmp) {
-                       duk_push_tval(ctx, tmp);
-
-                       DUK_DDD(DUK_DDDPRINT("-> %!T (base is object, key is a 
number, array part "
-                                            "fast path)",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1)));
-                       return 1;
-               }
-
-               if (duk__getprop_fastpath_bufobj_tval(thr, curr, tv_key) != 0) {
-                       /* Read value pushed on stack. */
-                       DUK_DDD(DUK_DDDPRINT("-> %!T (base is bufobj, key is a 
number, bufferobject "
-                                            "fast path)",
-                                            (duk_tval *) duk_get_tval(ctx, 
-1)));
-                       return 1;
-               }
-
-#if defined(DUK_USE_ES6_PROXY)
-               if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(curr))) {
-                       duk_hobject *h_target;
-
-                       if (duk__proxy_check_prop(thr, curr, DUK_STRIDX_GET, 
tv_key, &h_target)) {
-                               /* -> [ ... trap handler ] */
-                               DUK_DDD(DUK_DDDPRINT("-> proxy object 'get' for 
key %!T", (duk_tval *) tv_key));
-                               duk_push_hobject(ctx, h_target);  /* target */
-                               duk_push_tval(ctx, tv_key);       /* P */
-                               duk_push_tval(ctx, tv_obj);       /* Receiver: 
Proxy object */
-                               duk_call_method(ctx, 3 /*nargs*/);
-
-                               /* Target object must be checked for a 
conflicting
-                                * non-configurable property.
-                                */
-                               arr_idx = 
duk__push_tval_to_hstring_arr_idx(ctx, tv_key, &key);
-                               DUK_ASSERT(key != NULL);
-
-                               if (duk__get_own_propdesc_raw(thr, h_target, 
key, arr_idx, &desc, DUK_GETDESC_FLAG_PUSH_VALUE)) {
-                                       duk_tval *tv_hook = 
duk_require_tval(ctx, -3);  /* value from hook */
-                                       duk_tval *tv_targ = 
duk_require_tval(ctx, -1);  /* value from target */
-                                       duk_bool_t datadesc_reject;
-                                       duk_bool_t accdesc_reject;
-
-                                       DUK_DDD(DUK_DDDPRINT("proxy 'get': 
target has matching property %!O, check for "
-                                                            "conflicting 
property; tv_hook=%!T, tv_targ=%!T, desc.flags=0x%08lx, "
-                                                            "desc.get=%p, 
desc.set=%p",
-                                                            (duk_heaphdr *) 
key, (duk_tval *) tv_hook, (duk_tval *) tv_targ,
-                                                            (unsigned long) 
desc.flags,
-                                                            (void *) desc.get, 
(void *) desc.set));
-
-                                       datadesc_reject = !(desc.flags & 
DUK_PROPDESC_FLAG_ACCESSOR) &&
-                                                         !(desc.flags & 
DUK_PROPDESC_FLAG_CONFIGURABLE) &&
-                                                         !(desc.flags & 
DUK_PROPDESC_FLAG_WRITABLE) &&
-                                                         
!duk_js_samevalue(tv_hook, tv_targ);
-                                       accdesc_reject = (desc.flags & 
DUK_PROPDESC_FLAG_ACCESSOR) &&
-                                                        !(desc.flags & 
DUK_PROPDESC_FLAG_CONFIGURABLE) &&
-                                                        (desc.get == NULL) &&
-                                                        
!DUK_TVAL_IS_UNDEFINED(tv_hook);
-                                       if (datadesc_reject || accdesc_reject) {
-                                               DUK_ERROR_TYPE(thr, 
DUK_STR_PROXY_REJECTED);
-                                       }
-
-                                       duk_pop_2(ctx);
-                               } else {
-                                       duk_pop(ctx);
-                               }
-                               return 1;  /* return value */
-                       }
-
-                       curr = h_target;  /* resume lookup from target */
-                       DUK_TVAL_SET_OBJECT(tv_obj, curr);
-               }
-#endif  /* DUK_USE_ES6_PROXY */
-
-               if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(curr)) {
-                       arr_idx = duk__push_tval_to_hstring_arr_idx(ctx, 
tv_key, &key);
-                       DUK_ASSERT(key != NULL);
-
-                       if (duk__check_arguments_map_for_get(thr, curr, key, 
&desc)) {
-                               DUK_DDD(DUK_DDDPRINT("-> %!T (base is object 
with arguments exotic behavior, "
-                                                    "key matches magically 
bound property -> skip standard "
-                                                    "Get with replacement 
value)",
-                                                    (duk_tval *) 
duk_get_tval(ctx, -1)));
-
-                               /* no need for 'caller' post-check, because 
'key' must be an array index */
-
-                               duk_remove(ctx, -2);  /* [key result] -> 
[result] */
-                               return 1;
-                       }
-
-                       goto lookup;  /* avoid double coercion */
-               }
-               break;
-       }
-
-       /* Buffer has virtual properties similar to string, but indexed values
-        * are numbers, not 1-byte buffers/strings which would perform badly.
-        */
-       case DUK_TAG_BUFFER: {
-               duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
-               duk_int_t pop_count;
-
-               /*
-                *  Because buffer values are often looped over, a number fast 
path
-                *  is important.
-                */
-
-#if defined(DUK_USE_FASTINT)
-               if (DUK_TVAL_IS_FASTINT(tv_key)) {
-                       arr_idx = duk__tval_fastint_to_arr_idx(tv_

<TRUNCATED>

Reply via email to