Esempio n. 1
0
void duk_debug_dump_hobject(duk_hobject *obj) {
	duk_uint_fast32_t i;
	const char *str_empty = "";
	const char *str_excl = "!";

	DUK_D(DUK_DPRINT("=== hobject %p ===", (void *) obj));
	if (!obj) {
		return;
	}

	DUK_D(DUK_DPRINT("  %sextensible", DUK_HOBJECT_HAS_EXTENSIBLE(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sconstructable", DUK_HOBJECT_HAS_CONSTRUCTABLE(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sbound", DUK_HOBJECT_HAS_BOUND(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %scompiledfunction", DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snativefunction", DUK_HOBJECT_HAS_NATIVEFUNCTION(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sthread", DUK_HOBJECT_HAS_THREAD(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sarray_part", DUK_HOBJECT_HAS_ARRAY_PART(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sstrict", DUK_HOBJECT_HAS_STRICT(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snewenv", DUK_HOBJECT_HAS_NEWENV(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %snamebinding", DUK_HOBJECT_HAS_NAMEBINDING(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %screateargs", DUK_HOBJECT_HAS_CREATEARGS(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %senvrecclosed", DUK_HOBJECT_HAS_ENVRECCLOSED(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_array", DUK_HOBJECT_HAS_EXOTIC_ARRAY(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_stringobj", DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_arguments", DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_dukfunc", DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_bufferobj", DUK_HOBJECT_HAS_EXOTIC_BUFFEROBJ(obj) ? str_empty : str_excl));
	DUK_D(DUK_DPRINT("  %sexotic_proxyobj", DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj) ? str_empty : str_excl));

	DUK_D(DUK_DPRINT("  class: number %d -> %s",
	                 (int) DUK_HOBJECT_GET_CLASS_NUMBER(obj),
	                 duk__class_names[(DUK_HOBJECT_GET_CLASS_NUMBER(obj)) & ((1 << DUK_HOBJECT_FLAG_CLASS_BITS) - 1)]));

	DUK_D(DUK_DPRINT("  prototype: %p -> %!O",
	                 (void *) obj->prototype,
	                 (duk_heaphdr *) obj->prototype));

	DUK_D(DUK_DPRINT("  props: p=%p, e_size=%d, e_used=%d, a_size=%d, h_size=%d",
	                 (void *) obj->p,
	                 (int) obj->e_size,
	                 (int) obj->e_used,
	                 (int) obj->a_size,
	                 (int) obj->h_size));

	/*
	 *  Object (struct layout) specific dumping.  Inline code here
	 *  instead of helpers, to ensure debug line prefix is identical.
	 */

	if (DUK_HOBJECT_IS_COMPILEDFUNCTION(obj)) {
		duk_hcompiledfunction *h = (duk_hcompiledfunction *) obj;

		DUK_D(DUK_DPRINT("  hcompiledfunction"));
		DUK_D(DUK_DPRINT("  data: %!O", h->data));
		DUK_D(DUK_DPRINT("  nregs: %d", (int) h->nregs));
		DUK_D(DUK_DPRINT("  nargs: %d", (int) h->nargs));

		if (h->data && DUK_HBUFFER_HAS_DYNAMIC(h->data) && DUK_HBUFFER_GET_DATA_PTR(h->data)) {
			DUK_D(DUK_DPRINT("  consts: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(h)));
			DUK_D(DUK_DPRINT("  funcs: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(h)));
			DUK_D(DUK_DPRINT("  bytecode: %p (%d, %d bytes)",
			                 (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(h),
			                 (int) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(h)));
		} else {
			DUK_D(DUK_DPRINT("  consts: ???"));
			DUK_D(DUK_DPRINT("  funcs: ???"));
			DUK_D(DUK_DPRINT("  bytecode: ???"));
		}
	} else if (DUK_HOBJECT_IS_NATIVEFUNCTION(obj)) {
		duk_hnativefunction *h = (duk_hnativefunction *) obj;

		DUK_D(DUK_DPRINT("  hnativefunction"));
		/* XXX: h->func, cannot print function pointers portably */
		DUK_D(DUK_DPRINT("  nargs: %d", (int) h->nargs));
	} else if (DUK_HOBJECT_IS_THREAD(obj)) {
		duk_hthread *thr = (duk_hthread *) obj;
		duk_tval *p;

		DUK_D(DUK_DPRINT("  hthread"));
		DUK_D(DUK_DPRINT("  strict: %d", (int) thr->strict));
		DUK_D(DUK_DPRINT("  state: %d", (int) thr->state));

		DUK_D(DUK_DPRINT("  valstack_max: %d, callstack_max:%d, catchstack_max: %d",
		                 thr->valstack_max, thr->callstack_max, thr->catchstack_max));

		DUK_D(DUK_DPRINT("  callstack: ptr %p, size %d, top %d, preventcount %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->callstack,
		                 thr->callstack_size,
		                 thr->callstack_top,
		                 thr->callstack_preventcount,
		                 thr->callstack_top,
		                 thr->callstack_top * sizeof(duk_activation),
		                 thr->callstack_size,
		                 thr->callstack_size * sizeof(duk_activation)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		for (i = 0; i <= thr->callstack_size; i++) {
			if (i == thr->callstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (!thr->callstack) {
				DUK_DEBUG_SUMMARY_CHAR('@');
			} else if (i < thr->callstack_size) {
				if (i < thr->callstack_top) {
					/* tailcalling is nice to see immediately; other flags (e.g. strict)
					 * not that important.
					 */
					if (thr->callstack[i].flags & DUK_ACT_FLAG_TAILCALLED) {
						DUK_DEBUG_SUMMARY_CHAR('/');
					}
					DUK_DEBUG_SUMMARY_CHAR(duk__get_act_summary_char(&thr->callstack[i]));
				} else {
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  valstack: ptr %p, end %p (%d), bottom %p (%d), top %p (%d), used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->valstack,
		                 (void *) thr->valstack_end,
		                 (int) (thr->valstack_end - thr->valstack),
		                 (void *) thr->valstack_bottom,
		                 (int) (thr->valstack_bottom - thr->valstack),
		                 (void *) thr->valstack_top,
		                 (int) (thr->valstack_top - thr->valstack),
		                 (int) (thr->valstack_top - thr->valstack),
		                 (int) (thr->valstack_top - thr->valstack) * sizeof(duk_tval),
		                 (int) (thr->valstack_end - thr->valstack),
		                 (int) (thr->valstack_end - thr->valstack) * sizeof(duk_tval)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		p = thr->valstack;
		while (p <= thr->valstack_end) {
			i = (duk_uint_fast32_t) (p - thr->valstack);
			if (thr->callstack &&
			    thr->callstack_top > 0 &&
			    i == (duk_size_t) (thr->callstack + thr->callstack_top - 1)->idx_bottom) {
				DUK_DEBUG_SUMMARY_CHAR('>');
			}
			if (p == thr->valstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (p < thr->valstack_end) {
				if (p < thr->valstack_top) {
					DUK_DEBUG_SUMMARY_CHAR(duk__get_tval_summary_char(p));
				} else {
					/* XXX: safe printer for these?  would be nice, because
					 * we could visualize whether the values are in proper
					 * state.
					 */
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
			p++;
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  catchstack: ptr %p, size %d, top %d, used size %d entries (%d bytes), alloc size %d entries (%d bytes)",
		                 (void *) thr->catchstack,
		                 thr->catchstack_size,
		                 thr->catchstack_top,
		                 thr->catchstack_top,
		                 thr->catchstack_top * sizeof(duk_catcher),
		                 thr->catchstack_size,
		                 thr->catchstack_size * sizeof(duk_catcher)));

		DUK_DEBUG_SUMMARY_INIT();
		DUK_DEBUG_SUMMARY_CHAR('[');
		for (i = 0; i <= thr->catchstack_size; i++) {
			if (i == thr->catchstack_top) {
				DUK_DEBUG_SUMMARY_CHAR('|');
			}
			if (!thr->catchstack) {
				DUK_DEBUG_SUMMARY_CHAR('@');
			} else if (i < thr->catchstack_size) {
				if (i < thr->catchstack_top) {
					DUK_DEBUG_SUMMARY_CHAR(duk__get_cat_summary_char(&thr->catchstack[i]));
				} else {
					DUK_DEBUG_SUMMARY_CHAR('.');
				}
			}
		}
		DUK_DEBUG_SUMMARY_CHAR(']');
		DUK_DEBUG_SUMMARY_FINISH();

		DUK_D(DUK_DPRINT("  resumer: ptr %p",
		                 (void *) thr->resumer));

#if 0  /* worth dumping? */
		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
			DUK_D(DUK_DPRINT("  builtins[%d] -> %!@O", i, thr->builtins[i]));
		}
#endif
	}

	if (obj->p) {
		DUK_D(DUK_DPRINT("  props alloc size: %d",
		                 (int) DUK_HOBJECT_P_COMPUTE_SIZE(obj->e_size, obj->a_size, obj->h_size)));
	} else {
		DUK_D(DUK_DPRINT("  props alloc size: n/a"));
	}

	DUK_D(DUK_DPRINT("  prop entries:"));
	for (i = 0; i < obj->e_size; i++) {
		duk_hstring *k;
		duk_propvalue *v;

		k = DUK_HOBJECT_E_GET_KEY(obj, i);
		v = DUK_HOBJECT_E_GET_VALUE_PTR(obj, i);

		if (i >= obj->e_used) {
			DUK_D(DUK_DPRINT("    [%d]: UNUSED", i));
			continue;
		}

		if (!k) {
			DUK_D(DUK_DPRINT("    [%d]: NULL", i));
			continue;
		}

		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i)) {
			DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %!O -> get:%p set:%p; get %!O; set %!O",
			                 i,
			                 DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i),
			                 k,
			                 (void *) v->a.get,
			                 (void *) v->a.set,
			                 (duk_heaphdr *) v->a.get,
			                 (duk_heaphdr *) v->a.set));
		} else {
			DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %!O -> %!T",
			                 i,
			                 DUK_HOBJECT_E_SLOT_IS_WRITABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_CONFIGURABLE(obj, i),
			                 DUK_HOBJECT_E_SLOT_IS_ACCESSOR(obj, i),
			                 k,
			                 &v->v));
		}
	}

	DUK_D(DUK_DPRINT("  array entries:"));
	for (i = 0; i < obj->a_size; i++) {
		DUK_D(DUK_DPRINT("    [%d]: [w=%d e=%d c=%d a=%d] %d -> %!T",
		                 i,
		                 1,  /* implicit attributes */
		                 1,
		                 1,
		                 0,
		                 i,
		                 DUK_HOBJECT_A_GET_VALUE_PTR(obj, i)));
	}

	DUK_D(DUK_DPRINT("  hash entries:"));
	for (i = 0; i < obj->h_size; i++) {
		duk_uint32_t t = DUK_HOBJECT_H_GET_INDEX(obj, i);
		if (t == DUK_HOBJECT_HASHIDX_UNUSED) {
			DUK_D(DUK_DPRINT("    [%d]: unused", i));
		} else if (t == DUK_HOBJECT_HASHIDX_DELETED) {
			DUK_D(DUK_DPRINT("    [%d]: deleted", i));
		} else {
			DUK_D(DUK_DPRINT("    [%d]: %d",
			                 i,
			                 (int) t));
		}
	}
}
Esempio n. 2
0
void duk_hthread_create_builtin_objects(duk_hthread *thr) {
	duk_context *ctx = (duk_context *) thr;
	duk_bitdecoder_ctx bd_ctx;
	duk_bitdecoder_ctx *bd = &bd_ctx;  /* convenience */
	duk_hobject *h;
	int i, j;

	DUK_DPRINT("INITBUILTINS BEGIN");

	DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx));
	bd->data = (const duk_uint8_t *) duk_builtins_data;
	bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH;

	/*
	 *  First create all built-in bare objects on the empty valstack.
	 *  During init, their indices will correspond to built-in indices.
	 *
	 *  Built-ins will be reachable from both valstack and thr->builtins.
	 */

	/* XXX: there is no need to resize valstack because builtin count
	 * is much less than the default space; assert for it.
	 */

	DUK_DDPRINT("create empty built-ins");
	DUK_ASSERT_TOP(ctx, 0);
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		int class_num;
		int len = -1;

		class_num = duk_bd_decode(bd, DUK__CLASS_BITS);
		len = duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/);

		if (class_num == DUK_HOBJECT_CLASS_FUNCTION) {
			int natidx;
			int stridx;
			int c_nargs;
			duk_c_function c_func;
			duk_int16_t magic;

			DUK_DDDPRINT("len=%d", len);
			DUK_ASSERT(len >= 0);

			natidx = duk_bd_decode(bd, DUK__NATIDX_BITS);
			stridx = duk_bd_decode(bd, DUK__STRIDX_BITS);
			c_func = duk_bi_native_functions[natidx];

			c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/);
			if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
				c_nargs = DUK_VARARGS;
			}

			/* FIXME: set magic directly here? (it could share the c_nargs arg) */
			duk_push_c_function_nospecial(ctx, c_func, c_nargs);

			h = duk_require_hobject(ctx, -1);
			DUK_ASSERT(h != NULL);

			/* Currently all built-in native functions are strict.
			 * duk_push_c_function() now sets strict flag, so
			 * assert for it.
			 */
			DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h));

			/* FIXME: function properties */

			duk_push_hstring_stridx(ctx, stridx);
			duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);

			/* Almost all global level Function objects are constructable
			 * but not all: Function.prototype is a non-constructable,
			 * callable Function.
			 */
			if (duk_bd_decode_flag(bd)) {
				DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h));
			} else {
				DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h);
			}

			/* Cast converts magic to 16-bit signed value */
			magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/);
			((duk_hnativefunction *) h)->magic = magic;
		} else {
			/* FIXME: ARRAY_PART for Array prototype? */

			duk_push_object_helper(ctx,
			                       DUK_HOBJECT_FLAG_EXTENSIBLE,
			                       -1);  /* no prototype or class yet */

			h = duk_require_hobject(ctx, -1);
			DUK_ASSERT(h != NULL);
		}

		DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);

		thr->builtins[i] = h;
		DUK_HOBJECT_INCREF(thr, &h->hdr);

		if (len >= 0) {
			/*
			 *  For top-level objects, 'length' property has the following
			 *  default attributes: non-writable, non-enumerable, non-configurable
			 *  (E5 Section 15).
			 *
			 *  However, 'length' property for Array.prototype has attributes
			 *  expected of an Array instance which are different: writable,
			 *  non-enumerable, non-configurable (E5 Section 15.4.5.2).
			 *
			 *  This is currently determined implicitly based on class; there are
			 *  no attribute flags in the init data.
			 */

			duk_push_int(ctx, len);
			duk_def_prop_stridx(ctx,
			                    -2,
			                    DUK_STRIDX_LENGTH,
			                    (class_num == DUK_HOBJECT_CLASS_ARRAY ?  /* only Array.prototype matches */
			                     DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE));
		}

		/* enable special behaviors last */

		if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
			DUK_HOBJECT_SET_SPECIAL_ARRAY(h);
		}
		if (class_num == DUK_HOBJECT_CLASS_STRING) {
			DUK_HOBJECT_SET_SPECIAL_STRINGOBJ(h);
		}

		/* some assertions */

		DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h));
		/* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h));
		/* DUK_HOBJECT_FLAG_NATIVEFUNCTION varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h));       /* currently, even for Array.prototype */
		/* DUK_HOBJECT_FLAG_STRICT varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(h) ||  /* all native functions have NEWENV */
		           DUK_HOBJECT_HAS_NEWENV(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h));
		/* DUK_HOBJECT_FLAG_SPECIAL_ARRAY varies */
		/* DUK_HOBJECT_FLAG_SPECIAL_STRINGOBJ varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_ARGUMENTS(h));

		DUK_DDDPRINT("created built-in %d, class=%d, length=%d", i, class_num, len);
	}

	/*
	 *  Then decode the builtins init data (see genbuiltins.py) to
	 *  init objects
	 */

	DUK_DDPRINT("initialize built-in object properties");
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		unsigned char t;
		int num;

		DUK_DDDPRINT("initializing built-in object at index %d", i);
		h = thr->builtins[i];

		t = duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			DUK_DDDPRINT("set internal prototype: built-in %d", (int) t);
			DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]);
		}

		t = duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			/* 'prototype' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = false,
			 *  [[Enumerable]] = false,
			 *  [[Configurable]] = false
			 */
			DUK_DDDPRINT("set external prototype: built-in %d", (int) t);
			duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE);
		}

		t = duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			/* 'constructor' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = true,
			 *  [[Enumerable]] = false,	
			 *  [[Configurable]] = true
			 */
			DUK_DDDPRINT("set external constructor: built-in %d", (int) t);
			duk_def_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC);
		}

		/* normal valued properties */
		num = duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS);
		DUK_DDDPRINT("built-in object %d, %d normal valued properties", i, num);
		for (j = 0; j < num; j++) {
			int stridx;
			int prop_flags;

			stridx = duk_bd_decode(bd, DUK__STRIDX_BITS);

			/*
			 *  Property attribute defaults are defined in E5 Section 15 (first
			 *  few pages); there is a default for all properties and a special
			 *  default for 'length' properties.  Variation from the defaults is
			 *  signaled using a single flag bit in the bitstream.
			 */

			if (duk_bd_decode_flag(bd)) {
				prop_flags = duk_bd_decode(bd, DUK__PROP_FLAGS_BITS);
			} else {
				if (stridx == DUK_STRIDX_LENGTH) {
					prop_flags = DUK_PROPDESC_FLAGS_NONE;
				} else {
					prop_flags = DUK_PROPDESC_FLAGS_WC;
				}
			}

			t = duk_bd_decode(bd, DUK__PROP_TYPE_BITS);

			DUK_DDDPRINT("built-in %d, normal-valued property %d, stridx %d, flags 0x%02x, type %d",
			             i, j, stridx, prop_flags, (int) t);

			switch (t) {
			case DUK__PROP_TYPE_DOUBLE: {
				duk_double_union du;
				int k;

				for (k = 0; k < 8; k++) {
					/* Encoding endianness must match target memory layout,
					 * build scripts and genbuiltins.py must ensure this.
					 */
					du.uc[k] = (duk_uint8_t) duk_bd_decode(bd, 8);
				}

				duk_push_number(ctx, du.d);  /* push operation normalizes NaNs */
				break;
			}
			case DUK__PROP_TYPE_STRING: {
				int n;
				int k;
				char *p;

				n = duk_bd_decode(bd, DUK__STRING_LENGTH_BITS);
				p = (char *) duk_push_fixed_buffer(ctx, n);
				for (k = 0; k < n; k++) {
					*p++ = duk_bd_decode(bd, DUK__STRING_CHAR_BITS);
				}

				duk_to_string(ctx, -1);
				break;
			}
			case DUK__PROP_TYPE_STRIDX: {
				int n;

				n = duk_bd_decode(bd, DUK__STRIDX_BITS);
				DUK_ASSERT(n >= 0 && n < DUK_HEAP_NUM_STRINGS);
				duk_push_hstring_stridx(ctx, n);
				break;
			}
			case DUK__PROP_TYPE_BUILTIN: {
				int bidx;

				bidx = duk_bd_decode(bd, DUK__BIDX_BITS);
				DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER);
				duk_dup(ctx, bidx);
				break;
			}
			case DUK__PROP_TYPE_UNDEFINED: {
				duk_push_undefined(ctx);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_TRUE: {
				duk_push_true(ctx);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_FALSE: {
				duk_push_false(ctx);
				break;
			}
			case DUK__PROP_TYPE_ACCESSOR: {
				int natidx_getter = duk_bd_decode(bd, DUK__NATIDX_BITS);
				int natidx_setter = duk_bd_decode(bd, DUK__NATIDX_BITS);
				duk_c_function c_func_getter;
				duk_c_function c_func_setter;

				/* XXX: this is a bit awkward because there is no exposed helper
				 * in the API style, only this internal helper.
				 */
				DUK_DDDPRINT("built-in accessor property: objidx=%d, stridx=%d, getteridx=%d, setteridx=%d, flags=0x%04x",
				             i, stridx, natidx_getter, natidx_setter, prop_flags);

				c_func_getter = duk_bi_native_functions[natidx_getter];
				c_func_setter = duk_bi_native_functions[natidx_setter];
				duk_push_c_function_noconstruct_nospecial(ctx, c_func_getter, 0);  /* always 0 args */
				duk_push_c_function_noconstruct_nospecial(ctx, c_func_setter, 1);  /* always 1 arg */

				/* FIXME: magic for getter/setter? */

				prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR;  /* accessor flag not encoded explicitly */
				duk_hobject_define_accessor_internal(thr,
				                                     duk_require_hobject(ctx, i),
				                                     DUK_HTHREAD_GET_STRING(thr, stridx),
				                                     duk_require_hobject(ctx, -2),
				                                     duk_require_hobject(ctx, -1),
				                                     prop_flags);
				duk_pop_2(ctx);  /* getter and setter, now reachable through object */
				goto skip_value;
			}
			default: {
				/* exhaustive */
				DUK_UNREACHABLE();
			}
			}

			DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0);
			duk_def_prop_stridx(ctx, i, stridx, prop_flags);

		 skip_value:
			continue;  /* avoid empty label at the end of a compound statement */
		}

		/* native function properties */
		num = duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS);
		DUK_DDDPRINT("built-in object %d, %d function valued properties", i, num);
		for (j = 0; j < num; j++) {
			int stridx;
			int natidx;
			int c_nargs;
			int c_length;
			duk_int16_t magic;
			duk_c_function c_func;
			duk_hnativefunction *h_func;

			stridx = duk_bd_decode(bd, DUK__STRIDX_BITS);
			natidx = duk_bd_decode(bd, DUK__NATIDX_BITS);

			c_length = duk_bd_decode(bd, DUK__LENGTH_PROP_BITS);
			c_nargs = duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/);
			if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
				c_nargs = DUK_VARARGS;
			}

			c_func = duk_bi_native_functions[natidx];

			DUK_DDDPRINT("built-in %d, function-valued property %d, stridx %d, natidx %d, length %d, nargs %d",
			             i, j, stridx, natidx, c_length, (c_nargs == DUK_VARARGS ? -1 : c_nargs));

			/* [ (builtin objects) ] */

			duk_push_c_function_noconstruct_nospecial(ctx, c_func, c_nargs);
			h_func = duk_require_hnativefunction(ctx, -1);
			DUK_UNREF(h_func);

			/* Currently all built-in native functions are strict.
			 * This doesn't matter for many functions, but e.g.
			 * String.prototype.charAt (and other string functions)
			 * rely on being strict so that their 'this' binding is
			 * not automatically coerced.
			 */
			DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func);

			/* No built-in functions are constructable except the top
			 * level ones (Number, etc).
			 */
			DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func));

			/* FIXME: any way to avoid decoding magic bit; there are quite
			 * many function properties and relatively few with magic values.
			 */
			/* Cast converts magic to 16-bit signed value */
			magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0);
			h_func->magic = magic;

			/* [ (builtin objects) func ] */

			duk_push_int(ctx, c_length);
			duk_def_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);

			duk_push_hstring_stridx(ctx, stridx);
			duk_def_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);

			/* FIXME: other properties of function instances; 'arguments', 'caller'. */

			DUK_DDPRINT("built-in object %d, function property %d -> %!T", i, j, duk_get_tval(ctx, -1));

			/* [ (builtin objects) func ] */

			/*
			 *  The default property attributes are correct for all
			 *  function valued properties of built-in objects now.
			 */

			duk_def_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC);

			/* [ (builtin objects) ] */
		}
	}

	/*
	 *  Special post-tweaks, for cases not covered by the init data format.
	 *
	 *  - Set Date.prototype.toGMTString to Date.prototype.toUTCString.
	 *    toGMTString is required to have the same Function object as
	 *    toUTCString in E5 Section B.2.6.  Note that while Smjs respects
	 *    this, V8 does not (the Function objects are distinct).
	 *
	 *  - Make DoubleError non-extensible.
	 *
	 *  - Add info about most important effective compile options to Duktape.
	 */

	duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
	duk_def_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);

	h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR);
	DUK_ASSERT(h != NULL);
	DUK_HOBJECT_CLEAR_EXTENSIBLE(h);

	duk_push_string(ctx,
#if defined(DUK_USE_INTEGER_LE)
	                "l"
#elif defined(DUK_USE_INTEGER_BE)
	                "b"
#elif defined(DUK_USE_INTEGER_ME)  /* integer mixed endian not really used now */
	                "m"
#else
	                "?"
#endif
#if defined(DUK_USE_DOUBLE_LE)
	                "l"
#elif defined(DUK_USE_DOUBLE_BE)
	                "b"
#elif defined(DUK_USE_DOUBLE_ME)
	                "m"
#else
	                "?"
#endif
#if defined(DUK_USE_BYTEORDER_FORCED)
			"f"
#endif
	                " "
#if defined(DUK_USE_PACKED_TVAL)
	                "p"
#else
	                "u"
#endif
	                " "
#if defined(DUK_USE_HOBJECT_LAYOUT_1)
			"p1"
#elif defined(DUK_USE_HOBJECT_LAYOUT_2)
			"p2"
#elif defined(DUK_USE_HOBJECT_LAYOUT_3)
			"p3"
#else
			"p?"
#endif
			" "
#if defined(DUK_USE_ALIGN_4)
			"a4"
#elif defined(DUK_USE_ALIGN_8)
			"a8"
#else
			"a1"
#endif
			" "
	                DUK_USE_ARCH_STRING);
	duk_def_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);

	/*
	 *  InitJS code - Ecmascript code evaluated from a built-in source
	 *  which provides e.g. backward compatibility.  User can also provide
	 *  JS code to be evaluated at startup.
	 */

