/*
 *  Initialize built-in objects.  Current thread must have a valstack
 *  and initialization errors may longjmp, so a setjmp() catch point
 *  must exist.
 */

#include "duk_internal.h"

/*
 *  Encoding constants, must match genbuiltins.py
 */

#define DUK__PROP_FLAGS_BITS             3
#define DUK__LENGTH_PROP_BITS            3
#define DUK__NARGS_BITS                  3
#define DUK__PROP_TYPE_BITS              3

#define DUK__NARGS_VARARGS_MARKER        0x07

#define DUK__PROP_TYPE_DOUBLE            0
#define DUK__PROP_TYPE_STRING            1
#define DUK__PROP_TYPE_STRIDX            2
#define DUK__PROP_TYPE_BUILTIN           3
#define DUK__PROP_TYPE_UNDEFINED         4
#define DUK__PROP_TYPE_BOOLEAN_TRUE      5
#define DUK__PROP_TYPE_BOOLEAN_FALSE     6
#define DUK__PROP_TYPE_ACCESSOR          7

/*
 *  Create built-in objects by parsing an init bitstream generated
 *  by genbuiltins.py.
 */

#if defined(DUK_USE_ROM_OBJECTS)
#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) {
	duk_hobject *h_global;
#if defined(DUK_USE_ROM_GLOBAL_CLONE)
	duk_hobject *h_oldglobal;
	duk_uint8_t *props;
	duk_size_t alloc_size;
#endif
	duk_hobject *h_objenv;

	/* XXX: refactor into internal helper, duk_clone_hobject() */

#if defined(DUK_USE_ROM_GLOBAL_INHERIT)
	/* Inherit from ROM-based global object: less RAM usage, less transparent. */
	h_global = duk_push_object_helper(thr,
	                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
	                                  DUK_HOBJECT_FLAG_FASTREFS |
	                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
	                                  DUK_BIDX_GLOBAL);
	DUK_ASSERT(h_global != NULL);
#elif defined(DUK_USE_ROM_GLOBAL_CLONE)
	/* Clone the properties of the ROM-based global object to create a
	 * fully RAM-based global object.  Uses more memory than the inherit
	 * model but more compliant.
	 */
	h_global = duk_push_object_helper(thr,
	                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
	                                  DUK_HOBJECT_FLAG_FASTREFS |
	                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
	                                  DUK_BIDX_OBJECT_PROTOTYPE);
	DUK_ASSERT(h_global != NULL);
	h_oldglobal = thr->builtins[DUK_BIDX_GLOBAL];
	DUK_ASSERT(h_oldglobal != NULL);

	/* Copy the property table verbatim; this handles attributes etc.
	 * For ROM objects it's not necessary (or possible) to update
	 * refcounts so leave them as is.
	 */
	alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h_oldglobal);
	DUK_ASSERT(alloc_size > 0);
	props = DUK_ALLOC_CHECKED(thr, alloc_size);
	DUK_ASSERT(props != NULL);
	DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal) != NULL);
	duk_memcpy((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal), alloc_size);

	/* XXX: keep property attributes or tweak them here?
	 * Properties will now be non-configurable even when they're
	 * normally configurable for the global object.
	 */

	DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_global) == NULL);
	DUK_HOBJECT_SET_PROPS(thr->heap, h_global, props);
	DUK_HOBJECT_SET_ESIZE(h_global, DUK_HOBJECT_GET_ESIZE(h_oldglobal));
	DUK_HOBJECT_SET_ENEXT(h_global, DUK_HOBJECT_GET_ENEXT(h_oldglobal));
	DUK_HOBJECT_SET_ASIZE(h_global, DUK_HOBJECT_GET_ASIZE(h_oldglobal));
	DUK_HOBJECT_SET_HSIZE(h_global, DUK_HOBJECT_GET_HSIZE(h_oldglobal));