#ifdef DUK_USE_INITJS
	/* FIXME: compression */
	duk_eval_string(ctx, (const char *) duk_initjs_data);  /* initjs data is NUL terminated */
	duk_pop(ctx);
#endif  /* DUK_USE_INITJS */

#ifdef DUK_USE_USER_INITJS
	/* FIXME: compression, at least as an option? */
	/* FIXME: unused now */
	duk_eval_string(ctx, (const char *) DUK_USE_USER_INITJS);
	duk_pop(ctx);
#endif  /* DUK_USE_USER_INITJS */

	/*
	 *  Since built-ins are not often extended, compact them.
	 */

	DUK_DDPRINT("compact built-ins");
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_hobject_compact_props(thr, thr->builtins[i]);
	}

	DUK_DPRINT("INITBUILTINS END");

#ifdef DUK_USE_DDEBUG
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		DUK_DDPRINT("built-in object %d after initialization and compacting: %!@iO", i, thr->builtins[i]);
	}
#endif
	
#ifdef DUK_USE_DDDEBUG
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		DUK_DDDPRINT("built-in object %d after initialization and compacting", i);
		DUK_DEBUG_DUMP_HOBJECT(thr->builtins[i]);
	}
#endif

	/*
	 *  Pop built-ins from stack: they are now INCREF'd and
	 *  reachable from the builtins[] array.
	 */

	duk_pop_n(ctx, DUK_NUM_BUILTINS);
	DUK_ASSERT_TOP(ctx, 0);
}
DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
	duk_context *ctx = (duk_context *) thr;
	duk_bitdecoder_ctx bd_ctx;
	duk_bitdecoder_ctx *bd = &bd_ctx;  /* convenience */
	duk_hobject *h;
	duk_small_uint_t i, j;

	DUK_D(DUK_DPRINT("INITBUILTINS BEGIN"));

	DUK_MEMZERO(&bd_ctx, sizeof(bd_ctx));
	bd->data = (const duk_uint8_t *) duk_builtins_data;
	bd->length = (duk_size_t) DUK_BUILTINS_DATA_LENGTH;

	/*
	 *  First create all built-in bare objects on the empty valstack.
	 *  During init, their indices will correspond to built-in indices.
	 *
	 *  Built-ins will be reachable from both valstack and thr->builtins.
	 */

	/* XXX: there is no need to resize valstack because builtin count
	 * is much less than the default space; assert for it.
	 */

	DUK_DD(DUK_DDPRINT("create empty built-ins"));
	DUK_ASSERT_TOP(ctx, 0);
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_small_uint_t class_num;
		duk_small_int_t len = -1;  /* must be signed */

		class_num = (duk_small_uint_t) duk_bd_decode(bd, DUK__CLASS_BITS);
		len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/);

		if (class_num == DUK_HOBJECT_CLASS_FUNCTION) {
			duk_small_uint_t natidx;
			duk_small_uint_t stridx;
			duk_int_t c_nargs;  /* must hold DUK_VARARGS */
			duk_c_function c_func;
			duk_int16_t magic;

			DUK_DDD(DUK_DDDPRINT("len=%ld", (long) len));
			DUK_ASSERT(len >= 0);

			natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
			stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);
			c_func = duk_bi_native_functions[natidx];

			c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/);
			if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
				c_nargs = DUK_VARARGS;
			}

			/* XXX: set magic directly here? (it could share the c_nargs arg) */
			duk_push_c_function_noexotic(ctx, c_func, c_nargs);

			h = duk_require_hobject(ctx, -1);
			DUK_ASSERT(h != NULL);

			/* Currently all built-in native functions are strict.
			 * duk_push_c_function() now sets strict flag, so
			 * assert for it.
			 */
			DUK_ASSERT(DUK_HOBJECT_HAS_STRICT(h));

			/* XXX: function properties */

			duk_push_hstring_stridx(ctx, stridx);
			duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);

			/* Almost all global level Function objects are constructable
			 * but not all: Function.prototype is a non-constructable,
			 * callable Function.
			 */
			if (duk_bd_decode_flag(bd)) {
				DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h));
			} else {
				DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h);
			}

			/* Cast converts magic to 16-bit signed value */
			magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0 /*def_value*/);
			((duk_hnativefunction *) h)->magic = magic;
		} else {
			/* XXX: ARRAY_PART for Array prototype? */

			duk_push_object_helper(ctx,
			                       DUK_HOBJECT_FLAG_EXTENSIBLE,
			                       -1);  /* no prototype or class yet */

			h = duk_require_hobject(ctx, -1);
			DUK_ASSERT(h != NULL);
		}

		DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);

		thr->builtins[i] = h;
		DUK_HOBJECT_INCREF(thr, &h->hdr);

		if (len >= 0) {
			/*
			 *  For top-level objects, 'length' property has the following
			 *  default attributes: non-writable, non-enumerable, non-configurable
			 *  (E5 Section 15).
			 *
			 *  However, 'length' property for Array.prototype has attributes
			 *  expected of an Array instance which are different: writable,
			 *  non-enumerable, non-configurable (E5 Section 15.4.5.2).
			 *
			 *  This is currently determined implicitly based on class; there are
			 *  no attribute flags in the init data.
			 */

			duk_push_int(ctx, len);
			duk_xdef_prop_stridx(ctx,
			                     -2,
			                     DUK_STRIDX_LENGTH,
			                     (class_num == DUK_HOBJECT_CLASS_ARRAY ?  /* only Array.prototype matches */
			                      DUK_PROPDESC_FLAGS_W : DUK_PROPDESC_FLAGS_NONE));
		}

		/* enable exotic behaviors last */

		if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
			DUK_HOBJECT_SET_EXOTIC_ARRAY(h);
		}
		if (class_num == DUK_HOBJECT_CLASS_STRING) {
			DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h);
		}

		/* some assertions */

		DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h));
		/* DUK_HOBJECT_FLAG_CONSTRUCTABLE varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_COMPILEDFUNCTION(h));
		/* DUK_HOBJECT_FLAG_NATIVEFUNCTION varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h));       /* currently, even for Array.prototype */
		/* DUK_HOBJECT_FLAG_STRICT varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(h) ||  /* all native functions have NEWENV */
		           DUK_HOBJECT_HAS_NEWENV(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h));
		/* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */
		/* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h));

		DUK_DDD(DUK_DDDPRINT("created built-in %ld, class=%ld, length=%ld", (long) i, (long) class_num, (long) len));
	}

	/*
	 *  Then decode the builtins init data (see genbuiltins.py) to
	 *  init objects
	 */

	DUK_DD(DUK_DDPRINT("initialize built-in object properties"));
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_small_uint_t t;
		duk_small_uint_t num;

		DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i));
		h = thr->builtins[i];

		t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t));
			DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[t]);
		}

		t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			/* 'prototype' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = false,
			 *  [[Enumerable]] = false,
			 *  [[Configurable]] = false
			 */
			DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t));
			duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_PROTOTYPE, t, DUK_PROPDESC_FLAGS_NONE);
		}

		t = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
		if (t != DUK__NO_BIDX_MARKER) {
			/* 'constructor' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = true,
			 *  [[Enumerable]] = false,
			 *  [[Configurable]] = true
			 */
			DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t));
			duk_xdef_prop_stridx_builtin(ctx, i, DUK_STRIDX_CONSTRUCTOR, t, DUK_PROPDESC_FLAGS_WC);
		}

		/* normal valued properties */
		num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_NORMAL_PROPS_BITS);
		DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld normal valued properties", (long) i, (long) num));
		for (j = 0; j < num; j++) {
			duk_small_uint_t stridx;
			duk_small_uint_t prop_flags;

			stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);

			/*
			 *  Property attribute defaults are defined in E5 Section 15 (first
			 *  few pages); there is a default for all properties and a special
			 *  default for 'length' properties.  Variation from the defaults is
			 *  signaled using a single flag bit in the bitstream.
			 */

			if (duk_bd_decode_flag(bd)) {
				prop_flags = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_FLAGS_BITS);
			} else {
				if (stridx == DUK_STRIDX_LENGTH) {
					prop_flags = DUK_PROPDESC_FLAGS_NONE;
				} else {
					prop_flags = DUK_PROPDESC_FLAGS_WC;
				}
			}

			t = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_TYPE_BITS);

			DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, stridx %ld, flags 0x%02lx, type %ld",
			                     (long) i, (long) j, (long) stridx, (unsigned long) prop_flags, (long) t));

			switch (t) {
			case DUK__PROP_TYPE_DOUBLE: {
				duk_double_union du;
				duk_small_uint_t k;

				for (k = 0; k < 8; k++) {
					/* Encoding endianness must match target memory layout,
					 * build scripts and genbuiltins.py must ensure this.
					 */
					du.uc[k] = (duk_uint8_t) duk_bd_decode(bd, 8);
				}

				duk_push_number(ctx, du.d);  /* push operation normalizes NaNs */
				break;
			}
			case DUK__PROP_TYPE_STRING: {
				duk_small_uint_t n;
				duk_small_uint_t k;
				duk_uint8_t *p;

				n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRING_LENGTH_BITS);
				p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, n);
				for (k = 0; k < n; k++) {
					*p++ = (duk_uint8_t) duk_bd_decode(bd, DUK__STRING_CHAR_BITS);
				}

				duk_to_string(ctx, -1);
				break;
			}
			case DUK__PROP_TYPE_STRIDX: {
				duk_small_uint_t n;

				n = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);
				DUK_ASSERT_DISABLE(n >= 0);  /* unsigned */
				DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
				duk_push_hstring_stridx(ctx, n);
				break;
			}
			case DUK__PROP_TYPE_BUILTIN: {
				duk_small_uint_t bidx;

				bidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__BIDX_BITS);
				DUK_ASSERT(bidx != DUK__NO_BIDX_MARKER);
				duk_dup(ctx, (duk_idx_t) bidx);
				break;
			}
			case DUK__PROP_TYPE_UNDEFINED: {
				duk_push_undefined(ctx);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_TRUE: {
				duk_push_true(ctx);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_FALSE: {
				duk_push_false(ctx);
				break;
			}
			case DUK__PROP_TYPE_ACCESSOR: {
				duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
				duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);
				duk_c_function c_func_getter;
				duk_c_function c_func_setter;

				/* XXX: this is a bit awkward because there is no exposed helper
				 * in the API style, only this internal helper.
				 */
				DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, stridx=%ld, getteridx=%ld, setteridx=%ld, flags=0x%04lx",
				                     (long) i, (long) stridx, (long) natidx_getter, (long) natidx_setter, (unsigned long) prop_flags));

				c_func_getter = duk_bi_native_functions[natidx_getter];
				c_func_setter = duk_bi_native_functions[natidx_setter];
				duk_push_c_function_noconstruct_noexotic(ctx, c_func_getter, 0);  /* always 0 args */
				duk_push_c_function_noconstruct_noexotic(ctx, c_func_setter, 1);  /* always 1 arg */

				/* XXX: magic for getter/setter? */

				prop_flags |= DUK_PROPDESC_FLAG_ACCESSOR;  /* accessor flag not encoded explicitly */
				duk_hobject_define_accessor_internal(thr,
				                                     duk_require_hobject(ctx, i),
				                                     DUK_HTHREAD_GET_STRING(thr, stridx),
				                                     duk_require_hobject(ctx, -2),
				                                     duk_require_hobject(ctx, -1),
				                                     prop_flags);
				duk_pop_2(ctx);  /* getter and setter, now reachable through object */
				goto skip_value;
			}
			default: {
				/* exhaustive */
				DUK_UNREACHABLE();
			}
			}

			DUK_ASSERT((prop_flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0);
			duk_xdef_prop_stridx(ctx, i, stridx, prop_flags);

		 skip_value:
			continue;  /* avoid empty label at the end of a compound statement */
		}

		/* native function properties */
		num = (duk_small_uint_t) duk_bd_decode(bd, DUK__NUM_FUNC_PROPS_BITS);
		DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num));
		for (j = 0; j < num; j++) {
			duk_small_uint_t stridx;
			duk_small_uint_t natidx;
			duk_int_t c_nargs;  /* must hold DUK_VARARGS */
			duk_small_uint_t c_length;
			duk_int16_t magic;
			duk_c_function c_func;
			duk_hnativefunction *h_func;
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
			duk_small_int_t lightfunc_eligible;
#endif

			stridx = (duk_small_uint_t) duk_bd_decode(bd, DUK__STRIDX_BITS);
			natidx = (duk_small_uint_t) duk_bd_decode(bd, DUK__NATIDX_BITS);

			c_length = (duk_small_uint_t) duk_bd_decode(bd, DUK__LENGTH_PROP_BITS);
			c_nargs = (duk_int_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, (duk_int32_t) c_length /*def_value*/);
			if (c_nargs == DUK__NARGS_VARARGS_MARKER) {
				c_nargs = DUK_VARARGS;
			}

			c_func = duk_bi_native_functions[natidx];

			DUK_DDD(DUK_DDDPRINT("built-in %ld, function-valued property %ld, stridx %ld, natidx %ld, length %ld, nargs %ld",
			                     (long) i, (long) j, (long) stridx, (long) natidx, (long) c_length,
			                     (c_nargs == DUK_VARARGS ? (long) -1 : (long) c_nargs)));

			/* Cast converts magic to 16-bit signed value */
			magic = (duk_int16_t) duk_bd_decode_flagged(bd, DUK__MAGIC_BITS, 0);

#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
			lightfunc_eligible =
				((c_nargs >= DUK_LFUNC_NARGS_MIN && c_nargs <= DUK_LFUNC_NARGS_MAX) || (c_nargs == DUK_VARARGS)) &&
				(c_length <= DUK_LFUNC_LENGTH_MAX) &&
				(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX);
			if (stridx == DUK_STRIDX_EVAL ||
			    stridx == DUK_STRIDX_YIELD ||
			    stridx == DUK_STRIDX_RESUME ||
			    stridx == DUK_STRIDX_REQUIRE) {
				/* These functions have trouble working as lightfuncs.
				 * Some of them have specific asserts and some may have
			         * additional properties (e.g. 'require.id' may be written).
				 */
				DUK_D(DUK_DPRINT("reject as lightfunc: stridx=%d, i=%d, j=%d", (int) stridx, (int) i, (int) j));
				lightfunc_eligible = 0;
			}

			if (lightfunc_eligible) {
				duk_tval tv_lfunc;
				duk_small_uint_t lf_nargs = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs);
				duk_small_uint_t lf_flags = DUK_LFUNC_FLAGS_PACK(magic, c_length, lf_nargs);
				DUK_TVAL_SET_LIGHTFUNC(&tv_lfunc, c_func, lf_flags);
				duk_push_tval(ctx, &tv_lfunc);
				DUK_D(DUK_DPRINT("built-in function eligible as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld -> %!iT", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic, duk_get_tval(ctx, -1)));
				goto lightfunc_skip;
			}

			DUK_D(DUK_DPRINT("built-in function NOT ELIGIBLE as light function: i=%d, j=%d c_length=%ld, c_nargs=%ld, magic=%ld", (int) i, (int) j, (long) c_length, (long) c_nargs, (long) magic));
#endif  /* DUK_USE_LIGHTFUNC_BUILTINS */

			/* [ (builtin objects) ] */

			duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs);
			h_func = duk_require_hnativefunction(ctx, -1);
			DUK_UNREF(h_func);

			/* Currently all built-in native functions are strict.
			 * This doesn't matter for many functions, but e.g.
			 * String.prototype.charAt (and other string functions)
			 * rely on being strict so that their 'this' binding is
			 * not automatically coerced.
			 */
			DUK_HOBJECT_SET_STRICT((duk_hobject *) h_func);

			/* No built-in functions are constructable except the top
			 * level ones (Number, etc).
			 */
			DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_func));

			/* XXX: any way to avoid decoding magic bit; there are quite
			 * many function properties and relatively few with magic values.
			 */
			h_func->magic = magic;

			/* [ (builtin objects) func ] */

			duk_push_int(ctx, c_length);
			duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);

			duk_push_hstring_stridx(ctx, stridx);
			duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);

			/* XXX: other properties of function instances; 'arguments', 'caller'. */

			DUK_DD(DUK_DDPRINT("built-in object %ld, function property %ld -> %!T",
			                   (long) i, (long) j, (duk_tval *) duk_get_tval(ctx, -1)));

			/* [ (builtin objects) func ] */

			/*
			 *  The default property attributes are correct for all
			 *  function valued properties of built-in objects now.
			 */