#else
#error internal error in config defines
#endif

	duk_hobject_compact_props(thr, h_global);
	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
	DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL]));  /* no need to decref: ROM object */
	thr->builtins[DUK_BIDX_GLOBAL] = h_global;
	DUK_HOBJECT_INCREF(thr, h_global);
	DUK_D(DUK_DPRINT("duplicated global object: %!O", h_global));

	/* Create a fresh object environment for the global scope.  This is
	 * needed so that the global scope points to the newly created RAM-based
	 * global object.
	 */
	h_objenv = (duk_hobject *) duk_hobjenv_alloc(thr,
	                                             DUK_HOBJECT_FLAG_EXTENSIBLE |
	                                             DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
	DUK_ASSERT(h_objenv != NULL);
	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_objenv) == NULL);
	duk_push_hobject(thr, h_objenv);

	DUK_ASSERT(h_global != NULL);
	((duk_hobjenv *) h_objenv)->target = h_global;
	DUK_HOBJECT_INCREF(thr, h_global);
	DUK_ASSERT(((duk_hobjenv *) h_objenv)->has_this == 0);

	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL);
	DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV]));  /* no need to decref: ROM object */
	thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_objenv;
	DUK_HOBJECT_INCREF(thr, h_objenv);
	DUK_D(DUK_DPRINT("duplicated global env: %!O", h_objenv));

	DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) h_objenv);

	duk_pop_2(thr);  /* Pop global object and global env. */
}
#endif  /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */

DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
	/* Setup builtins from ROM objects.  All heaps/threads will share
	 * the same readonly objects.
	 */
	duk_small_uint_t i;

	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		duk_hobject *h;
		h = (duk_hobject *) DUK_LOSE_CONST(duk_rom_builtins_bidx[i]);
		DUK_ASSERT(h != NULL);
		thr->builtins[i] = h;
	}

#if defined(DUK_USE_ROM_GLOBAL_CLONE) || defined(DUK_USE_ROM_GLOBAL_INHERIT)
	/* By default the global object is read-only which is often much
	 * more of an issue than having read-only built-in objects (like
	 * RegExp, Date, etc).  Use a RAM-based copy of the global object
	 * and the global environment object for convenience.
	 */
	duk__duplicate_ram_global_object(thr);
#endif
}
#else  /* DUK_USE_ROM_OBJECTS */
DUK_LOCAL void duk__push_stridx(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_small_uint_t n;

	n = (duk_small_uint_t) duk_bd_decode_varuint(bd);
	DUK_ASSERT_DISABLE(n >= 0);  /* unsigned */
	DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
	duk_push_hstring_stridx(thr, n);
}
DUK_LOCAL void duk__push_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	/* XXX: built-ins data could provide a maximum length that is
	 * actually needed; bitpacked max length is now 256 bytes.
	 */
	duk_uint8_t tmp[DUK_BD_BITPACKED_STRING_MAXLEN];
	duk_small_uint_t len;

	len = duk_bd_decode_bitpacked_string(bd, tmp);
	duk_push_lstring(thr, (const char *) tmp, (duk_size_t) len);
}
DUK_LOCAL void duk__push_stridx_or_string(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_small_uint_t n;

	n = (duk_small_uint_t) duk_bd_decode_varuint(bd);
	if (n == 0) {
		duk__push_string(thr, bd);
	} else {
		n--;
		DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
		duk_push_hstring_stridx(thr, n);
	}
}
DUK_LOCAL void duk__push_double(duk_hthread *thr, duk_bitdecoder_ctx *bd) {
	duk_double_union du;
	duk_small_uint_t i;

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

	duk_push_number(thr, du.d);  /* push operation normalizes NaNs */
}

DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *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_NUM_BUILTINS=%d, DUK_NUM_BUILTINS_ALL=%d", (int) DUK_NUM_BUILTINS, (int) DUK_NUM_ALL_BUILTINS));

	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.
	 *
	 *  Built-ins in the index range [0,DUK_NUM_BUILTINS-1] have value
	 *  stack indices matching their eventual thr->builtins[] index.
	 *
	 *  Built-ins in the index range [DUK_NUM_BUILTINS,DUK_NUM_ALL_BUILTINS]
	 *  will exist on the value stack during init but won't be placed
	 *  into thr->builtins[].  These are objects referenced in some way
	 *  from thr->builtins[] roots but which don't need to be indexed by
	 *  Duktape through thr->builtins[] (e.g. user custom objects).
	 *
	 *  Internal prototypes will be incorrect (NULL) at this stage.
	 */

	duk_require_stack(thr, DUK_NUM_ALL_BUILTINS);

	DUK_DD(DUK_DDPRINT("create empty built-ins"));
	DUK_ASSERT_TOP(thr, 0);
	for (i = 0; i < DUK_NUM_ALL_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_varuint(bd);
		len = (duk_small_int_t) duk_bd_decode_flagged_signed(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_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_varuint(bd);
			DUK_ASSERT(natidx != 0);
			c_func = duk_bi_native_functions[natidx];
			DUK_ASSERT(c_func != NULL);

			c_nargs = (duk_small_int_t) duk_bd_decode_flagged_signed(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) */
			(void) duk_push_c_function_builtin(thr, c_func, c_nargs);
			h = duk_known_hobject(thr, -1);

			/* 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_stridx_or_string(thr, bd);
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
			duk_xdef_prop_stridx_short(thr,
			                           -2,
			                           DUK_STRIDX_NAME,
			                           DUK_PROPDESC_FLAGS_C);
#else
			duk_pop(thr);  /* Not very ideal but good enough for now. */
#endif

			/* 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_varuint(bd);
			((duk_hnatfunc *) h)->magic = magic;
		} else if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
			duk_push_array(thr);
		} else if (class_num == DUK_HOBJECT_CLASS_OBJENV) {
			duk_hobjenv *env;
			duk_hobject *global;

			DUK_ASSERT(i == DUK_BIDX_GLOBAL_ENV);
			DUK_ASSERT(DUK_BIDX_GLOBAL_ENV > DUK_BIDX_GLOBAL);

			env = duk_hobjenv_alloc(thr,
	                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
	                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
			DUK_ASSERT(env->target == NULL);
			duk_push_hobject(thr, (duk_hobject *) env);

			global = duk_known_hobject(thr, DUK_BIDX_GLOBAL);
			DUK_ASSERT(global != NULL);
			env->target = global;
			DUK_HOBJECT_INCREF(thr, global);
			DUK_ASSERT(env->has_this == 0);

			DUK_HOBJENV_ASSERT_VALID(env);
		} else {
			DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_DECENV);

			(void) duk_push_object_helper(thr,
			                              DUK_HOBJECT_FLAG_FASTREFS |
			                              DUK_HOBJECT_FLAG_EXTENSIBLE,
			                              -1);  /* no prototype or class yet */

		}

		h = duk_known_hobject(thr, -1);
		DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);

		if (i < DUK_NUM_BUILTINS) {
			thr->builtins[i] = h;
			DUK_HOBJECT_INCREF(thr, &h->hdr);
		}

		if (len >= 0) {
			/* In ES2015+ built-in function object .length property
			 * has property attributes C (configurable only):
			 * http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-standard-built-in-objects
			 *
			 * Array.prototype remains an Array instance in ES2015+
			 * and its length has attributes W (writable only).
			 * Because .length is now virtual for duk_harray, it is
			 * not encoded explicitly in init data.
			 */

			DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY);  /* .length is virtual */
			duk_push_int(thr, len);
			duk_xdef_prop_stridx_short(thr,
			                           -2,
			                           DUK_STRIDX_LENGTH,
			                           DUK_PROPDESC_FLAGS_C);
		}

		/* enable exotic behaviors last */

		if (class_num == DUK_HOBJECT_CLASS_ARRAY) {
			DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h));  /* set by duk_push_array() */
		}
		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_BOUNDFUNC(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h));
		/* DUK_HOBJECT_FLAG_NATFUNC varies */
		DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(h));
		DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(h));
		DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY);
		/* DUK_HOBJECT_FLAG_STRICT varies */
		DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(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_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.  Internal prototypes are set at this stage,
	 *  with thr->builtins[] populated.
	 */

	DUK_DD(DUK_DDPRINT("initialize built-in object properties"));
	for (i = 0; i < DUK_NUM_ALL_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 = duk_known_hobject(thr, (duk_idx_t) i);

		t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		if (t > 0) {
			t--;
			DUK_DDD(DUK_DDDPRINT("set internal prototype: built-in %ld", (long) t));
			DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, duk_known_hobject(thr, (duk_idx_t) t));
		} else if (DUK_HOBJECT_IS_NATFUNC(h)) {
			/* Standard native built-ins cannot inherit from
			 * %NativeFunctionPrototype%, they are required to
			 * inherit from Function.prototype directly.
			 */
			DUK_ASSERT(thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE] != NULL);
			DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
		}

		t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		if (t > 0) {
			/* 'prototype' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = false,
			 *  [[Enumerable]] = false,
			 *  [[Configurable]] = false
			 */
			t--;
			DUK_DDD(DUK_DDDPRINT("set external prototype: built-in %ld", (long) t));
			duk_dup(thr, (duk_idx_t) t);
			duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_NONE);
		}

		t = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		if (t > 0) {
			/* 'constructor' property for all built-in objects (which have it) has attributes:
			 *  [[Writable]] = true,
			 *  [[Enumerable]] = false,
			 *  [[Configurable]] = true
			 */
			t--;
			DUK_DDD(DUK_DDDPRINT("set external constructor: built-in %ld", (long) t));
			duk_dup(thr, (duk_idx_t) t);
			duk_xdef_prop_stridx(thr, (duk_idx_t) i, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);
		}

		/* normal valued properties */
		num = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		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 defprop_flags;

			duk__push_stridx_or_string(thr, bd);

			/*
			 *  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.
			 */

			defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
			                                                         DUK__PROP_FLAGS_BITS,
			                                                         (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
			defprop_flags |= DUK_DEFPROP_FORCE |
			                 DUK_DEFPROP_HAVE_VALUE |
			                 DUK_DEFPROP_HAVE_WRITABLE |
			                 DUK_DEFPROP_HAVE_ENUMERABLE |
			                 DUK_DEFPROP_HAVE_CONFIGURABLE;  /* Defaults for data properties. */

			/* The writable, enumerable, configurable flags in prop_flags
			 * match both duk_def_prop() and internal property flags.
			 */
			DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
			DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
			DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);

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

			DUK_DDD(DUK_DDDPRINT("built-in %ld, normal-valued property %ld, key %!T, flags 0x%02lx, type %ld",
			                     (long) i, (long) j, duk_get_tval(thr, -1), (unsigned long) defprop_flags, (long) t));

			switch (t) {
			case DUK__PROP_TYPE_DOUBLE: {
				duk__push_double(thr, bd);
				break;
			}
			case DUK__PROP_TYPE_STRING: {
				duk__push_string(thr, bd);
				break;
			}
			case DUK__PROP_TYPE_STRIDX: {
				duk__push_stridx(thr, bd);
				break;
			}
			case DUK__PROP_TYPE_BUILTIN: {
				duk_small_uint_t bidx;

				bidx = (duk_small_uint_t) duk_bd_decode_varuint(bd);
				duk_dup(thr, (duk_idx_t) bidx);
				break;
			}
			case DUK__PROP_TYPE_UNDEFINED: {
				duk_push_undefined(thr);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_TRUE: {
				duk_push_true(thr);
				break;
			}
			case DUK__PROP_TYPE_BOOLEAN_FALSE: {
				duk_push_false(thr);
				break;
			}
			case DUK__PROP_TYPE_ACCESSOR: {
				duk_small_uint_t natidx_getter = (duk_small_uint_t) duk_bd_decode_varuint(bd);
				duk_small_uint_t natidx_setter = (duk_small_uint_t) duk_bd_decode_varuint(bd);
				duk_small_uint_t accessor_magic = (duk_small_uint_t) duk_bd_decode_varuint(bd);
				duk_c_function c_func_getter;
				duk_c_function c_func_setter;

				DUK_DDD(DUK_DDDPRINT("built-in accessor property: objidx=%ld, key=%!T, getteridx=%ld, setteridx=%ld, flags=0x%04lx",
				                     (long) i, duk_get_tval(thr, -1), (long) natidx_getter, (long) natidx_setter, (unsigned long) defprop_flags));

				c_func_getter = duk_bi_native_functions[natidx_getter];
				if (c_func_getter != NULL) {
					duk_push_c_function_builtin_noconstruct(thr, c_func_getter, 0);  /* always 0 args */
					duk_set_magic(thr, -1, (duk_int_t) accessor_magic);
					defprop_flags |= DUK_DEFPROP_HAVE_GETTER;
				}
				c_func_setter = duk_bi_native_functions[natidx_setter];
				if (c_func_setter != NULL) {
					duk_push_c_function_builtin_noconstruct(thr, c_func_setter, 1);  /* always 1 arg */
					duk_set_magic(thr, -1, (duk_int_t) accessor_magic);
					defprop_flags |= DUK_DEFPROP_HAVE_SETTER;
				}

				/* Writable flag doesn't make sense for an accessor. */
				DUK_ASSERT((defprop_flags & DUK_PROPDESC_FLAG_WRITABLE) == 0);  /* genbuiltins.py ensures */

				defprop_flags &= ~(DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
				defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE;
				break;
			}
			default: {
				/* exhaustive */
				DUK_UNREACHABLE();
			}
			}

			duk_def_prop(thr, (duk_idx_t) i, defprop_flags);
			DUK_ASSERT_TOP(thr, DUK_NUM_ALL_BUILTINS);
		}

		/* native function properties */
		num = (duk_small_uint_t) duk_bd_decode_varuint(bd);
		DUK_DDD(DUK_DDDPRINT("built-in object %ld, %ld function valued properties", (long) i, (long) num));
		for (j = 0; j < num; j++) {
			duk_hstring *h_key;
			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_hnatfunc *h_func;
#if defined(DUK_USE_LIGHTFUNC_BUILTINS)
			duk_small_int_t lightfunc_eligible;
#endif
			duk_small_uint_t defprop_flags;

			duk__push_stridx_or_string(thr, bd);
			h_key = duk_known_hstring(thr, -1);
			DUK_UNREF(h_key);
			natidx = (duk_small_uint_t) duk_bd_decode_varuint(bd);

			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_uint32_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, key %!O, natidx %ld, length %ld, nargs %ld",
			                     (long) i, (long) j, (duk_heaphdr *) h_key, (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_varuint(bd);

#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);

			/* 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).
			 */
			if (c_func == duk_bi_global_object_eval) {
				lightfunc_eligible = 0;
			}
#if defined(DUK_USE_COROUTINE_SUPPORT)
			if (c_func == duk_bi_thread_yield ||
			    c_func == duk_bi_thread_resume) {
				lightfunc_eligible = 0;
			}
#endif
			if (c_func == duk_bi_function_prototype_call ||
			    c_func == duk_bi_function_prototype_apply ||
			    c_func == duk_bi_reflect_apply ||
			    c_func == duk_bi_reflect_construct) {
				lightfunc_eligible = 0;
			}

			if (lightfunc_eligible) {
				duk_tval tv_lfunc;
				duk_small_uint_t lf_nargs = (duk_small_uint_t) (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(thr, &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(thr, -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) name ] */

			duk_push_c_function_builtin_noconstruct(thr, c_func, c_nargs);
			h_func = duk_known_hnatfunc(thr, -1);
			DUK_UNREF(h_func);

			/* XXX: add into init data? */

			/* Special call handling, not described in init data. */
			if (c_func == duk_bi_global_object_eval ||
			    c_func == duk_bi_function_prototype_call ||
			    c_func == duk_bi_function_prototype_apply ||
			    c_func == duk_bi_reflect_apply ||
			    c_func == duk_bi_reflect_construct) {
				DUK_HOBJECT_SET_SPECIAL_CALL((duk_hobject *) 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) name func ] */

			duk_push_uint(thr, c_length);
			duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);

			duk_dup_m2(thr);
			duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);

			/* 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(thr, -1)));

			/* [ (builtin objects) name 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

			defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
			                                                         DUK__PROP_FLAGS_BITS,
			                                                         (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
			defprop_flags |= DUK_DEFPROP_FORCE |
			                 DUK_DEFPROP_HAVE_VALUE |
			                 DUK_DEFPROP_HAVE_WRITABLE |
			                 DUK_DEFPROP_HAVE_ENUMERABLE |
			                 DUK_DEFPROP_HAVE_CONFIGURABLE;
			DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
			DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
			DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);

			duk_def_prop(thr, (duk_idx_t) i, defprop_flags);

			/* [ (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.
	 */

#if defined(DUK_USE_DATE_BUILTIN)
	duk_get_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
	duk_xdef_prop_stridx_short(thr, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);
#endif

	h = duk_known_hobject(thr, DUK_BIDX_DOUBLE_ERROR);
	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

	/* XXX: relocate */
	duk_push_string(thr,
			/* 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
	                " "
			/* Packed or unpacked tval */
#if defined(DUK_USE_PACKED_TVAL)
	                "p"
#else
	                "u"
#endif
#if defined(DUK_USE_FASTINT)
			"f"
#endif
			" "
			/* Low memory/performance options */
#if defined(DUK_USE_STRTAB_PTRCOMP)
			"s"
#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
#if defined(DUK_USE_ROM_STRINGS) || defined(DUK_USE_ROM_OBJECTS)
			/* XXX: This won't be shown in practice now
			 * because this code is not run when builtins
			 * are in ROM.
			 */
			"Z"
#endif
#if defined(DUK_USE_LITCACHE_SIZE)
			"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 (DUK_USE_ALIGN_BY == 4)
			"a4"
#elif (DUK_USE_ALIGN_BY == 8)
			"a8"
#elif (DUK_USE_ALIGN_BY == 1)
			"a1"
#else
#error invalid DUK_USE_ALIGN_BY
#endif
			" "
			/* Architecture, OS, and compiler strings */
	                DUK_USE_ARCH_STRING
			" "
	                DUK_USE_OS_STRING
			" "
	                DUK_USE_COMPILER_STRING);
	duk_xdef_prop_stridx_short(thr, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);

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

	DUK_DD(DUK_DDPRINT("compact built-ins"));
	for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
		duk_hobject_compact_props(thr, duk_known_hobject(thr, (duk_idx_t) i));
	}

	DUK_D(DUK_DPRINT("INITBUILTINS END"));

#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1)
	for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
		DUK_DD(DUK_DDPRINT("built-in object %ld after initialization and compacting: %!@iO",
		                   (long) i, (duk_heaphdr *) duk_require_hobject(thr, (duk_idx_t) i)));
	}
#endif

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

	duk_set_top(thr, 0);
	DUK_ASSERT_TOP(thr, 0);
}
#endif  /* DUK_USE_ROM_OBJECTS */

DUK_INTERNAL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_hthread *thr_to) {
	duk_small_uint_t i;

	for (i = 0; i < DUK_NUM_BUILTINS; i++) {
		thr_to->builtins[i] = thr_from->builtins[i];
		DUK_HOBJECT_INCREF_ALLOWNULL(thr_to, thr_to->builtins[i]);  /* side effect free */
	}
}