#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
		 lightfunc_skip:
#endif

			duk_xdef_prop_stridx(ctx, i, stridx, DUK_PROPDESC_FLAGS_WC);

			/* [ (builtin objects) ] */
		}
	}

	/*
	 *  Special post-tweaks, for cases not covered by the init data format.
	 *
	 *  - Set Date.prototype.toGMTString to Date.prototype.toUTCString.
	 *    toGMTString is required to have the same Function object as
	 *    toUTCString in E5 Section B.2.6.  Note that while Smjs respects
	 *    this, V8 does not (the Function objects are distinct).
	 *
	 *  - Make DoubleError non-extensible.
	 *
	 *  - Add info about most important effective compile options to Duktape.
	 *
	 *  - Possibly remove some properties (values or methods) which are not
	 *    desirable with current feature options but are not currently
	 *    conditional in init data.
	 */

	duk_get_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
	duk_xdef_prop_stridx(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);

	h = duk_require_hobject(ctx, DUK_BIDX_DOUBLE_ERROR);
	DUK_ASSERT(h != NULL);
	DUK_HOBJECT_CLEAR_EXTENSIBLE(h);

#if !defined(DUK_USE_ES6_OBJECT_PROTO_PROPERTY)
	DUK_DD(DUK_DDPRINT("delete Object.prototype.__proto__ built-in which is not enabled in features"));
	(void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_PROTOTYPE], DUK_HTHREAD_STRING___PROTO__(thr), DUK_DELPROP_FLAG_THROW);
#endif

#if !defined(DUK_USE_ES6_OBJECT_SETPROTOTYPEOF)
	DUK_DD(DUK_DDPRINT("delete Object.setPrototypeOf built-in which is not enabled in features"));
	(void) duk_hobject_delprop_raw(thr, thr->builtins[DUK_BIDX_OBJECT_CONSTRUCTOR], DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr), DUK_DELPROP_FLAG_THROW);
#endif

	duk_push_string(ctx,
			/* Endianness indicator */
#if defined(DUK_USE_INTEGER_LE)
	                "l"
#elif defined(DUK_USE_INTEGER_BE)
	                "b"
#elif defined(DUK_USE_INTEGER_ME)  /* integer mixed endian not really used now */
	                "m"
#else
	                "?"
#endif
#if defined(DUK_USE_DOUBLE_LE)
	                "l"
#elif defined(DUK_USE_DOUBLE_BE)
	                "b"
#elif defined(DUK_USE_DOUBLE_ME)
	                "m"
#else
	                "?"
#endif
#if defined(DUK_USE_BYTEORDER_FORCED)
			"f"
#endif
	                " "
			/* Packed or unpacked tval */
#if defined(DUK_USE_PACKED_TVAL)
	                "p"
#else
	                "u"
#endif
#if defined(DUK_USE_FASTINT)
			"f"
#endif
			" "
			/* Low memory options */
#if defined(DUK_USE_STRTAB_CHAIN)
			"c"  /* chain */
#elif defined(DUK_USE_STRTAB_PROBE)
			"p"  /* probe */
#else
			"?"
#endif
#if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16)
			"n"
#endif
#if defined(DUK_USE_HEAPPTR16)
			"h"
#endif
#if defined(DUK_USE_DATAPTR16)
			"d"
#endif
#if defined(DUK_USE_FUNCPTR16)
			"f"
#endif
#if defined(DUK_USE_REFCOUNT16)
			"R"
#endif
#if defined(DUK_USE_STRHASH16)
			"H"
#endif
#if defined(DUK_USE_STRLEN16)
			"S"
#endif
#if defined(DUK_USE_BUFLEN16)
			"B"
#endif
#if defined(DUK_USE_OBJSIZES16)
			"O"
#endif
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
			"L"
#endif
	                " "
			/* Object property allocation layout */
#if defined(DUK_USE_HOBJECT_LAYOUT_1)
			"p1"
#elif defined(DUK_USE_HOBJECT_LAYOUT_2)
			"p2"
#elif defined(DUK_USE_HOBJECT_LAYOUT_3)
			"p3"
#else
			"p?"
#endif
			" "
			/* Alignment guarantee */
#if defined(DUK_USE_ALIGN_4)
			"a4"
#elif defined(DUK_USE_ALIGN_8)
			"a8"
#else
			"a1"
#endif
			" "
			/* Architecture, OS, and compiler strings */
	                DUK_USE_ARCH_STRING
			" "
	                DUK_USE_OS_STRING
			" "
	                DUK_USE_COMPILER_STRING);
	duk_xdef_prop_stridx(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);

	/*
	 *  InitJS code - Ecmascript code evaluated from a built-in source
	 *  which provides e.g. backward compatibility.  User can also provide
	 *  JS code to be evaluated at startup.
	 */

#ifdef DUK_USE_BUILTIN_INITJS
	/* XXX: compression */
	DUK_DD(DUK_DDPRINT("running built-in initjs"));
	duk_eval_string(ctx, (const char *) duk_initjs_data);  /* initjs data is NUL terminated */
	duk_pop(ctx);
#endif  /* DUK_USE_BUILTIN_INITJS */

#ifdef DUK_USE_USER_INITJS
	/* XXX: compression (as an option) */
	DUK_DD(DUK_DDPRINT("running user initjs"));
	duk_eval_string_noresult(ctx, (const char *) DUK_USE_USER_INITJS);
#endif  /* DUK_USE_USER_INITJS */

	/*
	 *  Since built-ins are not often extended, compact them.
	 */

	DUK_DD(DUK_DDPRINT("compact built-ins"));
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_hobject_compact_props(thr, thr->builtins[i]);
	}

	DUK_D(DUK_DPRINT("INITBUILTINS END"));

#ifdef DUK_USE_DDPRINT
	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO",
		                   (long) i, (duk_heaphdr *) thr->builtins[i]));
	}
#endif

	/*
	 *  Pop built-ins from stack: they are now INCREF'd and
	 *  reachable from the builtins[] array.
	 */

	duk_pop_n(ctx, DUK_NUM_BUILTINS);
	DUK_ASSERT_TOP(ctx, 0);
}
Esempio n. 4
0
DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) {
	duk_fixedbuffer *fb = st->fb;
	duk_uint_fast32_t i;
	duk_tval *tv;
	duk_hstring *key;
	duk_bool_t first = 1;
	const char *brace1 = "{";
	const char *brace2 = "}";
	duk_bool_t pushed_loopstack = 0;

	if (duk_fb_is_full(fb)) {
		return;
	}

	duk__print_shared_heaphdr(st, &h->hdr);

	if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) {
		brace1 = "[";
		brace2 = "]";
	}

	if (!h) {
		duk_fb_put_cstring(fb, "NULL");
		goto finished;
	}

	if (st->depth >= st->depth_limit) {
		const char *subtype = "generic";

		if (DUK_HOBJECT_IS_COMPFUNC(h)) {
			subtype = "compfunc";
		} else if (DUK_HOBJECT_IS_NATFUNC(h)) {
			subtype = "natfunc";
		} else if (DUK_HOBJECT_IS_THREAD(h)) {
			subtype = "thread";
		} else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
			subtype = "bufobj";
		} else if (DUK_HOBJECT_IS_ARRAY(h)) {
			subtype = "array";
		}
		duk_fb_sprintf(fb, "%sobject/%s %p%s", (const char *) brace1, subtype, (void *) h, (const char *) brace2);
		return;
	}

	for (i = 0; i < (duk_uint_fast32_t) st->loop_stack_index; i++) {
		if (st->loop_stack[i] == h) {
			duk_fb_sprintf(fb, "%sLOOP:%p%s", (const char *) brace1, (void *) h, (const char *) brace2);
			return;
		}
	}

	/* after this, return paths should 'goto finished' for decrement */
	st->depth++;

	if (st->loop_stack_index >= st->loop_stack_limit) {
		duk_fb_sprintf(fb, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1, (const char *) brace2);
		goto finished;
	}
	st->loop_stack[st->loop_stack_index++] = h;
	pushed_loopstack = 1;

	/*
	 *  Notation: double underscore used for internal properties which are not
	 *  stored in the property allocation (e.g. '__valstack').
	 */

	duk_fb_put_cstring(fb, brace1);

	if (DUK_HOBJECT_GET_PROPS(NULL, h)) {
		duk_uint32_t a_limit;

		a_limit = DUK_HOBJECT_GET_ASIZE(h);
		if (st->internal) {
			/* dump all allocated entries, unused entries print as 'unused',
			 * note that these may extend beyond current 'length' and look
			 * a bit funny.
			 */
		} else {
			/* leave out trailing 'unused' elements */
			while (a_limit > 0) {
				tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, a_limit - 1);
				if (!DUK_TVAL_IS_UNUSED(tv)) {
					break;
				}
				a_limit--;
			}
		}

		for (i = 0; i < a_limit; i++) {
			tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i);
			DUK__COMMA();
			duk__print_tval(st, tv);
		}
		for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) {
			key = DUK_HOBJECT_E_GET_KEY(NULL, h, i);
			if (!key) {
				continue;
			}
			if (!st->internal && DUK_HSTRING_HAS_HIDDEN(key)) {
				continue;
			}
			DUK__COMMA();
			duk__print_hstring(st, key, 0);
			duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COLON);
			if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL, h, i)) {
				duk_fb_sprintf(fb, "[get:%p,set:%p]",
				               (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.get,
				               (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.set);
			} else {
				tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v;
				duk__print_tval(st, tv);
			}
			if (st->heavy) {
				duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i));
			}
		}
	}
	if (st->internal) {
		if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true");
		}
		if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true");
		}
		if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__boundfunc:true");
		}
		if (DUK_HOBJECT_HAS_COMPFUNC(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__compfunc:true");
		}
		if (DUK_HOBJECT_HAS_NATFUNC(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__natfunc:true");
		}
		if (DUK_HOBJECT_HAS_BUFOBJ(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__bufobj:true");
		}
		if (DUK_HOBJECT_HAS_THREAD(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true");
		}
		if (DUK_HOBJECT_HAS_ARRAY_PART(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__array_part:true");
		}
		if (DUK_HOBJECT_HAS_STRICT(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__strict:true");
		}
		if (DUK_HOBJECT_HAS_NOTAIL(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__notail:true");
		}
		if (DUK_HOBJECT_HAS_NEWENV(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__newenv:true");
		}
		if (DUK_HOBJECT_HAS_NAMEBINDING(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__namebinding:true");
		}
		if (DUK_HOBJECT_HAS_CREATEARGS(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true");
		}
		if (DUK_HOBJECT_HAS_ENVRECCLOSED(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__envrecclosed:true");
		}
		if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_array:true");
		}
		if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_stringobj:true");
		}
		if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_arguments:true");
		}
		if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_dukfunc:true");
		}
		if (DUK_HOBJECT_IS_BUFOBJ(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_bufobj:true");
		}
		if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) {
			DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_proxyobj:true");
		}
	}

	if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) {
		duk_harray *a = (duk_harray *) h;
		DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) a->length);
		DUK__COMMA(); duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable);
	} else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) {
		duk_hcompfunc *f = (duk_hcompfunc *) h;
		DUK__COMMA(); duk_fb_put_cstring(fb, "__data:");
		duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f));
		DUK__COMMA(); duk_fb_put_cstring(fb, "__lexenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_LEXENV(NULL, f));
		DUK__COMMA(); duk_fb_put_cstring(fb, "__varenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_VARENV(NULL, f));
		DUK__COMMA(); duk_fb_sprintf(fb, "__nregs:%ld", (long) f->nregs);
		DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
		DUK__COMMA(); duk_fb_sprintf(fb, "__start_line:%ld", (long) f->start_line);
		DUK__COMMA(); duk_fb_sprintf(fb, "__end_line:%ld", (long) f->end_line);
#endif
		DUK__COMMA(); duk_fb_put_cstring(fb, "__data:");
		duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f));
	} else if (st->internal && DUK_HOBJECT_IS_NATFUNC(h)) {
		duk_hnatfunc *f = (duk_hnatfunc *) h;
		DUK__COMMA(); duk_fb_sprintf(fb, "__func:");
		duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func));
		DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs);
		DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic);
#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
	} else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) {
		duk_hbufobj *b = (duk_hbufobj *) h;
		DUK__COMMA(); duk_fb_sprintf(fb, "__buf:");
		duk__print_hbuffer(st, (duk_hbuffer *) b->buf);
		DUK__COMMA(); duk_fb_sprintf(fb, "__buf_prop:");
		duk__print_hobject(st, (duk_hobject *) b->buf_prop);
		DUK__COMMA(); duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset);
		DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) b->length);
		DUK__COMMA(); duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift);
		DUK__COMMA(); duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type);
#endif
	} else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) {
		duk_hthread *t = (duk_hthread *) h;
		DUK__COMMA(); duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict);
		DUK__COMMA(); duk_fb_sprintf(fb, "__state:%ld", (long) t->state);
		DUK__COMMA(); duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1);
		DUK__COMMA(); duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2);
		DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_max:%ld", (long) t->valstack_max);
		DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_max:%ld", (long) t->callstack_max);
		DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_max:%ld", (long) t->catchstack_max);
		DUK__COMMA(); duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack);
		DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack));
		DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_bottom:%p/%ld", (void *) t->valstack_bottom, (long) (t->valstack_bottom - t->valstack));
		DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack));
		DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack:%p", (void *) t->catchstack);
		DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_size:%ld", (long) t->catchstack_size);
		DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_top:%ld", (long) t->catchstack_top);
		DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer);
		/* XXX: print built-ins array? */

	}
#if defined(DUK_USE_REFERENCE_COUNTING)
	if (st->internal) {
		DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h));
	}
#endif
	if (st->internal) {
		DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h));
	}

	DUK__COMMA(); duk_fb_sprintf(fb, "__heapptr:%p", (void *) h);  /* own pointer */

	/* prototype should be last, for readability */
	if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) {
		if (st->follow_proto) {
			DUK__COMMA(); duk_fb_put_cstring(fb, "__prototype:"); duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
		} else {
			DUK__COMMA(); duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h));
		}
	}

	duk_fb_put_cstring(fb, brace2);

#if defined(DUK_USE_HOBJECT_HASH_PART)
	if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) {
		duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE);
		for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) {
			duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i);
			if (i > 0) {
				duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA);
			}
			if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) {
				duk_fb_sprintf(fb, "u");
			} else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) {
				duk_fb_sprintf(fb, "d");
			} else {
				duk_fb_sprintf(fb, "%ld", (long) h_idx);
			}
		}
		duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE);
	}
#endif

 finished:
	st->depth--;
	if (pushed_loopstack) {
		st->loop_stack_index--;
		st->loop_stack[st->loop_stack_index] = NULL;
	}
}