changeset 66:177c7e511bcb

duktape: upgrade to 2.2.1, closes #882
author David Demelier <markand@malikania.fr>
date Fri, 13 Jul 2018 12:57:10 +0200
parents 6aeb91259841
children 01ba3c658576
files VERSION.duktape.txt duktape/duk_config.h duktape/duktape.cpp duktape/duktape.h
diffstat 4 files changed, 16090 insertions(+), 14171 deletions(-) [+]
line wrap: on
line diff
--- a/VERSION.duktape.txt	Fri Jul 13 12:53:06 2018 +0200
+++ b/VERSION.duktape.txt	Fri Jul 13 12:57:10 2018 +0200
@@ -1,1 +1,1 @@
-2.1.0
+2.2.1
--- a/duktape/duk_config.h	Fri Jul 13 12:53:06 2018 +0200
+++ b/duktape/duk_config.h	Fri Jul 13 12:57:10 2018 +0200
@@ -1,9 +1,9 @@
 /*
  *  duk_config.h configuration header generated by genconfig.py.
  *
- *  Git commit: 1f1f51a4f9595ffe8def0e9ba45b20f14679393a
- *  Git describe: v2.1.0
- *  Git branch: master
+ *  Git commit: 25420e773c5fbc50d5b46bf487fc45717e35b94f
+ *  Git describe: v2.2.1
+ *  Git branch: v2.2-maintenance
  *
  *  Supported platforms:
  *      - Mac OSX, iPhone, Darwin
@@ -525,6 +525,11 @@
 #endif
 #elif defined(DUK_F_WINDOWS)
 /* --- Windows --- */
+/* Windows version can't obviously be determined at compile time,
+ * but _WIN32_WINNT indicates the minimum version targeted:
+ * - https://msdn.microsoft.com/en-us/library/6sehtctf.aspx
+ */
+
 /* Initial fix: disable secure CRT related warnings when compiling Duktape
  * itself (must be defined before including Windows headers).  Don't define
  * for user code including duktape.h.
@@ -535,12 +540,7 @@
 
 /* Windows 32-bit and 64-bit are currently the same. */
 /* MSVC does not have sys/param.h */
-#define DUK_USE_DATE_NOW_WINDOWS
-#define DUK_USE_DATE_TZO_WINDOWS
-/* Note: PRS and FMT are intentionally left undefined for now.  This means
- * there is no platform specific date parsing/formatting but there is still
- * the ISO 8601 standard format.
- */
+
 #if defined(DUK_COMPILING_DUKTAPE)
 /* Only include when compiling Duktape to avoid polluting application build
  * with a lot of unnecessary defines.
@@ -548,6 +548,34 @@
 #include <windows.h>
 #endif
 
+/* GetSystemTimePreciseAsFileTime() available from Windows 8:
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx
+ */
+#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS) || defined(DUK_USE_DATE_NOW_WINDOWS)
+/* User forced provider. */
+#else
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)
+#define DUK_USE_DATE_NOW_WINDOWS_SUBMS
+#else
+#define DUK_USE_DATE_NOW_WINDOWS
+#endif
+#endif
+
+#define DUK_USE_DATE_TZO_WINDOWS
+
+/* Note: PRS and FMT are intentionally left undefined for now.  This means
+ * there is no platform specific date parsing/formatting but there is still
+ * the ISO 8601 standard format.
+ */
+
+/* QueryPerformanceCounter() may go backwards in Windows XP, so enable for
+ * Vista and later: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions
+ */
+#if !defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC) && \
+    defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600)
+#define DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC
+#endif
+
 #define DUK_USE_OS_STRING "windows"
 
 /* On Windows, assume we're little endian.  Even Itanium which has a
@@ -667,6 +695,10 @@
 #define DUK_USE_DATE_PRS_STRPTIME
 #define DUK_USE_DATE_FMT_STRFTIME
 
+#if 0  /* XXX: safe condition? */
+#define DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME
+#endif
+
 #define DUK_USE_OS_STRING "linux"
 #elif defined(DUK_F_SUN)
 /* --- Solaris --- */
@@ -1307,7 +1339,16 @@
 #endif
 
 /* Avoid warning when doing DUK_UNREF(some_function). */
+#if defined(_MSC_VER) && (_MSC_VER < 1500)
+#pragma warning(disable: 4100 4101 4550 4551)
+#define DUK_UNREF(x)
+#else
 #define DUK_UNREF(x)  do { __pragma(warning(suppress:4100 4101 4550 4551)) (x); } while (0)
+#endif
+
+/* Older versions of MSVC don't support the LL/ULL suffix. */
+#define DUK_U64_CONSTANT(x) x##ui64
+#define DUK_I64_CONSTANT(x) x##i64
 #elif defined(DUK_F_EMSCRIPTEN)
 /* --- Emscripten --- */
 #define DUK_NORETURN(decl)  decl __attribute__((noreturn))
@@ -1535,12 +1576,14 @@
     defined(DUK_F_BCC) || \
     (defined(__WORDSIZE) && (__WORDSIZE == 32)) || \
     ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \
-      defined(DUK_F_HPUX)) && defined(_ILP32))
+      defined(DUK_F_HPUX)) && defined(_ILP32)) || \
+    defined(DUK_F_ARM32)
 #define DUK_F_32BIT_PTRS
 #elif defined(DUK_F_X64) || \
       (defined(__WORDSIZE) && (__WORDSIZE == 64)) || \
    ((defined(DUK_F_OLD_SOLARIS) || defined(DUK_F_AIX) || \
-     defined(DUK_F_HPUX)) && defined(_LP64))
+     defined(DUK_F_HPUX)) && defined(_LP64)) || \
+    defined(DUK_F_ARM64)
 #define DUK_F_64BIT_PTRS
 #else
 /* not sure, not needed with C99 anyway */
@@ -1743,13 +1786,16 @@
 typedef signed long long duk_int64_t;
 #endif
 #endif
-#if !defined(DUK_F_HAVE_64BIT) && \
-    (defined(DUK_F_MINGW) || defined(DUK_F_MSVC))
-/* Both MinGW and MSVC have a 64-bit type. */
+#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MINGW)
 #define DUK_F_HAVE_64BIT
 typedef unsigned long duk_uint64_t;
 typedef signed long duk_int64_t;
 #endif
+#if !defined(DUK_F_HAVE_64BIT) && defined(DUK_F_MSVC)
+#define DUK_F_HAVE_64BIT
+typedef unsigned __int64 duk_uint64_t;
+typedef signed __int64 duk_int64_t;
+#endif
 #if !defined(DUK_F_HAVE_64BIT)
 /* cannot detect 64-bit type, not always needed so don't error */
 #endif
@@ -1957,10 +2003,10 @@
 #define DUK_SMALL_UINT_FAST_MIN   DUK_UINT_FAST16_MIN
 #define DUK_SMALL_UINT_FAST_MAX   DUK_UINT_FAST16_MAX
 
-/* Boolean values are represented with the platform 'int'. */
-typedef duk_small_int_t duk_bool_t;
-#define DUK_BOOL_MIN              DUK_SMALL_INT_MIN
-#define DUK_BOOL_MAX              DUK_SMALL_INT_MAX
+/* Boolean values are represented with the platform 'unsigned int'. */
+typedef duk_small_uint_t duk_bool_t;
+#define DUK_BOOL_MIN              DUK_SMALL_UINT_MIN
+#define DUK_BOOL_MAX              DUK_SMALL_UINT_MAX
 
 /* Index values must have at least 32-bit signed range. */
 typedef duk_int_t duk_idx_t;
@@ -2029,7 +2075,10 @@
 #endif
 #endif
 
-/* Type for public API calls. */
+/* Type used in public API declarations and user code.  Typedef maps to
+ * 'struct duk_hthread' like the 'duk_hthread' typedef which is used
+ * exclusively in internals.
+ */
 typedef struct duk_hthread duk_context;
 
 /* Check whether we should use 64-bit integers or not.
@@ -2676,6 +2725,13 @@
 #undef DUK_USE_GCC_PRAGMAS
 #endif
 
+#if !defined(DUK_U64_CONSTANT)
+#define DUK_U64_CONSTANT(x) x##ULL
+#endif
+#if !defined(DUK_I64_CONSTANT)
+#define DUK_I64_CONSTANT(x) x##LL
+#endif
+
 /* Workaround for GH-323: avoid inlining control when compiling from
  * multiple sources, as it causes compiler portability trouble.
  */
@@ -2773,6 +2829,9 @@
 #define DUK_USE_BUFFEROBJECT_SUPPORT
 #undef DUK_USE_BUFLEN16
 #define DUK_USE_BYTECODE_DUMP_SUPPORT
+#define DUK_USE_CACHE_ACTIVATION
+#define DUK_USE_CACHE_CATCHER
+#define DUK_USE_CALLSTACK_LIMIT 10000
 #define DUK_USE_COMMONJS_MODULES
 #define DUK_USE_COMPILER_RECLIMIT 2500
 #define DUK_USE_COROUTINE_SUPPORT
@@ -2807,7 +2866,10 @@
 #define DUK_USE_ES6_PROXY
 #define DUK_USE_ES6_REGEXP_SYNTAX
 #define DUK_USE_ES6_UNICODE_ESCAPE
+#define DUK_USE_ES7
 #define DUK_USE_ES7_EXP_OPERATOR
+#define DUK_USE_ES8
+#define DUK_USE_ES9
 #define DUK_USE_ESBC_LIMITS
 #define DUK_USE_ESBC_MAX_BYTES 2147418112L
 #define DUK_USE_ESBC_MAX_LINENUMBER 2147418112L
@@ -2822,6 +2884,7 @@
 #undef DUK_USE_FASTINT
 #define DUK_USE_FAST_REFCOUNT_DEFAULT
 #undef DUK_USE_FATAL_HANDLER
+#define DUK_USE_FATAL_MAXLEN 128
 #define DUK_USE_FINALIZER_SUPPORT
 #undef DUK_USE_FINALIZER_TORTURE
 #undef DUK_USE_FUNCPTR16
@@ -2831,6 +2894,7 @@
 #define DUK_USE_FUNC_FILENAME_PROPERTY
 #define DUK_USE_FUNC_NAME_PROPERTY
 #undef DUK_USE_GC_TORTURE
+#undef DUK_USE_GET_MONOTONIC_TIME
 #undef DUK_USE_GET_RANDOM_DOUBLE
 #undef DUK_USE_GLOBAL_BINDING
 #define DUK_USE_GLOBAL_BUILTIN
@@ -2849,6 +2913,7 @@
 #define DUK_USE_HSTRING_ARRIDX
 #define DUK_USE_HSTRING_CLEN
 #undef DUK_USE_HSTRING_EXTDATA
+#define DUK_USE_HSTRING_LAZY_CLEN
 #define DUK_USE_HTML_COMMENTS
 #define DUK_USE_IDCHAR_FASTPATH
 #undef DUK_USE_INJECT_HEAP_ALLOC_ERROR
@@ -2885,12 +2950,15 @@
 #undef DUK_USE_OBJSIZES16
 #undef DUK_USE_PARANOID_ERRORS
 #define DUK_USE_PC2LINE
+#define DUK_USE_PERFORMANCE_BUILTIN
 #undef DUK_USE_PREFER_SIZE
+#undef DUK_USE_PROMISE_BUILTIN
 #define DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS
 #undef DUK_USE_REFCOUNT16
 #define DUK_USE_REFCOUNT32
 #define DUK_USE_REFERENCE_COUNTING
 #define DUK_USE_REFLECT_BUILTIN
+#define DUK_USE_REGEXP_CANON_BITMAP
 #undef DUK_USE_REGEXP_CANON_WORKAROUND
 #define DUK_USE_REGEXP_COMPILER_RECLIMIT 10000
 #define DUK_USE_REGEXP_EXECUTOR_RECLIMIT 10000
@@ -2925,6 +2993,10 @@
 #define DUK_USE_TRACEBACKS
 #define DUK_USE_TRACEBACK_DEPTH 10
 #define DUK_USE_USER_DECLARE() /* no user declarations */
+#define DUK_USE_VALSTACK_GROW_SHIFT 2
+#define DUK_USE_VALSTACK_LIMIT 1000000L
+#define DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT 2
+#define DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT 4
 #undef DUK_USE_VALSTACK_UNSAFE
 #define DUK_USE_VERBOSE_ERRORS
 #define DUK_USE_VERBOSE_EXECUTOR_ERRORS
@@ -2952,11 +3024,13 @@
 #if defined(DUK_USE_DATE_GET_NOW)
 /* External provider already defined. */
 #elif defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
-#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_gettimeofday((ctx))
+#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_gettimeofday()
 #elif defined(DUK_USE_DATE_NOW_TIME)
-#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_time((ctx))
+#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_time()
 #elif defined(DUK_USE_DATE_NOW_WINDOWS)
-#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_windows((ctx))
+#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_windows()
+#elif defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
+#define DUK_USE_DATE_GET_NOW(ctx)            duk_bi_date_get_now_windows_subms()
 #else
 #error no provider for DUK_USE_DATE_GET_NOW()
 #endif
@@ -2992,6 +3066,16 @@
 /* No provider for DUK_USE_DATE_FORMAT_STRING(), fall back to ISO 8601 only. */
 #endif
 
+#if defined(DUK_USE_GET_MONOTONIC_TIME)
+/* External provider already defined. */
+#elif defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
+#define DUK_USE_GET_MONOTONIC_TIME(ctx)  duk_bi_date_get_monotonic_time_clock_gettime()
+#elif defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
+#define DUK_USE_GET_MONOTONIC_TIME(ctx)  duk_bi_date_get_monotonic_time_windows_qpc()
+#else
+/* No provider for DUK_USE_GET_MONOTONIC_TIME(), fall back to DUK_USE_DATE_GET_NOW(). */
+#endif
+
 #endif  /* DUK_COMPILING_DUKTAPE */
 
 /*
--- a/duktape/duktape.cpp	Fri Jul 13 12:53:06 2018 +0200
+++ b/duktape/duktape.cpp	Fri Jul 13 12:57:10 2018 +0200
@@ -1,8 +1,8 @@
 /*
- *  Single source autogenerated distributable for Duktape 2.1.0.
- *
- *  Git commit 1f1f51a4f9595ffe8def0e9ba45b20f14679393a (v2.1.0).
- *  Git branch master.
+ *  Single source autogenerated distributable for Duktape 2.2.1.
+ *
+ *  Git commit 25420e773c5fbc50d5b46bf487fc45717e35b94f (v2.2.1).
+ *  Git branch v2.2-maintenance.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  licensing information.
@@ -84,6 +84,9 @@
 *  * Remko Tron\u00e7on (https://el-tramo.be)
 *  * Romero Malaquias (rbsm@ic.ufal.br)
 *  * Michael Drake <michael.drake@codethink.co.uk>
+*  * Steven Don (https://github.com/shdon)
+*  * Simon Stone (https://github.com/sstone1)
+*  * \J. McC. (https://github.com/jmhmccr)
 *
 *  Other contributions
 *  ===================
@@ -411,47 +414,47 @@
 #if defined(DUK_USE_DOUBLE_ME)
 /* Macros for 64-bit ops + mixed endian doubles. */
 #define DUK__DBLUNION_SET_NAN_FULL(u)  do { \
-		(u)->ull[DUK_DBL_IDX_ULL0] = 0x000000007ff80000ULL; \
+		(u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x000000007ff80000); \
 	} while (0)
 #define DUK__DBLUNION_IS_NAN_FULL(u) \
-	((((u)->ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL) == 0x000000007ff00000ULL) && \
-	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & 0xffffffff000fffffULL) != 0))
+	((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000)) && \
+	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0xffffffff000fffff)) != 0))
 #define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff80000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff80000))
 #define DUK__DBLUNION_IS_ANYINF(u) \
-	(((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x000000007ff00000ULL)
+	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x000000007ff00000))
 #define DUK__DBLUNION_IS_POSINF(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff00000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x000000007ff00000))
 #define DUK__DBLUNION_IS_NEGINF(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x00000000fff00000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x00000000fff00000))
 #define DUK__DBLUNION_IS_ANYZERO(u) \
-	(((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x0000000000000000ULL)
+	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0xffffffff7fffffff)) == DUK_U64_CONSTANT(0x0000000000000000))
 #define DUK__DBLUNION_IS_POSZERO(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000))
 #define DUK__DBLUNION_IS_NEGZERO(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000080000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000080000000))
 #else
 /* Macros for 64-bit ops + big/little endian doubles. */
 #define DUK__DBLUNION_SET_NAN_FULL(u)  do { \
-		(u)->ull[DUK_DBL_IDX_ULL0] = 0x7ff8000000000000ULL; \
+		(u)->ull[DUK_DBL_IDX_ULL0] = DUK_U64_CONSTANT(0x7ff8000000000000); \
 	} while (0)
 #define DUK__DBLUNION_IS_NAN_FULL(u) \
-	((((u)->ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL) == 0x7ff0000000000000UL) && \
-	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & 0x000fffffffffffffULL) != 0))
+	((((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000)) && \
+	 ((((u)->ull[DUK_DBL_IDX_ULL0]) & DUK_U64_CONSTANT(0x000fffffffffffff)) != 0))
 #define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff8000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff8000000000000))
 #define DUK__DBLUNION_IS_ANYINF(u) \
-	(((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x7ff0000000000000ULL)
+	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x7ff0000000000000))
 #define DUK__DBLUNION_IS_POSINF(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff0000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x7ff0000000000000))
 #define DUK__DBLUNION_IS_NEGINF(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0xfff0000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0xfff0000000000000))
 #define DUK__DBLUNION_IS_ANYZERO(u) \
-	(((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x0000000000000000ULL)
+	(((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7fffffffffffffff)) == DUK_U64_CONSTANT(0x0000000000000000))
 #define DUK__DBLUNION_IS_POSZERO(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x0000000000000000))
 #define DUK__DBLUNION_IS_NEGZERO(u) \
-	((u)->ull[DUK_DBL_IDX_ULL0] == 0x8000000000000000ULL)
+	((u)->ull[DUK_DBL_IDX_ULL0] == DUK_U64_CONSTANT(0x8000000000000000))
 #endif
 #else  /* DUK_USE_64BIT_OPS */
 /* Macros for no 64-bit ops, any endianness. */
@@ -598,7 +601,7 @@
 
 /* Some sign bit helpers. */
 #if defined(DUK_USE_64BIT_OPS)
-#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] & 0x8000000000000000ULL) != 0)
+#define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000)) != 0)
 #define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ull[DUK_DBL_IDX_ULL0] >> 63U))
 #else
 #define DUK_DBLUNION_HAS_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] & 0x80000000UL) != 0)
@@ -711,10 +714,12 @@
 struct duk_hobject;
 struct duk_hcompfunc;
 struct duk_hnatfunc;
+struct duk_hboundfunc;
 struct duk_hthread;
 struct duk_hbufobj;
 struct duk_hdecenv;
 struct duk_hobjenv;
+struct duk_hproxy;
 struct duk_hbuffer;
 struct duk_hbuffer_fixed;
 struct duk_hbuffer_dynamic;
@@ -769,10 +774,12 @@
 typedef struct duk_hobject duk_hobject;
 typedef struct duk_hcompfunc duk_hcompfunc;
 typedef struct duk_hnatfunc duk_hnatfunc;
+typedef struct duk_hboundfunc duk_hboundfunc;
 typedef struct duk_hthread duk_hthread;
 typedef struct duk_hbufobj duk_hbufobj;
 typedef struct duk_hdecenv duk_hdecenv;
 typedef struct duk_hobjenv duk_hobjenv;
+typedef struct duk_hproxy duk_hproxy;
 typedef struct duk_hbuffer duk_hbuffer;
 typedef struct duk_hbuffer_fixed duk_hbuffer_fixed;
 typedef struct duk_hbuffer_dynamic duk_hbuffer_dynamic;
@@ -948,7 +955,7 @@
 	} while (0)
 #else
 #define DUK__TVAL_SET_I48(tv,i)  do { \
-		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & 0x0000ffffffffffffULL); \
+		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (((duk_uint64_t) (i)) & DUK_U64_CONSTANT(0x0000ffffffffffff)); \
 	} while (0)
 #define DUK__TVAL_SET_U32(tv,i)  do { \
 		(tv)->ull[DUK_DBL_IDX_ULL0] = (((duk_uint64_t) DUK_TAG_FASTINT) << 48) | (duk_uint64_t) (i); \
@@ -1045,7 +1052,7 @@
 #define DUK_TVAL_SET_TVAL(tv,x)              do { *(tv) = *(x); } while (0)
 
 /* getters */
-#define DUK_TVAL_GET_BOOLEAN(tv)             ((duk_small_int_t) (tv)->us[DUK_DBL_IDX_US1])
+#define DUK_TVAL_GET_BOOLEAN(tv)             ((duk_small_uint_t) (tv)->us[DUK_DBL_IDX_US1])
 #if defined(DUK_USE_FASTINT)
 #define DUK_TVAL_GET_DOUBLE(tv)              ((tv)->d)
 #define DUK_TVAL_GET_FASTINT(tv)             DUK__TVAL_GET_FASTINT((tv))
@@ -1061,7 +1068,7 @@
 		(out_fp) = (duk_c_function) (tv)->ui[DUK_DBL_IDX_UI1]; \
 	} while (0)
 #define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv)   ((duk_c_function) ((tv)->ui[DUK_DBL_IDX_UI1]))
-#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)     (((duk_small_int_t) (tv)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL)
+#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)     (((duk_small_uint_t) (tv)->ui[DUK_DBL_IDX_UI0]) & 0xffffUL)
 #define DUK_TVAL_GET_STRING(tv)              ((duk_hstring *) (tv)->vp[DUK_DBL_IDX_VP1])
 #define DUK_TVAL_GET_OBJECT(tv)              ((duk_hobject *) (tv)->vp[DUK_DBL_IDX_VP1])
 #define DUK_TVAL_GET_BUFFER(tv)              ((duk_hbuffer *) (tv)->vp[DUK_DBL_IDX_VP1])
@@ -1199,7 +1206,7 @@
 		duk_tval *duk__tv; \
 		duk__tv = (tv); \
 		duk__tv->t = DUK_TAG_BOOLEAN; \
-		duk__tv->v.i = (val); \
+		duk__tv->v.i = (duk_small_int_t) (val); \
 	} while (0)
 
 #if defined(DUK_USE_FASTINT)
@@ -1330,7 +1337,7 @@
 #define DUK_TVAL_SET_TVAL(tv,x)            do { *(tv) = *(x); } while (0)
 
 /* getters */
-#define DUK_TVAL_GET_BOOLEAN(tv)           ((tv)->v.i)
+#define DUK_TVAL_GET_BOOLEAN(tv)           ((duk_small_uint_t) (tv)->v.i)
 #if defined(DUK_USE_FASTINT)
 #define DUK_TVAL_GET_DOUBLE(tv)            ((tv)->v.d)
 #define DUK_TVAL_GET_FASTINT(tv)           ((tv)->v.fi)
@@ -1357,7 +1364,7 @@
 		(out_fp) = (tv)->v.lightfunc; \
 	} while (0)
 #define DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv) ((tv)->v.lightfunc)
-#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)   ((duk_uint32_t) ((tv)->v_extra))
+#define DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv)   ((duk_small_uint_t) ((tv)->v_extra))
 #define DUK_TVAL_GET_STRING(tv)            ((tv)->v.hstring)
 #define DUK_TVAL_GET_OBJECT(tv)            ((tv)->v.hobject)
 #define DUK_TVAL_GET_BUFFER(tv)            ((tv)->v.hbuffer)
@@ -1422,11 +1429,11 @@
 #define DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags) \
 	((duk_int32_t) (duk_int8_t) (((duk_uint16_t) (lf_flags)) >> 8))
 #define DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags) \
-	(((lf_flags) >> 4) & 0x0f)
+	(((lf_flags) >> 4) & 0x0fU)
 #define DUK_LFUNC_FLAGS_GET_NARGS(lf_flags) \
-	((lf_flags) & 0x0f)
+	((lf_flags) & 0x0fU)
 #define DUK_LFUNC_FLAGS_PACK(magic,length,nargs) \
-	(((magic) & 0xff) << 8) | ((length) << 4) | (nargs)
+	((((duk_small_uint_t) (magic)) & 0xffU) << 8) | ((length) << 4) | (nargs)
 
 #define DUK_LFUNC_NARGS_VARARGS             0x0f   /* varargs marker */
 #define DUK_LFUNC_NARGS_MIN                 0x00
@@ -1438,8 +1445,8 @@
 
 /* fastint constants etc */
 #if defined(DUK_USE_FASTINT)
-#define DUK_FASTINT_MIN           (-0x800000000000LL)
-#define DUK_FASTINT_MAX           0x7fffffffffffLL
+#define DUK_FASTINT_MIN           (DUK_I64_CONSTANT(-0x800000000000))
+#define DUK_FASTINT_MAX           (DUK_I64_CONSTANT(0x7fffffffffff))
 #define DUK_FASTINT_BITS          48
 
 DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_fast(duk_tval *tv, duk_double_t x);
@@ -1669,302 +1676,296 @@
 #define DUK_STRIDX_CALLER                                             69                             /* 'caller' */
 #define DUK_HEAP_STRING_CALLER(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CALLER)
 #define DUK_HTHREAD_STRING_CALLER(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CALLER)
-#define DUK_STRIDX_DELETE_PROPERTY                                    70                             /* 'deleteProperty' */
+#define DUK_STRIDX_APPLY                                              70                             /* 'apply' */
+#define DUK_HEAP_STRING_APPLY(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_APPLY)
+#define DUK_HTHREAD_STRING_APPLY(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_APPLY)
+#define DUK_STRIDX_CONSTRUCT                                          71                             /* 'construct' */
+#define DUK_HEAP_STRING_CONSTRUCT(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONSTRUCT)
+#define DUK_HTHREAD_STRING_CONSTRUCT(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONSTRUCT)
+#define DUK_STRIDX_DELETE_PROPERTY                                    72                             /* 'deleteProperty' */
 #define DUK_HEAP_STRING_DELETE_PROPERTY(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE_PROPERTY)
 #define DUK_HTHREAD_STRING_DELETE_PROPERTY(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE_PROPERTY)
-#define DUK_STRIDX_GET                                                71                             /* 'get' */
+#define DUK_STRIDX_GET                                                73                             /* 'get' */
 #define DUK_HEAP_STRING_GET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_GET)
 #define DUK_HTHREAD_STRING_GET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_GET)
-#define DUK_STRIDX_HAS                                                72                             /* 'has' */
+#define DUK_STRIDX_HAS                                                74                             /* 'has' */
 #define DUK_HEAP_STRING_HAS(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HAS)
 #define DUK_HTHREAD_STRING_HAS(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HAS)
-#define DUK_STRIDX_OWN_KEYS                                           73                             /* 'ownKeys' */
+#define DUK_STRIDX_OWN_KEYS                                           75                             /* 'ownKeys' */
 #define DUK_HEAP_STRING_OWN_KEYS(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_OWN_KEYS)
 #define DUK_HTHREAD_STRING_OWN_KEYS(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_OWN_KEYS)
-#define DUK_STRIDX_SET_PROTOTYPE_OF                                   74                             /* 'setPrototypeOf' */
+#define DUK_STRIDX_SET_PROTOTYPE_OF                                   76                             /* 'setPrototypeOf' */
 #define DUK_HEAP_STRING_SET_PROTOTYPE_OF(heap)                        DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET_PROTOTYPE_OF)
 #define DUK_HTHREAD_STRING_SET_PROTOTYPE_OF(thr)                      DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET_PROTOTYPE_OF)
-#define DUK_STRIDX___PROTO__                                          75                             /* '__proto__' */
+#define DUK_STRIDX___PROTO__                                          77                             /* '__proto__' */
 #define DUK_HEAP_STRING___PROTO__(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX___PROTO__)
 #define DUK_HTHREAD_STRING___PROTO__(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX___PROTO__)
-#define DUK_STRIDX_TO_STRING                                          76                             /* 'toString' */
+#define DUK_STRIDX_TO_STRING                                          78                             /* 'toString' */
 #define DUK_HEAP_STRING_TO_STRING(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_STRING)
 #define DUK_HTHREAD_STRING_TO_STRING(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_STRING)
-#define DUK_STRIDX_TO_JSON                                            77                             /* 'toJSON' */
+#define DUK_STRIDX_TO_JSON                                            79                             /* 'toJSON' */
 #define DUK_HEAP_STRING_TO_JSON(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TO_JSON)
 #define DUK_HTHREAD_STRING_TO_JSON(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TO_JSON)
-#define DUK_STRIDX_TYPE                                               78                             /* 'type' */
+#define DUK_STRIDX_TYPE                                               80                             /* 'type' */
 #define DUK_HEAP_STRING_TYPE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPE)
 #define DUK_HTHREAD_STRING_TYPE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPE)
-#define DUK_STRIDX_DATA                                               79                             /* 'data' */
+#define DUK_STRIDX_DATA                                               81                             /* 'data' */
 #define DUK_HEAP_STRING_DATA(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DATA)
 #define DUK_HTHREAD_STRING_DATA(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DATA)
-#define DUK_STRIDX_LENGTH                                             80                             /* 'length' */
+#define DUK_STRIDX_LENGTH                                             82                             /* 'length' */
 #define DUK_HEAP_STRING_LENGTH(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LENGTH)
 #define DUK_HTHREAD_STRING_LENGTH(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LENGTH)
-#define DUK_STRIDX_SET                                                81                             /* 'set' */
+#define DUK_STRIDX_SET                                                83                             /* 'set' */
 #define DUK_HEAP_STRING_SET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SET)
 #define DUK_HTHREAD_STRING_SET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SET)
-#define DUK_STRIDX_STACK                                              82                             /* 'stack' */
+#define DUK_STRIDX_STACK                                              84                             /* 'stack' */
 #define DUK_HEAP_STRING_STACK(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STACK)
 #define DUK_HTHREAD_STRING_STACK(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STACK)
-#define DUK_STRIDX_PC                                                 83                             /* 'pc' */
+#define DUK_STRIDX_PC                                                 85                             /* 'pc' */
 #define DUK_HEAP_STRING_PC(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PC)
 #define DUK_HTHREAD_STRING_PC(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PC)
-#define DUK_STRIDX_LINE_NUMBER                                        84                             /* 'lineNumber' */
+#define DUK_STRIDX_LINE_NUMBER                                        86                             /* 'lineNumber' */
 #define DUK_HEAP_STRING_LINE_NUMBER(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LINE_NUMBER)
 #define DUK_HTHREAD_STRING_LINE_NUMBER(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LINE_NUMBER)
-#define DUK_STRIDX_INT_TRACEDATA                                      85                             /* '\xffTracedata' */
+#define DUK_STRIDX_INT_TRACEDATA                                      87                             /* '\x82Tracedata' */
 #define DUK_HEAP_STRING_INT_TRACEDATA(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TRACEDATA)
 #define DUK_HTHREAD_STRING_INT_TRACEDATA(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TRACEDATA)
-#define DUK_STRIDX_NAME                                               86                             /* 'name' */
+#define DUK_STRIDX_NAME                                               88                             /* 'name' */
 #define DUK_HEAP_STRING_NAME(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NAME)
 #define DUK_HTHREAD_STRING_NAME(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NAME)
-#define DUK_STRIDX_FILE_NAME                                          87                             /* 'fileName' */
+#define DUK_STRIDX_FILE_NAME                                          89                             /* 'fileName' */
 #define DUK_HEAP_STRING_FILE_NAME(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FILE_NAME)
 #define DUK_HTHREAD_STRING_FILE_NAME(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FILE_NAME)
-#define DUK_STRIDX_LC_POINTER                                         88                             /* 'pointer' */
+#define DUK_STRIDX_LC_POINTER                                         90                             /* 'pointer' */
 #define DUK_HEAP_STRING_LC_POINTER(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_POINTER)
 #define DUK_HTHREAD_STRING_LC_POINTER(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_POINTER)
-#define DUK_STRIDX_INT_VALUE                                          89                             /* '\xffValue' */
-#define DUK_HEAP_STRING_INT_VALUE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE)
-#define DUK_HTHREAD_STRING_INT_VALUE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE)
-#define DUK_STRIDX_INT_NEXT                                           90                             /* '\xffNext' */
+#define DUK_STRIDX_INT_TARGET                                         91                             /* '\x82Target' */
+#define DUK_HEAP_STRING_INT_TARGET(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TARGET)
+#define DUK_HTHREAD_STRING_INT_TARGET(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TARGET)
+#define DUK_STRIDX_INT_NEXT                                           92                             /* '\x82Next' */
 #define DUK_HEAP_STRING_INT_NEXT(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_NEXT)
 #define DUK_HTHREAD_STRING_INT_NEXT(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_NEXT)
-#define DUK_STRIDX_INT_BYTECODE                                       91                             /* '\xffBytecode' */
+#define DUK_STRIDX_INT_BYTECODE                                       93                             /* '\x82Bytecode' */
 #define DUK_HEAP_STRING_INT_BYTECODE(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_BYTECODE)
 #define DUK_HTHREAD_STRING_INT_BYTECODE(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_BYTECODE)
-#define DUK_STRIDX_INT_FORMALS                                        92                             /* '\xffFormals' */
+#define DUK_STRIDX_INT_FORMALS                                        94                             /* '\x82Formals' */
 #define DUK_HEAP_STRING_INT_FORMALS(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FORMALS)
 #define DUK_HTHREAD_STRING_INT_FORMALS(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FORMALS)
-#define DUK_STRIDX_INT_VARMAP                                         93                             /* '\xffVarmap' */
+#define DUK_STRIDX_INT_VARMAP                                         95                             /* '\x82Varmap' */
 #define DUK_HEAP_STRING_INT_VARMAP(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARMAP)
 #define DUK_HTHREAD_STRING_INT_VARMAP(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARMAP)
-#define DUK_STRIDX_INT_SOURCE                                         94                             /* '\xffSource' */
+#define DUK_STRIDX_INT_SOURCE                                         96                             /* '\x82Source' */
 #define DUK_HEAP_STRING_INT_SOURCE(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_SOURCE)
 #define DUK_HTHREAD_STRING_INT_SOURCE(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_SOURCE)
-#define DUK_STRIDX_INT_PC2LINE                                        95                             /* '\xffPc2line' */
+#define DUK_STRIDX_INT_PC2LINE                                        97                             /* '\x82Pc2line' */
 #define DUK_HEAP_STRING_INT_PC2LINE(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_PC2LINE)
 #define DUK_HTHREAD_STRING_INT_PC2LINE(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_PC2LINE)
-#define DUK_STRIDX_INT_THIS                                           96                             /* '\xffThis' */
-#define DUK_HEAP_STRING_INT_THIS(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_THIS)
-#define DUK_HTHREAD_STRING_INT_THIS(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_THIS)
-#define DUK_STRIDX_INT_ARGS                                           97                             /* '\xffArgs' */
-#define DUK_HEAP_STRING_INT_ARGS(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_ARGS)
-#define DUK_HTHREAD_STRING_INT_ARGS(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_ARGS)
-#define DUK_STRIDX_INT_MAP                                            98                             /* '\xffMap' */
+#define DUK_STRIDX_INT_MAP                                            98                             /* '\x82Map' */
 #define DUK_HEAP_STRING_INT_MAP(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_MAP)
 #define DUK_HTHREAD_STRING_INT_MAP(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_MAP)
-#define DUK_STRIDX_INT_VARENV                                         99                             /* '\xffVarenv' */
+#define DUK_STRIDX_INT_VARENV                                         99                             /* '\x82Varenv' */
 #define DUK_HEAP_STRING_INT_VARENV(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VARENV)
 #define DUK_HTHREAD_STRING_INT_VARENV(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VARENV)
-#define DUK_STRIDX_INT_FINALIZER                                      100                            /* '\xffFinalizer' */
+#define DUK_STRIDX_INT_FINALIZER                                      100                            /* '\x82Finalizer' */
 #define DUK_HEAP_STRING_INT_FINALIZER(heap)                           DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_FINALIZER)
 #define DUK_HTHREAD_STRING_INT_FINALIZER(thr)                         DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_FINALIZER)
-#define DUK_STRIDX_INT_TARGET                                         101                            /* '\xffTarget' */
-#define DUK_HEAP_STRING_INT_TARGET(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_TARGET)
-#define DUK_HTHREAD_STRING_INT_TARGET(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_TARGET)
-#define DUK_STRIDX_INT_HANDLER                                        102                            /* '\xffHandler' */
-#define DUK_HEAP_STRING_INT_HANDLER(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_HANDLER)
-#define DUK_HTHREAD_STRING_INT_HANDLER(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_HANDLER)
-#define DUK_STRIDX_COMPILE                                            103                            /* 'compile' */
+#define DUK_STRIDX_INT_VALUE                                          101                            /* '\x82Value' */
+#define DUK_HEAP_STRING_INT_VALUE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_VALUE)
+#define DUK_HTHREAD_STRING_INT_VALUE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_VALUE)
+#define DUK_STRIDX_COMPILE                                            102                            /* 'compile' */
 #define DUK_HEAP_STRING_COMPILE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_COMPILE)
 #define DUK_HTHREAD_STRING_COMPILE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_COMPILE)
-#define DUK_STRIDX_INPUT                                              104                            /* 'input' */
+#define DUK_STRIDX_INPUT                                              103                            /* 'input' */
 #define DUK_HEAP_STRING_INPUT(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INPUT)
 #define DUK_HTHREAD_STRING_INPUT(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INPUT)
-#define DUK_STRIDX_ERR_CREATE                                         105                            /* 'errCreate' */
+#define DUK_STRIDX_ERR_CREATE                                         104                            /* 'errCreate' */
 #define DUK_HEAP_STRING_ERR_CREATE(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_CREATE)
 #define DUK_HTHREAD_STRING_ERR_CREATE(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_CREATE)
-#define DUK_STRIDX_ERR_THROW                                          106                            /* 'errThrow' */
+#define DUK_STRIDX_ERR_THROW                                          105                            /* 'errThrow' */
 #define DUK_HEAP_STRING_ERR_THROW(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ERR_THROW)
 #define DUK_HTHREAD_STRING_ERR_THROW(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ERR_THROW)
-#define DUK_STRIDX_ENV                                                107                            /* 'env' */
+#define DUK_STRIDX_ENV                                                106                            /* 'env' */
 #define DUK_HEAP_STRING_ENV(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENV)
 #define DUK_HTHREAD_STRING_ENV(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENV)
-#define DUK_STRIDX_HEX                                                108                            /* 'hex' */
+#define DUK_STRIDX_HEX                                                107                            /* 'hex' */
 #define DUK_HEAP_STRING_HEX(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_HEX)
 #define DUK_HTHREAD_STRING_HEX(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_HEX)
-#define DUK_STRIDX_BASE64                                             109                            /* 'base64' */
+#define DUK_STRIDX_BASE64                                             108                            /* 'base64' */
 #define DUK_HEAP_STRING_BASE64(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BASE64)
 #define DUK_HTHREAD_STRING_BASE64(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BASE64)
-#define DUK_STRIDX_JX                                                 110                            /* 'jx' */
+#define DUK_STRIDX_JX                                                 109                            /* 'jx' */
 #define DUK_HEAP_STRING_JX(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JX)
 #define DUK_HTHREAD_STRING_JX(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JX)
-#define DUK_STRIDX_JC                                                 111                            /* 'jc' */
+#define DUK_STRIDX_JC                                                 110                            /* 'jc' */
 #define DUK_HEAP_STRING_JC(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JC)
 #define DUK_HTHREAD_STRING_JC(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JC)
-#define DUK_STRIDX_RESUME                                             112                            /* 'resume' */
-#define DUK_HEAP_STRING_RESUME(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RESUME)
-#define DUK_HTHREAD_STRING_RESUME(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RESUME)
-#define DUK_STRIDX_JSON_EXT_UNDEFINED                                 113                            /* '{"_undef":true}' */
+#define DUK_STRIDX_JSON_EXT_UNDEFINED                                 111                            /* '{"_undef":true}' */
 #define DUK_HEAP_STRING_JSON_EXT_UNDEFINED(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_UNDEFINED)
 #define DUK_HTHREAD_STRING_JSON_EXT_UNDEFINED(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_UNDEFINED)
-#define DUK_STRIDX_JSON_EXT_NAN                                       114                            /* '{"_nan":true}' */
+#define DUK_STRIDX_JSON_EXT_NAN                                       112                            /* '{"_nan":true}' */
 #define DUK_HEAP_STRING_JSON_EXT_NAN(heap)                            DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NAN)
 #define DUK_HTHREAD_STRING_JSON_EXT_NAN(thr)                          DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NAN)
-#define DUK_STRIDX_JSON_EXT_POSINF                                    115                            /* '{"_inf":true}' */
+#define DUK_STRIDX_JSON_EXT_POSINF                                    113                            /* '{"_inf":true}' */
 #define DUK_HEAP_STRING_JSON_EXT_POSINF(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_POSINF)
 #define DUK_HTHREAD_STRING_JSON_EXT_POSINF(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_POSINF)
-#define DUK_STRIDX_JSON_EXT_NEGINF                                    116                            /* '{"_ninf":true}' */
+#define DUK_STRIDX_JSON_EXT_NEGINF                                    114                            /* '{"_ninf":true}' */
 #define DUK_HEAP_STRING_JSON_EXT_NEGINF(heap)                         DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_NEGINF)
 #define DUK_HTHREAD_STRING_JSON_EXT_NEGINF(thr)                       DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_NEGINF)
-#define DUK_STRIDX_JSON_EXT_FUNCTION1                                 117                            /* '{"_func":true}' */
+#define DUK_STRIDX_JSON_EXT_FUNCTION1                                 115                            /* '{"_func":true}' */
 #define DUK_HEAP_STRING_JSON_EXT_FUNCTION1(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION1)
 #define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION1(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION1)
-#define DUK_STRIDX_JSON_EXT_FUNCTION2                                 118                            /* '{_func:true}' */
+#define DUK_STRIDX_JSON_EXT_FUNCTION2                                 116                            /* '{_func:true}' */
 #define DUK_HEAP_STRING_JSON_EXT_FUNCTION2(heap)                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_JSON_EXT_FUNCTION2)
 #define DUK_HTHREAD_STRING_JSON_EXT_FUNCTION2(thr)                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_JSON_EXT_FUNCTION2)
-#define DUK_STRIDX_BREAK                                              119                            /* 'break' */
+#define DUK_STRIDX_BREAK                                              117                            /* 'break' */
 #define DUK_HEAP_STRING_BREAK(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_BREAK)
 #define DUK_HTHREAD_STRING_BREAK(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_BREAK)
-#define DUK_STRIDX_CASE                                               120                            /* 'case' */
+#define DUK_STRIDX_CASE                                               118                            /* 'case' */
 #define DUK_HEAP_STRING_CASE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CASE)
 #define DUK_HTHREAD_STRING_CASE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CASE)
-#define DUK_STRIDX_CATCH                                              121                            /* 'catch' */
+#define DUK_STRIDX_CATCH                                              119                            /* 'catch' */
 #define DUK_HEAP_STRING_CATCH(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CATCH)
 #define DUK_HTHREAD_STRING_CATCH(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CATCH)
-#define DUK_STRIDX_CONTINUE                                           122                            /* 'continue' */
+#define DUK_STRIDX_CONTINUE                                           120                            /* 'continue' */
 #define DUK_HEAP_STRING_CONTINUE(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONTINUE)
 #define DUK_HTHREAD_STRING_CONTINUE(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONTINUE)
-#define DUK_STRIDX_DEBUGGER                                           123                            /* 'debugger' */
+#define DUK_STRIDX_DEBUGGER                                           121                            /* 'debugger' */
 #define DUK_HEAP_STRING_DEBUGGER(heap)                                DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEBUGGER)
 #define DUK_HTHREAD_STRING_DEBUGGER(thr)                              DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEBUGGER)
-#define DUK_STRIDX_DEFAULT                                            124                            /* 'default' */
+#define DUK_STRIDX_DEFAULT                                            122                            /* 'default' */
 #define DUK_HEAP_STRING_DEFAULT(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DEFAULT)
 #define DUK_HTHREAD_STRING_DEFAULT(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DEFAULT)
-#define DUK_STRIDX_DELETE                                             125                            /* 'delete' */
+#define DUK_STRIDX_DELETE                                             123                            /* 'delete' */
 #define DUK_HEAP_STRING_DELETE(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DELETE)
 #define DUK_HTHREAD_STRING_DELETE(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DELETE)
-#define DUK_STRIDX_DO                                                 126                            /* 'do' */
+#define DUK_STRIDX_DO                                                 124                            /* 'do' */
 #define DUK_HEAP_STRING_DO(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_DO)
 #define DUK_HTHREAD_STRING_DO(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_DO)
-#define DUK_STRIDX_ELSE                                               127                            /* 'else' */
+#define DUK_STRIDX_ELSE                                               125                            /* 'else' */
 #define DUK_HEAP_STRING_ELSE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ELSE)
 #define DUK_HTHREAD_STRING_ELSE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ELSE)
-#define DUK_STRIDX_FINALLY                                            128                            /* 'finally' */
+#define DUK_STRIDX_FINALLY                                            126                            /* 'finally' */
 #define DUK_HEAP_STRING_FINALLY(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FINALLY)
 #define DUK_HTHREAD_STRING_FINALLY(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FINALLY)
-#define DUK_STRIDX_FOR                                                129                            /* 'for' */
+#define DUK_STRIDX_FOR                                                127                            /* 'for' */
 #define DUK_HEAP_STRING_FOR(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FOR)
 #define DUK_HTHREAD_STRING_FOR(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FOR)
-#define DUK_STRIDX_LC_FUNCTION                                        130                            /* 'function' */
+#define DUK_STRIDX_LC_FUNCTION                                        128                            /* 'function' */
 #define DUK_HEAP_STRING_LC_FUNCTION(heap)                             DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_FUNCTION)
 #define DUK_HTHREAD_STRING_LC_FUNCTION(thr)                           DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_FUNCTION)
-#define DUK_STRIDX_IF                                                 131                            /* 'if' */
+#define DUK_STRIDX_IF                                                 129                            /* 'if' */
 #define DUK_HEAP_STRING_IF(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IF)
 #define DUK_HTHREAD_STRING_IF(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IF)
-#define DUK_STRIDX_IN                                                 132                            /* 'in' */
+#define DUK_STRIDX_IN                                                 130                            /* 'in' */
 #define DUK_HEAP_STRING_IN(heap)                                      DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IN)
 #define DUK_HTHREAD_STRING_IN(thr)                                    DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IN)
-#define DUK_STRIDX_INSTANCEOF                                         133                            /* 'instanceof' */
+#define DUK_STRIDX_INSTANCEOF                                         131                            /* 'instanceof' */
 #define DUK_HEAP_STRING_INSTANCEOF(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INSTANCEOF)
 #define DUK_HTHREAD_STRING_INSTANCEOF(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INSTANCEOF)
-#define DUK_STRIDX_NEW                                                134                            /* 'new' */
+#define DUK_STRIDX_NEW                                                132                            /* 'new' */
 #define DUK_HEAP_STRING_NEW(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_NEW)
 #define DUK_HTHREAD_STRING_NEW(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_NEW)
-#define DUK_STRIDX_RETURN                                             135                            /* 'return' */
+#define DUK_STRIDX_RETURN                                             133                            /* 'return' */
 #define DUK_HEAP_STRING_RETURN(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_RETURN)
 #define DUK_HTHREAD_STRING_RETURN(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_RETURN)
-#define DUK_STRIDX_SWITCH                                             136                            /* 'switch' */
+#define DUK_STRIDX_SWITCH                                             134                            /* 'switch' */
 #define DUK_HEAP_STRING_SWITCH(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SWITCH)
 #define DUK_HTHREAD_STRING_SWITCH(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SWITCH)
-#define DUK_STRIDX_THIS                                               137                            /* 'this' */
+#define DUK_STRIDX_THIS                                               135                            /* 'this' */
 #define DUK_HEAP_STRING_THIS(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THIS)
 #define DUK_HTHREAD_STRING_THIS(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THIS)
-#define DUK_STRIDX_THROW                                              138                            /* 'throw' */
+#define DUK_STRIDX_THROW                                              136                            /* 'throw' */
 #define DUK_HEAP_STRING_THROW(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_THROW)
 #define DUK_HTHREAD_STRING_THROW(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_THROW)
-#define DUK_STRIDX_TRY                                                139                            /* 'try' */
+#define DUK_STRIDX_TRY                                                137                            /* 'try' */
 #define DUK_HEAP_STRING_TRY(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRY)
 #define DUK_HTHREAD_STRING_TRY(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRY)
-#define DUK_STRIDX_TYPEOF                                             140                            /* 'typeof' */
+#define DUK_STRIDX_TYPEOF                                             138                            /* 'typeof' */
 #define DUK_HEAP_STRING_TYPEOF(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TYPEOF)
 #define DUK_HTHREAD_STRING_TYPEOF(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TYPEOF)
-#define DUK_STRIDX_VAR                                                141                            /* 'var' */
+#define DUK_STRIDX_VAR                                                139                            /* 'var' */
 #define DUK_HEAP_STRING_VAR(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VAR)
 #define DUK_HTHREAD_STRING_VAR(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VAR)
-#define DUK_STRIDX_CONST                                              142                            /* 'const' */
+#define DUK_STRIDX_CONST                                              140                            /* 'const' */
 #define DUK_HEAP_STRING_CONST(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CONST)
 #define DUK_HTHREAD_STRING_CONST(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CONST)
-#define DUK_STRIDX_VOID                                               143                            /* 'void' */
+#define DUK_STRIDX_VOID                                               141                            /* 'void' */
 #define DUK_HEAP_STRING_VOID(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_VOID)
 #define DUK_HTHREAD_STRING_VOID(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_VOID)
-#define DUK_STRIDX_WHILE                                              144                            /* 'while' */
+#define DUK_STRIDX_WHILE                                              142                            /* 'while' */
 #define DUK_HEAP_STRING_WHILE(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WHILE)
 #define DUK_HTHREAD_STRING_WHILE(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WHILE)
-#define DUK_STRIDX_WITH                                               145                            /* 'with' */
+#define DUK_STRIDX_WITH                                               143                            /* 'with' */
 #define DUK_HEAP_STRING_WITH(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_WITH)
 #define DUK_HTHREAD_STRING_WITH(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_WITH)
-#define DUK_STRIDX_CLASS                                              146                            /* 'class' */
+#define DUK_STRIDX_CLASS                                              144                            /* 'class' */
 #define DUK_HEAP_STRING_CLASS(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_CLASS)
 #define DUK_HTHREAD_STRING_CLASS(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_CLASS)
-#define DUK_STRIDX_ENUM                                               147                            /* 'enum' */
+#define DUK_STRIDX_ENUM                                               145                            /* 'enum' */
 #define DUK_HEAP_STRING_ENUM(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_ENUM)
 #define DUK_HTHREAD_STRING_ENUM(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_ENUM)
-#define DUK_STRIDX_EXPORT                                             148                            /* 'export' */
+#define DUK_STRIDX_EXPORT                                             146                            /* 'export' */
 #define DUK_HEAP_STRING_EXPORT(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXPORT)
 #define DUK_HTHREAD_STRING_EXPORT(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXPORT)
-#define DUK_STRIDX_EXTENDS                                            149                            /* 'extends' */
+#define DUK_STRIDX_EXTENDS                                            147                            /* 'extends' */
 #define DUK_HEAP_STRING_EXTENDS(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_EXTENDS)
 #define DUK_HTHREAD_STRING_EXTENDS(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_EXTENDS)
-#define DUK_STRIDX_IMPORT                                             150                            /* 'import' */
+#define DUK_STRIDX_IMPORT                                             148                            /* 'import' */
 #define DUK_HEAP_STRING_IMPORT(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPORT)
 #define DUK_HTHREAD_STRING_IMPORT(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPORT)
-#define DUK_STRIDX_SUPER                                              151                            /* 'super' */
+#define DUK_STRIDX_SUPER                                              149                            /* 'super' */
 #define DUK_HEAP_STRING_SUPER(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_SUPER)
 #define DUK_HTHREAD_STRING_SUPER(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_SUPER)
-#define DUK_STRIDX_LC_NULL                                            152                            /* 'null' */
+#define DUK_STRIDX_LC_NULL                                            150                            /* 'null' */
 #define DUK_HEAP_STRING_LC_NULL(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LC_NULL)
 #define DUK_HTHREAD_STRING_LC_NULL(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LC_NULL)
-#define DUK_STRIDX_TRUE                                               153                            /* 'true' */
+#define DUK_STRIDX_TRUE                                               151                            /* 'true' */
 #define DUK_HEAP_STRING_TRUE(heap)                                    DUK_HEAP_GET_STRING((heap),DUK_STRIDX_TRUE)
 #define DUK_HTHREAD_STRING_TRUE(thr)                                  DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_TRUE)
-#define DUK_STRIDX_FALSE                                              154                            /* 'false' */
+#define DUK_STRIDX_FALSE                                              152                            /* 'false' */
 #define DUK_HEAP_STRING_FALSE(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_FALSE)
 #define DUK_HTHREAD_STRING_FALSE(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_FALSE)
-#define DUK_STRIDX_IMPLEMENTS                                         155                            /* 'implements' */
+#define DUK_STRIDX_IMPLEMENTS                                         153                            /* 'implements' */
 #define DUK_HEAP_STRING_IMPLEMENTS(heap)                              DUK_HEAP_GET_STRING((heap),DUK_STRIDX_IMPLEMENTS)
 #define DUK_HTHREAD_STRING_IMPLEMENTS(thr)                            DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_IMPLEMENTS)
-#define DUK_STRIDX_INTERFACE                                          156                            /* 'interface' */
+#define DUK_STRIDX_INTERFACE                                          154                            /* 'interface' */
 #define DUK_HEAP_STRING_INTERFACE(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INTERFACE)
 #define DUK_HTHREAD_STRING_INTERFACE(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INTERFACE)
-#define DUK_STRIDX_LET                                                157                            /* 'let' */
+#define DUK_STRIDX_LET                                                155                            /* 'let' */
 #define DUK_HEAP_STRING_LET(heap)                                     DUK_HEAP_GET_STRING((heap),DUK_STRIDX_LET)
 #define DUK_HTHREAD_STRING_LET(thr)                                   DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_LET)
-#define DUK_STRIDX_PACKAGE                                            158                            /* 'package' */
+#define DUK_STRIDX_PACKAGE                                            156                            /* 'package' */
 #define DUK_HEAP_STRING_PACKAGE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PACKAGE)
 #define DUK_HTHREAD_STRING_PACKAGE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PACKAGE)
-#define DUK_STRIDX_PRIVATE                                            159                            /* 'private' */
+#define DUK_STRIDX_PRIVATE                                            157                            /* 'private' */
 #define DUK_HEAP_STRING_PRIVATE(heap)                                 DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PRIVATE)
 #define DUK_HTHREAD_STRING_PRIVATE(thr)                               DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PRIVATE)
-#define DUK_STRIDX_PROTECTED                                          160                            /* 'protected' */
+#define DUK_STRIDX_PROTECTED                                          158                            /* 'protected' */
 #define DUK_HEAP_STRING_PROTECTED(heap)                               DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PROTECTED)
 #define DUK_HTHREAD_STRING_PROTECTED(thr)                             DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PROTECTED)
-#define DUK_STRIDX_PUBLIC                                             161                            /* 'public' */
+#define DUK_STRIDX_PUBLIC                                             159                            /* 'public' */
 #define DUK_HEAP_STRING_PUBLIC(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_PUBLIC)
 #define DUK_HTHREAD_STRING_PUBLIC(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_PUBLIC)
-#define DUK_STRIDX_STATIC                                             162                            /* 'static' */
+#define DUK_STRIDX_STATIC                                             160                            /* 'static' */
 #define DUK_HEAP_STRING_STATIC(heap)                                  DUK_HEAP_GET_STRING((heap),DUK_STRIDX_STATIC)
 #define DUK_HTHREAD_STRING_STATIC(thr)                                DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_STATIC)
-#define DUK_STRIDX_YIELD                                              163                            /* 'yield' */
+#define DUK_STRIDX_YIELD                                              161                            /* 'yield' */
 #define DUK_HEAP_STRING_YIELD(heap)                                   DUK_HEAP_GET_STRING((heap),DUK_STRIDX_YIELD)
 #define DUK_HTHREAD_STRING_YIELD(thr)                                 DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_YIELD)
 
-#define DUK_HEAP_NUM_STRINGS                                          164
-#define DUK_STRIDX_START_RESERVED                                     119
-#define DUK_STRIDX_START_STRICT_RESERVED                              155
-#define DUK_STRIDX_END_RESERVED                                       164                            /* exclusive endpoint */
+#define DUK_HEAP_NUM_STRINGS                                          162
+#define DUK_STRIDX_START_RESERVED                                     117
+#define DUK_STRIDX_START_STRICT_RESERVED                              153
+#define DUK_STRIDX_END_RESERVED                                       162                            /* exclusive endpoint */
 
 /* To convert a heap stridx to a token number, subtract
  * DUK_STRIDX_START_RESERVED and add DUK_TOK_START_RESERVED.
  */
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[903];
+DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[892];
 #endif  /* !DUK_SINGLE_FILE */
 #define DUK_STRDATA_MAX_STRLEN                                        17
-#define DUK_STRDATA_DATA_LENGTH                                       903
+#define DUK_STRDATA_DATA_LENGTH                                       892
 #endif  /* DUK_USE_ROM_STRINGS */
 
 #if defined(DUK_USE_ROM_OBJECTS)
@@ -2020,10 +2021,14 @@
 DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_length(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_native_function_name(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx);
@@ -2090,10 +2095,13 @@
 DUK_INTERNAL_DECL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_clz32(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_imul(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_max(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_min(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_random(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_math_object_sign(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_parse(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx);
@@ -2107,6 +2115,8 @@
 DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_resume(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_thread_current(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_apply(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_construct(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_delete_property(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_reflect_object_has(duk_context *ctx);
@@ -2135,8 +2145,9 @@
 DUK_INTERNAL_DECL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_context *ctx);
 DUK_INTERNAL_DECL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_performance_now(duk_context *ctx);
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[166];
+DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[176];
 #endif  /* !DUK_SINGLE_FILE */
 #define DUK_BIDX_GLOBAL                                               0
 #define DUK_BIDX_GLOBAL_ENV                                           1
@@ -2144,92 +2155,69 @@
 #define DUK_BIDX_OBJECT_PROTOTYPE                                     3
 #define DUK_BIDX_FUNCTION_CONSTRUCTOR                                 4
 #define DUK_BIDX_FUNCTION_PROTOTYPE                                   5
-#define DUK_BIDX_ARRAY_CONSTRUCTOR                                    6
-#define DUK_BIDX_ARRAY_PROTOTYPE                                      7
-#define DUK_BIDX_STRING_CONSTRUCTOR                                   8
-#define DUK_BIDX_STRING_PROTOTYPE                                     9
-#define DUK_BIDX_BOOLEAN_CONSTRUCTOR                                  10
-#define DUK_BIDX_BOOLEAN_PROTOTYPE                                    11
-#define DUK_BIDX_NUMBER_CONSTRUCTOR                                   12
-#define DUK_BIDX_NUMBER_PROTOTYPE                                     13
-#define DUK_BIDX_DATE_CONSTRUCTOR                                     14
-#define DUK_BIDX_DATE_PROTOTYPE                                       15
-#define DUK_BIDX_REGEXP_CONSTRUCTOR                                   16
-#define DUK_BIDX_REGEXP_PROTOTYPE                                     17
-#define DUK_BIDX_ERROR_CONSTRUCTOR                                    18
-#define DUK_BIDX_ERROR_PROTOTYPE                                      19
-#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR                               20
-#define DUK_BIDX_EVAL_ERROR_PROTOTYPE                                 21
-#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR                              22
-#define DUK_BIDX_RANGE_ERROR_PROTOTYPE                                23
-#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR                          24
-#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE                            25
-#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR                             26
-#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE                               27
-#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR                               28
-#define DUK_BIDX_TYPE_ERROR_PROTOTYPE                                 29
-#define DUK_BIDX_URI_ERROR_CONSTRUCTOR                                30
-#define DUK_BIDX_URI_ERROR_PROTOTYPE                                  31
-#define DUK_BIDX_MATH                                                 32
-#define DUK_BIDX_JSON                                                 33
-#define DUK_BIDX_TYPE_ERROR_THROWER                                   34
-#define DUK_BIDX_DUKTAPE                                              35
-#define DUK_BIDX_THREAD_CONSTRUCTOR                                   36
-#define DUK_BIDX_THREAD_PROTOTYPE                                     37
-#define DUK_BIDX_POINTER_CONSTRUCTOR                                  38
-#define DUK_BIDX_POINTER_PROTOTYPE                                    39
-#define DUK_BIDX_DOUBLE_ERROR                                         40
-#define DUK_BIDX_PROXY_CONSTRUCTOR                                    41
-#define DUK_BIDX_REFLECT                                              42
-#define DUK_BIDX_SYMBOL_PROTOTYPE                                     43
-#define DUK_BIDX_ARRAYBUFFER_CONSTRUCTOR                              44
-#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE                                45
-#define DUK_BIDX_DATAVIEW_CONSTRUCTOR                                 46
-#define DUK_BIDX_DATAVIEW_PROTOTYPE                                   47
-#define DUK_BIDX_TYPEDARRAY_CONSTRUCTOR                               48
-#define DUK_BIDX_TYPEDARRAY_PROTOTYPE                                 49
-#define DUK_BIDX_INT8ARRAY_CONSTRUCTOR                                50
-#define DUK_BIDX_INT8ARRAY_PROTOTYPE                                  51
-#define DUK_BIDX_UINT8ARRAY_CONSTRUCTOR                               52
-#define DUK_BIDX_UINT8ARRAY_PROTOTYPE                                 53
-#define DUK_BIDX_UINT8CLAMPEDARRAY_CONSTRUCTOR                        54
-#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE                          55
-#define DUK_BIDX_INT16ARRAY_CONSTRUCTOR                               56
-#define DUK_BIDX_INT16ARRAY_PROTOTYPE                                 57
-#define DUK_BIDX_UINT16ARRAY_CONSTRUCTOR                              58
-#define DUK_BIDX_UINT16ARRAY_PROTOTYPE                                59
-#define DUK_BIDX_INT32ARRAY_CONSTRUCTOR                               60
-#define DUK_BIDX_INT32ARRAY_PROTOTYPE                                 61
-#define DUK_BIDX_UINT32ARRAY_CONSTRUCTOR                              62
-#define DUK_BIDX_UINT32ARRAY_PROTOTYPE                                63
-#define DUK_BIDX_FLOAT32ARRAY_CONSTRUCTOR                             64
-#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE                               65
-#define DUK_BIDX_FLOAT64ARRAY_CONSTRUCTOR                             66
-#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE                               67
-#define DUK_BIDX_NODEJS_BUFFER_CONSTRUCTOR                            68
-#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE                              69
-#define DUK_BIDX_TEXTENCODER_CONSTRUCTOR                              70
-#define DUK_BIDX_TEXTENCODER_PROTOTYPE                                71
-#define DUK_BIDX_TEXTDECODER_CONSTRUCTOR                              72
-#define DUK_BIDX_TEXTDECODER_PROTOTYPE                                73
-#define DUK_NUM_BUILTINS                                              74
-#define DUK_NUM_BIDX_BUILTINS                                         74
-#define DUK_NUM_ALL_BUILTINS                                          74
+#define DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE                            6
+#define DUK_BIDX_ARRAY_CONSTRUCTOR                                    7
+#define DUK_BIDX_ARRAY_PROTOTYPE                                      8
+#define DUK_BIDX_STRING_CONSTRUCTOR                                   9
+#define DUK_BIDX_STRING_PROTOTYPE                                     10
+#define DUK_BIDX_BOOLEAN_CONSTRUCTOR                                  11
+#define DUK_BIDX_BOOLEAN_PROTOTYPE                                    12
+#define DUK_BIDX_NUMBER_CONSTRUCTOR                                   13
+#define DUK_BIDX_NUMBER_PROTOTYPE                                     14
+#define DUK_BIDX_DATE_CONSTRUCTOR                                     15
+#define DUK_BIDX_DATE_PROTOTYPE                                       16
+#define DUK_BIDX_REGEXP_CONSTRUCTOR                                   17
+#define DUK_BIDX_REGEXP_PROTOTYPE                                     18
+#define DUK_BIDX_ERROR_CONSTRUCTOR                                    19
+#define DUK_BIDX_ERROR_PROTOTYPE                                      20
+#define DUK_BIDX_EVAL_ERROR_CONSTRUCTOR                               21
+#define DUK_BIDX_EVAL_ERROR_PROTOTYPE                                 22
+#define DUK_BIDX_RANGE_ERROR_CONSTRUCTOR                              23
+#define DUK_BIDX_RANGE_ERROR_PROTOTYPE                                24
+#define DUK_BIDX_REFERENCE_ERROR_CONSTRUCTOR                          25
+#define DUK_BIDX_REFERENCE_ERROR_PROTOTYPE                            26
+#define DUK_BIDX_SYNTAX_ERROR_CONSTRUCTOR                             27
+#define DUK_BIDX_SYNTAX_ERROR_PROTOTYPE                               28
+#define DUK_BIDX_TYPE_ERROR_CONSTRUCTOR                               29
+#define DUK_BIDX_TYPE_ERROR_PROTOTYPE                                 30
+#define DUK_BIDX_URI_ERROR_CONSTRUCTOR                                31
+#define DUK_BIDX_URI_ERROR_PROTOTYPE                                  32
+#define DUK_BIDX_TYPE_ERROR_THROWER                                   33
+#define DUK_BIDX_DUKTAPE                                              34
+#define DUK_BIDX_THREAD_PROTOTYPE                                     35
+#define DUK_BIDX_POINTER_PROTOTYPE                                    36
+#define DUK_BIDX_DOUBLE_ERROR                                         37
+#define DUK_BIDX_SYMBOL_PROTOTYPE                                     38
+#define DUK_BIDX_ARRAYBUFFER_PROTOTYPE                                39
+#define DUK_BIDX_DATAVIEW_PROTOTYPE                                   40
+#define DUK_BIDX_INT8ARRAY_PROTOTYPE                                  41
+#define DUK_BIDX_UINT8ARRAY_PROTOTYPE                                 42
+#define DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE                          43
+#define DUK_BIDX_INT16ARRAY_PROTOTYPE                                 44
+#define DUK_BIDX_UINT16ARRAY_PROTOTYPE                                45
+#define DUK_BIDX_INT32ARRAY_PROTOTYPE                                 46
+#define DUK_BIDX_UINT32ARRAY_PROTOTYPE                                47
+#define DUK_BIDX_FLOAT32ARRAY_PROTOTYPE                               48
+#define DUK_BIDX_FLOAT64ARRAY_PROTOTYPE                               49
+#define DUK_BIDX_NODEJS_BUFFER_PROTOTYPE                              50
+#define DUK_NUM_BUILTINS                                              51
+#define DUK_NUM_BIDX_BUILTINS                                         51
+#define DUK_NUM_ALL_BUILTINS                                          76
 #if defined(DUK_USE_DOUBLE_LE)
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819];
+DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3972];
 #endif  /* !DUK_SINGLE_FILE */
-#define DUK_BUILTINS_DATA_LENGTH                                      3819
+#define DUK_BUILTINS_DATA_LENGTH                                      3972
 #elif defined(DUK_USE_DOUBLE_BE)
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819];
+DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3972];
 #endif  /* !DUK_SINGLE_FILE */
-#define DUK_BUILTINS_DATA_LENGTH                                      3819
+#define DUK_BUILTINS_DATA_LENGTH                                      3972
 #elif defined(DUK_USE_DOUBLE_ME)
 #if !defined(DUK_SINGLE_FILE)
-DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819];
+DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3972];
 #endif  /* !DUK_SINGLE_FILE */
-#define DUK_BUILTINS_DATA_LENGTH                                      3819
+#define DUK_BUILTINS_DATA_LENGTH                                      3972
 #else
 #error invalid endianness defines
 #endif
@@ -2346,10 +2334,10 @@
 /*
  *  Buffer writer (dynamic buffer only)
  *
- *  Helper for writing to a dynamic buffer with a concept of a "spare" area
+ *  Helper for writing to a dynamic buffer with a concept of a "slack" area
  *  to reduce resizes.  You can ensure there is enough space beforehand and
  *  then write for a while without further checks, relying on a stable data
- *  pointer.  Spare handling is automatic so call sites only indicate how
+ *  pointer.  Slack handling is automatic so call sites only indicate how
  *  much data they need right now.
  *
  *  There are several ways to write using bufwriter.  The best approach
@@ -2375,8 +2363,13 @@
 	duk_hbuffer_dynamic *buf;
 };
 
-#define DUK_BW_SPARE_ADD           64
-#define DUK_BW_SPARE_SHIFT         4    /* 2^4 -> 1/16 = 6.25% spare */
+#if defined(DUK_USE_PREFER_SIZE)
+#define DUK_BW_SLACK_ADD           64
+#define DUK_BW_SLACK_SHIFT         4    /* 2^4 -> 1/16 = 6.25% slack */
+#else
+#define DUK_BW_SLACK_ADD           64
+#define DUK_BW_SLACK_SHIFT         2    /* 2^2 -> 1/4 = 25% slack */
+#endif
 
 /* Initialization and finalization (compaction), converting to other types. */
 
@@ -2391,7 +2384,7 @@
 		duk_bw_compact((thr), (bw_ctx)); \
 	} while (0)
 #define DUK_BW_PUSH_AS_STRING(thr,bw_ctx) do { \
-		duk_push_lstring((duk_context *) (thr), \
+		duk_push_lstring((thr), \
 		                 (const char *) (bw_ctx)->p_base, \
 		                 (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \
 	} while (0)
@@ -2474,7 +2467,7 @@
 		duk_bw_compact((thr), (bw_ctx)); \
 	} while (0)
 
-/* Fast write calls which assume you control the spare beforehand.
+/* Fast write calls which assume you control the slack beforehand.
  * Multibyte write variants exist and use a temporary write pointer
  * because byte writes alias with anything: with a stored pointer
  * explicit pointer load/stores get generated (e.g. gcc -Os).
@@ -2537,7 +2530,7 @@
 #define DUK_BW_WRITE_RAW_XUTF8(thr,bw_ctx,cp) do { \
 		duk_ucodepoint_t duk__cp; \
 		duk_small_int_t duk__enc_len; \
-		duk__cp = (cp); \
+		duk__cp = (duk_ucodepoint_t) (cp); \
 		DUK_BW_ASSERT_SPACE((thr), (bw_ctx), duk_unicode_get_xutf8_length(duk__cp)); \
 		duk__enc_len = duk_unicode_encode_xutf8(duk__cp, (bw_ctx)->p); \
 		(bw_ctx)->p += duk__enc_len; \
@@ -2864,10 +2857,17 @@
 #define DUK_STR_BUFFER_TOO_LONG                  "buffer too long"
 #define DUK_STR_ALLOC_FAILED                     "alloc failed"
 #define DUK_STR_WRONG_BUFFER_TYPE                "wrong buffer type"
-#define DUK_STR_ENCODE_FAILED                    "encode failed"
-#define DUK_STR_DECODE_FAILED                    "decode failed"
+#define DUK_STR_BASE64_ENCODE_FAILED             "base64 encode failed"
+#define DUK_STR_SOURCE_DECODE_FAILED             "source decode failed"
+#define DUK_STR_UTF8_DECODE_FAILED               "utf-8 decode failed"
+#define DUK_STR_BASE64_DECODE_FAILED             "base64 decode failed"
+#define DUK_STR_HEX_DECODE_FAILED                "hex decode failed"
+#define DUK_STR_INVALID_BYTECODE                 "invalid bytecode"
 #define DUK_STR_NO_SOURCECODE                    "no sourcecode"
 #define DUK_STR_RESULT_TOO_LONG                  "result too long"
+#define DUK_STR_INVALID_CFUNC_RC                 "invalid C function rc"
+#define DUK_STR_INVALID_INSTANCEOF_RVAL          "invalid instanceof rval"
+#define DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO  "instanceof rval has no .prototype"
 
 /* JSON */
 #define DUK_STR_FMT_PTR                          "%p"
@@ -2877,7 +2877,6 @@
 #define DUK_STR_CYCLIC_INPUT                     "cyclic input"
 
 /* Object property access */
-#define DUK_STR_PROXY_REVOKED                    "proxy revoked"
 #define DUK_STR_INVALID_BASE                     "invalid base value"
 #define DUK_STR_STRICT_CALLER_READ               "cannot read strict 'caller'"
 #define DUK_STR_PROXY_REJECTED                   "proxy rejected"
@@ -2886,6 +2885,7 @@
 #define DUK_STR_INVALID_DESCRIPTOR               "invalid descriptor"
 
 /* Proxy */
+#define DUK_STR_PROXY_REVOKED                    "proxy revoked"
 #define DUK_STR_INVALID_TRAP_RESULT              "invalid trap result"
 
 /* Variables */
@@ -2910,6 +2910,7 @@
 #define DUK_STR_CANNOT_DELETE_IDENTIFIER         "cannot delete identifier"
 #define DUK_STR_INVALID_EXPRESSION               "invalid expression"
 #define DUK_STR_INVALID_LVALUE                   "invalid lvalue"
+#define DUK_STR_INVALID_NEWTARGET                "invalid new.target"
 #define DUK_STR_EXPECTED_IDENTIFIER              "expected identifier"
 #define DUK_STR_EMPTY_EXPR_NOT_ALLOWED           "empty expression not allowed"
 #define DUK_STR_INVALID_FOR                      "invalid for statement"
@@ -2926,7 +2927,7 @@
 #define DUK_STR_INVALID_GETSET_NAME              "invalid getter/setter name"
 #define DUK_STR_FUNC_NAME_REQUIRED               "function name required"
 
-/* Regexp */
+/* RegExp */
 #define DUK_STR_INVALID_QUANTIFIER               "invalid regexp quantifier"
 #define DUK_STR_INVALID_QUANTIFIER_NO_ATOM       "quantifier without preceding atom"
 #define DUK_STR_INVALID_QUANTIFIER_VALUES        "quantifier values invalid (qmin > qmax)"
@@ -2945,7 +2946,6 @@
 /* Limits */
 #define DUK_STR_VALSTACK_LIMIT                   "valstack limit"
 #define DUK_STR_CALLSTACK_LIMIT                  "callstack limit"
-#define DUK_STR_CATCHSTACK_LIMIT                 "catchstack limit"
 #define DUK_STR_PROTOTYPE_CHAIN_LIMIT            "prototype chain limit"
 #define DUK_STR_BOUND_CHAIN_LIMIT                "function call bound chain limit"
 #define DUK_STR_C_CALLSTACK_LIMIT                "C call stack depth limit"
@@ -3125,20 +3125,20 @@
 /* Opcodes. */
 #define DUK_OP_LDREG                0
 #define DUK_OP_STREG                1
-#define DUK_OP_LDCONST              2
-#define DUK_OP_LDINT                3
-#define DUK_OP_LDINTX               4
-#define DUK_OP_LDTHIS               5
-#define DUK_OP_LDUNDEF              6
-#define DUK_OP_LDNULL               7
-#define DUK_OP_LDTRUE               8
-#define DUK_OP_LDFALSE              9
-#define DUK_OP_BNOT                 10
-#define DUK_OP_LNOT                 11
-#define DUK_OP_UNM                  12
-#define DUK_OP_UNP                  13
-#define DUK_OP_TYPEOF               14
-#define DUK_OP_TYPEOFID             15
+#define DUK_OP_JUMP                 2
+#define DUK_OP_LDCONST              3
+#define DUK_OP_LDINT                4
+#define DUK_OP_LDINTX               5
+#define DUK_OP_LDTHIS               6
+#define DUK_OP_LDUNDEF              7
+#define DUK_OP_LDNULL               8
+#define DUK_OP_LDTRUE               9
+#define DUK_OP_LDFALSE              10
+#define DUK_OP_GETVAR               11
+#define DUK_OP_BNOT                 12
+#define DUK_OP_LNOT                 13
+#define DUK_OP_UNM                  14
+#define DUK_OP_UNP                  15
 #define DUK_OP_EQ                   16
 #define DUK_OP_EQ_RR                16
 #define DUK_OP_EQ_CR                17
@@ -3308,67 +3308,68 @@
 #define DUK_OP_REGEXP_CR            149
 #define DUK_OP_REGEXP_RC            150
 #define DUK_OP_REGEXP_CC            151
-#define DUK_OP_CSVAR                152
-#define DUK_OP_CSVAR_RR             152
-#define DUK_OP_CSVAR_CR             153
-#define DUK_OP_CSVAR_RC             154
-#define DUK_OP_CSVAR_CC             155
-#define DUK_OP_CLOSURE              156
-#define DUK_OP_GETVAR               157
-#define DUK_OP_PUTVAR               158
-#define DUK_OP_DELVAR               159
-#define DUK_OP_JUMP                 160
-#define DUK_OP_RETREG               161
-#define DUK_OP_RETUNDEF             162
-#define DUK_OP_RETCONST             163
-#define DUK_OP_RETCONSTN            164  /* return const without incref (e.g. number) */
-#define DUK_OP_LABEL                165
-#define DUK_OP_ENDLABEL             166
-#define DUK_OP_BREAK                167
-#define DUK_OP_CONTINUE             168
-#define DUK_OP_TRYCATCH             169
-#define DUK_OP_ENDTRY               170
-#define DUK_OP_ENDCATCH             171
-#define DUK_OP_ENDFIN               172
-#define DUK_OP_THROW                173
-#define DUK_OP_CSREG                174
-#define DUK_OP_EVALCALL             175
-#define DUK_OP_CALL                 176  /* must be even */
-#define DUK_OP_TAILCALL             177  /* must be odd */
-#define DUK_OP_NEW                  178
-#define DUK_OP_NEWOBJ               179
-#define DUK_OP_NEWARR               180
-#define DUK_OP_MPUTOBJ              181
-#define DUK_OP_MPUTOBJI             182
-#define DUK_OP_INITSET              183
-#define DUK_OP_INITGET              184
-#define DUK_OP_MPUTARR              185
-#define DUK_OP_MPUTARRI             186
-#define DUK_OP_SETALEN              187
-#define DUK_OP_INITENUM             188
-#define DUK_OP_NEXTENUM             189
-#define DUK_OP_INVLHS               190
-#define DUK_OP_DEBUGGER             191
-#define DUK_OP_NOP                  192
-#define DUK_OP_INVALID              193
-#define DUK_OP_UNUSED194            194
-#define DUK_OP_UNUSED195            195
-#define DUK_OP_UNUSED196            196
-#define DUK_OP_UNUSED197            197
-#define DUK_OP_UNUSED198            198
-#define DUK_OP_UNUSED199            199
-#define DUK_OP_UNUSED200            200
-#define DUK_OP_UNUSED201            201
-#define DUK_OP_UNUSED202            202
-#define DUK_OP_UNUSED203            203
-#define DUK_OP_UNUSED204            204
-#define DUK_OP_UNUSED205            205
-#define DUK_OP_UNUSED206            206
+#define DUK_OP_CLOSURE              152
+#define DUK_OP_TYPEOF               153
+#define DUK_OP_TYPEOFID             154
+#define DUK_OP_PUTVAR               155
+#define DUK_OP_DELVAR               156
+#define DUK_OP_RETREG               157
+#define DUK_OP_RETUNDEF             158
+#define DUK_OP_RETCONST             159
+#define DUK_OP_RETCONSTN            160  /* return const without incref (e.g. number) */
+#define DUK_OP_LABEL                161
+#define DUK_OP_ENDLABEL             162
+#define DUK_OP_BREAK                163
+#define DUK_OP_CONTINUE             164
+#define DUK_OP_TRYCATCH             165
+#define DUK_OP_ENDTRY               166
+#define DUK_OP_ENDCATCH             167
+#define DUK_OP_ENDFIN               168
+#define DUK_OP_THROW                169
+#define DUK_OP_INVLHS               170
+#define DUK_OP_CSREG                171
+#define DUK_OP_CSVAR                172
+#define DUK_OP_CSVAR_RR             172
+#define DUK_OP_CSVAR_CR             173
+#define DUK_OP_CSVAR_RC             174
+#define DUK_OP_CSVAR_CC             175
+#define DUK_OP_CALL0                176  /* DUK_OP_CALL0 & 0x0F must be zero. */
+#define DUK_OP_CALL1                177
+#define DUK_OP_CALL2                178
+#define DUK_OP_CALL3                179
+#define DUK_OP_CALL4                180
+#define DUK_OP_CALL5                181
+#define DUK_OP_CALL6                182
+#define DUK_OP_CALL7                183
+#define DUK_OP_CALL8                184
+#define DUK_OP_CALL9                185
+#define DUK_OP_CALL10               186
+#define DUK_OP_CALL11               187
+#define DUK_OP_CALL12               188
+#define DUK_OP_CALL13               189
+#define DUK_OP_CALL14               190
+#define DUK_OP_CALL15               191
+#define DUK_OP_NEWOBJ               192
+#define DUK_OP_NEWARR               193
+#define DUK_OP_MPUTOBJ              194
+#define DUK_OP_MPUTOBJI             195
+#define DUK_OP_INITSET              196
+#define DUK_OP_INITGET              197
+#define DUK_OP_MPUTARR              198
+#define DUK_OP_MPUTARRI             199
+#define DUK_OP_SETALEN              200
+#define DUK_OP_INITENUM             201
+#define DUK_OP_NEXTENUM             202
+#define DUK_OP_NEWTARGET            203
+#define DUK_OP_DEBUGGER             204
+#define DUK_OP_NOP                  205
+#define DUK_OP_INVALID              206
 #define DUK_OP_UNUSED207            207
-#define DUK_OP_UNUSED208            208
-#define DUK_OP_UNUSED209            209
-#define DUK_OP_UNUSED210            210
-#define DUK_OP_UNUSED211            211
+#define DUK_OP_GETPROPC             208
+#define DUK_OP_GETPROPC_RR          208
+#define DUK_OP_GETPROPC_CR          209
+#define DUK_OP_GETPROPC_RC          210
+#define DUK_OP_GETPROPC_CC          211
 #define DUK_OP_UNUSED212            212
 #define DUK_OP_UNUSED213            213
 #define DUK_OP_UNUSED214            214
@@ -3418,14 +3419,24 @@
 /* XXX: Allocate flags from opcode field?  Would take 16 opcode slots
  * but avoids shuffling in more cases.  Maybe not worth it.
  */
-/* DUK_OP_TRYCATCH flags in A */
-#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH     (1 << 0)
-#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY   (1 << 1)
-#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING  (1 << 2)
-#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING   (1 << 3)
-
-/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags (DUK_PROPDESC_FLAG_XXX) */
-#define DUK_BC_DECLVAR_FLAG_FUNC_DECL       (1 << 4)  /* function declaration */
+/* DUK_OP_TRYCATCH flags in A. */
+#define DUK_BC_TRYCATCH_FLAG_HAVE_CATCH     (1U << 0)
+#define DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY   (1U << 1)
+#define DUK_BC_TRYCATCH_FLAG_CATCH_BINDING  (1U << 2)
+#define DUK_BC_TRYCATCH_FLAG_WITH_BINDING   (1U << 3)
+
+/* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags
+ * (DUK_PROPDESC_FLAG_XXX).
+ */
+#define DUK_BC_DECLVAR_FLAG_FUNC_DECL       (1U << 4)  /* function declaration */
+
+/* DUK_OP_CALLn flags, part of opcode field.  Three lowest bits must match
+ * DUK_CALL_FLAG_xxx directly.
+ */
+#define DUK_BC_CALL_FLAG_TAILCALL           (1U << 0)
+#define DUK_BC_CALL_FLAG_CONSTRUCT          (1U << 1)
+#define DUK_BC_CALL_FLAG_CALLED_AS_EVAL     (1U << 2)
+#define DUK_BC_CALL_FLAG_INDIRECT           (1U << 3)
 
 /* Misc constants and helper macros. */
 #define DUK_BC_LDINT_BIAS           (1L << 15)
@@ -3609,6 +3620,8 @@
 
 #define DUK_TOK_MAXVAL                            101  /* inclusive */
 
+#define DUK_TOK_INVALID                           DUK_SMALL_UINT_MAX
+
 /* Convert heap string index to a token (reserved words) */
 #define DUK_STRIDX_TO_TOK(x)                        ((x) - DUK_STRIDX_START_RESERVED + DUK_TOK_START_RESERVED)
 
@@ -3787,8 +3800,8 @@
  * stale values otherwise.
  */
 struct duk_token {
-	duk_small_int_t t;            /* token type (with reserved word identification) */
-	duk_small_int_t t_nores;      /* token type (with reserved words as DUK_TOK_IDENTIFER) */
+	duk_small_uint_t t;           /* token type (with reserved word identification) */
+	duk_small_uint_t t_nores;     /* token type (with reserved words as DUK_TOK_IDENTIFER) */
 	duk_double_t num;             /* numeric value of token */
 	duk_hstring *str1;            /* string 1 of token (borrowed, stored to ctx->slot1_idx) */
 	duk_hstring *str2;            /* string 2 of token (borrowed, stored to ctx->slot2_idx) */
@@ -3803,11 +3816,11 @@
 
 /* A regexp token value. */
 struct duk_re_token {
-	duk_small_int_t t;           /* token type */
-	duk_small_int_t greedy;
-	duk_uint_fast32_t num;       /* numeric value (character, count) */
-	duk_uint_fast32_t qmin;
-	duk_uint_fast32_t qmax;
+	duk_small_uint_t t;          /* token type */
+	duk_small_uint_t greedy;
+	duk_uint32_t num;            /* numeric value (character, count) */
+	duk_uint32_t qmin;
+	duk_uint32_t qmax;
 };
 
 /* A structure for 'snapshotting' a point for rewinding */
@@ -3911,13 +3924,12 @@
  * Chosen so that when a regconst is cast to duk_int32_t, all consts are
  * negative values.
  */
-#define DUK_REGCONST_CONST_MARKER    0x80000000UL
-
-/* type to represent a reg/const reference during compilation */
-typedef duk_uint32_t duk_regconst_t;
-
-/* type to represent a straight register reference, with <0 indicating none */
-typedef duk_int32_t duk_reg_t;
+#define DUK_REGCONST_CONST_MARKER    DUK_INT32_MIN  /* = -0x80000000 */
+
+/* Type to represent a reg/const reference during compilation, with <0
+ * indicating a constant.  Some call sites also use -1 to indicate 'none'.
+ */
+typedef duk_int32_t duk_regconst_t;
 
 typedef struct {
 	duk_small_uint_t t;          /* DUK_ISPEC_XXX */
@@ -3957,8 +3969,8 @@
  *  Compiler state
  */
 
-#define DUK_LABEL_FLAG_ALLOW_BREAK       (1 << 0)
-#define DUK_LABEL_FLAG_ALLOW_CONTINUE    (1 << 1)
+#define DUK_LABEL_FLAG_ALLOW_BREAK       (1U << 0)
+#define DUK_LABEL_FLAG_ALLOW_CONTINUE    (1U << 1)
 
 #define DUK_DECL_TYPE_VAR                0
 #define DUK_DECL_TYPE_FUNC               1
@@ -4016,14 +4028,14 @@
 	duk_idx_t varmap_idx;
 
 	/* Temp reg handling. */
-	duk_reg_t temp_first;               /* first register that is a temporary (below: variables) */
-	duk_reg_t temp_next;                /* next temporary register to allocate */
-	duk_reg_t temp_max;                 /* highest value of temp_reg (temp_max - 1 is highest used reg) */
+	duk_regconst_t temp_first;           /* first register that is a temporary (below: variables) */
+	duk_regconst_t temp_next;            /* next temporary register to allocate */
+	duk_regconst_t temp_max;             /* highest value of temp_reg (temp_max - 1 is highest used reg) */
 
 	/* Shuffle registers if large number of regs/consts. */
-	duk_reg_t shuffle1;
-	duk_reg_t shuffle2;
-	duk_reg_t shuffle3;
+	duk_regconst_t shuffle1;
+	duk_regconst_t shuffle2;
+	duk_regconst_t shuffle3;
 
 	/* Stats for current expression being parsed. */
 	duk_int_t nud_count;
@@ -4039,7 +4051,7 @@
 	duk_int_t with_depth;               /* with stack depth (affects identifier lookups) */
 	duk_int_t fnum_next;                /* inner function numbering */
 	duk_int_t num_formals;              /* number of formal arguments */
-	duk_reg_t reg_stmt_value;           /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */
+	duk_regconst_t reg_stmt_value;      /* register for writing value of 'non-empty' statements (global or eval code), -1 is marker */
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 	duk_int_t min_line;                 /* XXX: typing (duk_hcompfunc has duk_uint32_t) */
 	duk_int_t max_line;
@@ -4140,9 +4152,9 @@
 #define DUK_REOP_ASSERT_NOT_WORD_BOUNDARY  19
 
 /* flags */
-#define DUK_RE_FLAG_GLOBAL                 (1 << 0)
-#define DUK_RE_FLAG_IGNORE_CASE            (1 << 1)
-#define DUK_RE_FLAG_MULTILINE              (1 << 2)
+#define DUK_RE_FLAG_GLOBAL                 (1U << 0)
+#define DUK_RE_FLAG_IGNORE_CASE            (1U << 1)
+#define DUK_RE_FLAG_MULTILINE              (1U << 2)
 
 struct duk_re_matcher_ctx {
 	duk_hthread *thr;
@@ -5158,6 +5170,33 @@
 
 #endif  /* DUK_USE_REFERENCE_COUNTING */
 
+/*
+ *  Some convenience macros that don't have optimized implementations now.
+ */
+
+#define DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr,tv_dst,tv_src) do { \
+		duk_hthread *duk__thr = (thr); \
+		duk_tval *duk__dst = (tv_dst); \
+		duk_tval *duk__src = (tv_src); \
+		DUK_UNREF(duk__thr); \
+		DUK_TVAL_DECREF_NORZ(thr, duk__dst); \
+		DUK_TVAL_SET_TVAL(duk__dst, duk__src); \
+		DUK_TVAL_INCREF(thr, duk__dst); \
+	} while (0)
+
+#define DUK_TVAL_SET_U32_UPDREF_NORZ(thr,tv_dst,val) do { \
+		duk_hthread *duk__thr = (thr); \
+		duk_tval *duk__dst = (tv_dst); \
+		duk_uint32_t duk__val = (duk_uint32_t) (val); \
+		DUK_UNREF(duk__thr); \
+		DUK_TVAL_DECREF_NORZ(thr, duk__dst); \
+		DUK_TVAL_SET_U32(duk__dst, duk__val); \
+	} while (0)
+
+/*
+ *  Prototypes
+ */
+
 #if defined(DUK_USE_REFERENCE_COUNTING)
 #if defined(DUK_USE_FINALIZER_SUPPORT)
 DUK_INTERNAL_DECL void duk_refzero_check_slow(duk_hthread *thr);
@@ -5203,6 +5242,8 @@
 #if !defined(DUK_API_INTERNAL_H_INCLUDED)
 #define DUK_API_INTERNAL_H_INCLUDED
 
+#define DUK_INTERNAL_SYMBOL(x)     ("\x82" x)
+
 /* duk_push_sprintf constants */
 #define DUK_PUSH_SPRINTF_INITIAL_SIZE  256L
 #define DUK_PUSH_SPRINTF_SANITY_LIMIT  (1L * 1024L * 1024L * 1024L)
@@ -5212,186 +5253,197 @@
  */
 #define DUK_ERRCODE_FLAG_NOBLAME_FILELINE  (1L << 24)
 
-/* Valstack resize flags */
-#define DUK_VSRESIZE_FLAG_SHRINK           (1 << 0)
-#define DUK_VSRESIZE_FLAG_COMPACT          (1 << 1)
-#define DUK_VSRESIZE_FLAG_THROW            (1 << 2)
-
 /* Current convention is to use duk_size_t for value stack sizes and global indices,
  * and duk_idx_t for local frame indices.
  */
-DUK_INTERNAL_DECL
-duk_bool_t duk_valstack_resize_raw(duk_context *ctx,
-                                   duk_size_t min_new_size,
-                                   duk_small_uint_t flags);
-
-DUK_INTERNAL_DECL void duk_dup_0(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_dup_1(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_dup_2(duk_context *ctx);
+DUK_INTERNAL_DECL void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes);
+DUK_INTERNAL_DECL duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes);
+DUK_INTERNAL_DECL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug);
+
+DUK_INTERNAL_DECL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count);
+
+DUK_INTERNAL_DECL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count);
+
+DUK_INTERNAL_DECL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start);
+
+DUK_INTERNAL_DECL void duk_dup_0(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_dup_1(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_dup_2(duk_hthread *thr);
 /* duk_dup_m1() would be same as duk_dup_top() */
-DUK_INTERNAL_DECL void duk_dup_m2(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_dup_m3(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_dup_m4(duk_context *ctx);
-
-DUK_INTERNAL_DECL void duk_remove_m2(duk_context *ctx);
+DUK_INTERNAL_DECL void duk_dup_m2(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_dup_m3(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_dup_m4(duk_hthread *thr);
+
+DUK_INTERNAL_DECL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_remove_m2(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);
+DUK_INTERNAL_DECL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);
 
 DUK_INTERNAL_DECL duk_int_t duk_get_type_tval(duk_tval *tv);
 DUK_INTERNAL_DECL duk_uint_t duk_get_type_mask_tval(duk_tval *tv);
 
 #if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
-DUK_INTERNAL_DECL const char *duk_get_type_name(duk_context *ctx, duk_idx_t idx);
-#endif
-DUK_INTERNAL_DECL duk_small_uint_t duk_get_class_number(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_tval *duk_get_tval_or_unused(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL void duk_push_tval(duk_context *ctx, duk_tval *tv);
+DUK_INTERNAL_DECL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx);
+#endif
+DUK_INTERNAL_DECL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_push_tval(duk_hthread *thr, duk_tval *tv);
 
 /* Push the current 'this' binding; throw TypeError if binding is not object
  * coercible (CheckObjectCoercible).
  */
-DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_context *ctx);
+DUK_INTERNAL_DECL void duk_push_this_check_object_coercible(duk_hthread *thr);
 
 /* duk_push_this() + CheckObjectCoercible() + duk_to_object() */
-DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx);
+DUK_INTERNAL_DECL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr);
 
 /* duk_push_this() + CheckObjectCoercible() + duk_to_string() */
-DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx);
-
-DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_context *ctx, duk_uint_t i);
+DUK_INTERNAL_DECL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr);
+
+DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i);
 
 /* Get a borrowed duk_tval pointer to the current 'this' binding.  Caller must
  * make sure there's an active callstack entry.  Note that the returned pointer
  * is unstable with regards to side effects.
  */
-DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx);
+DUK_INTERNAL_DECL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr);
 
 /* XXX: add fastint support? */
-#define duk_push_u64(ctx,val) \
-	duk_push_number((ctx), (duk_double_t) (val))
-#define duk_push_i64(ctx,val) \
-	duk_push_number((ctx), (duk_double_t) (val))
+#define duk_push_u64(thr,val) \
+	duk_push_number((thr), (duk_double_t) (val))
+#define duk_push_i64(thr,val) \
+	duk_push_number((thr), (duk_double_t) (val))
 
 /* duk_push_(u)int() is guaranteed to support at least (un)signed 32-bit range */
-#define duk_push_u32(ctx,val) \
-	duk_push_uint((ctx), (duk_uint_t) (val))
-#define duk_push_i32(ctx,val) \
-	duk_push_int((ctx), (duk_int_t) (val))
+#define duk_push_u32(thr,val) \
+	duk_push_uint((thr), (duk_uint_t) (val))
+#define duk_push_i32(thr,val) \
+	duk_push_int((thr), (duk_int_t) (val))
 
 /* sometimes stack and array indices need to go on the stack */
-#define duk_push_idx(ctx,val) \
-	duk_push_int((ctx), (duk_int_t) (val))
-#define duk_push_uarridx(ctx,val) \
-	duk_push_uint((ctx), (duk_uint_t) (val))
-#define duk_push_size_t(ctx,val) \
-	duk_push_uint((ctx), (duk_uint_t) (val))  /* XXX: assumed to fit for now */
-
-DUK_INTERNAL_DECL duk_bool_t duk_is_string_notsymbol(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hstring *duk_get_hstring_notsymbol(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL const char *duk_get_string_notsymbol(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hcompfunc *duk_get_hcompfunc(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hnatfunc *duk_get_hnatfunc(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len, duk_bool_t throw_flag, duk_bool_t *out_isbuffer);
-
-DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t idx, duk_small_uint_t classnum);
-
-DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_promote_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask);
-DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_promote_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask);
-DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_accept_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask);
-#define duk_require_hobject_promote_lfunc(ctx,idx) \
-	duk_require_hobject_promote_mask((ctx), (idx), DUK_TYPE_MASK_LIGHTFUNC)
-#define duk_get_hobject_promote_lfunc(ctx,idx) \
-	duk_get_hobject_promote_mask((ctx), (idx), DUK_TYPE_MASK_LIGHTFUNC)
+#define duk_push_idx(thr,val) \
+	duk_push_int((thr), (duk_int_t) (val))
+#define duk_push_uarridx(thr,val) \
+	duk_push_uint((thr), (duk_uint_t) (val))
+#define duk_push_size_t(thr,val) \
+	duk_push_uint((thr), (duk_uint_t) (val))  /* XXX: assumed to fit for now */
+
+DUK_INTERNAL_DECL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv);
+
+DUK_INTERNAL_DECL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len, duk_bool_t throw_flag, duk_bool_t *out_isbuffer);
+
+DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum);
+
+DUK_INTERNAL_DECL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
+DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
+DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask);
+#define duk_require_hobject_promote_lfunc(thr,idx) \
+	duk_require_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC)
+#define duk_get_hobject_promote_lfunc(thr,idx) \
+	duk_get_hobject_promote_mask((thr), (idx), DUK_TYPE_MASK_LIGHTFUNC)
 
 #if 0  /*unused*/
-DUK_INTERNAL_DECL void *duk_get_voidptr(duk_context *ctx, duk_idx_t idx);
-#endif
-
-DUK_INTERNAL_DECL duk_hstring *duk_known_hstring(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hobject *duk_known_hobject(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hbuffer *duk_known_hbuffer(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hcompfunc *duk_known_hcompfunc(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hnatfunc *duk_known_hnatfunc(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_double_t duk_to_number_tval(duk_context *ctx, duk_tval *tv);
-
-DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_m1(duk_context *ctx);
-DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_acceptsymbol(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_context *ctx);
-DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_context *ctx);
+DUK_INTERNAL_DECL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx);
+#endif
+
+DUK_INTERNAL_DECL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv);
+
+DUK_INTERNAL_DECL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_m1(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr);
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)  /* only needed by debugger for now */
-DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t idx);
-#endif
-DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv);
-
-DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped);  /* out_clamped=NULL, RangeError if outside range */
-DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
-DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
+DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx);
+#endif
+DUK_INTERNAL_DECL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv);
+
+DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped);  /* out_clamped=NULL, RangeError if outside range */
+DUK_INTERNAL_DECL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
+DUK_INTERNAL_DECL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval);
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t idx);
-#endif
-DUK_INTERNAL_DECL duk_hstring *duk_to_property_key_hstring(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hstring *duk_require_hstring_notsymbol(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL const char *duk_require_lstring_notsymbol(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
-DUK_INTERNAL_DECL const char *duk_require_string_notsymbol(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hcompfunc *duk_require_hcompfunc(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL duk_hnatfunc *duk_require_hnatfunc(duk_context *ctx, duk_idx_t idx);
-
-DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_idx_t idx, duk_small_uint_t classnum);
-
-DUK_INTERNAL_DECL void duk_push_hstring(duk_context *ctx, duk_hstring *h);
-DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_context *ctx, duk_small_uint_t stridx);
-DUK_INTERNAL_DECL void duk_push_hstring_empty(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_push_hobject(duk_context *ctx, duk_hobject *h);
-DUK_INTERNAL_DECL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h);
-#define duk_push_hthread(ctx,h) \
-	duk_push_hobject((ctx), (duk_hobject *) (h))
-#define duk_push_hnatfunc(ctx,h) \
-	duk_push_hobject((ctx), (duk_hobject *) (h))
-DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx);
-DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
-DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto);
-DUK_INTERNAL_DECL duk_hcompfunc *duk_push_hcompfunc(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
-DUK_INTERNAL_DECL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs);
+DUK_INTERNAL_DECL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx);
+#endif
+DUK_INTERNAL_DECL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len);
+DUK_INTERNAL_DECL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx);
+
+DUK_INTERNAL_DECL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum);
+
+DUK_INTERNAL_DECL void duk_push_hstring(duk_hthread *thr, duk_hstring *h);
+DUK_INTERNAL_DECL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx);
+DUK_INTERNAL_DECL void duk_push_hstring_empty(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_push_hobject(duk_hthread *thr, duk_hobject *h);
+DUK_INTERNAL_DECL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h);
+#define duk_push_hthread(thr,h) \
+	duk_push_hobject((thr), (duk_hobject *) (h))
+#define duk_push_hnatfunc(thr,h) \
+	duk_push_hobject((thr), (duk_hobject *) (h))
+DUK_INTERNAL_DECL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx);
+DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
+DUK_INTERNAL_DECL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto);
+DUK_INTERNAL_DECL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs);
+DUK_INTERNAL_DECL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs);
 
 /* XXX: duk_push_harray() and duk_push_hcompfunc() are inconsistent with
  * duk_push_hobject() etc which don't create a new value.
  */
-DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_context *ctx);
-DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_context *ctx, duk_uint32_t size);
-
-DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz);
-DUK_INTERNAL_DECL void duk_push_lightfunc_name_raw(duk_context *ctx, duk_c_function func, duk_small_uint_t lf_flags);
-DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv);
-DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv);
+DUK_INTERNAL_DECL duk_harray *duk_push_harray(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size);
+DUK_INTERNAL_DECL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size);
+
+DUK_INTERNAL_DECL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz);
+DUK_INTERNAL_DECL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags);
+DUK_INTERNAL_DECL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv);
+DUK_INTERNAL_DECL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv);
+#if 0  /* not used yet */
+DUK_INTERNAL_DECL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h);
+#endif
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL_DECL duk_hbufobj *duk_push_bufobj_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
-#endif
-
-DUK_INTERNAL_DECL void *duk_push_fixed_buffer_nozero(duk_context *ctx, duk_size_t len);
-DUK_INTERNAL_DECL void *duk_push_fixed_buffer_zero(duk_context *ctx, duk_size_t len);
-
-DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_context *ctx, duk_tval *tv);
-DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_context *ctx, duk_tval *tv);
+DUK_INTERNAL_DECL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx);
+#endif
+
+DUK_INTERNAL_DECL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len);
+DUK_INTERNAL_DECL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len);
+
+DUK_INTERNAL_DECL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv);
+DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv);
 
 /* The duk_xxx_prop_stridx_short() variants expect their arguments to be short
  * enough to be packed into a single 32-bit integer argument.  Argument limits
@@ -5400,112 +5452,135 @@
  * arguments and such call sites are also easiest to verify to be correct.
  */
 
-DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [val] */
-DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args);
-#define duk_get_prop_stridx_short(ctx,obj_idx,stridx) \
+DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [val] */
+DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
+#define duk_get_prop_stridx_short(thr,obj_idx,stridx) \
 	(DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \
 	 DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
-	 duk_get_prop_stridx_short_raw((ctx), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
-DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop);  /* [] -> [] */
-
-DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [val] -> [] */
-DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args);
-#define duk_put_prop_stridx_short(ctx,obj_idx,stridx) \
+	 duk_get_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
+DUK_INTERNAL_DECL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop);  /* [] -> [] */
+
+DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [val] -> [] */
+DUK_INTERNAL_DECL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
+#define duk_put_prop_stridx_short(thr,obj_idx,stridx) \
 	(DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x8000L && (duk_int_t) (obj_idx) <= 0x7fffL), \
 	 DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
-	 duk_put_prop_stridx_short_raw((ctx), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
-
-DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
+	 duk_put_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
+
+DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
 #if 0  /* Too few call sites to be useful. */
-DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args);
-#define duk_del_prop_stridx_short(ctx,obj_idx,stridx) \
+DUK_INTERNAL_DECL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
+#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \
 	(DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \
 	 DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \
-	 duk_del_prop_stridx_short_raw((ctx), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
-#endif
-#define duk_del_prop_stridx_short(ctx,obj_idx,stridx) \
-	duk_del_prop_stridx((ctx), (obj_idx), (stridx))
-
-DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
+	 duk_del_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
+#endif
+#define duk_del_prop_stridx_short(thr,obj_idx,stridx) \
+	duk_del_prop_stridx((thr), (obj_idx), (stridx))
+
+DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);     /* [] -> [] */
 #if 0  /* Too few call sites to be useful. */
-DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args);
-#define duk_has_prop_stridx_short(ctx,obj_idx,stridx) \
+DUK_INTERNAL_DECL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
+#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \
 	(DUK_ASSERT_EXPR((obj_idx) >= -0x8000L && (obj_idx) <= 0x7fffL), \
 	 DUK_ASSERT_EXPR((stridx) >= 0 && (stridx) <= 0xffffL), \
-	 duk_has_prop_stridx_short_raw((ctx), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
-#endif
-#define duk_has_prop_stridx_short(ctx,obj_idx,stridx) \
-	duk_has_prop_stridx((ctx), (obj_idx), (stridx))
-
-DUK_INTERNAL_DECL void duk_xdef_prop(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t desc_flags);  /* [key val] -> [] */
-
-DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags);  /* [val] -> [] */
+	 duk_has_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 16) + ((duk_uint_t) (stridx))))
+#endif
+#define duk_has_prop_stridx_short(thr,obj_idx,stridx) \
+	duk_has_prop_stridx((thr), (obj_idx), (stridx))
+
+DUK_INTERNAL_DECL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags);  /* [key val] -> [] */
+
+DUK_INTERNAL_DECL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags);  /* [val] -> [] */
 
 /* XXX: Because stridx and desc_flags have a limited range, this call could
  * always pack stridx and desc_flags into a single argument.
  */
-DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags);  /* [val] -> [] */
-DUK_INTERNAL_DECL void duk_xdef_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args);
-#define duk_xdef_prop_stridx_short(ctx,obj_idx,stridx,desc_flags) \
+DUK_INTERNAL_DECL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags);  /* [val] -> [] */
+DUK_INTERNAL_DECL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args);
+#define duk_xdef_prop_stridx_short(thr,obj_idx,stridx,desc_flags) \
 	(DUK_ASSERT_EXPR((duk_int_t) (obj_idx) >= -0x80L && (duk_int_t) (obj_idx) <= 0x7fL), \
 	 DUK_ASSERT_EXPR((duk_int_t) (stridx) >= 0 && (duk_int_t) (stridx) <= 0xffffL), \
 	 DUK_ASSERT_EXPR((duk_int_t) (desc_flags) >= 0 && (duk_int_t) (desc_flags) <= 0xffL), \
-	 duk_xdef_prop_stridx_short_raw((ctx), (((duk_uint_t) (obj_idx)) << 24) + (((duk_uint_t) (stridx)) << 8) + (duk_uint_t) (desc_flags)))
-
-#define duk_xdef_prop_wec(ctx,obj_idx) \
-	duk_xdef_prop((ctx), (obj_idx), DUK_PROPDESC_FLAGS_WEC)
-#define duk_xdef_prop_index_wec(ctx,obj_idx,arr_idx) \
-	duk_xdef_prop_index((ctx), (obj_idx), (arr_idx), DUK_PROPDESC_FLAGS_WEC)
-#define duk_xdef_prop_stridx_wec(ctx,obj_idx,stridx) \
-	duk_xdef_prop_stridx((ctx), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)
-#define duk_xdef_prop_stridx_short_wec(ctx,obj_idx,stridx) \
-	duk_xdef_prop_stridx_short((ctx), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)
-
-DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags);  /* [] -> [] */
-
-DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx);  /* [] -> [] */
-
-DUK_INTERNAL_DECL void duk_pack(duk_context *ctx, duk_idx_t count);
+	 duk_xdef_prop_stridx_short_raw((thr), (((duk_uint_t) (obj_idx)) << 24) + (((duk_uint_t) (stridx)) << 8) + (duk_uint_t) (desc_flags)))
+
+#define duk_xdef_prop_wec(thr,obj_idx) \
+	duk_xdef_prop((thr), (obj_idx), DUK_PROPDESC_FLAGS_WEC)
+#define duk_xdef_prop_index_wec(thr,obj_idx,arr_idx) \
+	duk_xdef_prop_index((thr), (obj_idx), (arr_idx), DUK_PROPDESC_FLAGS_WEC)
+#define duk_xdef_prop_stridx_wec(thr,obj_idx,stridx) \
+	duk_xdef_prop_stridx((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)
+#define duk_xdef_prop_stridx_short_wec(thr,obj_idx,stridx) \
+	duk_xdef_prop_stridx_short((thr), (obj_idx), (stridx), DUK_PROPDESC_FLAGS_WEC)
+
+#if 0  /*unused*/
+DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags);  /* [] -> [] */
+#endif
+
+DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx);  /* [] -> [] */
+
+DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count);
+DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx);
 #if 0
-DUK_INTERNAL_DECL void duk_unpack(duk_context *ctx);
-#endif
-
-DUK_INTERNAL_DECL void duk_require_constructor_call(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx);
-DUK_INTERNAL_DECL void duk_push_symbol_descriptive_string(duk_context *ctx, duk_hstring *h);
-
-DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_context *ctx);
-
-DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_context *ctx, duk_idx_t min_top);
-DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_context *ctx);
-DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_context *ctx, duk_idx_t count);
-DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count);
-DUK_INTERNAL_DECL void duk_pop_unsafe(duk_context *ctx);
-
-DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx);
+DUK_INTERNAL_DECL void duk_unpack(duk_hthread *thr);
+#endif
+
+DUK_INTERNAL_DECL void duk_require_constructor_call(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_require_constructable(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h);
+
+DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_hthread *thr);
+
+DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top);
+DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr);
+
+DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count);
+DUK_INTERNAL_DECL void duk_pop_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_2_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_3_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count);
+DUK_INTERNAL_DECL void duk_pop_nodecref_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_2_nodecref_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_3_nodecref_unsafe(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_pop_undefined(duk_hthread *thr);
+
+DUK_INTERNAL_DECL void duk_compact_m1(duk_hthread *thr);
+
+DUK_INTERNAL_DECL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze);
+
+DUK_INTERNAL_DECL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count);
+
+DUK_INTERNAL_DECL void duk_concat_2(duk_hthread *thr);
+
+DUK_INTERNAL_DECL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags);
 
 /* Raw internal valstack access macros: access is unsafe so call site
  * must have a guarantee that the index is valid.  When that is the case,
  * using these macro results in faster and smaller code than duk_get_tval().
  * Both 'ctx' and 'idx' are evaluted multiple times, but only for asserts.
  */
-#define DUK_ASSERT_VALID_NEGIDX(ctx,idx) \
-	(DUK_ASSERT_EXPR((duk_int_t) (idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx))))
-#define DUK_ASSERT_VALID_POSIDX(ctx,idx) \
-	(DUK_ASSERT_EXPR((duk_int_t) (idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((ctx), (idx))))
-#define DUK_GET_TVAL_NEGIDX(ctx,idx) \
-	(DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_top + (idx))
-#define DUK_GET_TVAL_POSIDX(ctx,idx) \
-	(DUK_ASSERT_VALID_POSIDX((ctx),(idx)), ((duk_hthread *) (ctx))->valstack_bottom + (idx))
-#define DUK_GET_HOBJECT_NEGIDX(ctx,idx) \
-	(DUK_ASSERT_VALID_NEGIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_top + (idx)))
-#define DUK_GET_HOBJECT_POSIDX(ctx,idx) \
-	(DUK_ASSERT_VALID_POSIDX((ctx),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (ctx))->valstack_bottom + (idx)))
+#define DUK_ASSERT_VALID_NEGIDX(thr,idx) \
+	(DUK_ASSERT_EXPR((duk_int_t) (idx) < 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx))))
+#define DUK_ASSERT_VALID_POSIDX(thr,idx) \
+	(DUK_ASSERT_EXPR((duk_int_t) (idx) >= 0), DUK_ASSERT_EXPR(duk_is_valid_index((thr), (idx))))
+#define DUK_GET_TVAL_NEGIDX(thr,idx) \
+	(DUK_ASSERT_VALID_NEGIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_top + (idx))
+#define DUK_GET_TVAL_POSIDX(thr,idx) \
+	(DUK_ASSERT_VALID_POSIDX((thr),(idx)), ((duk_hthread *) (thr))->valstack_bottom + (idx))
+#define DUK_GET_HOBJECT_NEGIDX(thr,idx) \
+	(DUK_ASSERT_VALID_NEGIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_top + (idx)))
+#define DUK_GET_HOBJECT_POSIDX(thr,idx) \
+	(DUK_ASSERT_VALID_POSIDX((thr),(idx)), DUK_TVAL_GET_OBJECT(((duk_hthread *) (thr))->valstack_bottom + (idx)))
 
 #define DUK_GET_THIS_TVAL_PTR(thr) \
 	(DUK_ASSERT_EXPR((thr)->valstack_bottom > (thr)->valstack), \
 	 (thr)->valstack_bottom - 1)
 
+DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr);
+
 #endif  /* DUK_API_INTERNAL_H_INCLUDED */
 /* #include duk_hstring.h */
 #line 1 "duk_hstring.h"
@@ -5646,7 +5721,9 @@
 #define DUK_HSTRING_GET_DATA_END(x) \
 	(DUK_HSTRING_GET_DATA((x)) + (x)->blen)
 
-/* marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest valid) */
+/* Marker value; in E5 2^32-1 is not a valid array index (2^32-2 is highest
+ * valid).
+ */
 #define DUK_HSTRING_NO_ARRAY_INDEX  (0xffffffffUL)
 
 #if defined(DUK_USE_HSTRING_ARRIDX)
@@ -5664,6 +5741,12 @@
 	(duk_js_to_arrayindex_hstring_fast((h)))
 #endif
 
+/* XXX: these actually fit into duk_hstring */
+#define DUK_SYMBOL_TYPE_HIDDEN 0
+#define DUK_SYMBOL_TYPE_GLOBAL 1
+#define DUK_SYMBOL_TYPE_LOCAL 2
+#define DUK_SYMBOL_TYPE_WELLKNOWN 3
+
 /*
  *  Misc
  */
@@ -5732,7 +5815,11 @@
  */
 
 DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk_hstring *h, duk_uint_t pos, duk_bool_t surrogate_aware);
+DUK_INTERNAL_DECL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr);
 DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h);
+#if !defined(DUK_USE_HSTRING_LAZY_CLEN)
+DUK_INTERNAL_DECL void duk_hstring_init_charlen(duk_hstring *h);
+#endif
 
 #endif  /* DUK_HSTRING_H_INCLUDED */
 /* #include duk_hobject.h */
@@ -5780,7 +5867,8 @@
  */
 #define DUK_HOBJECT_FLAG_EXTENSIBLE            DUK_HEAPHDR_USER_FLAG(0)   /* object is extensible */
 #define DUK_HOBJECT_FLAG_CONSTRUCTABLE         DUK_HEAPHDR_USER_FLAG(1)   /* object is constructable */
-#define DUK_HOBJECT_FLAG_BOUNDFUNC             DUK_HEAPHDR_USER_FLAG(2)   /* object established using Function.prototype.bind() */
+#define DUK_HOBJECT_FLAG_CALLABLE              DUK_HEAPHDR_USER_FLAG(2)   /* object is callable */
+#define DUK_HOBJECT_FLAG_BOUNDFUNC             DUK_HEAPHDR_USER_FLAG(3)   /* object established using Function.prototype.bind() */
 #define DUK_HOBJECT_FLAG_COMPFUNC              DUK_HEAPHDR_USER_FLAG(4)   /* object is a compiled function (duk_hcompfunc) */
 #define DUK_HOBJECT_FLAG_NATFUNC               DUK_HEAPHDR_USER_FLAG(5)   /* object is a native function (duk_hnatfunc) */
 #define DUK_HOBJECT_FLAG_BUFOBJ                DUK_HEAPHDR_USER_FLAG(6)   /* object is a buffer object (duk_hbufobj) (always exotic) */
@@ -5791,12 +5879,12 @@
 #define DUK_HOBJECT_FLAG_NEWENV                DUK_HEAPHDR_USER_FLAG(11)  /* function: create new environment when called (see duk_hcompfunc) */
 #define DUK_HOBJECT_FLAG_NAMEBINDING           DUK_HEAPHDR_USER_FLAG(12)  /* function: create binding for func name (function templates only, used for named function expressions) */
 #define DUK_HOBJECT_FLAG_CREATEARGS            DUK_HEAPHDR_USER_FLAG(13)  /* function: create an arguments object on function call */
-#define DUK_HOBJECT_FLAG_HAVE_FINALIZER        DUK_HEAPHDR_USER_FLAG(14)  /* object has a callable finalizer property */
+#define DUK_HOBJECT_FLAG_HAVE_FINALIZER        DUK_HEAPHDR_USER_FLAG(14)  /* object has a callable (own) finalizer property */
 #define DUK_HOBJECT_FLAG_EXOTIC_ARRAY          DUK_HEAPHDR_USER_FLAG(15)  /* 'Array' object, array length and index exotic behavior */
 #define DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ      DUK_HEAPHDR_USER_FLAG(16)  /* 'String' object, array index exotic behavior */
 #define DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS      DUK_HEAPHDR_USER_FLAG(17)  /* 'Arguments' object and has arguments exotic behavior (non-strict callee) */
-#define DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC        DUK_HEAPHDR_USER_FLAG(18)  /* Duktape/C (nativefunction) object, exotic 'length' */
-#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ       DUK_HEAPHDR_USER_FLAG(19)  /* 'Proxy' object */
+#define DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ       DUK_HEAPHDR_USER_FLAG(18)  /* 'Proxy' object */
+#define DUK_HOBJECT_FLAG_SPECIAL_CALL          DUK_HEAPHDR_USER_FLAG(19)  /* special casing in call behavior, for .call(), .apply(), etc. */
 
 #define DUK_HOBJECT_FLAG_CLASS_BASE            DUK_HEAPHDR_USER_FLAG_NUMBER(20)
 #define DUK_HOBJECT_FLAG_CLASS_BITS            5
@@ -5903,8 +5991,17 @@
 #define DUK_HOBJECT_IS_BOUNDFUNC(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
 #define DUK_HOBJECT_IS_COMPFUNC(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
 #define DUK_HOBJECT_IS_NATFUNC(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 #define DUK_HOBJECT_IS_BUFOBJ(h)               DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
+#else
+#define DUK_HOBJECT_IS_BUFOBJ(h)               0
+#endif
 #define DUK_HOBJECT_IS_THREAD(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_THREAD)
+#if defined(DUK_USE_ES6_PROXY)
+#define DUK_HOBJECT_IS_PROXY(h)                DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((h))
+#else
+#define DUK_HOBJECT_IS_PROXY(h)                0
+#endif
 
 #define DUK_HOBJECT_IS_NONBOUND_FUNCTION(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
                                                         DUK_HOBJECT_FLAG_COMPFUNC | \
@@ -5915,16 +6012,12 @@
                                                         DUK_HOBJECT_FLAG_COMPFUNC | \
                                                         DUK_HOBJECT_FLAG_NATFUNC)
 
-#define DUK_HOBJECT_IS_CALLABLE(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \
-                                                        DUK_HOBJECT_FLAG_BOUNDFUNC | \
-                                                        DUK_HOBJECT_FLAG_COMPFUNC | \
-                                                        DUK_HOBJECT_FLAG_NATFUNC)
+#define DUK_HOBJECT_IS_CALLABLE(h)             DUK_HOBJECT_HAS_CALLABLE((h))
 
 /* Object has any exotic behavior(s). */
 #define DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS      (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
                                                 DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS | \
                                                 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \
-                                                DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | \
                                                 DUK_HOBJECT_FLAG_BUFOBJ | \
                                                 DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
 #define DUK_HOBJECT_HAS_EXOTIC_BEHAVIOR(h)     DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS)
@@ -5932,16 +6025,20 @@
 /* Object has any virtual properties (not counting Proxy behavior). */
 #define DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS     (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \
                                                 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | \
-                                                DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC | \
                                                 DUK_HOBJECT_FLAG_BUFOBJ)
 #define DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(h)  DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_VIRTUAL_PROPERTY_FLAGS)
 
 #define DUK_HOBJECT_HAS_EXTENSIBLE(h)          DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
 #define DUK_HOBJECT_HAS_CONSTRUCTABLE(h)       DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
+#define DUK_HOBJECT_HAS_CALLABLE(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
 #define DUK_HOBJECT_HAS_BOUNDFUNC(h)           DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
 #define DUK_HOBJECT_HAS_COMPFUNC(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
 #define DUK_HOBJECT_HAS_NATFUNC(h)             DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 #define DUK_HOBJECT_HAS_BUFOBJ(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
+#else
+#define DUK_HOBJECT_HAS_BUFOBJ(h)              0
+#endif
 #define DUK_HOBJECT_HAS_FASTREFS(h)            DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
 #define DUK_HOBJECT_HAS_ARRAY_PART(h)          DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
 #define DUK_HOBJECT_HAS_STRICT(h)              DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
@@ -5953,15 +6050,22 @@
 #define DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)        DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
 #define DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
 #define DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)    DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
-#define DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)      DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC)
+#if defined(DUK_USE_ES6_PROXY)
 #define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)     DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
+#else
+#define DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)     0
+#endif
+#define DUK_HOBJECT_HAS_SPECIAL_CALL(h)        DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)
 
 #define DUK_HOBJECT_SET_EXTENSIBLE(h)          DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
 #define DUK_HOBJECT_SET_CONSTRUCTABLE(h)       DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
+#define DUK_HOBJECT_SET_CALLABLE(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
 #define DUK_HOBJECT_SET_BOUNDFUNC(h)           DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
 #define DUK_HOBJECT_SET_COMPFUNC(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
 #define DUK_HOBJECT_SET_NATFUNC(h)             DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 #define DUK_HOBJECT_SET_BUFOBJ(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
+#endif
 #define DUK_HOBJECT_SET_FASTREFS(h)            DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
 #define DUK_HOBJECT_SET_ARRAY_PART(h)          DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
 #define DUK_HOBJECT_SET_STRICT(h)              DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
@@ -5973,15 +6077,20 @@
 #define DUK_HOBJECT_SET_EXOTIC_ARRAY(h)        DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
 #define DUK_HOBJECT_SET_EXOTIC_STRINGOBJ(h)    DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
 #define DUK_HOBJECT_SET_EXOTIC_ARGUMENTS(h)    DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
-#define DUK_HOBJECT_SET_EXOTIC_DUKFUNC(h)      DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC)
+#if defined(DUK_USE_ES6_PROXY)
 #define DUK_HOBJECT_SET_EXOTIC_PROXYOBJ(h)     DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
+#endif
+#define DUK_HOBJECT_SET_SPECIAL_CALL(h)        DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)
 
 #define DUK_HOBJECT_CLEAR_EXTENSIBLE(h)        DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXTENSIBLE)
 #define DUK_HOBJECT_CLEAR_CONSTRUCTABLE(h)     DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CONSTRUCTABLE)
+#define DUK_HOBJECT_CLEAR_CALLABLE(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CALLABLE)
 #define DUK_HOBJECT_CLEAR_BOUNDFUNC(h)         DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BOUNDFUNC)
 #define DUK_HOBJECT_CLEAR_COMPFUNC(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_COMPFUNC)
 #define DUK_HOBJECT_CLEAR_NATFUNC(h)           DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NATFUNC)
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 #define DUK_HOBJECT_CLEAR_BUFOBJ(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ)
+#endif
 #define DUK_HOBJECT_CLEAR_FASTREFS(h)          DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_FASTREFS)
 #define DUK_HOBJECT_CLEAR_ARRAY_PART(h)        DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ARRAY_PART)
 #define DUK_HOBJECT_CLEAR_STRICT(h)            DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_STRICT)
@@ -5993,25 +6102,29 @@
 #define DUK_HOBJECT_CLEAR_EXOTIC_ARRAY(h)      DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARRAY)
 #define DUK_HOBJECT_CLEAR_EXOTIC_STRINGOBJ(h)  DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ)
 #define DUK_HOBJECT_CLEAR_EXOTIC_ARGUMENTS(h)  DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS)
-#define DUK_HOBJECT_CLEAR_EXOTIC_DUKFUNC(h)    DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC)
+#if defined(DUK_USE_ES6_PROXY)
 #define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h)   DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ)
+#endif
+#define DUK_HOBJECT_CLEAR_SPECIAL_CALL(h)      DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_SPECIAL_CALL)
 
 /* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond
- * duk_hobject base header.
+ * duk_hobject base header.  This is used just for asserts so doesn't need to
+ * be optimized.
  */
 #define DUK_HOBJECT_PROHIBITS_FASTREFS(h) \
 	(DUK_HOBJECT_IS_COMPFUNC((h)) || DUK_HOBJECT_IS_DECENV((h)) || DUK_HOBJECT_IS_OBJENV((h)) || \
-	 DUK_HOBJECT_IS_BUFOBJ((h)) || DUK_HOBJECT_IS_THREAD((h)))
+	 DUK_HOBJECT_IS_BUFOBJ((h)) || DUK_HOBJECT_IS_THREAD((h)) || DUK_HOBJECT_IS_PROXY((h)) || \
+	 DUK_HOBJECT_IS_BOUNDFUNC((h)))
 #define DUK_HOBJECT_ALLOWS_FASTREFS(h) (!DUK_HOBJECT_PROHIBITS_FASTREFS((h)))
 
 /* Flags used for property attributes in duk_propdesc and packed flags.
  * Must fit into 8 bits.
  */
-#define DUK_PROPDESC_FLAG_WRITABLE              (1 << 0)    /* E5 Section 8.6.1 */
-#define DUK_PROPDESC_FLAG_ENUMERABLE            (1 << 1)    /* E5 Section 8.6.1 */
-#define DUK_PROPDESC_FLAG_CONFIGURABLE          (1 << 2)    /* E5 Section 8.6.1 */
-#define DUK_PROPDESC_FLAG_ACCESSOR              (1 << 3)    /* accessor */
-#define DUK_PROPDESC_FLAG_VIRTUAL               (1 << 4)    /* property is virtual: used in duk_propdesc, never stored
+#define DUK_PROPDESC_FLAG_WRITABLE              (1U << 0)    /* E5 Section 8.6.1 */
+#define DUK_PROPDESC_FLAG_ENUMERABLE            (1U << 1)    /* E5 Section 8.6.1 */
+#define DUK_PROPDESC_FLAG_CONFIGURABLE          (1U << 2)    /* E5 Section 8.6.1 */
+#define DUK_PROPDESC_FLAG_ACCESSOR              (1U << 3)    /* accessor */
+#define DUK_PROPDESC_FLAG_VIRTUAL               (1U << 4)    /* property is virtual: used in duk_propdesc, never stored
                                                              * (used by e.g. buffer virtual properties)
                                                              */
 #define DUK_PROPDESC_FLAGS_MASK                 (DUK_PROPDESC_FLAG_WRITABLE | \
@@ -6022,7 +6135,7 @@
 /* Additional flags which are passed in the same flags argument as property
  * flags but are not stored in object properties.
  */
-#define DUK_PROPDESC_FLAG_NO_OVERWRITE          (1 << 4)    /* internal define property: skip write silently if exists */
+#define DUK_PROPDESC_FLAG_NO_OVERWRITE          (1U << 4)    /* internal define property: skip write silently if exists */
 
 /* Convenience defines for property attributes. */
 #define DUK_PROPDESC_FLAGS_NONE                 0
@@ -6037,8 +6150,8 @@
                                                  DUK_PROPDESC_FLAG_CONFIGURABLE)
 
 /* Flags for duk_hobject_get_own_propdesc() and variants. */
-#define DUK_GETDESC_FLAG_PUSH_VALUE          (1 << 0)  /* push value to stack */
-#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP    (1 << 1)  /* don't throw for prototype loop */
+#define DUK_GETDESC_FLAG_PUSH_VALUE          (1U << 0)  /* push value to stack */
+#define DUK_GETDESC_FLAG_IGNORE_PROTOLOOP    (1U << 1)  /* don't throw for prototype loop */
 
 /*
  *  Macro for object validity check
@@ -6347,9 +6460,6 @@
  */
 #define DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY      10000L
 
-/* Maximum traversal depth for "bound function" chains. */
-#define DUK_HOBJECT_BOUND_CHAIN_SANITY          10000L
-
 /*
  *  Ecmascript [[Class]]
  */
@@ -6381,9 +6491,22 @@
 	} while (0)
 #endif
 
-/* note: this updates refcounts */
+/* Set prototype, DECREF earlier value, INCREF new value (tolerating NULLs). */
 #define DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr,h,p)       duk_hobject_set_prototype_updref((thr), (h), (p))
 
+/* Set initial prototype, assume NULL previous prototype, INCREF new value,
+ * tolerate NULL.
+ */
+#define DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr,h,proto) do { \
+		duk_hthread *duk__thr = (thr); \
+		duk_hobject *duk__obj = (h); \
+		duk_hobject *duk__proto = (proto); \
+		DUK_UNREF(duk__thr); \
+		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(duk__thr->heap, duk__obj) == NULL); \
+		DUK_HOBJECT_SET_PROTOTYPE(duk__thr->heap, duk__obj, duk__proto); \
+		DUK_HOBJECT_INCREF_ALLOWNULL(duk__thr, duk__proto); \
+	} while (0)
+
 /*
  *  Finalizer check
  */
@@ -6579,6 +6702,7 @@
 DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
+DUK_INTERNAL_DECL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags);
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 #endif
@@ -6586,6 +6710,7 @@
 DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 DUK_INTERNAL_DECL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 DUK_INTERNAL_DECL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
+DUK_INTERNAL_DECL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags);
 
 /* resize */
 DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr,
@@ -6594,11 +6719,19 @@
                                                  duk_uint32_t new_a_size,
                                                  duk_uint32_t new_h_size,
                                                  duk_bool_t abandon_array);
+DUK_INTERNAL_DECL void duk_hobject_resize_entrypart(duk_hthread *thr,
+                                                    duk_hobject *obj,
+                                                    duk_uint32_t new_e_size);
+#if 0  /*unused*/
+DUK_INTERNAL_DECL void duk_hobject_resize_arraypart(duk_hthread *thr,
+                                                    duk_hobject *obj,
+                                                    duk_uint32_t new_a_size);
+#endif
 
 /* low-level property functions */
-DUK_INTERNAL_DECL 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_INTERNAL_DECL duk_bool_t 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_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_hstring *key);
-DUK_INTERNAL_DECL 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_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint_t *out_attrs);
 DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i);
 DUK_INTERNAL_DECL 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);
 
@@ -6615,8 +6748,8 @@
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key);
 
 /* internal property functions */
-#define DUK_DELPROP_FLAG_THROW  (1 << 0)
-#define DUK_DELPROP_FLAG_FORCE  (1 << 1)
+#define DUK_DELPROP_FLAG_THROW  (1U << 0)
+#define DUK_DELPROP_FLAG_FORCE  (1U << 1)
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags);
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobject *obj, duk_hstring *key);
 DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags);
@@ -6629,28 +6762,26 @@
 #endif
 
 /* helpers for defineProperty() and defineProperties() */
-DUK_INTERNAL_DECL
-void duk_hobject_prepare_property_descriptor(duk_context *ctx,
-                                             duk_idx_t idx_in,
-                                             duk_uint_t *out_defprop_flags,
-                                             duk_idx_t *out_idx_value,
-                                             duk_hobject **out_getter,
-                                             duk_hobject **out_setter);
-DUK_INTERNAL_DECL
-duk_bool_t duk_hobject_define_property_helper(duk_context *ctx,
-                                              duk_uint_t defprop_flags,
-                                              duk_hobject *obj,
-                                              duk_hstring *key,
-                                              duk_idx_t idx_value,
-                                              duk_hobject *get,
-                                              duk_hobject *set,
-                                              duk_bool_t throw_flag);
+DUK_INTERNAL_DECL void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
+                                                               duk_idx_t idx_in,
+                                                               duk_uint_t *out_defprop_flags,
+                                                               duk_idx_t *out_idx_value,
+                                                               duk_hobject **out_getter,
+                                                               duk_hobject **out_setter);
+DUK_INTERNAL_DECL duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr,
+                                                                duk_uint_t defprop_flags,
+                                                                duk_hobject *obj,
+                                                                duk_hstring *key,
+                                                                duk_idx_t idx_value,
+                                                                duk_hobject *get,
+                                                                duk_hobject *set,
+                                                                duk_bool_t throw_flag);
 
 /* Object built-in methods */
-DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_context *ctx, duk_idx_t obj_idx);
+DUK_INTERNAL_DECL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx);
 DUK_INTERNAL_DECL void duk_hobject_object_seal_freeze_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_freeze);
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread *thr, duk_hobject *obj, duk_bool_t is_frozen);
-DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_context *ctx, duk_small_uint_t required_desc_flags);
+DUK_INTERNAL_DECL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags);
 
 /* internal properties */
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_get_internal_value(duk_heap *heap, duk_hobject *obj, duk_tval *tv);
@@ -6661,14 +6792,14 @@
 
 /* ES2015 proxy */
 #if defined(DUK_USE_ES6_PROXY)
-DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hthread *thr, duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler);
-DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hthread *thr, duk_hobject *obj);
+DUK_INTERNAL_DECL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler);
+DUK_INTERNAL_DECL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj);
 #endif
 
 /* enumeration */
-DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint_t enum_flags);
-DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_small_uint_t enum_flags);
-DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value);
+DUK_INTERNAL_DECL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags);
+DUK_INTERNAL_DECL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags);
+DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value);
 
 /* macros */
 DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p);
@@ -6676,7 +6807,7 @@
 /* pc2line */
 #if defined(DUK_USE_PC2LINE)
 DUK_INTERNAL_DECL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length);
-DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_idx_t idx_func, duk_uint_fast32_t pc);
+DUK_INTERNAL_DECL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc);
 #endif
 
 /* misc */
@@ -6686,8 +6817,8 @@
 /* These declarations are needed when related built-in is disabled and
  * genbuiltins.py won't automatically emit the declerations.
  */
-DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx);
-DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_hthread *thr);
 #endif
 
 #endif  /* DUK_HOBJECT_H_INCLUDED */
@@ -6822,6 +6953,13 @@
 #define DUK_HCOMPFUNC_GET_CODE_COUNT(heap,h)  \
 	((duk_size_t) (DUK_HCOMPFUNC_GET_CODE_SIZE((heap), (h)) / sizeof(duk_instr_t)))
 
+/*
+ *  Validity assert
+ */
+
+#define DUK_ASSERT_HCOMPFUNC_VALID(h) do { \
+		DUK_ASSERT((h) != NULL); \
+	} while (0)
 
 /*
  *  Main struct
@@ -6985,11 +7123,53 @@
 	 * versa.
 	 *
 	 * Note: cannot place nargs/magic into the heaphdr flags, because
-	 * duk_hobject takes almost all flags already (and needs the spare).
+	 * duk_hobject takes almost all flags already.
 	 */
 };
 
 #endif  /* DUK_HNATFUNC_H_INCLUDED */
+/* #include duk_hboundfunc.h */
+#line 1 "duk_hboundfunc.h"
+/*
+ *  Bound function representation.
+ */
+
+#if !defined(DUK_HBOUNDFUNC_H_INCLUDED)
+#define DUK_HBOUNDFUNC_H_INCLUDED
+
+/* Artificial limit for args length.  Ensures arithmetic won't overflow
+ * 32 bits when combining bound functions.
+ */
+#define DUK_HBOUNDFUNC_MAX_ARGS 0x20000000UL
+
+#define DUK_ASSERT_HBOUNDFUNC_VALID(h) do { \
+		DUK_ASSERT((h) != NULL); \
+		DUK_ASSERT(DUK_HOBJECT_IS_BOUNDFUNC((duk_hobject *) (h))); \
+		DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&(h)->target) || \
+		           (DUK_TVAL_IS_OBJECT(&(h)->target) && \
+		            DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&(h)->target)))); \
+		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&(h)->this_binding)); \
+		DUK_ASSERT((h)->nargs == 0 || (h)->args != NULL); \
+	} while (0)
+
+struct duk_hboundfunc {
+	/* Shared object part. */
+	duk_hobject obj;
+
+	/* Final target function, stored as duk_tval so that lightfunc can be
+	 * represented too.
+	 */
+	duk_tval target;
+
+	/* This binding. */
+	duk_tval this_binding;
+
+	/* Arguments to prepend. */
+	duk_tval *args;  /* Separate allocation. */
+	duk_idx_t nargs;
+};
+
+#endif  /* DUK_HBOUNDFUNC_H_INCLUDED */
 /* #include duk_hbufobj.h */
 #line 1 "duk_hbufobj.h"
 /*
@@ -7128,9 +7308,9 @@
 
 DUK_INTERNAL_DECL duk_uint_t duk_hbufobj_clamp_bytelength(duk_hbufobj *h_bufobj, duk_uint_t len);
 DUK_INTERNAL_DECL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf);
-DUK_INTERNAL_DECL void duk_hbufobj_push_validated_read(duk_context *ctx, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
-DUK_INTERNAL_DECL void duk_hbufobj_validated_write(duk_context *ctx, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
-DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx);
+DUK_INTERNAL_DECL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
+DUK_INTERNAL_DECL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size);
+DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx);
 
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 #endif  /* DUK_HBUFOBJ_H_INCLUDED */
@@ -7139,8 +7319,9 @@
 /*
  *  Heap thread object representation.
  *
- *  duk_hthread is also the 'context' (duk_context) for exposed APIs
- *  which mostly operate on the topmost frame of the value stack.
+ *  duk_hthread is also the 'context' for public API functions via a
+ *  different typedef.  Most API calls operate on the topmost frame
+ *  of the value stack only.
  */
 
 #if !defined(DUK_HTHREAD_H_INCLUDED)
@@ -7150,61 +7331,46 @@
  *  Stack constants
  */
 
-#define DUK_VALSTACK_GROW_STEP          128     /* roughly 1 kiB */
-#define DUK_VALSTACK_SHRINK_THRESHOLD   256     /* roughly 2 kiB */
-#define DUK_VALSTACK_SHRINK_SPARE       64      /* roughly 0.5 kiB */
-#define DUK_VALSTACK_INITIAL_SIZE       128     /* roughly 1.0 kiB -> but rounds up to DUK_VALSTACK_GROW_STEP in practice */
-#define DUK_VALSTACK_INTERNAL_EXTRA     64      /* internal extra elements assumed on function entry,
-                                                 * always added to user-defined 'extra' for e.g. the
-                                                 * duk_check_stack() call.
-                                                 */
+/* Initial valstack size, roughly 0.7kiB. */
+#define DUK_VALSTACK_INITIAL_SIZE       96U
+
+/* Internal extra elements assumed on function entry, always added to
+ * user-defined 'extra' for e.g. the duk_check_stack() call.
+ */
+#define DUK_VALSTACK_INTERNAL_EXTRA     32U
+
+/* Number of elements guaranteed to be user accessible (in addition to call
+ * arguments) on Duktape/C function entry.  This is the major public API
+ * commitment.
+ */
 #define DUK_VALSTACK_API_ENTRY_MINIMUM  DUK_API_ENTRY_STACK
-                                                /* number of elements guaranteed to be user accessible
-                                                 * (in addition to call arguments) on Duktape/C function entry.
-                                                 */
-
-/* Note: DUK_VALSTACK_INITIAL_SIZE must be >= DUK_VALSTACK_API_ENTRY_MINIMUM
- * + DUK_VALSTACK_INTERNAL_EXTRA so that the initial stack conforms to spare
- * requirements.
- */
-
-#define DUK_VALSTACK_DEFAULT_MAX        1000000L
-
-#define DUK_CALLSTACK_GROW_STEP         8       /* roughly 256 bytes */
-#define DUK_CALLSTACK_SHRINK_THRESHOLD  16      /* roughly 512 bytes */
-#define DUK_CALLSTACK_SHRINK_SPARE      8       /* roughly 256 bytes */
-#define DUK_CALLSTACK_INITIAL_SIZE      8
-#define DUK_CALLSTACK_DEFAULT_MAX       10000L
-
-#define DUK_CATCHSTACK_GROW_STEP         4      /* roughly 64 bytes */
-#define DUK_CATCHSTACK_SHRINK_THRESHOLD  8      /* roughly 128 bytes */
-#define DUK_CATCHSTACK_SHRINK_SPARE      4      /* roughly 64 bytes */
-#define DUK_CATCHSTACK_INITIAL_SIZE      4
-#define DUK_CATCHSTACK_DEFAULT_MAX       10000L
 
 /*
  *  Activation defines
  */
 
-#define DUK_ACT_FLAG_STRICT             (1 << 0)  /* function executes in strict mode */
-#define DUK_ACT_FLAG_TAILCALLED         (1 << 1)  /* activation has tail called one or more times */
-#define DUK_ACT_FLAG_CONSTRUCT          (1 << 2)  /* function executes as a constructor (called via "new") */
-#define DUK_ACT_FLAG_PREVENT_YIELD      (1 << 3)  /* activation prevents yield (native call or "new") */
-#define DUK_ACT_FLAG_DIRECT_EVAL        (1 << 4)  /* activation is a direct eval call */
-#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE  (1 << 5)  /* activation has active breakpoint(s) */
-
-#define DUK_ACT_GET_FUNC(act)        ((act)->func)
+#define DUK_ACT_FLAG_STRICT             (1U << 0)  /* function executes in strict mode */
+#define DUK_ACT_FLAG_TAILCALLED         (1U << 1)  /* activation has tail called one or more times */
+#define DUK_ACT_FLAG_CONSTRUCT          (1U << 2)  /* function executes as a constructor (called via "new") */
+#define DUK_ACT_FLAG_PREVENT_YIELD      (1U << 3)  /* activation prevents yield (native call or "new") */
+#define DUK_ACT_FLAG_DIRECT_EVAL        (1U << 4)  /* activation is a direct eval call */
+#define DUK_ACT_FLAG_CONSTRUCT_PROXY    (1U << 5)  /* activation is for Proxy 'construct' call, special return value handling */
+#define DUK_ACT_FLAG_BREAKPOINT_ACTIVE  (1U << 6)  /* activation has active breakpoint(s) */
+
+#define DUK_ACT_GET_FUNC(act)           ((act)->func)
 
 /*
  *  Flags for __FILE__ / __LINE__ registered into tracedata
  */
 
-#define DUK_TB_FLAG_NOBLAME_FILELINE   (1 << 0)  /* don't report __FILE__ / __LINE__ as fileName/lineNumber */
+#define DUK_TB_FLAG_NOBLAME_FILELINE    (1U << 0)  /* don't report __FILE__ / __LINE__ as fileName/lineNumber */
 
 /*
  *  Catcher defines
  */
 
+/* XXX: remove catcher type entirely */
+
 /* flags field: LLLLLLFT, L = label (24 bits), F = flags (4 bits), T = type (4 bits) */
 #define DUK_CAT_TYPE_MASK            0x0000000fUL
 #define DUK_CAT_TYPE_BITS            4
@@ -7212,10 +7378,10 @@
 #define DUK_CAT_LABEL_BITS           24
 #define DUK_CAT_LABEL_SHIFT          8
 
-#define DUK_CAT_FLAG_CATCH_ENABLED          (1 << 4)   /* catch part will catch */
-#define DUK_CAT_FLAG_FINALLY_ENABLED        (1 << 5)   /* finally part will catch */
-#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED  (1 << 6)   /* request to create catch binding */
-#define DUK_CAT_FLAG_LEXENV_ACTIVE          (1 << 7)   /* catch or with binding is currently active */
+#define DUK_CAT_FLAG_CATCH_ENABLED          (1U << 4)   /* catch part will catch */
+#define DUK_CAT_FLAG_FINALLY_ENABLED        (1U << 5)   /* finally part will catch */
+#define DUK_CAT_FLAG_CATCH_BINDING_ENABLED  (1U << 6)   /* request to create catch binding */
+#define DUK_CAT_FLAG_LEXENV_ACTIVE          (1U << 7)   /* catch or with binding is currently active */
 
 #define DUK_CAT_TYPE_UNKNOWN         0
 #define DUK_CAT_TYPE_TCF             1
@@ -7296,25 +7462,39 @@
  *  diagnose behavior so it's worth checking even when the check is not 100%.
  */
 
-#if defined(DUK_USE_PREFER_SIZE)
-#define DUK_ASSERT_CTX_VSSIZE(ctx)  /*nop*/
-#else
-#define DUK_ASSERT_CTX_VSSIZE(ctx) \
-	DUK_ASSERT((duk_size_t) (((duk_hthread *) (ctx))->valstack_end - ((duk_hthread *) (ctx))->valstack) == \
-		((duk_hthread *) (ctx))->valstack_size)
-#endif
-#define DUK_ASSERT_CTX_VALID(ctx) do { \
-		DUK_ASSERT((ctx) != NULL); \
-		DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) (ctx)) == DUK_HTYPE_OBJECT); \
-		DUK_ASSERT(DUK_HOBJECT_IS_THREAD((duk_hobject *) (ctx))); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->unused1 == 0); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->unused2 == 0); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->valstack != NULL); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->valstack_end >= ((duk_hthread *) (ctx))->valstack); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->valstack_top >= ((duk_hthread *) (ctx))->valstack); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->valstack_top >= ((duk_hthread *) (ctx))->valstack_bottom); \
-		DUK_ASSERT(((duk_hthread *) (ctx))->valstack_end >= ((duk_hthread *) (ctx))->valstack_top); \
-		DUK_ASSERT_CTX_VSSIZE((ctx)); \
+/* Assertions for internals. */
+#define DUK_ASSERT_HTHREAD_VALID(thr) do { \
+		DUK_ASSERT((thr) != NULL); \
+		DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) (thr)) == DUK_HTYPE_OBJECT); \
+		DUK_ASSERT(DUK_HOBJECT_IS_THREAD((duk_hobject *) (thr))); \
+		DUK_ASSERT((thr)->unused1 == 0); \
+		DUK_ASSERT((thr)->unused2 == 0); \
+	} while (0)
+
+/* Assertions for public API calls; a bit stronger. */
+#define DUK_ASSERT_CTX_VALID(thr) do { \
+		DUK_ASSERT((thr) != NULL); \
+		DUK_ASSERT_HTHREAD_VALID((thr)); \
+		DUK_ASSERT((thr)->valstack != NULL); \
+		DUK_ASSERT((thr)->valstack_bottom != NULL); \
+		DUK_ASSERT((thr)->valstack_top != NULL); \
+		DUK_ASSERT((thr)->valstack_end != NULL); \
+		DUK_ASSERT((thr)->valstack_alloc_end != NULL); \
+		DUK_ASSERT((thr)->valstack_alloc_end >= (thr)->valstack); \
+		DUK_ASSERT((thr)->valstack_end >= (thr)->valstack); \
+		DUK_ASSERT((thr)->valstack_top >= (thr)->valstack); \
+		DUK_ASSERT((thr)->valstack_top >= (thr)->valstack_bottom); \
+		DUK_ASSERT((thr)->valstack_end >= (thr)->valstack_top); \
+		DUK_ASSERT((thr)->valstack_alloc_end >= (thr)->valstack_end); \
+	} while (0)
+
+/* Assertions for API call entry specifically.  Checks 'ctx' but also may
+ * check internal state (e.g. not in a debugger transport callback).
+ */
+#define DUK_ASSERT_API_ENTRY(thr) do { \
+		DUK_ASSERT_CTX_VALID((thr)); \
+		DUK_ASSERT((thr)->heap != NULL); \
+		DUK_ASSERT((thr)->heap->dbg_calling_transport == 0); \
 	} while (0)
 
 /*
@@ -7341,16 +7521,15 @@
  *  Struct defines
  */
 
-/* XXX: for a memory-code tradeoff, remove 'func' and make it's access either a function
- * or a macro.  This would make the activation 32 bytes long on 32-bit platforms again.
- */
-
-/* Note: it's nice if size is 2^N (at least for 32-bit platforms). */
+/* Fields are ordered for alignment/packing. */
 struct duk_activation {
 	duk_tval tv_func;       /* borrowed: full duk_tval for function being executed; for lightfuncs */
 	duk_hobject *func;      /* borrowed: function being executed; for bound function calls, this is the final, real function, NULL for lightfuncs */
+	duk_activation *parent; /* previous (parent) activation (or NULL if none) */
 	duk_hobject *var_env;   /* current variable environment (may be NULL if delayed) */
 	duk_hobject *lex_env;   /* current lexical environment (may be NULL if delayed) */
+	duk_catcher *cat;       /* current catcher (or NULL) */
+
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
 	/* Previous value of 'func' caller, restored when unwound.  Only in use
 	 * when 'func' is non-strict.
@@ -7359,52 +7538,61 @@
 #endif
 
 	duk_instr_t *curr_pc;   /* next instruction to execute (points to 'func' bytecode, stable pointer), NULL for native calls */
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-	duk_uint32_t prev_line; /* needed for stepping */
-#endif
-	duk_small_uint_t flags;
-
-	/* idx_bottom and idx_retval are only used for book-keeping of
-	 * Ecmascript-initiated calls, to allow returning to an Ecmascript
-	 * function properly.  They are duk_size_t to match the convention
-	 * that value stack sizes are duk_size_t and local frame indices
-	 * are duk_idx_t.
+
+	/* bottom_byteoff and retval_byteoff are only used for book-keeping
+	 * of Ecmascript-initiated calls, to allow returning to an Ecmascript
+	 * function properly.
 	 */
 
 	/* Bottom of valstack for this activation, used to reset
-	 * valstack_bottom on return; index is absolute.  Note:
-	 * idx_top not needed because top is set to 'nregs' always
-	 * when returning to an Ecmascript activation.
-	 */
-	duk_size_t idx_bottom;
+	 * valstack_bottom on return; offset is absolute.  There's
+	 * no need to track 'top' because native call handling deals
+	 * with that using locals, and for Ecmascript returns 'nregs'
+	 * indicates the necessary top.
+	 */
+	duk_size_t bottom_byteoff;
 
 	/* Return value when returning to this activation (points to caller
-	 * reg, not callee reg); index is absolute (only set if activation is
+	 * reg, not callee reg); offset is absolute (only set if activation is
 	 * not topmost).
 	 *
-	 * Note: idx_bottom is always set, while idx_retval is only applicable
-	 * for activations below the topmost one.  Currently idx_retval for
-	 * the topmost activation is considered garbage (and it not initialized
-	 * on entry or cleared on return; may contain previous or garbage
-	 * values).
-	 */
-	duk_size_t idx_retval;
-
-	/* Current 'this' binding is the value just below idx_bottom.
+	 * Note: bottom_byteoff is always set, while retval_byteoff is only
+	 * applicable for activations below the topmost one.  Currently
+	 * retval_byteoff for the topmost activation is considered garbage
+	 * (and it not initialized on entry or cleared on return; may contain
+	 * previous or garbage values).
+	 */
+	duk_size_t retval_byteoff;
+
+	/* Current 'this' binding is the value just below bottom.
 	 * Previously, 'this' binding was handled with an index to the
 	 * (calling) valstack.  This works for everything except tail
-	 * calls, which must not "cumulate" valstack temps.
-	 */
-};
-
-/* Note: it's nice if size is 2^N (not 4x4 = 16 bytes on 32 bit) */
+	 * calls, which must not "accumulate" valstack temps.
+	 */
+
+	/* Value stack reserve (valstack_end) byte offset to be restored
+	 * when returning to this activation.  Only used by the bytecode
+	 * executor.
+	 */
+	duk_size_t reserve_byteoff;
+
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+	duk_uint32_t prev_line; /* needed for stepping */
+#endif
+
+	duk_small_uint_t flags;
+};
+
 struct duk_catcher {
+	duk_catcher *parent;            /* previous (parent) catcher (or NULL if none) */
 	duk_hstring *h_varname;         /* borrowed reference to catch variable name (or NULL if none) */
 	                                /* (reference is valid as long activation exists) */
 	duk_instr_t *pc_base;           /* resume execution from pc_base or pc_base+1 (points to 'func' bytecode, stable pointer) */
-	duk_size_t callstack_index;     /* callstack index of related activation */
 	duk_size_t idx_base;            /* idx_base and idx_base+1 get completion value and type */
 	duk_uint32_t flags;             /* type and control flags, label number */
+	/* XXX: could pack 'flags' and 'idx_base' to same value in practice,
+	 * on 32-bit targets this would make duk_catcher 16 bytes.
+	 */
 };
 
 struct duk_hthread {
@@ -7429,40 +7617,49 @@
 	duk_uint8_t unused1;
 	duk_uint8_t unused2;
 
-	/* Sanity limits for stack sizes. */
-	duk_size_t valstack_max;
-	duk_size_t callstack_max;
-	duk_size_t catchstack_max;
-
-	/* XXX: Valstack, callstack, and catchstack are currently assumed
-	 * to have non-NULL pointers.  Relaxing this would not lead to big
-	 * benefits (except perhaps for terminated threads).
-	 */
-
-	/* Value stack: these are expressed as pointers for faster stack manipulation.
-	 * [valstack,valstack_top[ is GC-reachable, [valstack_top,valstack_end[ is
-	 * not GC-reachable but kept initialized as 'undefined'.
+	/* XXX: Valstack and callstack are currently assumed to have non-NULL
+	 * pointers.  Relaxing this would not lead to big benefits (except
+	 * perhaps for terminated threads).
+	 */
+
+	/* Value stack: these are expressed as pointers for faster stack
+	 * manipulation.  [valstack,valstack_top[ is GC-reachable,
+	 * [valstack_top,valstack_alloc_end[ is not GC-reachable but kept
+	 * initialized as 'undefined'.  [valstack,valstack_end[ is the
+	 * guaranteed/reserved space and the valstack cannot be resized to
+	 * a smaller size.  [valstack_end,valstack_alloc_end[ is currently
+	 * allocated slack that can be used to grow the current guaranteed
+	 * space but may be shrunk away without notice.
+	 *
+	 *
+	 * <----------------------- guaranteed --->
+	 *                                        <---- slack --->
+	 *               <--- frame --->
+	 * .-------------+=============+----------+--------------.
+	 * |xxxxxxxxxxxxx|yyyyyyyyyyyyy|uuuuuuuuuu|uuuuuuuuuuuuuu|
+	 * `-------------+=============+----------+--------------'
+	 *
+	 * ^             ^             ^          ^              ^
+	 * |             |             |          |              |
+	 * valstack      bottom        top        end            alloc_end
+	 *
+	 *     xxx = arbitrary values, below current frame
+	 *     yyy = arbitrary values, inside current frame
+	 *     uuu = outside active value stack, initialized to 'undefined'
 	 */
 	duk_tval *valstack;                     /* start of valstack allocation */
-	duk_tval *valstack_end;                 /* end of valstack allocation (exclusive) */
+	duk_tval *valstack_end;                 /* end of valstack reservation/guarantee (exclusive) */
+	duk_tval *valstack_alloc_end;           /* end of valstack allocation */
 	duk_tval *valstack_bottom;              /* bottom of current frame */
 	duk_tval *valstack_top;                 /* top of current frame (exclusive) */
-#if !defined(DUK_USE_PREFER_SIZE)
-	duk_size_t valstack_size;               /* cached: valstack_end - valstack (in entries, not bytes) */
-#endif
-
-	/* Call stack.  [0,callstack_top[ is GC reachable. */
-	duk_activation *callstack;
+
+	/* Call stack, represented as a linked list starting from the current
+	 * activation (or NULL if nothing is active).
+	 */
 	duk_activation *callstack_curr;         /* current activation (or NULL if none) */
-	duk_size_t callstack_size;              /* allocation size */
-	duk_size_t callstack_top;               /* next to use, highest used is top - 1 (or none if top == 0) */
+	duk_size_t callstack_top;               /* number of activation records in callstack (0 if none) */
 	duk_size_t callstack_preventcount;      /* number of activation records in callstack preventing a yield */
 
-	/* Catch stack.  [0,catchstack_top[ is GC reachable. */
-	duk_catcher *catchstack;
-	duk_size_t catchstack_size;             /* allocation size */
-	duk_size_t catchstack_top;              /* next to use, highest used is top - 1 */
-
 	/* Yield/resume book-keeping. */
 	duk_hthread *resumer;                   /* who resumed us (if any) */
 
@@ -7515,24 +7712,22 @@
 DUK_INTERNAL_DECL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr);
 DUK_INTERNAL_DECL void duk_hthread_terminate(duk_hthread *thr);
 
-DUK_INTERNAL_DECL void duk_hthread_callstack_grow(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_callstack_shrink_check(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_callstack_unwind_norz(duk_hthread *thr, duk_size_t new_top);
-DUK_INTERNAL_DECL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_top);
-DUK_INTERNAL_DECL void duk_hthread_catchstack_grow(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_catchstack_shrink_check(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_catchstack_unwind_norz(duk_hthread *thr, duk_size_t new_top);
-DUK_INTERNAL_DECL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new_top);
+DUK_INTERNAL_DECL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act);
+DUK_INTERNAL_DECL void duk_hthread_activation_unwind_norz(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr);
+DUK_INTERNAL_DECL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level);
+
+DUK_INTERNAL_DECL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr);
+DUK_INTERNAL_DECL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat);
+DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act);
+DUK_INTERNAL_DECL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act);
 
 #if defined(DUK_USE_FINALIZER_TORTURE)
 DUK_INTERNAL_DECL void duk_hthread_valstack_torture_realloc(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_callstack_torture_realloc(duk_hthread *thr);
-DUK_INTERNAL_DECL void duk_hthread_catchstack_torture_realloc(duk_hthread *thr);
 #endif
 
 DUK_INTERNAL_DECL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud);  /* indirect allocs */
-DUK_INTERNAL_DECL void *duk_hthread_get_callstack_ptr(duk_heap *heap, void *ud);  /* indirect allocs */
-DUK_INTERNAL_DECL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud);  /* indirect allocs */
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 DUK_INTERNAL_DECL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act);
@@ -7545,7 +7740,7 @@
 /* #include duk_harray.h */
 #line 1 "duk_harray.h"
 /*
- *  Heap Array object representation.  Used for actual Array instances.
+ *  Array object representation, used for actual Array instances.
  *
  *  All objects with the exotic array behavior (which must coincide with having
  *  internal class array) MUST be duk_harrays.  No other object can be a
@@ -7623,7 +7818,7 @@
 	 */
 	duk_hthread *thread;
 	duk_hobject *varmap;
-	duk_size_t regbase;
+	duk_size_t regbase_byteoff;
 };
 
 struct duk_hobjenv {
@@ -7700,9 +7895,6 @@
  *  Field access
  */
 
-/* Get/set the current user visible size, without accounting for a dynamic
- * buffer's "spare" (= usable size).
- */
 #if defined(DUK_USE_BUFLEN16)
 /* size stored in duk_heaphdr unused flag bits */
 #define DUK_HBUFFER_GET_SIZE(x)     ((x)->hdr.h_flags >> 16)
@@ -7822,7 +8014,7 @@
 	 * it is useful for writing robust native code.
 	 */
 
-	/* Current size (not counting a dynamic buffer's "spare"). */
+	/* Current size. */
 #if defined(DUK_USE_BUFLEN16)
 	/* Stored in duk_heaphdr unused flags. */
 #else
@@ -7971,6 +8163,34 @@
 DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic *buf);
 
 #endif  /* DUK_HBUFFER_H_INCLUDED */
+/* #include duk_hproxy.h */
+#line 1 "duk_hproxy.h"
+/*
+ *  Proxy object representation.
+ */
+
+#if !defined(DUK_HPROXY_H_INCLUDED)
+#define DUK_HPROXY_H_INCLUDED
+
+#define DUK_ASSERT_HPROXY_VALID(h) do { \
+		DUK_ASSERT((h) != NULL); \
+		DUK_ASSERT((h)->target != NULL); \
+		DUK_ASSERT((h)->handler != NULL); \
+		DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ((duk_hobject *) (h))); \
+	} while (0)
+
+struct duk_hproxy {
+	/* Shared object part. */
+	duk_hobject obj;
+
+	/* Proxy target object. */
+	duk_hobject *target;
+
+	/* Proxy handlers (traps). */
+	duk_hobject *handler;
+};
+
+#endif  /* DUK_HPROXY_H_INCLUDED */
 /* #include duk_heap.h */
 #line 1 "duk_heap.h"
 /*
@@ -7989,11 +8209,10 @@
  *  Heap flags
  */
 
-#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED            (1 << 0)  /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */
-#define DUK_HEAP_FLAG_ERRHANDLER_RUNNING                       (1 << 1)  /* an error handler (user callback to augment/replace error) is running */
-#define DUK_HEAP_FLAG_INTERRUPT_RUNNING                        (1 << 2)  /* executor interrupt running (used to avoid nested interrupts) */
-#define DUK_HEAP_FLAG_FINALIZER_NORESCUE                       (1 << 3)  /* heap destruction ongoing, finalizer rescue no longer possible */
-#define DUK_HEAP_FLAG_DEBUGGER_PAUSED                          (1 << 4)  /* debugger is paused: talk with debug client until step/resume */
+#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED            (1U << 0)  /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */
+#define DUK_HEAP_FLAG_INTERRUPT_RUNNING                        (1U << 1)  /* executor interrupt running (used to avoid nested interrupts) */
+#define DUK_HEAP_FLAG_FINALIZER_NORESCUE                       (1U << 2)  /* heap destruction ongoing, finalizer rescue no longer possible */
+#define DUK_HEAP_FLAG_DEBUGGER_PAUSED                          (1U << 3)  /* debugger is paused: talk with debug client until step/resume */
 
 #define DUK__HEAP_HAS_FLAGS(heap,bits)               ((heap)->flags & (bits))
 #define DUK__HEAP_SET_FLAGS(heap,bits)  do { \
@@ -8004,19 +8223,16 @@
 	} while (0)
 
 #define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
-#define DUK_HEAP_HAS_ERRHANDLER_RUNNING(heap)              DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
 #define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap)               DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
 #define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap)              DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
 #define DUK_HEAP_HAS_DEBUGGER_PAUSED(heap)                 DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)
 
 #define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
-#define DUK_HEAP_SET_ERRHANDLER_RUNNING(heap)              DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
 #define DUK_HEAP_SET_INTERRUPT_RUNNING(heap)               DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
 #define DUK_HEAP_SET_FINALIZER_NORESCUE(heap)              DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
 #define DUK_HEAP_SET_DEBUGGER_PAUSED(heap)                 DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)
 
 #define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
-#define DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(heap)            DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
 #define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap)             DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
 #define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap)            DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
 #define DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap)               DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED)
@@ -8045,22 +8261,22 @@
 /* Emergency mark-and-sweep: try extra hard, even at the cost of
  * performance.
  */
-#define DUK_MS_FLAG_EMERGENCY                (1 << 0)
+#define DUK_MS_FLAG_EMERGENCY                (1U << 0)
 
 /* Voluntary mark-and-sweep: triggered periodically. */
-#define DUK_MS_FLAG_VOLUNTARY                (1 << 1)
+#define DUK_MS_FLAG_VOLUNTARY                (1U << 1)
 
 /* Postpone rescue decisions for reachable objects with FINALIZED set.
  * Used during finalize_list processing to avoid incorrect rescue
  * decisions due to finalize_list being a reachability root.
  */
-#define DUK_MS_FLAG_POSTPONE_RESCUE          (1 << 2)
+#define DUK_MS_FLAG_POSTPONE_RESCUE          (1U << 2)
 
 /* Don't compact objects; needed during object property table resize
  * to prevent a recursive resize.  It would suffice to protect only the
  * current object being resized, but this is not yet implemented.
  */
-#define DUK_MS_FLAG_NO_OBJECT_COMPACTION     (1 << 2)
+#define DUK_MS_FLAG_NO_OBJECT_COMPACTION     (1U << 3)
 
 /*
  *  Thread switching
@@ -8079,6 +8295,18 @@
 #endif
 
 /*
+ *  Stats
+ */
+
+#if defined(DUK_USE_DEBUG)
+#define DUK_STATS_INC(heap,fieldname) do { \
+		(heap)->fieldname += 1; \
+	} while (0)
+#else
+#define DUK_STATS_INC(heap,fieldname) do {} while (0)
+#endif
+
+/*
  *  Other heap related defines
  */
 
@@ -8195,10 +8423,14 @@
 
 /*
  *  Checked allocation, relative to a thread
+ *
+ *  DUK_FREE_CHECKED() doesn't actually throw, but accepts a 'thr' argument
+ *  for convenience.
  */
 
 #define DUK_ALLOC_CHECKED(thr,size)                     duk_heap_mem_alloc_checked((thr), (size))
 #define DUK_ALLOC_CHECKED_ZEROED(thr,size)              duk_heap_mem_alloc_checked_zeroed((thr), (size))
+#define DUK_FREE_CHECKED(thr,ptr)                       duk_heap_mem_free((thr)->heap, (ptr))
 
 /*
  *  Memory constants
@@ -8234,11 +8466,14 @@
 /* Milliseconds between status notify and transport peeks. */
 #define DUK_HEAP_DBG_RATELIMIT_MILLISECS  200
 
-/* Step types */
-#define DUK_STEP_TYPE_NONE  0
-#define DUK_STEP_TYPE_INTO  1
-#define DUK_STEP_TYPE_OVER  2
-#define DUK_STEP_TYPE_OUT   3
+/* Debugger pause flags. */
+#define DUK_PAUSE_FLAG_ONE_OPCODE        (1U << 0)   /* pause when a single opcode has been executed */
+#define DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE (1U << 1)   /* one opcode pause actually active; artifact of current implementation */
+#define DUK_PAUSE_FLAG_LINE_CHANGE       (1U << 2)   /* pause when current line number changes */
+#define DUK_PAUSE_FLAG_FUNC_ENTRY        (1U << 3)   /* pause when entering a function */
+#define DUK_PAUSE_FLAG_FUNC_EXIT         (1U << 4)   /* pause when exiting current function */
+#define DUK_PAUSE_FLAG_CAUGHT_ERROR      (1U << 5)   /* pause when about to throw an error that is caught */
+#define DUK_PAUSE_FLAG_UNCAUGHT_ERROR    (1U << 6)   /* pause when about to throw an error that won't be caught */
 
 struct duk_breakpoint {
 	duk_hstring *filename;
@@ -8334,6 +8569,14 @@
 #endif
 #endif
 
+	/* Freelist for duk_activations and duk_catchers. */
+#if defined(DUK_USE_CACHE_ACTIVATION)
+	duk_activation *activation_free;
+#endif
+#if defined(DUK_USE_CACHE_CATCHER)
+	duk_catcher *catcher_free;
+#endif
+
 	/* Voluntary mark-and-sweep trigger counter.  Intentionally signed
 	 * because we continue decreasing the value when voluntary GC cannot
 	 * run.
@@ -8397,6 +8640,14 @@
 	 */
 	duk_bool_t creating_error;
 
+	/* Marker for indicating we're calling a user error augmentation
+	 * (errCreate/errThrow) function.  Errors created/thrown during
+	 * such a call are not augmented.
+	 */
+#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
+	duk_bool_t augmenting_error;
+#endif
+
 	/* Longjmp state. */
 	duk_ljstate lj;
 
@@ -8458,23 +8709,25 @@
 	duk_bool_t dbg_state_dirty;             /* resend state next time executor is about to run */
 	duk_bool_t dbg_force_restart;           /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */
 	duk_bool_t dbg_detaching;               /* debugger detaching; used to avoid calling detach handler recursively */
-	duk_small_uint_t dbg_step_type;         /* step type: none, step into, step over, step out */
-	duk_hthread *dbg_step_thread;           /* borrowed; NULL if no step state (NULLed in unwind) */
-	duk_size_t dbg_step_csindex;            /* callstack index */
-	duk_uint32_t dbg_step_startline;        /* starting line number */
+	duk_small_uint_t dbg_pause_flags;       /* flags for automatic pause behavior */
+	duk_activation *dbg_pause_act;          /* activation related to pause behavior (pause on line change, function entry/exit) */
+	duk_uint32_t dbg_pause_startline;       /* starting line number for line change related pause behavior */
 	duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS];  /* breakpoints: [0,breakpoint_count[ gc reachable */
 	duk_small_uint_t dbg_breakpoint_count;
 	duk_breakpoint *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1];  /* currently active breakpoints: NULL term, borrowed pointers */
 	/* XXX: make active breakpoints actual copies instead of pointers? */
 
 	/* These are for rate limiting Status notifications and transport peeking. */
-	duk_uint32_t dbg_exec_counter;          /* cumulative opcode execution count (overflows are OK) */
-	duk_uint32_t dbg_last_counter;          /* value of dbg_exec_counter when we last did a Date-based check */
+	duk_uint_t dbg_exec_counter;            /* cumulative opcode execution count (overflows are OK) */
+	duk_uint_t dbg_last_counter;            /* value of dbg_exec_counter when we last did a Date-based check */
 	duk_double_t dbg_last_time;             /* time when status/peek was last done (Date-based rate limit) */
 
 	/* Used to support single-byte stream lookahead. */
 	duk_bool_t dbg_have_next_byte;
 	duk_uint8_t dbg_next_byte;
+#endif  /* DUK_USE_DEBUGGER_SUPPORT */
+#if defined(DUK_USE_ASSERTIONS)
+	duk_bool_t dbg_calling_transport;       /* transport call in progress, calling into Duktape forbidden */
 #endif
 
 	/* String intern table (weak refs). */
@@ -8505,6 +8758,51 @@
 	duk_hstring *strs[DUK_HEAP_NUM_STRINGS];
 #endif
 #endif
+
+	/* Stats. */
+#if defined(DUK_USE_DEBUG)
+	duk_int_t stats_exec_opcodes;
+	duk_int_t stats_exec_interrupt;
+	duk_int_t stats_exec_throw;
+	duk_int_t stats_call_all;
+	duk_int_t stats_call_tailcall;
+	duk_int_t stats_call_ecmatoecma;
+	duk_int_t stats_safecall_all;
+	duk_int_t stats_safecall_nothrow;
+	duk_int_t stats_safecall_throw;
+	duk_int_t stats_ms_try_count;
+	duk_int_t stats_ms_skip_count;
+	duk_int_t stats_ms_emergency_count;
+	duk_int_t stats_strtab_intern_hit;
+	duk_int_t stats_strtab_intern_miss;
+	duk_int_t stats_strtab_resize_check;
+	duk_int_t stats_strtab_resize_grow;
+	duk_int_t stats_strtab_resize_shrink;
+	duk_int_t stats_object_realloc_props;
+	duk_int_t stats_object_abandon_array;
+	duk_int_t stats_getownpropdesc_count;
+	duk_int_t stats_getownpropdesc_hit;
+	duk_int_t stats_getownpropdesc_miss;
+	duk_int_t stats_getpropdesc_count;
+	duk_int_t stats_getpropdesc_hit;
+	duk_int_t stats_getpropdesc_miss;
+	duk_int_t stats_getprop_all;
+	duk_int_t stats_getprop_arrayidx;
+	duk_int_t stats_getprop_bufobjidx;
+	duk_int_t stats_getprop_bufferidx;
+	duk_int_t stats_getprop_bufferlen;
+	duk_int_t stats_getprop_stringidx;
+	duk_int_t stats_getprop_stringlen;
+	duk_int_t stats_getprop_proxy;
+	duk_int_t stats_getprop_arguments;
+	duk_int_t stats_putprop_all;
+	duk_int_t stats_putprop_arrayidx;
+	duk_int_t stats_putprop_bufobjidx;
+	duk_int_t stats_putprop_bufferidx;
+	duk_int_t stats_putprop_proxy;
+	duk_int_t stats_getvar_all;
+	duk_int_t stats_putvar_all;
+#endif
 };
 
 /*
@@ -8569,6 +8867,8 @@
 DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize);
 DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr);
 
+DUK_INTERNAL_DECL void duk_heap_free_freelists(duk_heap *heap);
+
 #if defined(DUK_USE_FINALIZER_SUPPORT)
 DUK_INTERNAL_DECL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj);
 DUK_INTERNAL_DECL void duk_heap_process_finalize_list(duk_heap *heap);
@@ -8656,8 +8956,8 @@
 /* The low 8 bits map directly to duk_hobject.h DUK_PROPDESC_FLAG_xxx.
  * The remaining flags are specific to the debugger.
  */
-#define DUK_DBG_PROPFLAG_SYMBOL          (1 << 8)
-#define DUK_DBG_PROPFLAG_HIDDEN          (1 << 9)
+#define DUK_DBG_PROPFLAG_SYMBOL          (1U << 8)
+#define DUK_DBG_PROPFLAG_HIDDEN          (1U << 9)
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 DUK_INTERNAL_DECL void duk_debug_do_detach(duk_heap *heap);
@@ -8728,7 +9028,7 @@
 DUK_INTERNAL_DECL duk_bool_t duk_debug_is_paused(duk_heap *heap);
 DUK_INTERNAL_DECL void duk_debug_set_paused(duk_heap *heap);
 DUK_INTERNAL_DECL void duk_debug_clear_paused(duk_heap *heap);
-DUK_INTERNAL_DECL void duk_debug_clear_step_state(duk_heap *heap);
+DUK_INTERNAL_DECL void duk_debug_clear_pause_state(duk_heap *heap);
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
 #endif  /* DUK_DEBUGGER_H_INCLUDED */
@@ -9093,6 +9393,10 @@
 #define DUK_ERROR_INTERNAL(thr) do { \
 		duk_err_error_internal((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
 	} while (0)
+#define DUK_DCERROR_INTERNAL(thr) do { \
+		DUK_ERROR_INTERNAL((thr)); \
+		return 0; \
+	} while (0)
 #define DUK_ERROR_ALLOC_FAILED(thr) do { \
 		duk_err_error_alloc_failed((thr), DUK_FILE_MACRO, (duk_int_t) DUK_LINE_MACRO); \
 	} while (0)
@@ -9118,6 +9422,10 @@
 #define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \
 		DUK_ERROR_RANGE((thr), DUK_STR_INVALID_COUNT); \
 	} while (0)
+#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \
+		DUK_ERROR_RANGE_INVALID_COUNT((thr)); \
+		return 0; \
+	} while (0)
 #define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \
 		DUK_ERROR_RANGE((thr), DUK_STR_INVALID_LENGTH); \
 	} while (0)
@@ -9173,6 +9481,10 @@
 #define DUK_ERROR_INTERNAL(thr) do { \
 		duk_err_error((thr)); \
 	} while (0)
+#define DUK_DCERROR_INTERNAL(thr) do { \
+		DUK_UNREF((thr)); \
+		return DUK_RET_ERROR; \
+	} while (0)
 #define DUK_ERROR_ALLOC_FAILED(thr) do { \
 		duk_err_error((thr)); \
 	} while (0)
@@ -9198,6 +9510,10 @@
 #define DUK_ERROR_RANGE_INVALID_COUNT(thr) do { \
 		duk_err_range((thr)); \
 	} while (0)
+#define DUK_DCERROR_RANGE_INVALID_COUNT(thr) do { \
+		DUK_UNREF((thr)); \
+		return DUK_RET_RANGE_ERROR; \
+	} while (0)
 #define DUK_ERROR_RANGE_INVALID_LENGTH(thr) do { \
 		duk_err_range((thr)); \
 	} while (0)
@@ -9323,6 +9639,19 @@
 	DUK_ASSERT(thr->valstack_top < thr->valstack_end)
 
 /*
+ *  Helper to initialize a memory area (e.g. struct) with garbage when
+ *  assertions enabled.
+ */
+
+#if defined(DUK_USE_ASSERTIONS)
+#define DUK_ASSERT_SET_GARBAGE(ptr,size) do { \
+		DUK_MEMSET((void *) (ptr), 0x5a, size); \
+	} while (0)
+#else
+#define DUK_ASSERT_SET_GARBAGE(ptr,size) do {} while (0)
+#endif
+
+/*
  *  Helper for valstack space
  *
  *  Caller of DUK_ASSERT_VALSTACK_SPACE() estimates the number of free stack entries
@@ -9361,8 +9690,11 @@
 
 DUK_NORETURN(DUK_INTERNAL_DECL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc));
 
+#define DUK_AUGMENT_FLAG_NOBLAME_FILELINE  (1U << 0)  /* if set, don't blame C file/line for .fileName and .lineNumber */
+#define DUK_AUGMENT_FLAG_SKIP_ONE          (1U << 1)  /* if set, skip topmost activation in traceback construction */
+
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_bool_t noblame_fileline);
+DUK_INTERNAL_DECL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *filename, duk_int_t line, duk_small_uint_t flags);
 #endif
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
 DUK_INTERNAL_DECL void duk_err_augment_error_throw(duk_hthread *thr);
@@ -9644,6 +9976,17 @@
 extern const duk_uint16_t duk_unicode_re_canon_lookup[65536];
 #endif
 
+#if defined(DUK_USE_REGEXP_CANON_BITMAP)
+/*
+ *  Automatically generated by extract_caseconv.py, do not edit!
+ */
+
+#define DUK_CANON_BITMAP_BLKSIZE                                      32
+#define DUK_CANON_BITMAP_BLKSHIFT                                     5
+#define DUK_CANON_BITMAP_BLKMASK                                      31
+extern const duk_uint8_t duk_unicode_re_canon_bitmap[256];
+#endif
+
 /*
  *  Extern
  */
@@ -9695,10 +10038,10 @@
 #define DUK_JSON_H_INCLUDED
 
 /* Encoding/decoding flags */
-#define DUK_JSON_FLAG_ASCII_ONLY              (1 << 0)  /* escape any non-ASCII characters */
-#define DUK_JSON_FLAG_AVOID_KEY_QUOTES        (1 << 1)  /* avoid key quotes when key is an ASCII Identifier */
-#define DUK_JSON_FLAG_EXT_CUSTOM              (1 << 2)  /* extended types: custom encoding */
-#define DUK_JSON_FLAG_EXT_COMPATIBLE          (1 << 3)  /* extended types: compatible encoding */
+#define DUK_JSON_FLAG_ASCII_ONLY              (1U << 0)  /* escape any non-ASCII characters */
+#define DUK_JSON_FLAG_AVOID_KEY_QUOTES        (1U << 1)  /* avoid key quotes when key is an ASCII Identifier */
+#define DUK_JSON_FLAG_EXT_CUSTOM              (1U << 2)  /* extended types: custom encoding */
+#define DUK_JSON_FLAG_EXT_COMPATIBLE          (1U << 3)  /* extended types: compatible encoding */
 
 /* How much stack to require on entry to object/array encode */
 #define DUK_JSON_ENC_REQSTACK                 32
@@ -9725,8 +10068,8 @@
 	duk_small_uint_t flag_ext_compatible;
 	duk_small_uint_t flag_ext_custom_or_compatible;
 #endif
-	duk_int_t recursion_depth;
-	duk_int_t recursion_limit;
+	duk_uint_t recursion_depth;
+	duk_uint_t recursion_limit;
 	duk_uint_t mask_for_undefined;      /* type bit mask: types which certainly produce 'undefined' */
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
 	duk_small_uint_t stridx_custom_undefined;
@@ -9764,20 +10107,22 @@
 #if !defined(DUK_JS_H_INCLUDED)
 #define DUK_JS_H_INCLUDED
 
-/* Flags for call handling. */
-#define DUK_CALL_FLAG_IGNORE_RECLIMIT        (1 << 0)  /* duk_handle_call_xxx: call ignores C recursion limit (for errhandler calls) */
-#define DUK_CALL_FLAG_CONSTRUCTOR_CALL       (1 << 1)  /* duk_handle_call_xxx: constructor call (i.e. called as 'new Foo()') */
-#define DUK_CALL_FLAG_IS_RESUME              (1 << 2)  /* duk_handle_ecma_call_setup: setup for a resume() */
-#define DUK_CALL_FLAG_IS_TAILCALL            (1 << 3)  /* duk_handle_ecma_call_setup: setup for a tail call */
-#define DUK_CALL_FLAG_DIRECT_EVAL            (1 << 4)  /* call is a direct eval call */
+/* Flags for call handling.  Lowest flags must match bytecode DUK_BC_CALL_FLAG_xxx 1:1. */
+#define DUK_CALL_FLAG_TAILCALL                 (1U << 0)  /* setup for a tail call */
+#define DUK_CALL_FLAG_CONSTRUCT                (1U << 1)  /* constructor call (i.e. called as 'new Foo()') */
+#define DUK_CALL_FLAG_CALLED_AS_EVAL           (1U << 2)  /* call was made using the identifier 'eval' */
+#define DUK_CALL_FLAG_ALLOW_ECMATOECMA         (1U << 3)  /* ecma-to-ecma call with executor reuse is possible */
+#define DUK_CALL_FLAG_DIRECT_EVAL              (1U << 4)  /* call is a direct eval call */
+#define DUK_CALL_FLAG_CONSTRUCT_PROXY          (1U << 5)  /* handled via 'construct' proxy trap, check return value invariant(s) */
+#define DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED (1U << 6)  /* prototype of 'default instance' updated, temporary flag in call handling */
 
 /* Flags for duk_js_equals_helper(). */
-#define DUK_EQUALS_FLAG_SAMEVALUE            (1 << 0)  /* use SameValue instead of non-strict equality */
-#define DUK_EQUALS_FLAG_STRICT               (1 << 1)  /* use strict equality instead of non-strict equality */
+#define DUK_EQUALS_FLAG_SAMEVALUE            (1U << 0)  /* use SameValue instead of non-strict equality */
+#define DUK_EQUALS_FLAG_STRICT               (1U << 1)  /* use strict equality instead of non-strict equality */
 
 /* Flags for duk_js_compare_helper(). */
-#define DUK_COMPARE_FLAG_NEGATE              (1 << 0)  /* negate result */
-#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST     (1 << 1)  /* eval left argument first */
+#define DUK_COMPARE_FLAG_NEGATE              (1U << 0)  /* negate result */
+#define DUK_COMPARE_FLAG_EVAL_LEFT_FIRST     (1U << 1)  /* eval left argument first */
 
 /* conversions, coercions, comparison, etc */
 DUK_INTERNAL_DECL duk_bool_t duk_js_toboolean(duk_tval *tv);
@@ -9792,13 +10137,13 @@
 DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h);
 DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h);
 #endif
-DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags);
+DUK_INTERNAL_DECL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags);
 DUK_INTERNAL_DECL duk_small_int_t duk_js_data_compare(const duk_uint8_t *buf1, const duk_uint8_t *buf2, duk_size_t len1, duk_size_t len2);
 DUK_INTERNAL_DECL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2);
 #if 0  /* unused */
 DUK_INTERNAL_DECL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2);
 #endif
-DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags);
+DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags);
 DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
 DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
 DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);
@@ -9842,22 +10187,24 @@
 DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, duk_hobject *env, duk_hstring *name);
 #endif
 DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name);
-DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_small_int_t prop_flags, duk_bool_t is_func_decl);
+DUK_INTERNAL_DECL duk_bool_t duk_js_declvar_activation(duk_hthread *thr, duk_activation *act, duk_hstring *name, duk_tval *val, duk_small_uint_t prop_flags, duk_bool_t is_func_decl);
 DUK_INTERNAL_DECL void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, duk_activation *act);
 DUK_INTERNAL_DECL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env);
-DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t idx_bottom);
-DUK_INTERNAL_DECL
-void duk_js_push_closure(duk_hthread *thr,
-                         duk_hcompfunc *fun_temp,
-                         duk_hobject *outer_var_env,
-                         duk_hobject *outer_lex_env,
-                         duk_bool_t add_auto_proto);
+DUK_INTERNAL_DECL duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, duk_hobject *func, duk_size_t bottom_byteoff);
+DUK_INTERNAL_DECL void duk_js_push_closure(duk_hthread *thr,
+                                           duk_hcompfunc *fun_temp,
+                                           duk_hobject *outer_var_env,
+                                           duk_hobject *outer_lex_env,
+                                           duk_bool_t add_auto_proto);
 
 /* call handling */
-DUK_INTERNAL_DECL duk_int_t duk_handle_call_protected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
-DUK_INTERNAL_DECL void duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
+DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected(duk_hthread *thr, duk_idx_t idx_func, duk_small_uint_t call_flags);
+DUK_INTERNAL_DECL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags);
 DUK_INTERNAL_DECL duk_int_t duk_handle_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t num_stack_args, duk_idx_t num_stack_res);
-DUK_INTERNAL_DECL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, duk_idx_t num_stack_args, duk_small_uint_t call_flags);
+DUK_INTERNAL_DECL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant);
+#if defined(DUK_USE_VERBOSE_ERRORS)
+DUK_INTERNAL_DECL void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_targ, duk_tval *tv_base, duk_tval *tv_key);
+#endif
 
 /* bytecode execution */
 DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr);
@@ -9876,23 +10223,23 @@
 /* Output a specified number of digits instead of using the shortest
  * form.  Used for toPrecision() and toFixed().
  */
-#define DUK_N2S_FLAG_FIXED_FORMAT         (1 << 0)
+#define DUK_N2S_FLAG_FIXED_FORMAT         (1U << 0)
 
 /* Force exponential format.  Used for toExponential(). */
-#define DUK_N2S_FLAG_FORCE_EXP            (1 << 1)
+#define DUK_N2S_FLAG_FORCE_EXP            (1U << 1)
 
 /* If number would need zero padding (for whole number part), use
  * exponential format instead.  E.g. if input number is 12300, 3
  * digits are generated ("123"), output "1.23e+4" instead of "12300".
  * Used for toPrecision().
  */
-#define DUK_N2S_FLAG_NO_ZERO_PAD          (1 << 2)
+#define DUK_N2S_FLAG_NO_ZERO_PAD          (1U << 2)
 
 /* Digit count indicates number of fractions (i.e. an absolute
  * digit index instead of a relative one).  Used together with
  * DUK_N2S_FLAG_FIXED_FORMAT for toFixed().
  */
-#define DUK_N2S_FLAG_FRACTION_DIGITS      (1 << 3)
+#define DUK_N2S_FLAG_FRACTION_DIGITS      (1U << 3)
 
 /*
  *  String-to-number conversion
@@ -9905,64 +10252,64 @@
 #define DUK_S2N_MAX_EXPONENT              1000000000
 
 /* Trim white space (= allow leading and trailing whitespace) */
-#define DUK_S2N_FLAG_TRIM_WHITE           (1 << 0)
+#define DUK_S2N_FLAG_TRIM_WHITE           (1U << 0)
 
 /* Allow exponent */
-#define DUK_S2N_FLAG_ALLOW_EXP            (1 << 1)
+#define DUK_S2N_FLAG_ALLOW_EXP            (1U << 1)
 
 /* Allow trailing garbage (e.g. treat "123foo" as "123) */
-#define DUK_S2N_FLAG_ALLOW_GARBAGE        (1 << 2)
+#define DUK_S2N_FLAG_ALLOW_GARBAGE        (1U << 2)
 
 /* Allow leading plus sign */
-#define DUK_S2N_FLAG_ALLOW_PLUS           (1 << 3)
+#define DUK_S2N_FLAG_ALLOW_PLUS           (1U << 3)
 
 /* Allow leading minus sign */
-#define DUK_S2N_FLAG_ALLOW_MINUS          (1 << 4)
+#define DUK_S2N_FLAG_ALLOW_MINUS          (1U << 4)
 
 /* Allow 'Infinity' */
-#define DUK_S2N_FLAG_ALLOW_INF            (1 << 5)
+#define DUK_S2N_FLAG_ALLOW_INF            (1U << 5)
 
 /* Allow fraction part */
-#define DUK_S2N_FLAG_ALLOW_FRAC           (1 << 6)
+#define DUK_S2N_FLAG_ALLOW_FRAC           (1U << 6)
 
 /* Allow naked fraction (e.g. ".123") */
-#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC     (1 << 7)
+#define DUK_S2N_FLAG_ALLOW_NAKED_FRAC     (1U << 7)
 
 /* Allow empty fraction (e.g. "123.") */
-#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC     (1 << 8)
+#define DUK_S2N_FLAG_ALLOW_EMPTY_FRAC     (1U << 8)
 
 /* Allow empty string to be interpreted as 0 */
-#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO  (1 << 9)
+#define DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO  (1U << 9)
 
 /* Allow leading zeroes (e.g. "0123" -> "123") */
-#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO   (1 << 10)
+#define DUK_S2N_FLAG_ALLOW_LEADING_ZERO   (1U << 10)
 
 /* Allow automatic detection of hex base ("0x" or "0X" prefix),
  * overrides radix argument and forces integer mode.
  */
-#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT   (1 << 11)
+#define DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT   (1U << 11)
 
 /* Allow automatic detection of legacy octal base ("0n"),
  * overrides radix argument and forces integer mode.
  */
-#define DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT   (1 << 12)
+#define DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT   (1U << 12)
 
 /* Allow automatic detection of ES2015 octal base ("0o123"),
  * overrides radix argument and forces integer mode.
  */
-#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT   (1 << 13)
+#define DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT   (1U << 13)
 
 /* Allow automatic detection of ES2015 binary base ("0b10001"),
  * overrides radix argument and forces integer mode.
  */
-#define DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT   (1 << 14)
+#define DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT   (1U << 14)
 
 /*
  *  Prototypes
  */
 
-DUK_INTERNAL_DECL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags);
-DUK_INTERNAL_DECL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk_small_uint_t flags);
+DUK_INTERNAL_DECL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags);
+DUK_INTERNAL_DECL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags);
 
 #endif  /* DUK_NUMCONV_H_INCLUDED */
 /* #include duk_bi_protos.h */
@@ -9992,13 +10339,16 @@
 DUK_INTERNAL_DECL duk_bool_t duk_bi_date_timeval_in_leeway_range(duk_double_t x);
 /* Built-in providers */
 #if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
-DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx);
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_gettimeofday(void);
 #endif
 #if defined(DUK_USE_DATE_NOW_TIME)
-DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(duk_context *ctx);
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_time(void);
 #endif
 #if defined(DUK_USE_DATE_NOW_WINDOWS)
-DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(duk_context *ctx);
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows(void);
+#endif
+#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_now_windows_subms(void);
 #endif
 #if defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME)
 DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d);
@@ -10010,31 +10360,38 @@
 DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d);
 #endif
 #if defined(DUK_USE_DATE_PRS_STRPTIME)
-DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, const char *str);
+DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str);
 #endif
 #if defined(DUK_USE_DATE_PRS_GETDATE)
-DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const char *str);
+DUK_INTERNAL_DECL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str);
 #endif
 #if defined(DUK_USE_DATE_FMT_STRFTIME)
-DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags);
+DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags);
+#endif
+
+#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void);
+#endif
+#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
+DUK_INTERNAL_DECL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void);
 #endif
 
 DUK_INTERNAL_DECL
-void duk_bi_json_parse_helper(duk_context *ctx,
+void duk_bi_json_parse_helper(duk_hthread *thr,
                               duk_idx_t idx_value,
                               duk_idx_t idx_reviver,
                               duk_small_uint_t flags);
 DUK_INTERNAL_DECL
-void duk_bi_json_stringify_helper(duk_context *ctx,
+void duk_bi_json_stringify_helper(duk_hthread *thr,
                                   duk_idx_t idx_value,
                                   duk_idx_t idx_replacer,
                                   duk_idx_t idx_space,
                                   duk_small_uint_t flags);
 
-DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_context *ctx);
+DUK_INTERNAL_DECL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr);
 
 #if defined(DUK_USE_ES6_PROXY)
-DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h_proxy_target, duk_uint_t flags);
+DUK_INTERNAL_DECL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags);
 #endif
 
 #endif  /* DUK_BUILTIN_PROTOS_H_INCLUDED */
@@ -10055,7 +10412,7 @@
 #endif
 
 #endif  /* DUK_SELFTEST_H_INCLUDED */
-#line 80 "duk_internal.h"
+#line 82 "duk_internal.h"
 
 #endif  /* DUK_INTERNAL_H_INCLUDED */
 #line 10 "duk_replacements.c"
@@ -10240,7 +10597,7 @@
 #if defined(DUK_USE_ROM_STRINGS)
 #error ROM support not enabled, rerun configure.py with --rom-support
 #else  /* DUK_USE_ROM_STRINGS */
-DUK_INTERNAL const duk_uint8_t duk_strings_data[903] = {
+DUK_INTERNAL const duk_uint8_t duk_strings_data[892] = {
 79,40,209,144,168,105,6,78,54,139,89,185,44,48,46,90,120,8,154,140,35,103,
 35,113,193,73,5,52,112,180,104,166,135,52,188,4,98,12,27,146,156,80,211,31,
 129,115,150,64,52,220,109,24,18,68,156,24,38,67,114,36,55,9,119,151,132,
@@ -10261,38 +10618,37 @@
 67,72,49,241,160,227,81,196,52,168,106,39,132,252,183,136,105,80,212,79,2,
 249,110,128,126,88,95,133,109,237,237,237,151,235,127,46,249,119,203,190,
 186,206,33,181,2,208,61,190,12,19,34,65,19,81,132,108,228,97,1,107,33,12,
-32,45,100,139,134,69,146,100,227,226,231,146,51,192,204,73,140,224,145,221,
-102,241,68,196,157,34,79,143,139,166,233,225,228,227,138,157,173,167,197,
-211,118,214,210,38,238,74,113,67,76,105,187,169,147,154,73,225,228,32,193,
-48,25,100,105,166,113,200,147,44,166,1,40,79,18,150,134,147,141,163,2,72,
-171,115,147,136,4,65,130,96,35,64,194,32,168,89,56,208,48,135,123,144,217,
-146,38,220,229,64,186,16,187,156,105,47,52,238,112,56,153,4,225,145,27,156,
-43,162,192,46,71,220,229,65,22,1,231,220,228,157,72,136,136,220,227,197,
-164,180,52,133,220,228,206,137,23,115,128,137,164,77,206,48,15,62,231,42,8,
-145,181,86,231,10,134,129,104,201,34,125,206,76,17,49,38,141,206,28,13,26,
-201,19,137,204,122,22,66,161,175,164,210,72,199,130,137,1,50,32,145,143,38,
-120,186,195,35,106,51,146,230,8,36,77,109,65,38,226,72,141,18,74,140,35,
-247,247,182,168,209,144,187,223,58,156,104,79,190,183,127,123,105,160,110,
-247,206,167,26,19,239,173,223,222,218,67,75,189,243,169,198,132,251,235,
-183,247,182,154,134,151,123,231,83,141,9,247,215,111,239,109,22,141,22,247,
-206,167,26,19,239,172,223,218,45,26,47,157,78,52,39,223,74,24,144,10,32,
-129,34,20,64,152,142,129,57,179,67,104,68,12,129,161,140,72,156,100,40,40,
-185,152,100,89,38,65,13,196,34,228,67,149,13,2,215,129,149,209,65,104,209,
-77,14,104,144,81,33,170,67,101,48,52,68,113,70,210,88,209,36,233,22,154,86,
-68,196,114,76,232,145,102,120,186,195,156,112,105,225,228,113,71,80,68,162,
-115,101,50,85,200,25,108,116,44,132,178,38,114,137,96,148,136,70,209,134,
-37,222,232,204,228,188,200,209,200,200,99,221,25,150,84,121,34,70,209,107,
-36,227,66,20,160,92,136,164,49,235,35,8,217,201,40,108,201,18,128,68,26,
-201,51,188,2,80,12,67,190,40,168,38,68,190,46,153,5,50,12,207,160,86,129,
-26,83,4,208,34,225,4,88,192,
+32,45,100,137,64,247,175,9,19,155,41,198,130,155,134,69,146,100,227,226,
+231,146,51,192,204,73,140,224,145,221,102,241,68,196,157,34,79,143,139,166,
+233,225,228,227,138,157,173,167,197,211,118,214,210,38,238,74,113,67,76,
+105,187,169,147,154,73,225,228,32,193,48,25,100,105,166,113,200,147,44,166,
+1,40,79,18,150,134,147,141,163,2,72,171,115,147,136,4,65,130,96,35,64,194,
+32,168,89,56,208,48,135,123,144,217,146,39,220,228,193,19,18,101,220,227,
+73,121,167,115,129,196,200,39,12,136,220,225,93,22,1,114,62,231,42,8,176,
+15,62,231,36,234,68,68,70,231,30,45,37,161,164,38,231,24,7,159,115,149,4,
+72,218,171,115,133,67,64,180,100,145,54,231,42,5,208,135,19,152,244,44,133,
+67,95,73,164,145,143,5,18,2,100,65,35,30,76,241,117,134,70,212,103,37,204,
+16,72,154,218,130,77,196,145,63,127,123,106,141,25,11,189,243,169,198,132,
+251,235,119,247,182,154,6,239,124,234,113,161,62,250,221,253,237,164,52,
+187,223,58,156,104,79,190,187,127,123,105,168,105,119,190,117,56,208,159,
+125,118,254,246,209,104,209,111,124,234,113,161,62,250,205,253,162,209,162,
+249,212,227,66,125,244,161,137,0,162,8,18,33,68,9,136,232,19,155,52,54,132,
+64,200,26,24,196,137,198,66,130,139,153,134,69,146,100,16,220,66,46,68,57,
+80,208,45,120,25,93,20,22,141,20,208,230,137,5,18,26,164,54,83,3,68,71,20,
+109,37,141,18,78,145,105,165,100,76,71,36,206,137,22,103,139,172,57,199,6,
+158,30,71,20,117,4,74,39,54,83,37,92,129,150,199,66,200,75,34,103,40,150,9,
+72,132,109,24,98,93,238,140,206,75,204,141,28,140,134,61,209,153,101,71,
+146,36,109,22,178,78,52,33,74,5,200,138,67,30,178,48,141,156,146,134,204,
+145,40,4,65,172,147,59,192,37,0,196,59,226,138,130,100,75,226,233,144,83,
+32,204,250,5,104,17,165,48,77,2,46,16,69,140,
 };
 #endif  /* DUK_USE_ROM_STRINGS */
 
 #if defined(DUK_USE_ROM_OBJECTS)
 #error ROM support not enabled, rerun configure.py with --rom-support
 #else  /* DUK_USE_ROM_OBJECTS */
-/* native functions: 166 */
-DUK_INTERNAL const duk_c_function duk_bi_native_functions[166] = {
+/* native functions: 176 */
+DUK_INTERNAL const duk_c_function duk_bi_native_functions[176] = {
 	NULL,
 	duk_bi_array_constructor,
 	duk_bi_array_constructor_is_array,
@@ -10364,12 +10720,17 @@
 	duk_bi_global_object_unescape,
 	duk_bi_json_object_parse,
 	duk_bi_json_object_stringify,
+	duk_bi_math_object_clz32,
 	duk_bi_math_object_hypot,
+	duk_bi_math_object_imul,
 	duk_bi_math_object_max,
 	duk_bi_math_object_min,
 	duk_bi_math_object_onearg_shared,
 	duk_bi_math_object_random,
+	duk_bi_math_object_sign,
 	duk_bi_math_object_twoarg_shared,
+	duk_bi_native_function_length,
+	duk_bi_native_function_name,
 	duk_bi_nodejs_buffer_byte_length,
 	duk_bi_nodejs_buffer_concat,
 	duk_bi_nodejs_buffer_constructor,
@@ -10400,16 +10761,21 @@
 	duk_bi_object_constructor_prevent_extensions,
 	duk_bi_object_constructor_seal_freeze_shared,
 	duk_bi_object_getprototype_shared,
+	duk_bi_object_prototype_defineaccessor,
 	duk_bi_object_prototype_has_own_property,
 	duk_bi_object_prototype_is_prototype_of,
+	duk_bi_object_prototype_lookupaccessor,
 	duk_bi_object_prototype_property_is_enumerable,
 	duk_bi_object_prototype_to_locale_string,
 	duk_bi_object_prototype_to_string,
 	duk_bi_object_prototype_value_of,
 	duk_bi_object_setprototype_shared,
+	duk_bi_performance_now,
 	duk_bi_pointer_constructor,
 	duk_bi_pointer_prototype_tostring_shared,
 	duk_bi_proxy_constructor,
+	duk_bi_reflect_apply,
+	duk_bi_reflect_construct,
 	duk_bi_reflect_object_delete_property,
 	duk_bi_reflect_object_get,
 	duk_bi_reflect_object_has,
@@ -10461,541 +10827,556 @@
 	duk_bi_uint8array_plainof,
 };
 #if defined(DUK_USE_DOUBLE_LE)
-DUK_INTERNAL const duk_uint8_t duk_builtins_data[3819] = {
-144,148,105,221,32,68,52,228,62,12,104,200,165,134,148,248,81,77,61,191,
-135,35,154,103,34,72,6,157,159,197,145,77,245,126,52,130,106,234,163,196,
-52,226,18,51,161,26,113,1,60,37,64,190,18,49,116,116,33,26,113,1,92,136,26,
-98,112,145,139,163,165,8,211,136,14,228,72,82,68,141,17,56,72,197,209,212,
-132,105,196,5,242,88,108,193,126,18,49,116,117,161,26,113,1,60,158,30,78,
-18,49,116,118,33,26,113,1,29,164,80,78,198,46,142,212,36,68,51,71,232,59,
-147,60,93,110,79,15,39,9,24,186,33,13,63,111,185,16,211,206,251,114,98,17,
-171,160,11,199,197,215,196,66,26,102,38,68,53,212,77,136,104,255,5,114,120,
-121,7,192,70,32,192,67,95,249,59,13,13,127,228,248,134,191,242,133,208,215,
-254,81,204,67,95,249,75,33,13,127,229,61,84,53,255,149,52,80,215,254,85,
-217,67,95,249,91,121,13,90,181,168,134,143,152,95,38,75,207,132,104,156,50,
-70,33,163,225,66,249,50,94,124,25,4,225,146,49,14,24,28,196,0,0,0,0,0,0,15,
-135,252,204,0,0,0,0,0,0,15,7,252,188,72,6,176,77,225,28,24,103,14,33,197,
-138,113,227,28,152,231,46,65,205,19,194,84,11,225,35,23,68,231,138,228,64,
-211,19,132,140,93,19,162,59,145,33,73,18,52,68,225,35,23,68,233,139,228,
-176,217,130,252,36,98,232,157,81,60,158,30,78,18,49,116,78,184,142,210,40,
-39,99,23,68,236,201,59,114,142,224,126,14,138,152,30,67,188,23,143,139,175,
-131,194,135,228,72,85,144,83,60,53,163,208,76,60,68,211,197,78,60,116,243,
-200,80,60,149,19,202,82,60,181,51,204,84,60,213,83,206,86,60,240,190,76,
-151,159,8,209,56,100,137,232,133,242,100,188,248,50,9,195,36,79,73,26,238,
-108,129,15,4,100,78,33,179,207,160,41,224,140,137,194,173,192,158,120,128,
-168,151,26,14,55,58,64,132,75,133,67,81,50,103,8,18,50,9,195,39,105,20,101,
-136,36,50,9,195,39,105,20,11,174,99,220,210,54,121,114,4,145,162,112,201,
-218,69,25,130,9,17,162,112,201,218,69,2,235,152,247,52,141,158,100,128,196,
-144,128,242,102,136,17,70,146,66,3,201,160,32,0,130,225,48,113,137,62,62,
-46,155,167,135,147,142,47,24,147,79,205,68,48,98,79,142,179,120,248,185,
-228,140,241,193,146,66,138,31,55,71,126,129,51,18,124,117,155,199,197,207,
-36,103,142,52,12,36,184,100,129,129,41,32,205,221,175,3,10,36,4,201,188,64,
-112,200,84,52,156,124,92,242,70,120,223,48,64,100,42,26,78,62,46,121,35,52,
-18,91,212,2,72,128,95,20,128,197,137,9,146,113,73,8,190,36,169,27,62,18,
-243,35,100,135,54,92,66,4,34,92,145,0,178,15,132,64,132,75,133,139,178,70,
-240,137,6,34,92,37,230,70,201,1,89,56,36,4,81,49,46,25,5,76,73,241,214,111,
-31,23,60,145,158,57,44,48,46,92,184,100,160,145,46,2,0,201,168,207,198,230,
-144,117,60,176,48,156,160,48,188,192,7,28,18,227,172,222,62,46,121,35,60,
-113,200,26,137,113,241,116,221,60,60,156,113,121,4,20,124,92,242,70,120,
-226,37,194,54,140,36,64,21,147,146,68,24,32,57,0,125,78,84,0,160,123,215,
-140,146,1,4,5,175,40,124,8,20,52,121,51,228,24,96,129,209,46,2,49,6,20,135,
-33,20,53,50,128,194,65,4,12,39,52,64,155,31,48,112,72,6,247,62,16,1,31,73,
-30,25,240,60,73,82,70,68,138,0,89,29,5,156,96,2,201,104,17,35,160,18,78,
-140,228,16,26,79,90,4,73,43,192,244,108,142,130,206,89,240,58,26,50,95,142,
-43,159,65,107,4,167,196,52,100,191,28,87,63,128,15,255,240,164,169,35,136,
-6,128,146,115,9,0,210,7,43,163,194,0,71,128,105,65,176,15,128,105,131,21,
-11,153,35,0,211,134,137,7,65,18,33,244,23,18,14,130,39,34,131,30,113,15,
-224,3,255,254,12,80,81,133,139,153,193,28,17,224,156,50,119,15,131,75,23,
-51,130,112,201,199,185,13,159,116,248,228,68,219,66,149,83,83,238,3,11,238,
-0,48,142,8,240,19,239,144,40,71,4,120,39,12,156,4,252,4,11,19,134,78,61,
-200,108,248,9,248,9,3,9,205,16,39,225,62,7,67,70,75,241,197,241,154,5,172,
-18,159,16,209,146,252,113,124,102,144,106,220,32,44,156,19,152,240,68,158,
-66,2,176,19,17,252,164,7,137,30,176,8,158,116,3,72,128,136,143,232,32,44,
-150,129,19,210,128,89,61,104,159,169,1,50,160,101,56,161,166,246,160,46,
-110,226,221,98,71,130,4,137,222,0,140,221,197,184,64,89,56,183,88,145,224,
-129,34,119,128,23,55,114,143,121,35,193,2,68,239,2,17,155,184,183,8,11,39,
-40,247,146,60,16,36,78,240,32,73,197,12,247,128,26,36,121,1,63,49,2,165,48,
-70,114,229,145,51,250,205,2,8,209,203,150,68,207,235,52,130,16,209,46,131,
-36,188,70,128,210,160,101,56,251,16,131,28,7,35,38,218,50,234,103,130,97,
-103,129,6,73,0,79,88,11,237,84,11,161,32,127,255,255,255,255,255,247,191,
-137,235,16,221,170,129,116,36,0,16,0,0,0,0,0,0,12,196,0,0,0,0,0,0,15,135,
-242,61,123,164,137,162,164,218,67,74,134,162,120,128,0,0,0,0,0,1,224,254,
-71,173,33,129,52,84,155,72,105,80,212,79,16,0,0,0,0,0,0,60,63,199,36,38,
-218,0,0,0,0,0,0,0,0,4,29,78,224,140,38,216,140,46,228,0,243,119,10,139,144,
-123,82,6,205,220,37,222,230,145,179,64,23,180,32,92,221,199,196,130,68,144,
-230,237,200,131,44,24,43,193,25,18,185,0,251,73,138,199,240,27,93,106,192,
-57,41,54,210,0,0,0,0,0,0,62,31,241,58,155,192,12,155,184,48,76,156,148,226,
-134,154,240,32,201,187,147,67,9,201,78,40,105,175,2,225,47,3,18,155,184,
-183,8,11,39,6,9,147,146,156,80,211,94,7,37,55,113,110,16,22,78,77,12,39,37,
-56,161,166,188,16,48,215,130,14,30,240,66,213,93,35,11,124,0,230,36,249,52,
-48,151,192,22,98,79,133,162,215,204,16,17,178,16,199,24,147,237,38,34,246,
-139,95,48,64,70,200,68,16,98,79,140,115,102,123,33,20,89,137,62,210,98,103,
-92,217,158,200,70,14,98,79,131,4,201,100,35,138,49,39,218,76,67,232,38,75,
-33,32,49,137,62,12,24,178,18,68,152,147,237,38,33,244,24,178,18,132,24,147,
-225,221,72,202,200,75,22,98,79,180,152,143,215,82,50,178,19,5,24,147,227,
-16,218,76,146,178,19,70,152,147,237,38,38,117,13,164,201,43,33,56,81,137,
-62,72,130,115,71,43,33,60,105,137,62,210,98,151,72,39,52,114,178,20,7,152,
-147,227,16,181,162,68,19,154,57,89,10,36,140,73,246,147,19,58,133,173,18,
-32,156,209,202,200,82,34,98,79,147,67,9,151,52,156,113,75,34,78,208,1,228,
-73,242,104,97,46,16,62,68,159,24,133,173,18,32,156,209,202,217,83,37,34,79,
-180,152,153,212,45,104,145,4,230,142,86,202,160,169,18,124,145,4,230,142,
-86,215,213,27,34,79,180,152,165,210,9,205,28,173,175,172,42,68,159,24,134,
-210,100,149,183,245,198,200,147,237,38,38,117,13,164,201,43,111,236,8,145,
-39,195,186,145,149,185,246,69,200,147,237,38,35,245,212,140,173,207,180,30,
-68,159,6,9,146,217,91,21,34,79,180,152,135,208,76,150,202,224,137,18,124,
-99,155,51,219,95,116,92,137,62,210,98,103,92,217,158,218,251,194,228,73,
-240,180,90,249,130,2,54,223,223,29,34,79,180,152,139,218,45,124,193,1,27,
-111,240,33,204,73,243,4,4,108,134,8,60,137,62,96,128,141,178,193,193,154,3,
-147,32,227,36,0,0,0,0,0,0,0,0,99,115,245,195,19,159,176,75,175,159,176,24,
-172,253,129,49,121,251,2,176,66,92,130,235,16,18,100,148,251,36,106,123,64,
-65,158,3,147,160,108,202,62,68,165,107,243,227,113,198,211,62,39,20,108,
-115,226,241,130,106,113,224,78,162,4,242,130,236,197,60,37,64,190,18,49,
-116,114,37,40,157,76,9,229,37,217,138,185,16,52,196,225,35,23,71,34,82,137,
-213,64,158,84,93,152,187,145,33,73,18,52,68,225,35,23,71,34,82,137,213,192,
-158,86,93,152,175,146,195,102,11,240,145,139,163,145,41,68,235,32,79,44,46,
-204,83,201,225,228,225,35,23,71,34,82,137,214,192,158,90,93,152,163,180,
-138,9,216,197,209,200,148,161,194,32,30,18,3,74,184,164,88,85,248,42,0,78,
-173,186,58,16,5,149,109,110,236,90,192,144,1,245,109,210,129,222,115,245,
-252,132,93,204,126,23,171,113,180,137,3,250,8,173,149,28,87,220,252,55,86,
-227,104,232,18,0,119,41,48,171,222,94,217,248,46,189,16,6,11,81,21,62,200,
-66,80,3,246,80,140,244,118,180,160,102,157,191,179,79,80,115,31,133,236,
-161,25,233,64,205,59,127,102,158,160,246,63,41,248,30,75,12,11,151,242,233,
-187,146,156,80,211,114,96,54,230,41,20,129,128,50,211,16,16,2,116,180,196,
-129,1,36,55,76,74,16,19,3,116,196,193,65,48,55,75,80,128,65,6,51,211,20,
-128,130,34,23,166,39,6,39,75,76,80,1,146,239,211,20,16,165,91,157,29,49,66,
-10,124,61,211,209,175,1,173,198,211,20,48,139,113,180,180,197,36,42,220,
-109,29,13,49,74,6,192,95,72,188,6,196,55,74,188,6,247,91,80,136,26,32,104,
-220,205,56,1,98,234,52,122,98,136,14,72,110,152,162,132,148,35,61,49,70,7,
-48,55,76,81,194,206,52,104,180,197,45,192,80,175,4,100,77,10,2,101,56,161,
-166,65,113,162,98,8,3,131,7,169,35,36,57,176,0,0,0,0,0,40,116,208,45,158,
-10,225,223,132,17,13,43,176,228,3,0,167,129,32,17,133,134,32,25,80,220,40,
-240,25,26,44,32,240,24,200,44,24,240,56,156,199,128,83,193,17,7,4,13,128,0,
-10,79,202,28,223,195,1,197,72,196,141,159,220,7,48,33,7,8,3,152,49,117,60,
-240,76,47,60,9,224,187,56,43,224,221,64,172,156,36,98,232,228,96,220,145,
-139,163,182,134,237,146,49,116,118,206,6,141,104,105,136,32,14,4,128,160,
-123,215,140,147,32,145,57,178,156,104,41,228,151,168,225,144,168,105,56,
-248,185,228,140,241,190,100,209,244,80,210,116,151,134,12,73,241,214,111,
-31,23,60,145,158,56,50,72,81,67,230,232,239,209,7,24,147,227,226,233,186,
-120,121,56,226,241,137,116,189,52,6,34,92,37,230,70,201,1,89,56,36,154,110,
-25,49,23,196,149,35,103,194,94,100,108,144,230,203,136,73,174,234,63,52,
-252,212,87,0,131,138,4,12,137,114,168,37,166,144,230,37,5,7,19,39,22,70,
-154,103,143,252,4,11,37,160,68,164,139,7,24,3,152,182,20,28,76,156,89,26,
-105,158,63,240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,132,
-176,230,36,248,134,207,98,138,99,4,24,147,229,16,217,236,81,75,98,12,73,
-241,13,158,142,181,20,198,137,49,39,202,33,179,209,214,162,151,4,24,147,
-226,27,61,61,42,41,142,18,98,79,148,67,103,167,165,69,46,138,49,39,194,173,
-192,158,158,149,20,188,40,196,159,10,183,2,122,218,148,82,248,121,18,124,
-67,103,177,77,177,130,36,73,242,136,108,246,41,181,177,18,36,248,134,207,
-71,90,155,99,68,200,147,229,16,217,232,235,83,107,130,36,73,241,13,158,158,
-149,54,199,9,145,39,202,33,179,211,210,166,215,69,72,147,225,86,224,79,79,
-74,155,94,21,34,79,133,91,129,61,109,74,109,126,14,56,7,6,20,28,76,156,89,
-26,105,158,63,240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,
-130,235,191,232,8,149,2,8,196,24,164,137,141,200,8,71,161,196,201,45,167,
-146,59,68,89,24,70,206,0,0,0,0,0,0,7,129,249,142,49,232,71,161,196,201,45,
-167,146,59,68,89,24,70,206,0,0,0,0,0,0,7,129,249,141,201,8,71,161,196,201,
-45,167,146,59,68,89,24,70,206,0,0,0,0,0,0,7,129,250,138,2,214,225,113,235,
-2,27,128,0,10,66,3,189,96,67,120,226,224,0,2,148,140,113,145,66,61,14,38,
-73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,60,15,204,110,80,66,61,14,
-38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,60,15,204,113,147,66,61,
-14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,60,15,204,110,88,66,
-61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,0,16,12,113,149,
-66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,0,16,12,110,96,
-66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,0,16,12,113,
-151,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,0,16,12,
-110,104,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,4,16,
-12,113,153,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,4,
-16,12,110,112,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,0,
-4,16,12,113,155,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,0,
-0,4,16,12,110,120,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,0,
-0,0,4,16,12,113,157,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,
-0,0,0,4,16,12,110,128,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,
-0,0,0,0,8,16,12,113,159,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,
-0,0,0,0,0,8,16,8,58,32,128,24,78,104,129,61,82,2,145,46,17,162,112,208,211,
-107,200,16,137,112,52,41,73,29,113,2,131,137,147,139,35,77,51,234,80,14,39,
-49,224,137,40,35,100,141,9,136,19,18,0,125,162,58,217,236,81,64,68,72,1,
-241,13,158,197,20,150,50,36,0,251,68,117,179,209,214,234,201,69,16,100,72,
-1,246,136,235,103,163,173,208,146,138,68,23,18,0,124,67,103,163,173,213,
-146,138,76,23,18,0,124,67,103,163,173,208,146,138,84,25,18,0,125,162,58,
-217,233,233,117,100,162,138,50,36,0,251,68,117,179,211,210,232,73,69,34,
-139,137,0,62,33,179,211,210,234,201,69,38,139,137,0,62,33,179,211,210,232,
-73,69,42,139,137,0,62,21,110,4,250,178,81,70,23,18,0,124,42,220,9,244,36,
-162,145,134,68,128,31,6,234,5,100,234,201,69,28,100,72,1,240,110,160,86,78,
-132,148,82,56,168,144,3,237,17,214,207,171,37,22,128,42,36,0,251,68,117,
-179,232,73,69,164,9,137,0,62,33,179,234,201,69,168,9,137,0,62,33,179,232,
-73,69,172,10,180,81,50,118,136,235,103,177,77,129,54,138,38,78,33,179,216,
-166,210,198,218,40,153,59,68,117,179,209,214,234,201,77,144,109,162,137,
-147,180,71,91,61,29,110,132,148,218,32,203,69,19,39,16,217,232,235,117,100,
-166,211,6,90,40,153,56,134,207,71,91,161,37,54,168,54,209,68,201,218,35,
-173,158,158,151,86,74,108,163,109,20,76,157,162,58,217,233,233,116,36,166,
-209,70,90,40,153,56,134,207,79,75,171,37,54,154,50,209,68,201,196,54,122,
-122,93,9,41,181,81,150,138,38,78,21,110,4,250,178,83,102,25,104,162,100,
-225,86,224,79,161,37,54,140,54,209,68,201,193,186,129,89,58,178,83,103,27,
-104,162,100,224,221,64,172,157,9,41,180,113,118,138,38,78,209,29,108,250,
-178,83,136,2,237,20,76,157,162,58,217,244,36,167,18,5,90,40,153,56,134,207,
-171,37,56,160,42,209,68,201,196,54,125,9,41,197,141,78,197,141,86,192,0,
-133,66,215,173,96,49,33,64,46,84,8,14,39,49,224,137,40,18,4,19,159,141,100,
-1,100,180,8,148,146,0,91,69,19,38,202,8,58,64,28,209,160,130,52,78,26,26,
-110,255,80,64,196,104,156,50,125,4,144,116,192,57,165,97,4,104,156,52,52,
-221,254,64,20,160,152,23,223,228,32,148,25,174,137,58,23,51,191,200,84,12,
-50,9,195,39,196,80,
+DUK_INTERNAL const duk_uint8_t duk_builtins_data[3972] = {
+144,148,105,223,160,68,52,228,62,12,104,200,165,132,52,167,194,138,105,242,
+252,57,28,211,57,18,64,52,238,62,44,138,111,171,241,164,19,87,125,30,33,
+167,16,145,159,8,211,136,9,225,42,5,240,145,139,163,163,8,211,136,10,228,
+64,211,19,132,140,93,29,56,70,156,64,119,34,66,146,36,104,137,194,70,46,
+142,172,35,78,32,47,146,195,102,11,240,145,139,163,175,8,211,136,9,228,240,
+242,112,145,139,163,179,8,211,136,8,237,34,130,118,49,116,118,225,26,48,0,
+1,80,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
+33,8,66,34,33,154,112,0,1,73,247,35,79,91,237,198,174,192,47,31,23,95,17,
+13,51,19,35,93,68,216,209,128,0,10,192,174,79,15,32,248,8,196,24,8,107,192,
+0,5,98,118,27,94,0,0,43,19,227,94,0,0,43,20,46,215,128,0,10,197,28,198,188,
+0,0,86,41,100,53,224,0,2,177,79,85,175,0,0,21,138,154,45,120,0,0,172,85,
+217,107,192,0,5,98,182,243,86,193,106,52,127,66,249,50,94,124,35,68,225,
+146,49,13,31,170,23,201,146,243,224,200,39,12,145,136,67,134,11,49,0,0,0,0,
+0,0,3,225,255,51,0,0,0,0,0,0,3,193,255,47,18,1,172,19,120,71,10,25,196,136,
+113,162,156,136,199,42,57,204,144,115,132,240,149,2,248,72,197,209,58,2,
+185,16,52,196,225,35,23,68,233,14,228,72,82,68,141,17,56,72,197,209,58,130,
+249,44,54,96,191,9,24,186,39,88,79,39,135,147,132,140,93,19,176,35,180,138,
+9,216,197,209,59,82,79,31,40,242,1,248,58,42,96,121,14,232,94,62,46,190,15,
+38,31,145,33,86,65,76,242,150,143,69,48,242,179,79,45,56,243,51,207,53,64,
+243,116,79,57,72,243,180,207,61,80,243,245,79,65,88,244,34,249,50,94,124,
+35,68,225,146,39,163,23,201,146,243,224,200,39,12,145,61,40,183,146,37,116,
+88,6,136,158,244,241,174,230,202,80,135,130,50,39,16,217,231,208,20,240,70,
+68,225,86,224,79,60,64,84,75,141,7,27,157,32,66,37,194,161,168,153,51,132,
+9,25,4,225,147,180,138,50,196,18,25,4,225,147,180,138,5,215,49,238,105,27,
+60,185,2,72,209,56,100,237,34,140,193,4,136,209,56,100,237,34,129,117,204,
+123,154,70,207,50,64,98,72,64,121,51,68,8,163,73,33,1,228,208,16,0,65,112,
+152,56,196,159,31,23,77,211,195,201,199,23,150,73,169,234,34,24,49,39,199,
+89,188,124,92,242,70,120,224,201,33,69,15,155,163,196,64,153,137,62,58,205,
+227,226,231,146,51,199,26,6,18,92,130,64,192,148,144,102,240,23,129,133,18,
+2,100,224,160,56,100,42,26,78,62,46,121,35,60,112,216,32,50,21,13,39,31,23,
+60,145,154,9,46,18,1,36,64,47,148,64,98,196,132,201,57,68,132,95,18,84,141,
+159,9,121,145,178,67,155,46,73,2,17,46,72,128,89,7,199,32,66,37,194,197,
+217,35,120,228,131,17,46,18,243,35,100,128,172,156,98,2,40,152,151,32,130,
+166,36,248,235,55,143,139,158,72,207,28,150,24,23,46,92,130,80,72,151,21,0,
+100,213,103,229,245,8,186,190,144,24,78,136,24,94,152,3,142,9,113,214,111,
+31,23,60,145,158,57,164,13,68,184,248,186,110,158,30,78,56,188,226,10,62,
+46,121,35,60,113,18,225,27,70,18,32,10,201,208,32,134,214,208,200,84,52,
+156,49,39,50,71,107,107,152,129,13,173,161,144,168,105,57,34,78,100,142,
+214,215,49,16,134,214,210,220,229,81,252,49,39,50,71,107,107,158,65,13,173,
+165,185,202,163,249,34,78,100,142,214,215,60,146,12,16,28,128,62,175,42,6,
+143,36,136,16,64,90,242,135,192,129,67,71,147,62,65,5,215,231,214,6,215,62,
+180,8,49,1,3,162,92,4,98,12,41,14,67,40,106,229,1,132,130,8,24,78,104,129,
+54,62,96,224,144,13,238,124,32,2,62,146,60,51,224,120,146,164,140,137,20,0,
+178,58,11,56,192,5,146,208,34,71,64,36,157,25,200,32,52,158,180,8,146,87,
+129,232,217,29,5,156,179,224,116,52,100,191,28,87,62,130,214,9,79,136,104,
+201,126,56,174,127,0,31,255,225,73,82,71,16,13,1,36,230,18,1,164,14,87,71,
+132,0,143,0,210,131,96,31,0,211,6,42,23,50,70,1,167,13,18,14,130,36,67,232,
+46,36,29,4,78,69,6,60,226,31,192,7,255,252,24,192,163,11,23,51,130,56,35,
+193,56,100,243,31,6,150,46,103,4,225,147,143,114,27,63,57,241,200,169,194,
+133,42,166,175,240,6,23,240,0,97,28,17,224,39,233,32,80,142,8,240,78,25,56,
+9,250,136,22,39,12,156,123,144,217,240,19,245,18,6,19,154,32,79,214,124,14,
+134,140,151,227,139,237,52,11,88,37,62,33,163,37,248,226,251,77,32,213,184,
+64,89,56,39,49,224,137,61,196,5,96,38,35,251,200,15,18,61,96,17,62,40,6,
+145,1,17,31,228,64,89,45,2,39,205,0,178,122,209,63,162,2,101,64,202,113,67,
+77,247,64,92,221,197,186,196,143,4,9,19,208,1,25,187,139,112,128,178,113,
+110,177,35,193,2,68,244,0,46,110,229,30,242,71,130,4,137,232,4,35,55,113,
+110,16,22,78,81,239,36,120,32,72,158,128,64,147,138,25,249,0,52,72,242,2,
+127,2,5,74,96,140,229,203,34,103,250,154,4,17,163,151,44,137,159,234,105,4,
+33,162,93,6,73,123,13,1,165,64,202,113,251,33,6,64,14,71,78,20,101,213,207,
+4,194,207,2,12,162,0,158,176,23,218,168,23,66,64,255,255,255,255,255,255,
+239,127,19,214,33,187,85,2,232,72,0,32,0,0,0,0,0,0,25,136,0,0,0,0,0,0,31,
+15,228,122,247,73,19,69,73,180,134,149,13,68,241,0,0,0,0,0,0,3,193,252,143,
+90,67,2,104,169,54,144,210,161,168,158,32,0,0,0,0,0,0,120,127,142,73,78,20,
+0,0,0,0,0,0,0,0,8,58,189,233,24,77,217,24,93,240,1,230,238,21,23,32,247,68,
+13,155,184,75,189,205,35,102,128,47,114,64,185,187,143,137,4,137,33,205,
+222,17,6,96,48,87,130,50,37,114,1,246,147,21,143,224,54,186,213,128,114,90,
+112,164,0,0,0,0,0,0,124,63,226,117,119,128,25,55,112,96,153,57,41,197,13,
+53,224,65,147,119,38,134,19,146,156,80,211,94,5,194,94,6,37,55,113,110,16,
+22,78,12,19,39,37,56,161,166,188,14,74,110,226,220,32,44,156,154,24,78,74,
+113,67,77,120,32,97,175,4,28,61,224,133,172,186,70,22,248,1,204,73,242,104,
+97,47,128,44,196,159,11,69,175,152,32,35,100,33,142,49,39,218,76,69,237,22,
+190,96,128,141,144,136,32,196,159,24,230,204,246,66,40,179,18,125,164,196,
+206,185,179,61,144,140,28,196,159,6,9,146,200,71,20,98,79,180,152,135,208,
+76,150,66,64,99,18,124,24,49,100,36,137,49,39,218,76,67,232,49,100,37,8,49,
+39,195,186,145,149,144,150,44,196,159,105,49,31,174,164,101,100,38,10,49,
+39,198,33,180,153,37,100,38,141,49,39,218,76,76,234,27,73,146,86,66,112,
+163,18,124,145,4,230,142,86,66,120,211,18,125,164,197,46,144,78,104,229,
+100,40,15,49,39,198,33,107,68,136,39,52,114,178,20,73,24,147,237,38,38,117,
+11,90,36,65,57,163,149,144,164,68,196,159,38,134,19,46,105,56,226,150,68,
+157,160,3,200,147,228,208,194,92,32,124,137,62,49,11,90,36,65,57,163,149,
+178,166,74,68,159,105,49,51,168,90,209,34,9,205,28,173,149,65,82,36,249,34,
+9,205,28,173,175,170,54,68,159,105,49,75,164,19,154,57,91,95,88,84,137,62,
+49,13,164,201,43,111,235,141,145,39,218,76,76,234,27,73,146,86,223,216,17,
+34,79,135,117,35,43,115,236,139,145,39,218,76,71,235,169,25,91,159,104,60,
+137,62,12,19,37,178,182,42,68,159,105,49,15,160,153,45,149,193,18,36,248,
+199,54,103,182,190,232,185,18,125,164,196,206,185,179,61,181,247,133,200,
+147,225,104,181,243,4,4,109,191,190,58,68,159,105,49,23,180,90,249,130,2,
+54,223,224,67,152,147,230,8,8,217,12,16,121,18,124,193,1,27,101,131,131,56,
+7,38,193,198,72,0,0,0,0,0,0,0,0,198,231,240,134,39,63,136,151,95,63,136,49,
+89,252,66,98,243,248,133,96,132,185,5,224,32,36,201,41,248,200,213,249,0,
+131,64,7,39,192,218,148,124,137,74,216,231,198,227,141,182,124,78,40,217,
+231,197,227,4,213,227,192,159,72,10,5,21,218,138,120,74,129,124,36,98,232,
+228,74,81,62,160,20,10,107,181,21,114,32,105,137,194,70,46,142,68,165,19,
+235,1,64,170,187,81,119,34,66,146,36,104,137,194,70,46,142,68,165,19,236,1,
+64,174,187,81,95,37,134,204,23,225,35,23,71,34,82,137,246,128,160,89,93,
+168,167,147,195,201,194,70,46,142,68,165,19,238,1,64,182,187,81,71,105,20,
+19,177,139,163,145,41,68,16,7,6,15,82,70,72,115,96,0,0,0,0,32,93,105,160,
+91,60,149,195,200,194,8,134,149,216,114,1,128,83,192,144,8,194,195,16,12,
+168,110,20,120,12,141,22,16,120,12,100,22,12,120,28,78,99,192,41,224,136,
+115,36,14,100,197,213,245,193,48,189,112,40,2,237,96,175,131,117,2,178,112,
+145,139,163,145,131,114,70,46,142,218,27,182,72,197,209,219,56,26,53,161,
+166,28,1,204,178,10,14,38,78,44,141,52,207,31,0,0,21,64,129,100,180,8,148,
+145,92,203,176,160,226,100,226,200,211,76,241,240,0,1,84,2,131,137,147,142,
+41,100,73,199,192,0,5,88,6,13,10,82,70,62,0,0,42,66,88,115,18,124,67,103,
+177,69,49,130,12,73,242,136,108,246,40,165,177,6,36,248,134,207,71,90,138,
+99,68,152,147,229,16,217,232,235,81,75,130,12,73,241,13,158,158,149,20,199,
+9,49,39,202,33,179,211,210,162,151,69,24,147,225,86,224,79,79,74,138,94,20,
+98,79,133,91,129,61,109,74,41,124,60,137,62,33,179,216,166,216,193,18,36,
+249,68,54,123,20,218,216,137,18,124,67,103,163,173,77,177,162,100,73,242,
+136,108,244,117,169,181,193,18,36,248,134,207,79,74,155,99,132,200,147,229,
+16,217,233,233,83,107,162,164,73,240,171,112,39,167,165,77,175,10,145,39,
+194,173,192,158,182,165,54,191,153,51,72,71,161,196,201,45,167,146,59,68,
+89,24,70,206,0,0,0,0,0,0,7,129,249,153,51,104,71,161,196,201,45,167,146,59,
+68,89,24,70,206,0,0,0,0,0,0,7,129,249,153,51,136,71,161,196,201,45,167,146,
+59,68,89,24,70,206,0,0,0,0,0,0,7,129,249,153,51,168,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,0,2,1,153,51,200,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,0,2,1,153,51,232,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,0,130,1,153,52,8,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,0,130,1,153,52,40,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,0,130,1,153,52,72,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,0,0,0,1,2,1,135,52,102,32,76,72,1,246,136,235,
+103,177,69,1,17,32,7,196,54,123,20,82,88,200,144,3,237,17,214,207,71,91,
+171,37,20,65,145,32,7,218,35,173,158,142,183,66,74,41,16,92,72,1,241,13,
+158,142,183,86,74,41,48,92,72,1,241,13,158,142,183,66,74,41,80,100,72,1,
+246,136,235,103,167,165,213,146,138,40,200,144,3,237,17,214,207,79,75,161,
+37,20,138,46,36,0,248,134,207,79,75,171,37,20,154,46,36,0,248,134,207,79,
+75,161,37,20,170,46,36,0,248,85,184,19,234,201,69,24,92,72,1,240,171,112,
+39,208,146,138,70,25,18,0,124,27,168,21,147,171,37,20,113,145,32,7,193,186,
+129,89,58,18,81,72,226,162,64,15,180,71,91,62,172,148,90,0,168,144,3,237,
+17,214,207,161,37,22,144,38,36,0,248,134,207,171,37,22,160,38,36,0,248,134,
+207,161,37,22,176,42,209,68,201,218,35,173,158,197,54,4,218,40,153,56,134,
+207,98,155,75,27,104,162,100,237,17,214,207,71,91,171,37,54,65,182,138,38,
+78,209,29,108,244,117,186,18,83,104,131,45,20,76,156,67,103,163,173,213,
+146,155,76,25,104,162,100,226,27,61,29,110,132,148,218,160,219,69,19,39,
+104,142,182,122,122,93,89,41,178,141,180,81,50,118,136,235,103,167,165,208,
+146,155,69,25,104,162,100,226,27,61,61,46,172,148,218,104,203,69,19,39,16,
+217,233,233,116,36,166,213,70,90,40,153,56,85,184,19,234,201,77,152,101,
+162,137,147,133,91,129,62,132,148,218,48,219,69,19,39,6,234,5,100,234,201,
+77,156,109,162,137,147,131,117,2,178,116,36,166,209,197,218,40,153,59,68,
+117,179,234,201,78,32,11,180,81,50,118,136,235,103,208,146,156,72,21,104,
+162,100,226,27,62,172,148,226,128,171,69,19,39,16,217,244,36,167,22,53,123,
+102,53,155,80,2,21,11,94,201,128,196,133,0,185,80,32,56,156,199,130,36,160,
+72,16,78,126,54,48,5,146,208,34,82,72,1,109,20,76,155,120,28,34,1,225,32,
+52,171,138,69,133,95,130,160,4,234,219,163,161,0,89,86,214,238,197,172,9,0,
+31,86,221,40,29,231,63,95,200,69,220,199,225,122,183,27,72,144,63,160,138,
+217,81,197,125,207,195,117,110,54,142,129,32,7,114,147,10,189,229,237,159,
+130,235,209,0,96,181,17,83,236,132,37,0,63,101,8,207,71,107,74,6,105,219,
+251,52,245,7,49,248,94,202,17,158,148,12,211,183,246,105,234,15,99,242,159,
+129,228,176,192,185,127,46,155,185,41,197,13,55,38,3,127,255,20,138,160,
+192,25,106,8,8,1,58,90,130,64,128,146,27,168,37,8,9,129,186,130,96,160,152,
+27,165,171,64,32,131,25,234,10,64,65,17,11,212,19,133,18,243,167,165,163,
+32,24,157,45,65,64,6,75,191,80,80,66,149,110,116,117,5,8,41,240,247,79,72,
+188,8,134,81,122,84,1,173,198,212,20,48,139,113,180,181,5,36,42,220,109,29,
+13,65,74,6,192,95,76,188,6,196,55,78,188,6,247,91,86,136,26,32,104,220,205,
+72,1,98,234,52,122,130,136,18,72,51,117,68,3,146,27,168,40,161,37,8,207,80,
+81,129,204,13,212,20,112,179,141,26,45,65,75,112,20,43,193,25,19,66,128,
+153,78,40,105,144,92,104,152,131,124,27,253,128,0,10,116,3,68,146,163,9,
+128,0,10,102,3,138,145,137,27,60,0,0,82,129,7,2,4,16,7,2,70,143,178,203,
+164,237,35,14,25,10,134,147,143,139,158,72,207,28,54,77,47,109,13,55,113,
+120,96,196,159,29,102,241,241,115,201,25,227,131,36,133,20,62,110,143,17,
+16,113,137,62,62,46,155,167,135,147,142,47,44,151,79,221,64,98,37,194,94,
+100,108,144,21,147,140,73,168,228,19,17,124,73,82,54,124,37,230,70,201,14,
+108,185,36,155,14,243,243,83,212,69,131,132,4,12,137,114,168,37,166,145,7,
+10,4,28,200,14,12,40,56,153,56,178,52,211,60,124,0,0,85,0,160,226,100,227,
+138,89,18,113,240,0,1,86,1,131,66,148,145,143,128,0,10,144,93,134,0,0,43,
+80,17,42,4,17,136,49,73,19,49,134,16,143,67,137,146,91,79,36,118,136,178,
+48,141,156,0,0,0,0,0,0,15,3,243,49,135,16,143,67,137,146,91,79,36,118,136,
+178,48,141,156,0,0,0,0,0,0,15,3,245,20,5,173,194,227,214,4,55,0,0,21,196,7,
+122,192,134,241,197,192,0,5,121,25,140,64,132,122,28,76,146,218,121,35,180,
+69,145,132,108,224,0,0,0,0,0,0,120,31,153,140,72,132,122,28,76,146,218,121,
+35,180,69,145,132,108,224,0,0,0,0,0,0,0,32,25,140,80,132,122,28,76,146,218,
+121,35,180,69,145,132,108,224,0,0,0,0,0,0,0,32,25,140,88,132,122,28,76,146,
+218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,8,32,25,140,96,132,122,28,76,
+146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,8,32,25,140,104,132,122,
+28,76,146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,8,32,25,140,112,
+132,122,28,76,146,218,121,35,180,69,145,132,108,224,0,0,0,0,0,0,16,32,16,
+113,225,0,48,156,209,2,122,244,5,34,92,35,68,225,161,166,218,16,33,18,224,
+104,82,146,59,50,5,7,19,39,22,70,154,103,215,32,28,78,99,193,18,80,70,131,
+165,1,205,34,8,35,68,225,161,166,239,255,4,12,70,137,195,39,248,73,7,78,3,
+154,102,16,70,137,195,67,77,223,248,1,74,9,129,125,255,130,9,65,154,232,
+147,161,115,59,255,5,64,195,32,156,50,126,197,14,2,3,107,173,213,0,
 };
 #elif defined(DUK_USE_DOUBLE_BE)
-DUK_INTERNAL const duk_uint8_t duk_builtins_data[3819] = {
-144,148,105,221,32,68,52,228,62,12,104,200,165,134,148,248,81,77,61,191,
-135,35,154,103,34,72,6,157,159,197,145,77,245,126,52,130,106,234,163,196,
-52,226,18,51,161,26,113,1,60,37,64,190,18,49,116,116,33,26,113,1,92,136,26,
-98,112,145,139,163,165,8,211,136,14,228,72,82,68,141,17,56,72,197,209,212,
-132,105,196,5,242,88,108,193,126,18,49,116,117,161,26,113,1,60,158,30,78,
-18,49,116,118,33,26,113,1,29,164,80,78,198,46,142,212,36,68,51,71,232,59,
-147,60,93,110,79,15,39,9,24,186,33,13,63,111,185,16,211,206,251,114,98,17,
-171,160,11,199,197,215,196,66,26,102,38,68,53,212,77,136,104,255,5,114,120,
-121,7,192,70,32,192,67,95,249,59,13,13,127,228,248,134,191,242,133,208,215,
-254,81,204,67,95,249,75,33,13,127,229,61,84,53,255,149,52,80,215,254,85,
-217,67,95,249,91,121,13,90,181,168,134,143,152,95,38,75,207,132,104,156,50,
-70,33,163,225,66,249,50,94,124,25,4,225,146,49,14,24,28,196,7,255,128,0,0,
-0,0,0,12,204,7,255,0,0,0,0,0,0,12,188,72,6,176,77,225,28,24,103,14,33,197,
-138,113,227,28,152,231,46,65,205,19,194,84,11,225,35,23,68,231,138,228,64,
-211,19,132,140,93,19,162,59,145,33,73,18,52,68,225,35,23,68,233,139,228,
-176,217,130,252,36,98,232,157,81,60,158,30,78,18,49,116,78,184,142,210,40,
-39,99,23,68,236,201,59,114,142,224,126,14,138,152,30,67,188,23,143,139,175,
-131,194,135,228,72,85,144,83,60,53,163,208,76,60,68,211,197,78,60,116,243,
-200,80,60,149,19,202,82,60,181,51,204,84,60,213,83,206,86,60,240,190,76,
-151,159,8,209,56,100,137,232,133,242,100,188,248,50,9,195,36,79,73,26,238,
-108,129,15,4,100,78,33,179,207,160,41,224,140,137,194,173,192,158,120,128,
-168,151,26,14,55,58,64,132,75,133,67,81,50,103,8,18,50,9,195,39,105,20,101,
-136,36,50,9,195,39,105,20,11,174,99,220,210,54,121,114,4,145,162,112,201,
-218,69,25,130,9,17,162,112,201,218,69,2,235,152,247,52,141,158,100,128,196,
-144,128,242,102,136,17,70,146,66,3,201,160,32,0,130,225,48,113,137,62,62,
-46,155,167,135,147,142,47,24,147,79,205,68,48,98,79,142,179,120,248,185,
-228,140,241,193,146,66,138,31,55,71,126,129,51,18,124,117,155,199,197,207,
-36,103,142,52,12,36,184,100,129,129,41,32,205,221,175,3,10,36,4,201,188,64,
-112,200,84,52,156,124,92,242,70,120,223,48,64,100,42,26,78,62,46,121,35,52,
-18,91,212,2,72,128,95,20,128,197,137,9,146,113,73,8,190,36,169,27,62,18,
-243,35,100,135,54,92,66,4,34,92,145,0,178,15,132,64,132,75,133,139,178,70,
-240,137,6,34,92,37,230,70,201,1,89,56,36,4,81,49,46,25,5,76,73,241,214,111,
-31,23,60,145,158,57,44,48,46,92,184,100,160,145,46,2,0,201,168,207,198,230,
-144,117,60,176,48,156,160,48,188,192,7,28,18,227,172,222,62,46,121,35,60,
-113,200,26,137,113,241,116,221,60,60,156,113,121,4,20,124,92,242,70,120,
-226,37,194,54,140,36,64,21,147,146,68,24,32,57,0,125,78,84,0,160,123,215,
-140,146,1,4,5,175,40,124,8,20,52,121,51,228,24,96,129,209,46,2,49,6,20,135,
-33,20,53,50,128,194,65,4,12,39,52,64,155,31,48,112,72,6,247,62,16,1,31,73,
-30,25,240,60,73,82,70,68,138,0,89,29,5,156,96,2,201,104,17,35,160,18,78,
-140,228,16,26,79,90,4,73,43,192,244,108,142,130,206,89,240,58,26,50,95,142,
-43,159,65,107,4,167,196,52,100,191,28,87,63,128,15,255,240,164,169,35,136,
-6,128,146,115,9,0,210,7,43,163,194,0,71,128,105,65,176,15,128,105,131,21,
-11,153,35,0,211,134,137,7,65,18,33,244,23,18,14,130,39,34,131,30,113,15,
-224,3,255,254,12,80,81,133,139,153,193,28,17,224,156,50,119,15,131,75,23,
-51,130,112,201,199,185,13,159,116,248,228,68,219,66,149,83,83,238,3,11,238,
-0,48,142,8,240,19,239,144,40,71,4,120,39,12,156,4,252,4,11,19,134,78,61,
-200,108,248,9,248,9,3,9,205,16,39,225,62,7,67,70,75,241,197,241,154,5,172,
-18,159,16,209,146,252,113,124,102,144,106,220,32,44,156,19,152,240,68,158,
-66,2,176,19,17,252,164,7,137,30,176,8,158,116,3,72,128,136,143,232,32,44,
-150,129,19,210,128,89,61,104,159,169,1,50,160,101,56,161,166,246,160,46,
-110,226,221,98,71,130,4,137,222,0,140,221,197,184,64,89,56,183,88,145,224,
-129,34,119,128,23,55,114,143,121,35,193,2,68,239,2,17,155,184,183,8,11,39,
-40,247,146,60,16,36,78,240,32,73,197,12,247,128,26,36,121,1,63,49,2,165,48,
-70,114,229,145,51,250,205,2,8,209,203,150,68,207,235,52,130,16,209,46,131,
-36,188,70,128,210,160,101,56,251,16,131,28,7,35,38,218,50,234,103,130,97,
-103,129,6,73,0,79,88,11,237,84,11,161,32,63,247,255,255,255,255,255,255,
-137,235,16,221,170,129,116,36,0,0,0,0,0,0,0,0,28,196,7,255,128,0,0,0,0,0,2,
-61,123,164,137,162,164,218,67,74,134,162,120,128,255,224,0,0,0,0,0,0,71,
-173,33,129,52,84,155,72,105,80,212,79,16,63,252,0,0,0,0,0,0,7,36,38,218,0,
-0,0,0,0,0,0,0,4,29,78,224,140,38,216,140,46,228,0,243,119,10,139,144,123,
-82,6,205,220,37,222,230,145,179,64,23,180,32,92,221,199,196,130,68,144,230,
-237,200,131,44,24,43,193,25,18,185,0,251,73,138,199,240,27,93,106,192,57,
-41,54,210,31,254,0,0,0,0,0,0,49,58,155,192,12,155,184,48,76,156,148,226,
-134,154,240,32,201,187,147,67,9,201,78,40,105,175,2,225,47,3,18,155,184,
-183,8,11,39,6,9,147,146,156,80,211,94,7,37,55,113,110,16,22,78,77,12,39,37,
-56,161,166,188,16,48,215,130,14,30,240,66,213,93,35,11,124,0,230,36,249,52,
-48,151,192,22,98,79,133,162,215,204,16,17,178,16,199,24,147,237,38,34,246,
-139,95,48,64,70,200,68,16,98,79,140,115,102,123,33,20,89,137,62,210,98,103,
-92,217,158,200,70,14,98,79,131,4,201,100,35,138,49,39,218,76,67,232,38,75,
-33,32,49,137,62,12,24,178,18,68,152,147,237,38,33,244,24,178,18,132,24,147,
-225,221,72,202,200,75,22,98,79,180,152,143,215,82,50,178,19,5,24,147,227,
-16,218,76,146,178,19,70,152,147,237,38,38,117,13,164,201,43,33,56,81,137,
-62,72,130,115,71,43,33,60,105,137,62,210,98,151,72,39,52,114,178,20,7,152,
-147,227,16,181,162,68,19,154,57,89,10,36,140,73,246,147,19,58,133,173,18,
-32,156,209,202,200,82,34,98,79,147,67,9,151,52,156,113,75,34,78,208,1,228,
-73,242,104,97,46,16,62,68,159,24,133,173,18,32,156,209,202,217,83,37,34,79,
-180,152,153,212,45,104,145,4,230,142,86,202,160,169,18,124,145,4,230,142,
-86,215,213,27,34,79,180,152,165,210,9,205,28,173,175,172,42,68,159,24,134,
-210,100,149,183,245,198,200,147,237,38,38,117,13,164,201,43,111,236,8,145,
-39,195,186,145,149,185,246,69,200,147,237,38,35,245,212,140,173,207,180,30,
-68,159,6,9,146,217,91,21,34,79,180,152,135,208,76,150,202,224,137,18,124,
-99,155,51,219,95,116,92,137,62,210,98,103,92,217,158,218,251,194,228,73,
-240,180,90,249,130,2,54,223,223,29,34,79,180,152,139,218,45,124,193,1,27,
-111,240,33,204,73,243,4,4,108,134,8,60,137,62,96,128,141,178,193,193,154,3,
-147,32,227,36,0,0,0,0,0,0,0,0,99,115,245,195,19,159,176,75,175,159,176,24,
-172,253,129,49,121,251,2,176,66,92,130,235,16,18,100,148,251,36,106,123,64,
-65,158,3,147,160,108,202,62,68,165,107,243,227,113,198,211,62,39,20,108,
-115,226,241,130,106,113,224,78,162,4,242,130,236,197,60,37,64,190,18,49,
-116,114,37,40,157,76,9,229,37,217,138,185,16,52,196,225,35,23,71,34,82,137,
-213,64,158,84,93,152,187,145,33,73,18,52,68,225,35,23,71,34,82,137,213,192,
-158,86,93,152,175,146,195,102,11,240,145,139,163,145,41,68,235,32,79,44,46,
-204,83,201,225,228,225,35,23,71,34,82,137,214,192,158,90,93,152,163,180,
-138,9,216,197,209,200,148,161,194,32,30,18,2,0,45,248,84,88,162,187,72,78,
-173,186,58,16,16,0,154,236,110,237,85,69,129,245,109,210,128,127,204,92,
-133,253,244,115,222,23,171,113,180,137,0,255,220,85,29,148,174,11,248,55,
-86,227,104,232,18,1,254,222,91,216,169,55,40,112,46,189,16,16,2,72,126,213,
-17,11,70,3,246,80,140,244,118,180,160,31,243,80,79,51,63,157,230,133,236,
-161,25,233,64,63,246,160,158,102,127,59,205,41,248,30,75,12,11,151,242,233,
-187,146,156,80,211,114,96,54,230,41,20,129,128,50,211,16,16,2,116,180,196,
-129,1,36,55,76,74,16,19,3,116,196,193,65,48,55,75,80,128,65,6,51,211,20,
-128,130,34,23,166,39,6,39,75,76,80,1,146,239,211,20,16,165,91,157,29,49,66,
-10,124,61,211,209,175,1,173,198,211,20,48,139,113,180,180,197,36,42,220,
-109,29,13,49,74,6,192,95,72,188,6,196,55,74,188,6,247,91,80,136,26,32,104,
-220,205,56,1,98,234,52,122,98,136,14,72,110,152,162,132,148,35,61,49,70,7,
-48,55,76,81,194,206,52,104,180,197,45,192,80,175,4,100,77,10,2,101,56,161,
-166,65,113,162,98,8,3,131,7,169,35,36,57,176,16,52,232,64,0,0,0,0,45,158,
-10,225,223,132,17,13,43,176,228,3,0,167,129,32,17,133,134,32,25,80,220,40,
-240,25,26,44,32,240,24,200,44,24,240,56,156,199,128,83,193,17,7,4,13,128,0,
-10,79,202,28,223,195,1,197,72,196,141,159,220,7,48,33,7,8,3,152,49,117,60,
-240,76,47,60,9,224,187,56,43,224,221,64,172,156,36,98,232,228,96,220,145,
-139,163,182,134,237,146,49,116,118,206,6,141,104,105,136,32,14,4,128,160,
-123,215,140,147,32,145,57,178,156,104,41,228,151,168,225,144,168,105,56,
-248,185,228,140,241,190,100,209,244,80,210,116,151,134,12,73,241,214,111,
-31,23,60,145,158,56,50,72,81,67,230,232,239,209,7,24,147,227,226,233,186,
-120,121,56,226,241,137,116,189,52,6,34,92,37,230,70,201,1,89,56,36,154,110,
-25,49,23,196,149,35,103,194,94,100,108,144,230,203,136,73,174,234,63,52,
-252,212,87,0,131,138,4,12,137,114,168,37,166,144,230,37,5,7,19,39,22,70,
-154,103,143,252,4,11,37,160,68,164,139,7,24,3,152,182,20,28,76,156,89,26,
-105,158,63,240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,132,
-176,230,36,248,134,207,98,138,99,4,24,147,229,16,217,236,81,75,98,12,73,
-241,13,158,142,181,20,198,137,49,39,202,33,179,209,214,162,151,4,24,147,
-226,27,61,61,42,41,142,18,98,79,148,67,103,167,165,69,46,138,49,39,194,173,
-192,158,158,149,20,188,40,196,159,10,183,2,122,218,148,82,248,121,18,124,
-67,103,177,77,177,130,36,73,242,136,108,246,41,181,177,18,36,248,134,207,
-71,90,155,99,68,200,147,229,16,217,232,235,83,107,130,36,73,241,13,158,158,
-149,54,199,9,145,39,202,33,179,211,210,166,215,69,72,147,225,86,224,79,79,
-74,155,94,21,34,79,133,91,129,61,109,74,109,126,14,56,7,6,20,28,76,156,89,
-26,105,158,63,240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,
-130,235,191,232,8,149,2,8,196,24,164,137,141,200,8,71,161,196,201,45,167,
-146,59,68,89,24,70,206,1,255,128,0,0,0,0,0,1,142,49,232,71,161,196,201,45,
-167,146,59,68,89,24,70,206,1,255,128,0,0,0,0,0,1,141,201,8,71,161,196,201,
-45,167,146,59,68,89,24,70,206,1,255,128,0,0,0,0,0,2,138,2,214,225,113,235,
-2,27,128,0,10,66,3,189,96,67,120,226,224,0,2,148,140,113,145,66,61,14,38,
-73,109,60,145,218,34,200,194,54,112,15,252,0,0,0,0,0,0,12,110,80,66,61,14,
-38,73,109,60,145,218,34,200,194,54,112,15,252,0,0,0,0,0,0,12,113,147,66,61,
-14,38,73,109,60,145,218,34,200,194,54,112,15,252,0,0,0,0,0,0,12,110,88,66,
-61,14,38,73,109,60,145,218,34,200,194,54,112,16,0,0,0,0,0,0,0,12,113,149,
-66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,0,0,0,0,0,0,0,12,110,96,
-66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,0,0,0,0,0,0,0,12,113,
-151,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,0,0,0,0,0,0,0,12,
-110,104,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,0,0,0,0,0,
-12,113,153,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,0,0,0,0,
-0,12,110,112,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,0,0,0,
-0,0,12,113,155,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,0,0,
-0,0,0,12,110,120,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,0,
-0,0,0,0,12,113,157,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,4,0,
-0,0,0,0,0,12,110,128,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,8,
-0,0,0,0,0,0,12,113,159,66,61,14,38,73,109,60,145,218,34,200,194,54,112,16,
-8,0,0,0,0,0,0,8,58,32,128,24,78,104,129,61,82,2,145,46,17,162,112,208,211,
-107,200,16,137,112,52,41,73,29,113,2,131,137,147,139,35,77,51,234,80,14,39,
-49,224,137,40,35,100,141,9,136,19,18,0,125,162,58,217,236,81,64,68,72,1,
-241,13,158,197,20,150,50,36,0,251,68,117,179,209,214,234,201,69,16,100,72,
-1,246,136,235,103,163,173,208,146,138,68,23,18,0,124,67,103,163,173,213,
-146,138,76,23,18,0,124,67,103,163,173,208,146,138,84,25,18,0,125,162,58,
-217,233,233,117,100,162,138,50,36,0,251,68,117,179,211,210,232,73,69,34,
-139,137,0,62,33,179,211,210,234,201,69,38,139,137,0,62,33,179,211,210,232,
-73,69,42,139,137,0,62,21,110,4,250,178,81,70,23,18,0,124,42,220,9,244,36,
-162,145,134,68,128,31,6,234,5,100,234,201,69,28,100,72,1,240,110,160,86,78,
-132,148,82,56,168,144,3,237,17,214,207,171,37,22,128,42,36,0,251,68,117,
-179,232,73,69,164,9,137,0,62,33,179,234,201,69,168,9,137,0,62,33,179,232,
-73,69,172,10,180,81,50,118,136,235,103,177,77,129,54,138,38,78,33,179,216,
-166,210,198,218,40,153,59,68,117,179,209,214,234,201,77,144,109,162,137,
-147,180,71,91,61,29,110,132,148,218,32,203,69,19,39,16,217,232,235,117,100,
-166,211,6,90,40,153,56,134,207,71,91,161,37,54,168,54,209,68,201,218,35,
-173,158,158,151,86,74,108,163,109,20,76,157,162,58,217,233,233,116,36,166,
-209,70,90,40,153,56,134,207,79,75,171,37,54,154,50,209,68,201,196,54,122,
-122,93,9,41,181,81,150,138,38,78,21,110,4,250,178,83,102,25,104,162,100,
-225,86,224,79,161,37,54,140,54,209,68,201,193,186,129,89,58,178,83,103,27,
-104,162,100,224,221,64,172,157,9,41,180,113,118,138,38,78,209,29,108,250,
-178,83,136,2,237,20,76,157,162,58,217,244,36,167,18,5,90,40,153,56,134,207,
-171,37,56,160,42,209,68,201,196,54,125,9,41,197,141,78,197,141,86,192,0,
-133,66,215,173,96,49,33,64,46,84,8,14,39,49,224,137,40,18,4,19,159,141,100,
-1,100,180,8,148,146,0,91,69,19,38,202,8,58,64,28,209,160,130,52,78,26,26,
-110,255,80,64,196,104,156,50,125,4,144,116,192,57,165,97,4,104,156,52,52,
-221,254,64,20,160,152,23,223,228,32,148,25,174,137,58,23,51,191,200,84,12,
-50,9,195,39,196,80,
+DUK_INTERNAL const duk_uint8_t duk_builtins_data[3972] = {
+144,148,105,223,160,68,52,228,62,12,104,200,165,132,52,167,194,138,105,242,
+252,57,28,211,57,18,64,52,238,62,44,138,111,171,241,164,19,87,125,30,33,
+167,16,145,159,8,211,136,9,225,42,5,240,145,139,163,163,8,211,136,10,228,
+64,211,19,132,140,93,29,56,70,156,64,119,34,66,146,36,104,137,194,70,46,
+142,172,35,78,32,47,146,195,102,11,240,145,139,163,175,8,211,136,9,228,240,
+242,112,145,139,163,179,8,211,136,8,237,34,130,118,49,116,118,225,26,48,0,
+1,80,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
+33,8,66,34,33,154,112,0,1,73,247,35,79,91,237,198,174,192,47,31,23,95,17,
+13,51,19,35,93,68,216,209,128,0,10,192,174,79,15,32,248,8,196,24,8,107,192,
+0,5,98,118,27,94,0,0,43,19,227,94,0,0,43,20,46,215,128,0,10,197,28,198,188,
+0,0,86,41,100,53,224,0,2,177,79,85,175,0,0,21,138,154,45,120,0,0,172,85,
+217,107,192,0,5,98,182,243,86,193,106,52,127,66,249,50,94,124,35,68,225,
+146,49,13,31,170,23,201,146,243,224,200,39,12,145,136,67,134,11,49,1,255,
+224,0,0,0,0,0,3,51,1,255,192,0,0,0,0,0,3,47,18,1,172,19,120,71,10,25,196,
+136,113,162,156,136,199,42,57,204,144,115,132,240,149,2,248,72,197,209,58,
+2,185,16,52,196,225,35,23,68,233,14,228,72,82,68,141,17,56,72,197,209,58,
+130,249,44,54,96,191,9,24,186,39,88,79,39,135,147,132,140,93,19,176,35,180,
+138,9,216,197,209,59,82,79,31,40,242,1,248,58,42,96,121,14,232,94,62,46,
+190,15,38,31,145,33,86,65,76,242,150,143,69,48,242,179,79,45,56,243,51,207,
+53,64,243,116,79,57,72,243,180,207,61,80,243,245,79,65,88,244,34,249,50,94,
+124,35,68,225,146,39,163,23,201,146,243,224,200,39,12,145,61,40,183,146,37,
+116,88,6,136,158,244,241,174,230,202,80,135,130,50,39,16,217,231,208,20,
+240,70,68,225,86,224,79,60,64,84,75,141,7,27,157,32,66,37,194,161,168,153,
+51,132,9,25,4,225,147,180,138,50,196,18,25,4,225,147,180,138,5,215,49,238,
+105,27,60,185,2,72,209,56,100,237,34,140,193,4,136,209,56,100,237,34,129,
+117,204,123,154,70,207,50,64,98,72,64,121,51,68,8,163,73,33,1,228,208,16,0,
+65,112,152,56,196,159,31,23,77,211,195,201,199,23,150,73,169,234,34,24,49,
+39,199,89,188,124,92,242,70,120,224,201,33,69,15,155,163,196,64,153,137,62,
+58,205,227,226,231,146,51,199,26,6,18,92,130,64,192,148,144,102,240,23,129,
+133,18,2,100,224,160,56,100,42,26,78,62,46,121,35,60,112,216,32,50,21,13,
+39,31,23,60,145,154,9,46,18,1,36,64,47,148,64,98,196,132,201,57,68,132,95,
+18,84,141,159,9,121,145,178,67,155,46,73,2,17,46,72,128,89,7,199,32,66,37,
+194,197,217,35,120,228,131,17,46,18,243,35,100,128,172,156,98,2,40,152,151,
+32,130,166,36,248,235,55,143,139,158,72,207,28,150,24,23,46,92,130,80,72,
+151,21,0,100,213,103,229,245,8,186,190,144,24,78,136,24,94,152,3,142,9,113,
+214,111,31,23,60,145,158,57,164,13,68,184,248,186,110,158,30,78,56,188,226,
+10,62,46,121,35,60,113,18,225,27,70,18,32,10,201,208,32,134,214,208,200,84,
+52,156,49,39,50,71,107,107,152,129,13,173,161,144,168,105,57,34,78,100,142,
+214,215,49,16,134,214,210,220,229,81,252,49,39,50,71,107,107,158,65,13,173,
+165,185,202,163,249,34,78,100,142,214,215,60,146,12,16,28,128,62,175,42,6,
+143,36,136,16,64,90,242,135,192,129,67,71,147,62,65,5,215,231,214,6,215,62,
+180,8,49,1,3,162,92,4,98,12,41,14,67,40,106,229,1,132,130,8,24,78,104,129,
+54,62,96,224,144,13,238,124,32,2,62,146,60,51,224,120,146,164,140,137,20,0,
+178,58,11,56,192,5,146,208,34,71,64,36,157,25,200,32,52,158,180,8,146,87,
+129,232,217,29,5,156,179,224,116,52,100,191,28,87,62,130,214,9,79,136,104,
+201,126,56,174,127,0,31,255,225,73,82,71,16,13,1,36,230,18,1,164,14,87,71,
+132,0,143,0,210,131,96,31,0,211,6,42,23,50,70,1,167,13,18,14,130,36,67,232,
+46,36,29,4,78,69,6,60,226,31,192,7,255,252,24,192,163,11,23,51,130,56,35,
+193,56,100,243,31,6,150,46,103,4,225,147,143,114,27,63,57,241,200,169,194,
+133,42,166,175,240,6,23,240,0,97,28,17,224,39,233,32,80,142,8,240,78,25,56,
+9,250,136,22,39,12,156,123,144,217,240,19,245,18,6,19,154,32,79,214,124,14,
+134,140,151,227,139,237,52,11,88,37,62,33,163,37,248,226,251,77,32,213,184,
+64,89,56,39,49,224,137,61,196,5,96,38,35,251,200,15,18,61,96,17,62,40,6,
+145,1,17,31,228,64,89,45,2,39,205,0,178,122,209,63,162,2,101,64,202,113,67,
+77,247,64,92,221,197,186,196,143,4,9,19,208,1,25,187,139,112,128,178,113,
+110,177,35,193,2,68,244,0,46,110,229,30,242,71,130,4,137,232,4,35,55,113,
+110,16,22,78,81,239,36,120,32,72,158,128,64,147,138,25,249,0,52,72,242,2,
+127,2,5,74,96,140,229,203,34,103,250,154,4,17,163,151,44,137,159,234,105,4,
+33,162,93,6,73,123,13,1,165,64,202,113,251,33,6,64,14,71,78,20,101,213,207,
+4,194,207,2,12,162,0,158,176,23,218,168,23,66,64,127,239,255,255,255,255,
+255,255,19,214,33,187,85,2,232,72,0,0,0,0,0,0,0,0,57,136,15,255,0,0,0,0,0,
+0,4,122,247,73,19,69,73,180,134,149,13,68,241,1,255,192,0,0,0,0,0,0,143,90,
+67,2,104,169,54,144,210,161,168,158,32,127,248,0,0,0,0,0,0,14,73,78,20,0,0,
+0,0,0,0,0,0,8,58,189,233,24,77,217,24,93,240,1,230,238,21,23,32,247,68,13,
+155,184,75,189,205,35,102,128,47,114,64,185,187,143,137,4,137,33,205,222,
+17,6,96,48,87,130,50,37,114,1,246,147,21,143,224,54,186,213,128,114,90,112,
+164,63,252,0,0,0,0,0,0,98,117,119,128,25,55,112,96,153,57,41,197,13,53,224,
+65,147,119,38,134,19,146,156,80,211,94,5,194,94,6,37,55,113,110,16,22,78,
+12,19,39,37,56,161,166,188,14,74,110,226,220,32,44,156,154,24,78,74,113,67,
+77,120,32,97,175,4,28,61,224,133,172,186,70,22,248,1,204,73,242,104,97,47,
+128,44,196,159,11,69,175,152,32,35,100,33,142,49,39,218,76,69,237,22,190,
+96,128,141,144,136,32,196,159,24,230,204,246,66,40,179,18,125,164,196,206,
+185,179,61,144,140,28,196,159,6,9,146,200,71,20,98,79,180,152,135,208,76,
+150,66,64,99,18,124,24,49,100,36,137,49,39,218,76,67,232,49,100,37,8,49,39,
+195,186,145,149,144,150,44,196,159,105,49,31,174,164,101,100,38,10,49,39,
+198,33,180,153,37,100,38,141,49,39,218,76,76,234,27,73,146,86,66,112,163,
+18,124,145,4,230,142,86,66,120,211,18,125,164,197,46,144,78,104,229,100,40,
+15,49,39,198,33,107,68,136,39,52,114,178,20,73,24,147,237,38,38,117,11,90,
+36,65,57,163,149,144,164,68,196,159,38,134,19,46,105,56,226,150,68,157,160,
+3,200,147,228,208,194,92,32,124,137,62,49,11,90,36,65,57,163,149,178,166,
+74,68,159,105,49,51,168,90,209,34,9,205,28,173,149,65,82,36,249,34,9,205,
+28,173,175,170,54,68,159,105,49,75,164,19,154,57,91,95,88,84,137,62,49,13,
+164,201,43,111,235,141,145,39,218,76,76,234,27,73,146,86,223,216,17,34,79,
+135,117,35,43,115,236,139,145,39,218,76,71,235,169,25,91,159,104,60,137,62,
+12,19,37,178,182,42,68,159,105,49,15,160,153,45,149,193,18,36,248,199,54,
+103,182,190,232,185,18,125,164,196,206,185,179,61,181,247,133,200,147,225,
+104,181,243,4,4,109,191,190,58,68,159,105,49,23,180,90,249,130,2,54,223,
+224,67,152,147,230,8,8,217,12,16,121,18,124,193,1,27,101,131,131,56,7,38,
+193,198,72,0,0,0,0,0,0,0,0,198,231,240,134,39,63,136,151,95,63,136,49,89,
+252,66,98,243,248,133,96,132,185,5,224,32,36,201,41,248,200,213,249,0,131,
+64,7,39,192,218,148,124,137,74,216,231,198,227,141,182,124,78,40,217,231,
+197,227,4,213,227,192,159,72,10,5,21,218,138,120,74,129,124,36,98,232,228,
+74,81,62,160,20,10,107,181,21,114,32,105,137,194,70,46,142,68,165,19,235,1,
+64,170,187,81,119,34,66,146,36,104,137,194,70,46,142,68,165,19,236,1,64,
+174,187,81,95,37,134,204,23,225,35,23,71,34,82,137,246,128,160,89,93,168,
+167,147,195,201,194,70,46,142,68,165,19,238,1,64,182,187,81,71,105,20,19,
+177,139,163,145,41,68,16,7,6,15,82,70,72,115,96,32,105,221,32,0,0,0,0,91,
+60,149,195,200,194,8,134,149,216,114,1,128,83,192,144,8,194,195,16,12,168,
+110,20,120,12,141,22,16,120,12,100,22,12,120,28,78,99,192,41,224,136,115,
+36,14,100,197,213,245,193,48,189,112,40,2,237,96,175,131,117,2,178,112,145,
+139,163,145,131,114,70,46,142,218,27,182,72,197,209,219,56,26,53,161,166,
+28,1,204,178,10,14,38,78,44,141,52,207,31,0,0,21,64,129,100,180,8,148,145,
+92,203,176,160,226,100,226,200,211,76,241,240,0,1,84,2,131,137,147,142,41,
+100,73,199,192,0,5,88,6,13,10,82,70,62,0,0,42,66,88,115,18,124,67,103,177,
+69,49,130,12,73,242,136,108,246,40,165,177,6,36,248,134,207,71,90,138,99,
+68,152,147,229,16,217,232,235,81,75,130,12,73,241,13,158,158,149,20,199,9,
+49,39,202,33,179,211,210,162,151,69,24,147,225,86,224,79,79,74,138,94,20,
+98,79,133,91,129,61,109,74,41,124,60,137,62,33,179,216,166,216,193,18,36,
+249,68,54,123,20,218,216,137,18,124,67,103,163,173,77,177,162,100,73,242,
+136,108,244,117,169,181,193,18,36,248,134,207,79,74,155,99,132,200,147,229,
+16,217,233,233,83,107,162,164,73,240,171,112,39,167,165,77,175,10,145,39,
+194,173,192,158,182,165,54,191,153,51,72,71,161,196,201,45,167,146,59,68,
+89,24,70,206,1,255,128,0,0,0,0,0,1,153,51,104,71,161,196,201,45,167,146,59,
+68,89,24,70,206,1,255,128,0,0,0,0,0,1,153,51,136,71,161,196,201,45,167,146,
+59,68,89,24,70,206,1,255,128,0,0,0,0,0,1,153,51,168,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,0,0,0,0,0,0,0,1,153,51,200,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,0,0,0,0,0,0,0,1,153,51,232,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,0,128,0,0,0,0,0,1,153,52,8,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,0,128,0,0,0,0,0,1,153,52,40,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,0,128,0,0,0,0,0,1,153,52,72,71,161,196,201,45,167,
+146,59,68,89,24,70,206,2,1,0,0,0,0,0,0,1,135,52,102,32,76,72,1,246,136,235,
+103,177,69,1,17,32,7,196,54,123,20,82,88,200,144,3,237,17,214,207,71,91,
+171,37,20,65,145,32,7,218,35,173,158,142,183,66,74,41,16,92,72,1,241,13,
+158,142,183,86,74,41,48,92,72,1,241,13,158,142,183,66,74,41,80,100,72,1,
+246,136,235,103,167,165,213,146,138,40,200,144,3,237,17,214,207,79,75,161,
+37,20,138,46,36,0,248,134,207,79,75,171,37,20,154,46,36,0,248,134,207,79,
+75,161,37,20,170,46,36,0,248,85,184,19,234,201,69,24,92,72,1,240,171,112,
+39,208,146,138,70,25,18,0,124,27,168,21,147,171,37,20,113,145,32,7,193,186,
+129,89,58,18,81,72,226,162,64,15,180,71,91,62,172,148,90,0,168,144,3,237,
+17,214,207,161,37,22,144,38,36,0,248,134,207,171,37,22,160,38,36,0,248,134,
+207,161,37,22,176,42,209,68,201,218,35,173,158,197,54,4,218,40,153,56,134,
+207,98,155,75,27,104,162,100,237,17,214,207,71,91,171,37,54,65,182,138,38,
+78,209,29,108,244,117,186,18,83,104,131,45,20,76,156,67,103,163,173,213,
+146,155,76,25,104,162,100,226,27,61,29,110,132,148,218,160,219,69,19,39,
+104,142,182,122,122,93,89,41,178,141,180,81,50,118,136,235,103,167,165,208,
+146,155,69,25,104,162,100,226,27,61,61,46,172,148,218,104,203,69,19,39,16,
+217,233,233,116,36,166,213,70,90,40,153,56,85,184,19,234,201,77,152,101,
+162,137,147,133,91,129,62,132,148,218,48,219,69,19,39,6,234,5,100,234,201,
+77,156,109,162,137,147,131,117,2,178,116,36,166,209,197,218,40,153,59,68,
+117,179,234,201,78,32,11,180,81,50,118,136,235,103,208,146,156,72,21,104,
+162,100,226,27,62,172,148,226,128,171,69,19,39,16,217,244,36,167,22,53,123,
+102,53,155,80,2,21,11,94,201,128,196,133,0,185,80,32,56,156,199,130,36,160,
+72,16,78,126,54,48,5,146,208,34,82,72,1,109,20,76,155,120,28,34,1,225,32,
+32,2,223,133,69,138,43,180,132,234,219,163,161,1,0,9,174,198,238,213,84,88,
+31,86,221,40,7,252,197,200,95,223,71,61,225,122,183,27,72,144,15,253,197,
+81,217,74,224,191,131,117,110,54,142,129,32,31,237,229,189,138,147,114,135,
+2,235,209,1,0,36,135,237,81,16,180,96,63,101,8,207,71,107,74,1,255,53,4,
+243,51,249,222,104,94,202,17,158,148,3,255,106,9,230,103,243,188,210,159,
+129,228,176,192,185,127,46,155,185,41,197,13,55,38,3,127,255,20,138,160,
+192,25,106,8,8,1,58,90,130,64,128,146,27,168,37,8,9,129,186,130,96,160,152,
+27,165,171,64,32,131,25,234,10,64,65,17,11,212,19,133,18,243,167,165,163,
+32,24,157,45,65,64,6,75,191,80,80,66,149,110,116,117,5,8,41,240,247,79,72,
+188,8,134,81,122,84,1,173,198,212,20,48,139,113,180,181,5,36,42,220,109,29,
+13,65,74,6,192,95,76,188,6,196,55,78,188,6,247,91,86,136,26,32,104,220,205,
+72,1,98,234,52,122,130,136,18,72,51,117,68,3,146,27,168,40,161,37,8,207,80,
+81,129,204,13,212,20,112,179,141,26,45,65,75,112,20,43,193,25,19,66,128,
+153,78,40,105,144,92,104,152,131,124,27,253,128,0,10,116,3,68,146,163,9,
+128,0,10,102,3,138,145,137,27,60,0,0,82,129,7,2,4,16,7,2,70,143,178,203,
+164,237,35,14,25,10,134,147,143,139,158,72,207,28,54,77,47,109,13,55,113,
+120,96,196,159,29,102,241,241,115,201,25,227,131,36,133,20,62,110,143,17,
+16,113,137,62,62,46,155,167,135,147,142,47,44,151,79,221,64,98,37,194,94,
+100,108,144,21,147,140,73,168,228,19,17,124,73,82,54,124,37,230,70,201,14,
+108,185,36,155,14,243,243,83,212,69,131,132,4,12,137,114,168,37,166,145,7,
+10,4,28,200,14,12,40,56,153,56,178,52,211,60,124,0,0,85,0,160,226,100,227,
+138,89,18,113,240,0,1,86,1,131,66,148,145,143,128,0,10,144,93,134,0,0,43,
+80,17,42,4,17,136,49,73,19,49,134,16,143,67,137,146,91,79,36,118,136,178,
+48,141,156,3,255,0,0,0,0,0,0,3,49,135,16,143,67,137,146,91,79,36,118,136,
+178,48,141,156,3,255,0,0,0,0,0,0,5,20,5,173,194,227,214,4,55,0,0,21,196,7,
+122,192,134,241,197,192,0,5,121,25,140,64,132,122,28,76,146,218,121,35,180,
+69,145,132,108,224,31,248,0,0,0,0,0,0,25,140,72,132,122,28,76,146,218,121,
+35,180,69,145,132,108,224,32,0,0,0,0,0,0,0,25,140,80,132,122,28,76,146,218,
+121,35,180,69,145,132,108,224,32,0,0,0,0,0,0,0,25,140,88,132,122,28,76,146,
+218,121,35,180,69,145,132,108,224,32,8,0,0,0,0,0,0,25,140,96,132,122,28,76,
+146,218,121,35,180,69,145,132,108,224,32,8,0,0,0,0,0,0,25,140,104,132,122,
+28,76,146,218,121,35,180,69,145,132,108,224,32,8,0,0,0,0,0,0,25,140,112,
+132,122,28,76,146,218,121,35,180,69,145,132,108,224,32,16,0,0,0,0,0,0,16,
+113,225,0,48,156,209,2,122,244,5,34,92,35,68,225,161,166,218,16,33,18,224,
+104,82,146,59,50,5,7,19,39,22,70,154,103,215,32,28,78,99,193,18,80,70,131,
+165,1,205,34,8,35,68,225,161,166,239,255,4,12,70,137,195,39,248,73,7,78,3,
+154,102,16,70,137,195,67,77,223,248,1,74,9,129,125,255,130,9,65,154,232,
+147,161,115,59,255,5,64,195,32,156,50,126,197,14,2,3,107,173,213,0,
 };
 #elif defined(DUK_USE_DOUBLE_ME)
-DUK_INTERNAL const duk_uint8_t duk_builtins_data[3819] = {
-144,148,105,221,32,68,52,228,62,12,104,200,165,134,148,248,81,77,61,191,
-135,35,154,103,34,72,6,157,159,197,145,77,245,126,52,130,106,234,163,196,
-52,226,18,51,161,26,113,1,60,37,64,190,18,49,116,116,33,26,113,1,92,136,26,
-98,112,145,139,163,165,8,211,136,14,228,72,82,68,141,17,56,72,197,209,212,
-132,105,196,5,242,88,108,193,126,18,49,116,117,161,26,113,1,60,158,30,78,
-18,49,116,118,33,26,113,1,29,164,80,78,198,46,142,212,36,68,51,71,232,59,
-147,60,93,110,79,15,39,9,24,186,33,13,63,111,185,16,211,206,251,114,98,17,
-171,160,11,199,197,215,196,66,26,102,38,68,53,212,77,136,104,255,5,114,120,
-121,7,192,70,32,192,67,95,249,59,13,13,127,228,248,134,191,242,133,208,215,
-254,81,204,67,95,249,75,33,13,127,229,61,84,53,255,149,52,80,215,254,85,
-217,67,95,249,91,121,13,90,181,168,134,143,152,95,38,75,207,132,104,156,50,
-70,33,163,225,66,249,50,94,124,25,4,225,146,49,14,24,28,196,0,0,15,135,240,
-0,0,0,12,204,0,0,15,7,240,0,0,0,12,188,72,6,176,77,225,28,24,103,14,33,197,
-138,113,227,28,152,231,46,65,205,19,194,84,11,225,35,23,68,231,138,228,64,
-211,19,132,140,93,19,162,59,145,33,73,18,52,68,225,35,23,68,233,139,228,
-176,217,130,252,36,98,232,157,81,60,158,30,78,18,49,116,78,184,142,210,40,
-39,99,23,68,236,201,59,114,142,224,126,14,138,152,30,67,188,23,143,139,175,
-131,194,135,228,72,85,144,83,60,53,163,208,76,60,68,211,197,78,60,116,243,
-200,80,60,149,19,202,82,60,181,51,204,84,60,213,83,206,86,60,240,190,76,
-151,159,8,209,56,100,137,232,133,242,100,188,248,50,9,195,36,79,73,26,238,
-108,129,15,4,100,78,33,179,207,160,41,224,140,137,194,173,192,158,120,128,
-168,151,26,14,55,58,64,132,75,133,67,81,50,103,8,18,50,9,195,39,105,20,101,
-136,36,50,9,195,39,105,20,11,174,99,220,210,54,121,114,4,145,162,112,201,
-218,69,25,130,9,17,162,112,201,218,69,2,235,152,247,52,141,158,100,128,196,
-144,128,242,102,136,17,70,146,66,3,201,160,32,0,130,225,48,113,137,62,62,
-46,155,167,135,147,142,47,24,147,79,205,68,48,98,79,142,179,120,248,185,
-228,140,241,193,146,66,138,31,55,71,126,129,51,18,124,117,155,199,197,207,
-36,103,142,52,12,36,184,100,129,129,41,32,205,221,175,3,10,36,4,201,188,64,
-112,200,84,52,156,124,92,242,70,120,223,48,64,100,42,26,78,62,46,121,35,52,
-18,91,212,2,72,128,95,20,128,197,137,9,146,113,73,8,190,36,169,27,62,18,
-243,35,100,135,54,92,66,4,34,92,145,0,178,15,132,64,132,75,133,139,178,70,
-240,137,6,34,92,37,230,70,201,1,89,56,36,4,81,49,46,25,5,76,73,241,214,111,
-31,23,60,145,158,57,44,48,46,92,184,100,160,145,46,2,0,201,168,207,198,230,
-144,117,60,176,48,156,160,48,188,192,7,28,18,227,172,222,62,46,121,35,60,
-113,200,26,137,113,241,116,221,60,60,156,113,121,4,20,124,92,242,70,120,
-226,37,194,54,140,36,64,21,147,146,68,24,32,57,0,125,78,84,0,160,123,215,
-140,146,1,4,5,175,40,124,8,20,52,121,51,228,24,96,129,209,46,2,49,6,20,135,
-33,20,53,50,128,194,65,4,12,39,52,64,155,31,48,112,72,6,247,62,16,1,31,73,
-30,25,240,60,73,82,70,68,138,0,89,29,5,156,96,2,201,104,17,35,160,18,78,
-140,228,16,26,79,90,4,73,43,192,244,108,142,130,206,89,240,58,26,50,95,142,
-43,159,65,107,4,167,196,52,100,191,28,87,63,128,15,255,240,164,169,35,136,
-6,128,146,115,9,0,210,7,43,163,194,0,71,128,105,65,176,15,128,105,131,21,
-11,153,35,0,211,134,137,7,65,18,33,244,23,18,14,130,39,34,131,30,113,15,
-224,3,255,254,12,80,81,133,139,153,193,28,17,224,156,50,119,15,131,75,23,
-51,130,112,201,199,185,13,159,116,248,228,68,219,66,149,83,83,238,3,11,238,
-0,48,142,8,240,19,239,144,40,71,4,120,39,12,156,4,252,4,11,19,134,78,61,
-200,108,248,9,248,9,3,9,205,16,39,225,62,7,67,70,75,241,197,241,154,5,172,
-18,159,16,209,146,252,113,124,102,144,106,220,32,44,156,19,152,240,68,158,
-66,2,176,19,17,252,164,7,137,30,176,8,158,116,3,72,128,136,143,232,32,44,
-150,129,19,210,128,89,61,104,159,169,1,50,160,101,56,161,166,246,160,46,
-110,226,221,98,71,130,4,137,222,0,140,221,197,184,64,89,56,183,88,145,224,
-129,34,119,128,23,55,114,143,121,35,193,2,68,239,2,17,155,184,183,8,11,39,
-40,247,146,60,16,36,78,240,32,73,197,12,247,128,26,36,121,1,63,49,2,165,48,
-70,114,229,145,51,250,205,2,8,209,203,150,68,207,235,52,130,16,209,46,131,
-36,188,70,128,210,160,101,56,251,16,131,28,7,35,38,218,50,234,103,130,97,
-103,129,6,73,0,79,88,11,237,84,11,161,32,127,255,247,191,255,255,255,255,
-137,235,16,221,170,129,116,36,0,0,0,0,0,16,0,0,12,196,0,0,15,135,240,0,0,0,
-2,61,123,164,137,162,164,218,67,74,134,162,120,128,0,1,224,254,0,0,0,0,71,
-173,33,129,52,84,155,72,105,80,212,79,16,0,0,60,63,192,0,0,0,7,36,38,218,0,
-0,0,0,0,0,0,0,4,29,78,224,140,38,216,140,46,228,0,243,119,10,139,144,123,
-82,6,205,220,37,222,230,145,179,64,23,180,32,92,221,199,196,130,68,144,230,
-237,200,131,44,24,43,193,25,18,185,0,251,73,138,199,240,27,93,106,192,57,
-41,54,210,0,0,62,31,192,0,0,0,49,58,155,192,12,155,184,48,76,156,148,226,
-134,154,240,32,201,187,147,67,9,201,78,40,105,175,2,225,47,3,18,155,184,
-183,8,11,39,6,9,147,146,156,80,211,94,7,37,55,113,110,16,22,78,77,12,39,37,
-56,161,166,188,16,48,215,130,14,30,240,66,213,93,35,11,124,0,230,36,249,52,
-48,151,192,22,98,79,133,162,215,204,16,17,178,16,199,24,147,237,38,34,246,
-139,95,48,64,70,200,68,16,98,79,140,115,102,123,33,20,89,137,62,210,98,103,
-92,217,158,200,70,14,98,79,131,4,201,100,35,138,49,39,218,76,67,232,38,75,
-33,32,49,137,62,12,24,178,18,68,152,147,237,38,33,244,24,178,18,132,24,147,
-225,221,72,202,200,75,22,98,79,180,152,143,215,82,50,178,19,5,24,147,227,
-16,218,76,146,178,19,70,152,147,237,38,38,117,13,164,201,43,33,56,81,137,
-62,72,130,115,71,43,33,60,105,137,62,210,98,151,72,39,52,114,178,20,7,152,
-147,227,16,181,162,68,19,154,57,89,10,36,140,73,246,147,19,58,133,173,18,
-32,156,209,202,200,82,34,98,79,147,67,9,151,52,156,113,75,34,78,208,1,228,
-73,242,104,97,46,16,62,68,159,24,133,173,18,32,156,209,202,217,83,37,34,79,
-180,152,153,212,45,104,145,4,230,142,86,202,160,169,18,124,145,4,230,142,
-86,215,213,27,34,79,180,152,165,210,9,205,28,173,175,172,42,68,159,24,134,
-210,100,149,183,245,198,200,147,237,38,38,117,13,164,201,43,111,236,8,145,
-39,195,186,145,149,185,246,69,200,147,237,38,35,245,212,140,173,207,180,30,
-68,159,6,9,146,217,91,21,34,79,180,152,135,208,76,150,202,224,137,18,124,
-99,155,51,219,95,116,92,137,62,210,98,103,92,217,158,218,251,194,228,73,
-240,180,90,249,130,2,54,223,223,29,34,79,180,152,139,218,45,124,193,1,27,
-111,240,33,204,73,243,4,4,108,134,8,60,137,62,96,128,141,178,193,193,154,3,
-147,32,227,36,0,0,0,0,0,0,0,0,99,115,245,195,19,159,176,75,175,159,176,24,
-172,253,129,49,121,251,2,176,66,92,130,235,16,18,100,148,251,36,106,123,64,
-65,158,3,147,160,108,202,62,68,165,107,243,227,113,198,211,62,39,20,108,
-115,226,241,130,106,113,224,78,162,4,242,130,236,197,60,37,64,190,18,49,
-116,114,37,40,157,76,9,229,37,217,138,185,16,52,196,225,35,23,71,34,82,137,
-213,64,158,84,93,152,187,145,33,73,18,52,68,225,35,23,71,34,82,137,213,192,
-158,86,93,152,175,146,195,102,11,240,145,139,163,145,41,68,235,32,79,44,46,
-204,83,201,225,228,225,35,23,71,34,82,137,214,192,158,90,93,152,163,180,
-138,9,216,197,209,200,148,161,194,32,30,18,0,85,248,42,3,74,184,164,88,78,
-173,186,58,16,44,90,192,144,5,149,109,110,193,245,109,210,128,132,93,204,
-127,222,115,245,252,23,171,113,180,137,1,28,87,220,255,250,8,173,148,55,86,
-227,104,232,18,3,222,94,217,248,119,41,48,168,46,189,16,62,200,66,80,6,11,
-81,21,3,246,80,140,244,118,180,160,79,80,115,31,230,157,191,179,5,236,161,
-25,233,64,158,160,246,63,205,59,127,102,41,248,30,75,12,11,151,242,233,187,
-146,156,80,211,114,96,54,230,41,20,129,128,50,211,16,16,2,116,180,196,129,
-1,36,55,76,74,16,19,3,116,196,193,65,48,55,75,80,128,65,6,51,211,20,128,
-130,34,23,166,39,6,39,75,76,80,1,146,239,211,20,16,165,91,157,29,49,66,10,
-124,61,211,209,175,1,173,198,211,20,48,139,113,180,180,197,36,42,220,109,
-29,13,49,74,6,192,95,72,188,6,196,55,74,188,6,247,91,80,136,26,32,104,220,
-205,56,1,98,234,52,122,98,136,14,72,110,152,162,132,148,35,61,49,70,7,48,
-55,76,81,194,206,52,104,180,197,45,192,80,175,4,100,77,10,2,101,56,161,166,
-65,113,162,98,8,3,131,7,169,35,36,57,176,0,40,116,208,0,0,0,0,45,158,10,
-225,223,132,17,13,43,176,228,3,0,167,129,32,17,133,134,32,25,80,220,40,240,
-25,26,44,32,240,24,200,44,24,240,56,156,199,128,83,193,17,7,4,13,128,0,10,
-79,202,28,223,195,1,197,72,196,141,159,220,7,48,33,7,8,3,152,49,117,60,240,
-76,47,60,9,224,187,56,43,224,221,64,172,156,36,98,232,228,96,220,145,139,
-163,182,134,237,146,49,116,118,206,6,141,104,105,136,32,14,4,128,160,123,
-215,140,147,32,145,57,178,156,104,41,228,151,168,225,144,168,105,56,248,
-185,228,140,241,190,100,209,244,80,210,116,151,134,12,73,241,214,111,31,23,
-60,145,158,56,50,72,81,67,230,232,239,209,7,24,147,227,226,233,186,120,121,
-56,226,241,137,116,189,52,6,34,92,37,230,70,201,1,89,56,36,154,110,25,49,
-23,196,149,35,103,194,94,100,108,144,230,203,136,73,174,234,63,52,252,212,
-87,0,131,138,4,12,137,114,168,37,166,144,230,37,5,7,19,39,22,70,154,103,
-143,252,4,11,37,160,68,164,139,7,24,3,152,182,20,28,76,156,89,26,105,158,
-63,240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,132,176,230,
-36,248,134,207,98,138,99,4,24,147,229,16,217,236,81,75,98,12,73,241,13,158,
-142,181,20,198,137,49,39,202,33,179,209,214,162,151,4,24,147,226,27,61,61,
-42,41,142,18,98,79,148,67,103,167,165,69,46,138,49,39,194,173,192,158,158,
-149,20,188,40,196,159,10,183,2,122,218,148,82,248,121,18,124,67,103,177,77,
-177,130,36,73,242,136,108,246,41,181,177,18,36,248,134,207,71,90,155,99,68,
-200,147,229,16,217,232,235,83,107,130,36,73,241,13,158,158,149,54,199,9,
-145,39,202,33,179,211,210,166,215,69,72,147,225,86,224,79,79,74,155,94,21,
-34,79,133,91,129,61,109,74,109,126,14,56,7,6,20,28,76,156,89,26,105,158,63,
-240,5,7,19,39,28,82,200,147,143,253,0,193,161,74,72,199,253,130,235,191,
-232,8,149,2,8,196,24,164,137,141,200,8,71,161,196,201,45,167,146,59,68,89,
-24,70,206,0,0,7,129,248,0,0,0,1,142,49,232,71,161,196,201,45,167,146,59,68,
-89,24,70,206,0,0,7,129,248,0,0,0,1,141,201,8,71,161,196,201,45,167,146,59,
-68,89,24,70,206,0,0,7,129,248,0,0,0,2,138,2,214,225,113,235,2,27,128,0,10,
-66,3,189,96,67,120,226,224,0,2,148,140,113,145,66,61,14,38,73,109,60,145,
-218,34,200,194,54,112,0,0,60,15,192,0,0,0,12,110,80,66,61,14,38,73,109,60,
-145,218,34,200,194,54,112,0,0,60,15,192,0,0,0,12,113,147,66,61,14,38,73,
-109,60,145,218,34,200,194,54,112,0,0,60,15,192,0,0,0,12,110,88,66,61,14,38,
-73,109,60,145,218,34,200,194,54,112,0,0,0,16,0,0,0,0,12,113,149,66,61,14,
-38,73,109,60,145,218,34,200,194,54,112,0,0,0,16,0,0,0,0,12,110,96,66,61,14,
-38,73,109,60,145,218,34,200,194,54,112,0,0,0,16,0,0,0,0,12,113,151,66,61,
-14,38,73,109,60,145,218,34,200,194,54,112,0,0,0,16,0,0,0,0,12,110,104,66,
-61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,0,0,12,113,153,
-66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,0,0,12,110,
-112,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,0,0,12,
-113,155,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,0,0,
-12,110,120,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,0,
-0,12,113,157,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,4,16,0,0,
-0,0,12,110,128,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,8,16,0,
-0,0,0,12,113,159,66,61,14,38,73,109,60,145,218,34,200,194,54,112,0,0,8,16,
-0,0,0,0,8,58,32,128,24,78,104,129,61,82,2,145,46,17,162,112,208,211,107,
-200,16,137,112,52,41,73,29,113,2,131,137,147,139,35,77,51,234,80,14,39,49,
-224,137,40,35,100,141,9,136,19,18,0,125,162,58,217,236,81,64,68,72,1,241,
-13,158,197,20,150,50,36,0,251,68,117,179,209,214,234,201,69,16,100,72,1,
-246,136,235,103,163,173,208,146,138,68,23,18,0,124,67,103,163,173,213,146,
-138,76,23,18,0,124,67,103,163,173,208,146,138,84,25,18,0,125,162,58,217,
-233,233,117,100,162,138,50,36,0,251,68,117,179,211,210,232,73,69,34,139,
-137,0,62,33,179,211,210,234,201,69,38,139,137,0,62,33,179,211,210,232,73,
-69,42,139,137,0,62,21,110,4,250,178,81,70,23,18,0,124,42,220,9,244,36,162,
-145,134,68,128,31,6,234,5,100,234,201,69,28,100,72,1,240,110,160,86,78,132,
-148,82,56,168,144,3,237,17,214,207,171,37,22,128,42,36,0,251,68,117,179,
-232,73,69,164,9,137,0,62,33,179,234,201,69,168,9,137,0,62,33,179,232,73,69,
-172,10,180,81,50,118,136,235,103,177,77,129,54,138,38,78,33,179,216,166,
-210,198,218,40,153,59,68,117,179,209,214,234,201,77,144,109,162,137,147,
-180,71,91,61,29,110,132,148,218,32,203,69,19,39,16,217,232,235,117,100,166,
-211,6,90,40,153,56,134,207,71,91,161,37,54,168,54,209,68,201,218,35,173,
-158,158,151,86,74,108,163,109,20,76,157,162,58,217,233,233,116,36,166,209,
-70,90,40,153,56,134,207,79,75,171,37,54,154,50,209,68,201,196,54,122,122,
-93,9,41,181,81,150,138,38,78,21,110,4,250,178,83,102,25,104,162,100,225,86,
-224,79,161,37,54,140,54,209,68,201,193,186,129,89,58,178,83,103,27,104,162,
-100,224,221,64,172,157,9,41,180,113,118,138,38,78,209,29,108,250,178,83,
-136,2,237,20,76,157,162,58,217,244,36,167,18,5,90,40,153,56,134,207,171,37,
-56,160,42,209,68,201,196,54,125,9,41,197,141,78,197,141,86,192,0,133,66,
-215,173,96,49,33,64,46,84,8,14,39,49,224,137,40,18,4,19,159,141,100,1,100,
-180,8,148,146,0,91,69,19,38,202,8,58,64,28,209,160,130,52,78,26,26,110,255,
-80,64,196,104,156,50,125,4,144,116,192,57,165,97,4,104,156,52,52,221,254,
-64,20,160,152,23,223,228,32,148,25,174,137,58,23,51,191,200,84,12,50,9,195,
-39,196,80,
+DUK_INTERNAL const duk_uint8_t duk_builtins_data[3972] = {
+144,148,105,223,160,68,52,228,62,12,104,200,165,132,52,167,194,138,105,242,
+252,57,28,211,57,18,64,52,238,62,44,138,111,171,241,164,19,87,125,30,33,
+167,16,145,159,8,211,136,9,225,42,5,240,145,139,163,163,8,211,136,10,228,
+64,211,19,132,140,93,29,56,70,156,64,119,34,66,146,36,104,137,194,70,46,
+142,172,35,78,32,47,146,195,102,11,240,145,139,163,175,8,211,136,9,228,240,
+242,112,145,139,163,179,8,211,136,8,237,34,130,118,49,116,118,225,26,48,0,
+1,80,29,201,158,46,183,39,135,147,132,140,93,16,132,76,66,33,8,66,16,132,
+33,8,66,34,33,154,112,0,1,73,247,35,79,91,237,198,174,192,47,31,23,95,17,
+13,51,19,35,93,68,216,209,128,0,10,192,174,79,15,32,248,8,196,24,8,107,192,
+0,5,98,118,27,94,0,0,43,19,227,94,0,0,43,20,46,215,128,0,10,197,28,198,188,
+0,0,86,41,100,53,224,0,2,177,79,85,175,0,0,21,138,154,45,120,0,0,172,85,
+217,107,192,0,5,98,182,243,86,193,106,52,127,66,249,50,94,124,35,68,225,
+146,49,13,31,170,23,201,146,243,224,200,39,12,145,136,67,134,11,49,0,0,3,
+225,252,0,0,0,3,51,0,0,3,193,252,0,0,0,3,47,18,1,172,19,120,71,10,25,196,
+136,113,162,156,136,199,42,57,204,144,115,132,240,149,2,248,72,197,209,58,
+2,185,16,52,196,225,35,23,68,233,14,228,72,82,68,141,17,56,72,197,209,58,
+130,249,44,54,96,191,9,24,186,39,88,79,39,135,147,132,140,93,19,176,35,180,
+138,9,216,197,209,59,82,79,31,40,242,1,248,58,42,96,121,14,232,94,62,46,
+190,15,38,31,145,33,86,65,76,242,150,143,69,48,242,179,79,45,56,243,51,207,
+53,64,243,116,79,57,72,243,180,207,61,80,243,245,79,65,88,244,34,249,50,94,
+124,35,68,225,146,39,163,23,201,146,243,224,200,39,12,145,61,40,183,146,37,
+116,88,6,136,158,244,241,174,230,202,80,135,130,50,39,16,217,231,208,20,
+240,70,68,225,86,224,79,60,64,84,75,141,7,27,157,32,66,37,194,161,168,153,
+51,132,9,25,4,225,147,180,138,50,196,18,25,4,225,147,180,138,5,215,49,238,
+105,27,60,185,2,72,209,56,100,237,34,140,193,4,136,209,56,100,237,34,129,
+117,204,123,154,70,207,50,64,98,72,64,121,51,68,8,163,73,33,1,228,208,16,0,
+65,112,152,56,196,159,31,23,77,211,195,201,199,23,150,73,169,234,34,24,49,
+39,199,89,188,124,92,242,70,120,224,201,33,69,15,155,163,196,64,153,137,62,
+58,205,227,226,231,146,51,199,26,6,18,92,130,64,192,148,144,102,240,23,129,
+133,18,2,100,224,160,56,100,42,26,78,62,46,121,35,60,112,216,32,50,21,13,
+39,31,23,60,145,154,9,46,18,1,36,64,47,148,64,98,196,132,201,57,68,132,95,
+18,84,141,159,9,121,145,178,67,155,46,73,2,17,46,72,128,89,7,199,32,66,37,
+194,197,217,35,120,228,131,17,46,18,243,35,100,128,172,156,98,2,40,152,151,
+32,130,166,36,248,235,55,143,139,158,72,207,28,150,24,23,46,92,130,80,72,
+151,21,0,100,213,103,229,245,8,186,190,144,24,78,136,24,94,152,3,142,9,113,
+214,111,31,23,60,145,158,57,164,13,68,184,248,186,110,158,30,78,56,188,226,
+10,62,46,121,35,60,113,18,225,27,70,18,32,10,201,208,32,134,214,208,200,84,
+52,156,49,39,50,71,107,107,152,129,13,173,161,144,168,105,57,34,78,100,142,
+214,215,49,16,134,214,210,220,229,81,252,49,39,50,71,107,107,158,65,13,173,
+165,185,202,163,249,34,78,100,142,214,215,60,146,12,16,28,128,62,175,42,6,
+143,36,136,16,64,90,242,135,192,129,67,71,147,62,65,5,215,231,214,6,215,62,
+180,8,49,1,3,162,92,4,98,12,41,14,67,40,106,229,1,132,130,8,24,78,104,129,
+54,62,96,224,144,13,238,124,32,2,62,146,60,51,224,120,146,164,140,137,20,0,
+178,58,11,56,192,5,146,208,34,71,64,36,157,25,200,32,52,158,180,8,146,87,
+129,232,217,29,5,156,179,224,116,52,100,191,28,87,62,130,214,9,79,136,104,
+201,126,56,174,127,0,31,255,225,73,82,71,16,13,1,36,230,18,1,164,14,87,71,
+132,0,143,0,210,131,96,31,0,211,6,42,23,50,70,1,167,13,18,14,130,36,67,232,
+46,36,29,4,78,69,6,60,226,31,192,7,255,252,24,192,163,11,23,51,130,56,35,
+193,56,100,243,31,6,150,46,103,4,225,147,143,114,27,63,57,241,200,169,194,
+133,42,166,175,240,6,23,240,0,97,28,17,224,39,233,32,80,142,8,240,78,25,56,
+9,250,136,22,39,12,156,123,144,217,240,19,245,18,6,19,154,32,79,214,124,14,
+134,140,151,227,139,237,52,11,88,37,62,33,163,37,248,226,251,77,32,213,184,
+64,89,56,39,49,224,137,61,196,5,96,38,35,251,200,15,18,61,96,17,62,40,6,
+145,1,17,31,228,64,89,45,2,39,205,0,178,122,209,63,162,2,101,64,202,113,67,
+77,247,64,92,221,197,186,196,143,4,9,19,208,1,25,187,139,112,128,178,113,
+110,177,35,193,2,68,244,0,46,110,229,30,242,71,130,4,137,232,4,35,55,113,
+110,16,22,78,81,239,36,120,32,72,158,128,64,147,138,25,249,0,52,72,242,2,
+127,2,5,74,96,140,229,203,34,103,250,154,4,17,163,151,44,137,159,234,105,4,
+33,162,93,6,73,123,13,1,165,64,202,113,251,33,6,64,14,71,78,20,101,213,207,
+4,194,207,2,12,162,0,158,176,23,218,168,23,66,64,255,255,239,127,255,255,
+255,255,19,214,33,187,85,2,232,72,0,0,0,0,0,32,0,0,25,136,0,0,31,15,224,0,
+0,0,4,122,247,73,19,69,73,180,134,149,13,68,241,0,0,3,193,252,0,0,0,0,143,
+90,67,2,104,169,54,144,210,161,168,158,32,0,0,120,127,128,0,0,0,14,73,78,
+20,0,0,0,0,0,0,0,0,8,58,189,233,24,77,217,24,93,240,1,230,238,21,23,32,247,
+68,13,155,184,75,189,205,35,102,128,47,114,64,185,187,143,137,4,137,33,205,
+222,17,6,96,48,87,130,50,37,114,1,246,147,21,143,224,54,186,213,128,114,90,
+112,164,0,0,124,63,128,0,0,0,98,117,119,128,25,55,112,96,153,57,41,197,13,
+53,224,65,147,119,38,134,19,146,156,80,211,94,5,194,94,6,37,55,113,110,16,
+22,78,12,19,39,37,56,161,166,188,14,74,110,226,220,32,44,156,154,24,78,74,
+113,67,77,120,32,97,175,4,28,61,224,133,172,186,70,22,248,1,204,73,242,104,
+97,47,128,44,196,159,11,69,175,152,32,35,100,33,142,49,39,218,76,69,237,22,
+190,96,128,141,144,136,32,196,159,24,230,204,246,66,40,179,18,125,164,196,
+206,185,179,61,144,140,28,196,159,6,9,146,200,71,20,98,79,180,152,135,208,
+76,150,66,64,99,18,124,24,49,100,36,137,49,39,218,76,67,232,49,100,37,8,49,
+39,195,186,145,149,144,150,44,196,159,105,49,31,174,164,101,100,38,10,49,
+39,198,33,180,153,37,100,38,141,49,39,218,76,76,234,27,73,146,86,66,112,
+163,18,124,145,4,230,142,86,66,120,211,18,125,164,197,46,144,78,104,229,
+100,40,15,49,39,198,33,107,68,136,39,52,114,178,20,73,24,147,237,38,38,117,
+11,90,36,65,57,163,149,144,164,68,196,159,38,134,19,46,105,56,226,150,68,
+157,160,3,200,147,228,208,194,92,32,124,137,62,49,11,90,36,65,57,163,149,
+178,166,74,68,159,105,49,51,168,90,209,34,9,205,28,173,149,65,82,36,249,34,
+9,205,28,173,175,170,54,68,159,105,49,75,164,19,154,57,91,95,88,84,137,62,
+49,13,164,201,43,111,235,141,145,39,218,76,76,234,27,73,146,86,223,216,17,
+34,79,135,117,35,43,115,236,139,145,39,218,76,71,235,169,25,91,159,104,60,
+137,62,12,19,37,178,182,42,68,159,105,49,15,160,153,45,149,193,18,36,248,
+199,54,103,182,190,232,185,18,125,164,196,206,185,179,61,181,247,133,200,
+147,225,104,181,243,4,4,109,191,190,58,68,159,105,49,23,180,90,249,130,2,
+54,223,224,67,152,147,230,8,8,217,12,16,121,18,124,193,1,27,101,131,131,56,
+7,38,193,198,72,0,0,0,0,0,0,0,0,198,231,240,134,39,63,136,151,95,63,136,49,
+89,252,66,98,243,248,133,96,132,185,5,224,32,36,201,41,248,200,213,249,0,
+131,64,7,39,192,218,148,124,137,74,216,231,198,227,141,182,124,78,40,217,
+231,197,227,4,213,227,192,159,72,10,5,21,218,138,120,74,129,124,36,98,232,
+228,74,81,62,160,20,10,107,181,21,114,32,105,137,194,70,46,142,68,165,19,
+235,1,64,170,187,81,119,34,66,146,36,104,137,194,70,46,142,68,165,19,236,1,
+64,174,187,81,95,37,134,204,23,225,35,23,71,34,82,137,246,128,160,89,93,
+168,167,147,195,201,194,70,46,142,68,165,19,238,1,64,182,187,81,71,105,20,
+19,177,139,163,145,41,68,16,7,6,15,82,70,72,115,96,32,93,105,160,0,0,0,0,
+91,60,149,195,200,194,8,134,149,216,114,1,128,83,192,144,8,194,195,16,12,
+168,110,20,120,12,141,22,16,120,12,100,22,12,120,28,78,99,192,41,224,136,
+115,36,14,100,197,213,245,193,48,189,112,40,2,237,96,175,131,117,2,178,112,
+145,139,163,145,131,114,70,46,142,218,27,182,72,197,209,219,56,26,53,161,
+166,28,1,204,178,10,14,38,78,44,141,52,207,31,0,0,21,64,129,100,180,8,148,
+145,92,203,176,160,226,100,226,200,211,76,241,240,0,1,84,2,131,137,147,142,
+41,100,73,199,192,0,5,88,6,13,10,82,70,62,0,0,42,66,88,115,18,124,67,103,
+177,69,49,130,12,73,242,136,108,246,40,165,177,6,36,248,134,207,71,90,138,
+99,68,152,147,229,16,217,232,235,81,75,130,12,73,241,13,158,158,149,20,199,
+9,49,39,202,33,179,211,210,162,151,69,24,147,225,86,224,79,79,74,138,94,20,
+98,79,133,91,129,61,109,74,41,124,60,137,62,33,179,216,166,216,193,18,36,
+249,68,54,123,20,218,216,137,18,124,67,103,163,173,77,177,162,100,73,242,
+136,108,244,117,169,181,193,18,36,248,134,207,79,74,155,99,132,200,147,229,
+16,217,233,233,83,107,162,164,73,240,171,112,39,167,165,77,175,10,145,39,
+194,173,192,158,182,165,54,191,153,51,72,71,161,196,201,45,167,146,59,68,
+89,24,70,206,0,0,7,129,248,0,0,0,1,153,51,104,71,161,196,201,45,167,146,59,
+68,89,24,70,206,0,0,7,129,248,0,0,0,1,153,51,136,71,161,196,201,45,167,146,
+59,68,89,24,70,206,0,0,7,129,248,0,0,0,1,153,51,168,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,2,0,0,0,0,1,153,51,200,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,2,0,0,0,0,1,153,51,232,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,130,0,0,0,0,1,153,52,8,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,130,0,0,0,0,1,153,52,40,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,0,130,0,0,0,0,1,153,52,72,71,161,196,201,45,167,
+146,59,68,89,24,70,206,0,0,1,2,0,0,0,0,1,135,52,102,32,76,72,1,246,136,235,
+103,177,69,1,17,32,7,196,54,123,20,82,88,200,144,3,237,17,214,207,71,91,
+171,37,20,65,145,32,7,218,35,173,158,142,183,66,74,41,16,92,72,1,241,13,
+158,142,183,86,74,41,48,92,72,1,241,13,158,142,183,66,74,41,80,100,72,1,
+246,136,235,103,167,165,213,146,138,40,200,144,3,237,17,214,207,79,75,161,
+37,20,138,46,36,0,248,134,207,79,75,171,37,20,154,46,36,0,248,134,207,79,
+75,161,37,20,170,46,36,0,248,85,184,19,234,201,69,24,92,72,1,240,171,112,
+39,208,146,138,70,25,18,0,124,27,168,21,147,171,37,20,113,145,32,7,193,186,
+129,89,58,18,81,72,226,162,64,15,180,71,91,62,172,148,90,0,168,144,3,237,
+17,214,207,161,37,22,144,38,36,0,248,134,207,171,37,22,160,38,36,0,248,134,
+207,161,37,22,176,42,209,68,201,218,35,173,158,197,54,4,218,40,153,56,134,
+207,98,155,75,27,104,162,100,237,17,214,207,71,91,171,37,54,65,182,138,38,
+78,209,29,108,244,117,186,18,83,104,131,45,20,76,156,67,103,163,173,213,
+146,155,76,25,104,162,100,226,27,61,29,110,132,148,218,160,219,69,19,39,
+104,142,182,122,122,93,89,41,178,141,180,81,50,118,136,235,103,167,165,208,
+146,155,69,25,104,162,100,226,27,61,61,46,172,148,218,104,203,69,19,39,16,
+217,233,233,116,36,166,213,70,90,40,153,56,85,184,19,234,201,77,152,101,
+162,137,147,133,91,129,62,132,148,218,48,219,69,19,39,6,234,5,100,234,201,
+77,156,109,162,137,147,131,117,2,178,116,36,166,209,197,218,40,153,59,68,
+117,179,234,201,78,32,11,180,81,50,118,136,235,103,208,146,156,72,21,104,
+162,100,226,27,62,172,148,226,128,171,69,19,39,16,217,244,36,167,22,53,123,
+102,53,155,80,2,21,11,94,201,128,196,133,0,185,80,32,56,156,199,130,36,160,
+72,16,78,126,54,48,5,146,208,34,82,72,1,109,20,76,155,120,28,34,1,225,32,5,
+95,130,160,52,171,138,69,132,234,219,163,161,2,197,172,9,0,89,86,214,236,
+31,86,221,40,8,69,220,199,253,231,63,95,193,122,183,27,72,144,17,197,125,
+207,255,160,138,217,67,117,110,54,142,129,32,61,229,237,159,135,114,147,10,
+130,235,209,3,236,132,37,0,96,181,17,80,63,101,8,207,71,107,74,4,245,7,49,
+254,105,219,251,48,94,202,17,158,148,9,234,15,99,252,211,183,246,98,159,
+129,228,176,192,185,127,46,155,185,41,197,13,55,38,3,127,255,20,138,160,
+192,25,106,8,8,1,58,90,130,64,128,146,27,168,37,8,9,129,186,130,96,160,152,
+27,165,171,64,32,131,25,234,10,64,65,17,11,212,19,133,18,243,167,165,163,
+32,24,157,45,65,64,6,75,191,80,80,66,149,110,116,117,5,8,41,240,247,79,72,
+188,8,134,81,122,84,1,173,198,212,20,48,139,113,180,181,5,36,42,220,109,29,
+13,65,74,6,192,95,76,188,6,196,55,78,188,6,247,91,86,136,26,32,104,220,205,
+72,1,98,234,52,122,130,136,18,72,51,117,68,3,146,27,168,40,161,37,8,207,80,
+81,129,204,13,212,20,112,179,141,26,45,65,75,112,20,43,193,25,19,66,128,
+153,78,40,105,144,92,104,152,131,124,27,253,128,0,10,116,3,68,146,163,9,
+128,0,10,102,3,138,145,137,27,60,0,0,82,129,7,2,4,16,7,2,70,143,178,203,
+164,237,35,14,25,10,134,147,143,139,158,72,207,28,54,77,47,109,13,55,113,
+120,96,196,159,29,102,241,241,115,201,25,227,131,36,133,20,62,110,143,17,
+16,113,137,62,62,46,155,167,135,147,142,47,44,151,79,221,64,98,37,194,94,
+100,108,144,21,147,140,73,168,228,19,17,124,73,82,54,124,37,230,70,201,14,
+108,185,36,155,14,243,243,83,212,69,131,132,4,12,137,114,168,37,166,145,7,
+10,4,28,200,14,12,40,56,153,56,178,52,211,60,124,0,0,85,0,160,226,100,227,
+138,89,18,113,240,0,1,86,1,131,66,148,145,143,128,0,10,144,93,134,0,0,43,
+80,17,42,4,17,136,49,73,19,49,134,16,143,67,137,146,91,79,36,118,136,178,
+48,141,156,0,0,15,3,240,0,0,0,3,49,135,16,143,67,137,146,91,79,36,118,136,
+178,48,141,156,0,0,15,3,240,0,0,0,5,20,5,173,194,227,214,4,55,0,0,21,196,7,
+122,192,134,241,197,192,0,5,121,25,140,64,132,122,28,76,146,218,121,35,180,
+69,145,132,108,224,0,0,120,31,128,0,0,0,25,140,72,132,122,28,76,146,218,
+121,35,180,69,145,132,108,224,0,0,0,32,0,0,0,0,25,140,80,132,122,28,76,146,
+218,121,35,180,69,145,132,108,224,0,0,0,32,0,0,0,0,25,140,88,132,122,28,76,
+146,218,121,35,180,69,145,132,108,224,0,0,8,32,0,0,0,0,25,140,96,132,122,
+28,76,146,218,121,35,180,69,145,132,108,224,0,0,8,32,0,0,0,0,25,140,104,
+132,122,28,76,146,218,121,35,180,69,145,132,108,224,0,0,8,32,0,0,0,0,25,
+140,112,132,122,28,76,146,218,121,35,180,69,145,132,108,224,0,0,16,32,0,0,
+0,0,16,113,225,0,48,156,209,2,122,244,5,34,92,35,68,225,161,166,218,16,33,
+18,224,104,82,146,59,50,5,7,19,39,22,70,154,103,215,32,28,78,99,193,18,80,
+70,131,165,1,205,34,8,35,68,225,161,166,239,255,4,12,70,137,195,39,248,73,
+7,78,3,154,102,16,70,137,195,67,77,223,248,1,74,9,129,125,255,130,9,65,154,
+232,147,161,115,59,255,5,64,195,32,156,50,126,197,14,2,3,107,173,213,0,
 };
 #else
 #error invalid endianness defines
@@ -11045,12 +11426,12 @@
 #if defined(DUK_USE_PARANOID_ERRORS)
 DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) {
 	DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)",
-	                   expect_name, duk_get_type_name((duk_context *) thr, idx), (long) idx);
+	                   expect_name, duk_get_type_name(thr, idx), (long) idx);
 }
 #else
 DUK_INTERNAL DUK_COLD void duk_err_require_type_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx, const char *expect_name) {
 	DUK_ERROR_RAW_FMT3(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, "%s required, found %s (stack index %ld)",
-	                   expect_name, duk_push_string_readable((duk_context *) thr, idx), (long) idx);
+	                   expect_name, duk_push_string_readable(thr, idx), (long) idx);
 }
 #endif
 DUK_INTERNAL DUK_COLD void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) {
@@ -11085,8 +11466,8 @@
  * when non-verbose errors are used.
  */
 
-DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_uint_t code));
-DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_uint_t code) {
+DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_errcode_t code));
+DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_errcode_t code) {
 	DUK_ERROR_RAW(thr, NULL, 0, code, NULL);
 }
 DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr) {
@@ -11128,6 +11509,17 @@
 	/* Default behavior is to abort() on error.  There's no printout
 	 * which makes this awkward, so it's always recommended to use an
 	 * explicit fatal error handler.
+	 *
+	 * ====================================================================
+	 * NOTE: If you are seeing this, you are most likely dealing with an
+	 * uncaught error.  You should provide a fatal error handler in Duktape
+	 * heap creation, and should consider using a protected call as your
+	 * first call into an empty Duktape context to properly handle errors.
+	 * See:
+	 *   - http://duktape.org/guide.html#error-handling
+	 *   - http://wiki.duktape.org/HowtoFatalErrors.html
+	 *   - http://duktape.org/api.html#taglist-protected
+	 * ====================================================================
 	 */
 	DUK_D(DUK_DPRINT("built-in default fatal error handler called: %s", msg ? msg : "NULL"));
 	DUK_ABORT();
@@ -11970,8 +12362,8 @@
 	duk_codepoint_t start_i;
 	duk_codepoint_t start_o;
 
+	DUK_ASSERT(bd_ctx != NULL);
 	DUK_UNREF(thr);
-	DUK_ASSERT(bd_ctx != NULL);
 
 	DUK_DDD(DUK_DDDPRINT("slow case conversion for codepoint: %ld", (long) cp));
 
@@ -12153,15 +12545,14 @@
  *  Replace valstack top with case converted version.
  */
 
-DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_small_int_t uppercase) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_bool_t uppercase) {
 	duk_hstring *h_input;
 	duk_bufwriter_ctx bw_alloc;
 	duk_bufwriter_ctx *bw;
 	const duk_uint8_t *p, *p_start, *p_end;
 	duk_codepoint_t prev, curr, next;
 
-	h_input = duk_require_hstring(ctx, -1);  /* Accept symbols. */
+	h_input = duk_require_hstring(thr, -1);  /* Accept symbols. */
 	DUK_ASSERT(h_input != NULL);
 
 	bw = &bw_alloc;
@@ -12208,9 +12599,9 @@
 	}
 
 	DUK_BW_COMPACT(thr, bw);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe, output is encoded. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe, output is encoded. */
 	/* invalidates h_buf pointer */
-	duk_remove_m2(ctx);
+	duk_remove_m2(thr);
 }
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
@@ -12640,9 +13031,9 @@
 	 */
 #if defined(DUK_USE_64BIT_OPS)
 #if defined(DUK_USE_DOUBLE_ME)
-	return (du.ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL) == 0x000000007ff00000ULL;
-#else
-	return (du.ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL;
+	return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000);
+#else
+	return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000)) == DUK_U64_CONSTANT(0x7ff0000000000000);
 #endif
 #else
 	return (du.ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL;
@@ -12659,21 +13050,21 @@
 	du.d = x;
 #if defined(DUK_USE_64BIT_OPS)
 #if defined(DUK_USE_DOUBLE_ME)
-	t = du.ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL;
-	if (t == 0x0000000000000000ULL) {
-		t = du.ull[DUK_DBL_IDX_ULL0] & 0x0000000080000000ULL;
+	t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000);
+	if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
+		t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x0000000080000000);
 		return t == 0;
 	}
-	if (t == 0x000000007ff00000UL) {
+	if (t == DUK_U64_CONSTANT(0x000000007ff00000)) {
 		return 1;
 	}
 #else
-	t = du.ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL;
-	if (t == 0x0000000000000000ULL) {
-		t = du.ull[DUK_DBL_IDX_ULL0] & 0x8000000000000000ULL;
+	t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x7ff0000000000000);
+	if (t == DUK_U64_CONSTANT(0x0000000000000000)) {
+		t = du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x8000000000000000);
 		return t == 0;
 	}
-	if (t == 0x7ff0000000000000ULL) {
+	if (t == DUK_U64_CONSTANT(0x7ff0000000000000)) {
 		return 1;
 	}
 #endif
@@ -12697,7 +13088,7 @@
 
 DUK_INTERNAL duk_double_t duk_double_trunc_towards_zero(duk_double_t x) {
 	/* XXX: optimize */
-	duk_small_int_t s = duk_double_signbit(x);
+	duk_small_uint_t s = duk_double_signbit(x);
 	x = DUK_FLOOR(DUK_FABS(x));  /* truncate towards zero */
 	if (s) {
 		x = -x;
@@ -12903,13 +13294,12 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_EXTERNAL void *duk_resize_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t new_size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void *duk_resize_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t new_size) {
 	duk_hbuffer_dynamic *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) {
@@ -12922,15 +13312,14 @@
 	return DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h);
 }
 
-DUK_EXTERNAL void *duk_steal_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void *duk_steal_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
 	duk_hbuffer_dynamic *h;
 	void *ptr;
 	duk_size_t sz;
 
-	DUK_ASSERT(ctx != NULL);
-
-	h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hbuffer_dynamic *) duk_require_hbuffer(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	if (!(DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h))) {
@@ -12953,13 +13342,12 @@
 	return ptr;
 }
 
-DUK_EXTERNAL void duk_config_buffer(duk_context *ctx, duk_idx_t idx, void *ptr, duk_size_t len) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_config_buffer(duk_hthread *thr, duk_idx_t idx, void *ptr, duk_size_t len) {
 	duk_hbuffer_external *h;
 
-	DUK_ASSERT(ctx != NULL);
-
-	h = (duk_hbuffer_external *) duk_require_hbuffer(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hbuffer_external *) duk_require_hbuffer(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	if (!DUK_HBUFFER_HAS_EXTERNAL(h)) {
@@ -12987,31 +13375,31 @@
 
 #if defined(DUK_USE_BYTECODE_DUMP_SUPPORT)
 
-#define DUK__SER_MARKER  0xff
-#define DUK__SER_VERSION 0x00
+#define DUK__SER_MARKER  0xbf
 #define DUK__SER_STRING  0x00
 #define DUK__SER_NUMBER  0x01
 #define DUK__BYTECODE_INITIAL_ALLOC 256
+#define DUK__NO_FORMALS  0xffffffffUL
 
 /*
  *  Dump/load helpers, xxx_raw() helpers do no buffer checks
  */
 
-DUK_LOCAL duk_uint8_t *duk__load_string_raw(duk_context *ctx, duk_uint8_t *p) {
+DUK_LOCAL duk_uint8_t *duk__load_string_raw(duk_hthread *thr, duk_uint8_t *p) {
 	duk_uint32_t len;
 
 	len = DUK_RAW_READ_U32_BE(p);
-	duk_push_lstring(ctx, (const char *) p, len);
+	duk_push_lstring(thr, (const char *) p, len);
 	p += len;
 	return p;
 }
 
-DUK_LOCAL duk_uint8_t *duk__load_buffer_raw(duk_context *ctx, duk_uint8_t *p) {
+DUK_LOCAL duk_uint8_t *duk__load_buffer_raw(duk_hthread *thr, duk_uint8_t *p) {
 	duk_uint32_t len;
 	duk_uint8_t *buf;
 
 	len = DUK_RAW_READ_U32_BE(p);
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, (duk_size_t) len);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len);
 	DUK_ASSERT(buf != NULL);
 	DUK_MEMCPY((void *) buf, (const void *) p, (size_t) len);
 	p += len;
@@ -13067,7 +13455,7 @@
 		DUK_ASSERT(h_str != NULL);
 	}
 	DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
-	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(h_str), p);
+	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(h_str), p);
 	p = duk__dump_hstring_raw(p, h_str);
 	return p;
 }
@@ -13081,10 +13469,10 @@
 		h_buf = DUK_TVAL_GET_BUFFER(tv);
 		DUK_ASSERT(h_buf != NULL);
 		DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
-		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HBUFFER_GET_SIZE(h_buf), p);
+		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HBUFFER_GET_SIZE(h_buf), p);
 		p = duk__dump_hbuffer_raw(thr, p, h_buf);
 	} else {
-		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
+		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
 		DUK_RAW_WRITE_U32_BE(p, 0);
 	}
 	return p;
@@ -13100,7 +13488,7 @@
 	} else {
 		val = def_value;
 	}
-	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
+	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
 	DUK_RAW_WRITE_U32_BE(p, val);
 	return p;
 }
@@ -13141,12 +13529,12 @@
 #endif
 
 			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
-			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(key) + 4, p);
+			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(key) + 4U, p);
 			p = duk__dump_hstring_raw(p, key);
 			DUK_RAW_WRITE_U32_BE(p, val);
 		}
 	}
-	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
+	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
 	DUK_RAW_WRITE_U32_BE(p, 0);  /* end of _Varmap */
 	return p;
 }
@@ -13156,45 +13544,48 @@
 
 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_FORMALS(thr));
 	if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) {
-		duk_hobject *h;
-		duk_uint_fast32_t i;
-
-		h = DUK_TVAL_GET_OBJECT(tv);
+		duk_harray *h;
+		duk_uint32_t i;
+
+		/* Here we rely on _Formals being a dense array containing
+		 * strings.  This should be the case unless _Formals has been
+		 * tweaked by the application (which we don't support right
+		 * now).
+		 */
+		h = (duk_harray *) DUK_TVAL_GET_OBJECT(tv);
 		DUK_ASSERT(h != NULL);
-
-		/* We know _Formals is dense and all entries will be in the
-		 * array part.  GC and finalizers shouldn't affect _Formals
-		 * so side effects should be fine.
-		 */
-		for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
+		DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h));
+		DUK_ASSERT(h->length <= DUK_HOBJECT_GET_ASIZE((duk_hobject *) h));
+
+		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
+		DUK_ASSERT(h->length != DUK__NO_FORMALS);  /* limits */
+		DUK_RAW_WRITE_U32_BE(p, h->length);
+
+		for (i = 0; i < h->length; i++) {
 			duk_tval *tv_val;
 			duk_hstring *varname;
 
-			tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i);
+			tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, (duk_hobject *) h, i);
 			DUK_ASSERT(tv_val != NULL);
-			if (DUK_TVAL_IS_STRING(tv_val)) {
-				/* Array is dense and contains only strings, but ASIZE may
-				 * be larger than used part and there are UNUSED entries.
-				 */
-				varname = DUK_TVAL_GET_STRING(tv_val);
-				DUK_ASSERT(varname != NULL);
-				DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1);  /* won't be confused with terminator */
-
-				DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
-				p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(varname), p);
-				p = duk__dump_hstring_raw(p, varname);
-			}
-		}
-	} else {
-		DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit empty list"));
-	}
-	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
-	DUK_RAW_WRITE_U32_BE(p, 0);  /* end of _Formals */
+			DUK_ASSERT(DUK_TVAL_IS_STRING(tv_val));
+
+			varname = DUK_TVAL_GET_STRING(tv_val);
+			DUK_ASSERT(varname != NULL);
+			DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1);
+
+			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
+			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(varname), p);
+			p = duk__dump_hstring_raw(p, varname);
+		}
+	} else {
+		DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit marker to indicate missing _Formals"));
+		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
+		DUK_RAW_WRITE_U32_BE(p, DUK__NO_FORMALS);  /* marker: no formals */
+	}
 	return p;
 }
 
-static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
-	duk_hthread *thr;
+static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
 	duk_tval *tv, *tv_end;
 	duk_instr_t *ins, *ins_end;
 	duk_hobject **fn, **fn_end;
@@ -13204,10 +13595,6 @@
 	duk_uint16_t tmp16;
 	duk_double_t d;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(ctx);
-	DUK_UNREF(thr);
-
 	DUK_DD(DUK_DDPRINT("dumping function %p to %p: "
 	                   "consts=[%p,%p[ (%ld bytes, %ld items), "
 	                   "funcs=[%p,%p[ (%ld bytes, %ld items), "
@@ -13229,7 +13616,7 @@
 
 	DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL);  /* ensures no overflow */
 	count_instr = (duk_uint32_t) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func);
-	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p);
+	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3U * 4U + 2U * 2U + 3U * 4U + count_instr * 4U, p);
 
 	/* Fixed header info. */
 	tmp32 = count_instr;
@@ -13284,12 +13671,12 @@
 			h_str = DUK_TVAL_GET_STRING(tv);
 			DUK_ASSERT(h_str != NULL);
 			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
-			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p),
+			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 4U + DUK_HSTRING_GET_BYTELEN(h_str), p),
 			*p++ = DUK__SER_STRING;
 			p = duk__dump_hstring_raw(p, h_str);
 		} else {
 			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p);
+			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 8U, p);
 			*p++ = DUK__SER_NUMBER;
 			d = DUK_TVAL_GET_NUMBER(tv);
 			DUK_RAW_WRITE_DOUBLE_BE(p, d);
@@ -13308,7 +13695,7 @@
 		 * to serialize deep functions.
 		 */
 		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(*fn));
-		p = duk__dump_func(ctx, (duk_hcompfunc *) *fn, bw_ctx, p);
+		p = duk__dump_func(thr, (duk_hcompfunc *) *fn, bw_ctx, p);
 		fn++;
 	}
 
@@ -13353,8 +13740,7 @@
 		DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \
 	} while (0)
 
-static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t *p_end) {
-	duk_hthread *thr;
+static duk_uint8_t *duk__load_func(duk_hthread *thr, duk_uint8_t *p, duk_uint8_t *p_end) {
 	duk_hcompfunc *h_fun;
 	duk_hbuffer *h_data;
 	duk_size_t data_size;
@@ -13367,6 +13753,7 @@
 	duk_idx_t idx_base;
 	duk_tval *tv1;
 	duk_uarridx_t arr_idx;
+	duk_uarridx_t arr_limit;
 	duk_hobject *func_env;
 	duk_bool_t need_pop;
 
@@ -13375,8 +13762,7 @@
 	 * looks the same as created by duk_js_closure().
 	 */
 
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT(thr != NULL);
 
 	DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (void *) p, (void *) p_end));
 
@@ -13397,13 +13783,13 @@
 	 * inner functions being loaded.  Require enough space to handle
 	 * large functions correctly.
 	 */
-	duk_require_stack(ctx, 2 + count_const + count_funcs);
-	idx_base = duk_get_top(ctx);
+	duk_require_stack(thr, (duk_idx_t) (2 + count_const + count_funcs));
+	idx_base = duk_get_top(thr);
 
 	/* Push function object, init flags etc.  This must match
 	 * duk_js_push_closure() quite carefully.
 	 */
-	h_fun = duk_push_hcompfunc(ctx);
+	h_fun = duk_push_hcompfunc(thr);
 	DUK_ASSERT(h_fun != NULL);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) h_fun));
 	DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun) == NULL);
@@ -13424,8 +13810,11 @@
 	tmp32 = DUK_RAW_READ_U32_BE(p);
 	DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32);  /* masks flags to only change duk_hobject flags */
 
-	/* standard prototype */
+	/* standard prototype (no need to set here, already set) */
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+#if 0
 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+#endif
 
 	/* assert just a few critical flags */
 	DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
@@ -13438,7 +13827,7 @@
 	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj));
 
 	/* Create function 'data' buffer but don't attach it yet. */
-	fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, data_size);
+	fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, data_size);
 	DUK_ASSERT(fun_data != NULL);
 
 	/* Load bytecode instructions. */
@@ -13464,7 +13853,7 @@
 		const_type = DUK_RAW_READ_U8(p);
 		switch (const_type) {
 		case DUK__SER_STRING: {
-			p = duk__load_string_raw(ctx, p);
+			p = duk__load_string_raw(thr, p);
 			break;
 		}
 		case DUK__SER_NUMBER: {
@@ -13476,7 +13865,7 @@
 			DUK__ASSERT_LEFT(8);
 			val = DUK_RAW_READ_DOUBLE_BE(p);
 			DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(&tv_tmp, val);
-			duk_push_tval(ctx, &tv_tmp);
+			duk_push_tval(thr, &tv_tmp);
 			break;
 		}
 		default: {
@@ -13487,7 +13876,7 @@
 
 	/* Load inner functions to value stack, but don't yet copy to buffer. */
 	for (n = count_funcs; n > 0; n--) {
-		p = duk__load_func(ctx, p, p_end);
+		p = duk__load_func(thr, p, p_end);
 		if (p == NULL) {
 			goto format_error;
 		}
@@ -13502,12 +13891,12 @@
 	 * them afterwards.
 	 */
 
-	h_data = (duk_hbuffer *) duk_known_hbuffer(ctx, idx_base + 1);
+	h_data = (duk_hbuffer *) duk_known_hbuffer(thr, idx_base + 1);
 	DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data));
 	DUK_HCOMPFUNC_SET_DATA(thr->heap, h_fun, h_data);
 	DUK_HBUFFER_INCREF(thr, h_data);
 
-	tv1 = duk_get_tval(ctx, idx_base + 2);  /* may be NULL if no constants or inner funcs */
+	tv1 = duk_get_tval(thr, idx_base + 2);  /* may be NULL if no constants or inner funcs */
 	DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL);
 
 	q = fun_data;
@@ -13540,16 +13929,16 @@
 	/* The function object is now reachable and refcounts are fine,
 	 * so we can pop off all the temporaries.
 	 */
-	DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(ctx, idx_base)));
-	duk_set_top(ctx, idx_base + 1);
+	DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(thr, idx_base)));
+	duk_set_top(thr, idx_base + 1);
 
 	/* Setup function properties. */
 	tmp32 = DUK_RAW_READ_U32_BE(p);
-	duk_push_u32(ctx, tmp32);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
+	duk_push_u32(thr, tmp32);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
 
 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
-	p = duk__load_string_raw(ctx, p);  /* -> [ func funcname ] */
+	p = duk__load_string_raw(thr, p);  /* -> [ func funcname ] */
 	func_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
 	DUK_ASSERT(func_env != NULL);
 	need_pop = 0;
@@ -13566,7 +13955,7 @@
 		DUK_ASSERT(new_env != NULL);
 		DUK_ASSERT(new_env->thread == NULL);  /* Closed. */
 		DUK_ASSERT(new_env->varmap == NULL);
-		DUK_ASSERT(new_env->regbase == 0);
+		DUK_ASSERT(new_env->regbase_byteoff == 0);
 		DUK_ASSERT_HDECENV_VALID(new_env);
 		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
 		DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, func_env);
@@ -13574,11 +13963,11 @@
 
 		func_env = (duk_hobject *) new_env;
 
-		duk_push_hobject(ctx, (duk_hobject *) new_env);
-
-		duk_dup_m2(ctx);                                  /* -> [ func funcname env funcname ] */
-		duk_dup(ctx, idx_base);                           /* -> [ func funcname env funcname func ] */
-		duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE);  /* -> [ func funcname env ] */
+		duk_push_hobject(thr, (duk_hobject *) new_env);
+
+		duk_dup_m2(thr);                                  /* -> [ func funcname env funcname ] */
+		duk_dup(thr, idx_base);                           /* -> [ func funcname env funcname func ] */
+		duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE);  /* -> [ func funcname env ] */
 
 		need_pop = 1;  /* Need to pop env, but -after- updating h_fun and increfs. */
 	}
@@ -13588,93 +13977,85 @@
 	DUK_HOBJECT_INCREF(thr, func_env);
 	DUK_HOBJECT_INCREF(thr, func_env);
 	if (need_pop) {
-		duk_pop(ctx);
-	}
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
+		duk_pop(thr);
+	}
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
 #endif  /* DUK_USE_FUNC_NAME_PROPERTY */
 
 #if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
-	p = duk__load_string_raw(ctx, p);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
+	p = duk__load_string_raw(thr, p);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
 #endif  /* DUK_USE_FUNC_FILENAME_PROPERTY */
 
 	if (DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_fun)) {
 		/* Restore empty external .prototype only for constructable
 		 * functions.
 		 */
-		duk_push_object(ctx);
-		duk_dup_m2(ctx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* func.prototype.constructor = func */
-		duk_compact_m1(ctx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);
+		duk_push_object(thr);
+		duk_dup_m2(thr);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* func.prototype.constructor = func */
+		duk_compact_m1(thr);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);
 	}
 
 #if defined(DUK_USE_PC2LINE)
-	p = duk__load_buffer_raw(ctx, p);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);
+	p = duk__load_buffer_raw(thr, p);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);
 #endif  /* DUK_USE_PC2LINE */
 
-	duk_push_object(ctx);  /* _Varmap */
+	duk_push_object(thr);  /* _Varmap */
 	for (;;) {
 		/* XXX: awkward */
-		p = duk__load_string_raw(ctx, p);
-		if (duk_get_length(ctx, -1) == 0) {
-			duk_pop(ctx);
+		p = duk__load_string_raw(thr, p);
+		if (duk_get_length(thr, -1) == 0) {
+			duk_pop(thr);
 			break;
 		}
 		tmp32 = DUK_RAW_READ_U32_BE(p);
-		duk_push_u32(ctx, tmp32);
-		duk_put_prop(ctx, -3);
-	}
-	duk_compact_m1(ctx);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
-
-	/* If _Formals wasn't present in the original function, the list
-	 * here will be empty.  Same happens if _Formals was present but
-	 * had zero length.  We can omit _Formals from the result if its
-	 * length is zero and matches nargs.
-	 */
-	duk_push_array(ctx);  /* _Formals */
-	for (arr_idx = 0; ; arr_idx++) {
-		/* XXX: awkward */
-		p = duk__load_string_raw(ctx, p);
-		if (duk_get_length(ctx, -1) == 0) {
-			duk_pop(ctx);
-			break;
-		}
-		duk_put_prop_index(ctx, -2, arr_idx);
-	}
-	if (arr_idx == 0 && h_fun->nargs == 0) {
-		duk_pop(ctx);
-	} else {
-		duk_compact_m1(ctx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_u32(thr, tmp32);
+		duk_put_prop(thr, -3);
+	}
+	duk_compact_m1(thr);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
+
+	/* _Formals may have been missing in the original function, which is
+	 * handled using a marker length.
+	 */
+	arr_limit = DUK_RAW_READ_U32_BE(p);
+	if (arr_limit != DUK__NO_FORMALS) {
+		duk_push_array(thr);  /* _Formals */
+		for (arr_idx = 0; arr_idx < arr_limit; arr_idx++) {
+			p = duk__load_string_raw(thr, p);
+			duk_put_prop_index(thr, -2, arr_idx);
+		}
+		duk_compact_m1(thr);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
+	} else {
+		DUK_DD(DUK_DDPRINT("no _Formals in dumped function"));
 	}
 
 	/* Return with final function pushed on stack top. */
-	DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(ctx, -1)));
-	DUK_ASSERT_TOP(ctx, idx_base + 1);
+	DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(thr, -1)));
+	DUK_ASSERT_TOP(thr, idx_base + 1);
 	return p;
 
  format_error:
 	return NULL;
 }
 
-DUK_EXTERNAL void duk_dump_function(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
 	duk_hcompfunc *func;
 	duk_bufwriter_ctx bw_ctx_alloc;
 	duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc;
 	duk_uint8_t *p;
 
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Bound functions don't have all properties so we'd either need to
 	 * lookup the non-bound target function or reject bound functions.
-	 * For now, bound functions are rejected.
-	 */
-	func = duk_require_hcompfunc(ctx, -1);
+	 * For now, bound functions are rejected with TypeError.
+	 */
+	func = duk_require_hcompfunc(thr, -1);
 	DUK_ASSERT(func != NULL);
 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj));
 
@@ -13684,26 +14065,22 @@
 	DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC);
 	p = DUK_BW_GET_PTR(thr, bw_ctx);
 	*p++ = DUK__SER_MARKER;
-	*p++ = DUK__SER_VERSION;
-	p = duk__dump_func(ctx, func, bw_ctx, p);
+	p = duk__dump_func(thr, func, bw_ctx, p);
 	DUK_BW_SET_PTR(thr, bw_ctx, p);
 	DUK_BW_COMPACT(thr, bw_ctx);
 
-	DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(ctx, -1)));
-
-	duk_remove_m2(ctx);  /* [ ... func buf ] -> [ ... buf ] */
-}
-
-DUK_EXTERNAL void duk_load_function(duk_context *ctx) {
-	duk_hthread *thr;
+	DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1)));
+
+	duk_remove_m2(thr);  /* [ ... func buf ] -> [ ... buf ] */
+}
+
+DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
 	duk_uint8_t *p_buf, *p, *p_end;
 	duk_size_t sz;
 
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(ctx);
-
-	p_buf = (duk_uint8_t *) duk_require_buffer(ctx, -1, &sz);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	p_buf = (duk_uint8_t *) duk_require_buffer(thr, -1, &sz);
 	DUK_ASSERT(p_buf != NULL);
 
 	/* The caller is responsible for being sure that bytecode being loaded
@@ -13712,36 +14089,38 @@
 	 * (instruction validation would be quite complex to implement).
 	 *
 	 * This signature check is the only sanity check for detecting
-	 * accidental invalid inputs.  The initial 0xFF byte ensures no
-	 * ordinary string will be accepted by accident.
+	 * accidental invalid inputs.  The initial byte ensures no ordinary
+	 * string or Symbol will be accepted by accident.
 	 */
 	p = p_buf;
 	p_end = p_buf + sz;
-	if (sz < 2 || p[0] != DUK__SER_MARKER || p[1] != DUK__SER_VERSION) {
+	if (sz < 1 || p[0] != DUK__SER_MARKER) {
 		goto format_error;
 	}
-	p += 2;
-
-	p = duk__load_func(ctx, p, p_end);
+	p++;
+
+	p = duk__load_func(thr, p, p_end);
 	if (p == NULL) {
 		goto format_error;
 	}
 
-	duk_remove_m2(ctx);  /* [ ... buf func ] -> [ ... func ] */
+	duk_remove_m2(thr);  /* [ ... buf func ] -> [ ... func ] */
 	return;
 
  format_error:
-	DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED);
+	DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BYTECODE);
 }
 
 #else  /* DUK_USE_BYTECODE_DUMP_SUPPORT */
 
-DUK_EXTERNAL void duk_dump_function(duk_context *ctx) {
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
-}
-
-DUK_EXTERNAL void duk_load_function(duk_context *ctx) {
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ERROR_UNSUPPORTED(thr);
+}
+
+DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ERROR_UNSUPPORTED(thr);
 }
 
 #endif  /* DUK_USE_BYTECODE_DUMP_SUPPORT */
@@ -13749,229 +14128,325 @@
 /* automatic undefs */
 #undef DUK__ASSERT_LEFT
 #undef DUK__BYTECODE_INITIAL_ALLOC
+#undef DUK__NO_FORMALS
 #undef DUK__SER_MARKER
 #undef DUK__SER_NUMBER
 #undef DUK__SER_STRING
-#undef DUK__SER_VERSION
 #line 1 "duk_api_call.c"
 /*
  *  Calls.
  *
- *  Protected variants should avoid ever throwing an error.
+ *  Protected variants should avoid ever throwing an error.  Must be careful
+ *  to catch errors related to value stack manipulation and property lookup,
+ *  not just the call itself.
+ *
+ *  The only exception is when arguments are insane, e.g. nargs/nrets are out
+ *  of bounds; in such cases an error is thrown for two reasons.  First, we
+ *  can't always respect the value stack input/output guarantees in such cases
+ *  so the caller would end up with the value stack in an unexpected state.
+ *  Second, an attempt to create an error might itself fail (although this
+ *  could be avoided by pushing a preallocated object/string or a primitive
+ *  value).
  */
 
 /* #include duk_internal.h -> already included */
 
+/*
+ *  Helpers
+ */
+
+struct duk__pcall_prop_args {
+	duk_idx_t obj_idx;
+	duk_idx_t nargs;
+	duk_small_uint_t call_flags;
+};
+typedef struct duk__pcall_prop_args duk__pcall_prop_args;
+
+struct duk__pcall_method_args {
+	duk_idx_t nargs;
+	duk_small_uint_t call_flags;
+};
+typedef struct duk__pcall_method_args duk__pcall_method_args;
+
+struct duk__pcall_args {
+	duk_idx_t nargs;
+	duk_small_uint_t call_flags;
+};
+typedef struct duk__pcall_args duk__pcall_args;
+
+/* Compute and validate idx_func for a certain 'nargs' and 'other'
+ * parameter count (1 or 2, depending on whether 'this' binding is
+ * present).
+ */
+DUK_LOCAL duk_idx_t duk__call_get_idx_func(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) {
+	duk_idx_t idx_func;
+
+	/* XXX: byte arithmetic? */
+
+	DUK_ASSERT(other >= 0);
+
+	idx_func = duk_get_top(thr) - nargs - other;
+	if (DUK_UNLIKELY((idx_func | nargs) < 0)) {  /* idx_func < 0 || nargs < 0; OR sign bits */
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
+		/* unreachable */
+	}
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+	return idx_func;
+}
+
+/* Compute idx_func, assume index will be valid.  This is a valid assumption
+ * for protected calls: nargs < 0 is checked explicitly and duk_safe_call()
+ * validates the argument count.
+ */
+DUK_LOCAL duk_idx_t duk__call_get_idx_func_unvalidated(duk_hthread *thr, duk_idx_t nargs, duk_idx_t other) {
+	duk_idx_t idx_func;
+
+	/* XXX: byte arithmetic? */
+
+	DUK_ASSERT(nargs >= 0);
+	DUK_ASSERT(other >= 0);
+
+	idx_func = duk_get_top(thr) - nargs - other;
+	DUK_ASSERT(idx_func >= 0);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+	return idx_func;
+}
+
 /* Prepare value stack for a method call through an object property.
  * May currently throw an error e.g. when getting the property.
  */
-DUK_LOCAL void duk__call_prop_prep_stack(duk_context *ctx, duk_idx_t normalized_obj_idx, duk_idx_t nargs) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_LOCAL void duk__call_prop_prep_stack(duk_hthread *thr, duk_idx_t normalized_obj_idx, duk_idx_t nargs) {
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_ASSERT(nargs >= 0);
 
 	DUK_DDD(DUK_DDDPRINT("duk__call_prop_prep_stack, normalized_obj_idx=%ld, nargs=%ld, stacktop=%ld",
-	                     (long) normalized_obj_idx, (long) nargs, (long) duk_get_top(ctx)));
+	                     (long) normalized_obj_idx, (long) nargs, (long) duk_get_top(thr)));
 
 	/* [... key arg1 ... argN] */
 
 	/* duplicate key */
-	duk_dup(ctx, -nargs - 1);  /* Note: -nargs alone would fail for nargs == 0, this is OK */
-	duk_get_prop(ctx, normalized_obj_idx);
-
-	DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_dup(thr, -nargs - 1);  /* Note: -nargs alone would fail for nargs == 0, this is OK */
+	(void) duk_get_prop(thr, normalized_obj_idx);
+
+	DUK_DDD(DUK_DDDPRINT("func: %!T", (duk_tval *) duk_get_tval(thr, -1)));
+
+#if defined(DUK_USE_VERBOSE_ERRORS)
+	if (DUK_UNLIKELY(!duk_is_callable(thr, -1))) {
+		duk_tval *tv_targ;
+		duk_tval *tv_base;
+		duk_tval *tv_key;
+
+		tv_targ = DUK_GET_TVAL_NEGIDX(thr, -1);
+		tv_base = DUK_GET_TVAL_POSIDX(thr, normalized_obj_idx);
+		tv_key = DUK_GET_TVAL_NEGIDX(thr, -nargs - 2);
+		DUK_ASSERT(tv_targ >= thr->valstack_bottom && tv_targ < thr->valstack_top);
+		DUK_ASSERT(tv_base >= thr->valstack_bottom && tv_base < thr->valstack_top);
+		DUK_ASSERT(tv_key >= thr->valstack_bottom && tv_key < thr->valstack_top);
+
+		duk_call_setup_propcall_error(thr, tv_targ, tv_base, tv_key);
+	}
+#endif
 
 	/* [... key arg1 ... argN func] */
 
-	duk_replace(ctx, -nargs - 2);
+	duk_replace(thr, -nargs - 2);
 
 	/* [... func arg1 ... argN] */
 
-	duk_dup(ctx, normalized_obj_idx);
-	duk_insert(ctx, -nargs - 1);
+	duk_dup(thr, normalized_obj_idx);
+	duk_insert(thr, -nargs - 1);
 
 	/* [... func this arg1 ... argN] */
 }
 
-DUK_EXTERNAL void duk_call(duk_context *ctx, duk_idx_t nargs) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_call(duk_hthread *thr, duk_idx_t nargs) {
 	duk_small_uint_t call_flags;
 	duk_idx_t idx_func;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	idx_func = duk_get_top(ctx) - nargs - 1;
-	if (idx_func < 0 || nargs < 0) {
-		/* note that we can't reliably pop anything here */
-		DUK_ERROR_TYPE_INVALID_ARGS(thr);
-	}
-
-	/* XXX: awkward; we assume there is space for this, overwrite
-	 * directly instead?
-	 */
-	duk_push_undefined(ctx);
-	duk_insert(ctx, idx_func + 1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx_func = duk__call_get_idx_func(thr, nargs, 1);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+
+	duk_insert_undefined(thr, idx_func + 1);
 
 	call_flags = 0;  /* not protected, respect reclimit, not constructor */
-
-	duk_handle_call_unprotected(thr,           /* thread */
-	                            nargs,         /* num_stack_args */
-	                            call_flags);   /* call_flags */
-}
-
-DUK_EXTERNAL void duk_call_method(duk_context *ctx, duk_idx_t nargs) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	duk_handle_call_unprotected(thr, idx_func, call_flags);
+}
+
+DUK_EXTERNAL void duk_call_method(duk_hthread *thr, duk_idx_t nargs) {
 	duk_small_uint_t call_flags;
 	duk_idx_t idx_func;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	idx_func = duk_get_top(ctx) - nargs - 2;  /* must work for nargs <= 0 */
-	if (idx_func < 0 || nargs < 0) {
-		/* note that we can't reliably pop anything here */
-		DUK_ERROR_TYPE_INVALID_ARGS(thr);
-	}
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx_func = duk__call_get_idx_func(thr, nargs, 2);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
 
 	call_flags = 0;  /* not protected, respect reclimit, not constructor */
-
-	duk_handle_call_unprotected(thr,           /* thread */
-	                            nargs,         /* num_stack_args */
-	                            call_flags);   /* call_flags */
-}
-
-DUK_EXTERNAL void duk_call_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs) {
+	duk_handle_call_unprotected(thr, idx_func, call_flags);
+}
+
+DUK_EXTERNAL void duk_call_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) {
 	/*
 	 *  XXX: if duk_handle_call() took values through indices, this could be
 	 *  made much more sensible.  However, duk_handle_call() needs to fudge
-	 *  the 'this' and 'func' values to handle bound function chains, which
-	 *  is now done "in-place", so this is not a trivial change.
-	 */
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);  /* make absolute */
-
-	duk__call_prop_prep_stack(ctx, obj_idx, nargs);
-
-	duk_call_method(ctx, nargs);
-}
-
-DUK_EXTERNAL duk_int_t duk_pcall(duk_context *ctx, duk_idx_t nargs) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_small_uint_t call_flags;
+	 *  the 'this' and 'func' values to handle bound functions, which is now
+	 *  done "in-place", so this is not a trivial change.
+	 */
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);  /* make absolute */
+	if (DUK_UNLIKELY(nargs < 0)) {
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
+	}
+
+	duk__call_prop_prep_stack(thr, obj_idx, nargs);
+
+	duk_call_method(thr, nargs);
+}
+
+DUK_LOCAL duk_ret_t duk__pcall_raw(duk_hthread *thr, void *udata) {
+	duk__pcall_args *args;
 	duk_idx_t idx_func;
-	duk_int_t rc;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	idx_func = duk_get_top(ctx) - nargs - 1;  /* must work for nargs <= 0 */
-	if (idx_func < 0 || nargs < 0) {
-		/* We can't reliably pop anything here because the stack input
-		 * shape is incorrect.  So we throw an error; if the caller has
-		 * no catch point for this, a fatal error will occur.  Another
-		 * alternative would be to just return an error.  But then the
-		 * stack would be in an unknown state which might cause some
-		 * very hard to diagnose problems later on.  Also note that even
-		 * if we did not throw an error here, the underlying call handler
-		 * might STILL throw an out-of-memory error or some other internal
-		 * fatal error.
-		 */
+	duk_int_t ret;
+
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_ASSERT(udata != NULL);
+
+	args = (duk__pcall_args *) udata;
+	idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 1);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+
+	duk_insert_undefined(thr, idx_func + 1);
+
+	ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags);
+	DUK_ASSERT(ret == 0);
+	DUK_UNREF(ret);
+
+	return 1;
+}
+
+DUK_EXTERNAL duk_int_t duk_pcall(duk_hthread *thr, duk_idx_t nargs) {
+	duk__pcall_args args;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	args.nargs = nargs;
+	if (DUK_UNLIKELY(nargs < 0)) {
 		DUK_ERROR_TYPE_INVALID_ARGS(thr);
 		return DUK_EXEC_ERROR;  /* unreachable */
 	}
-
-	/* awkward; we assume there is space for this */
-	duk_push_undefined(ctx);
-	duk_insert(ctx, idx_func + 1);
-
-	call_flags = 0;  /* respect reclimit, not constructor */
-
-	rc = duk_handle_call_protected(thr,           /* thread */
-	                               nargs,         /* num_stack_args */
-	                               call_flags);   /* call_flags */
-
-	return rc;
-}
-
-DUK_EXTERNAL duk_int_t duk_pcall_method(duk_context *ctx, duk_idx_t nargs) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_small_uint_t call_flags;
+	args.call_flags = 0;
+
+	return duk_safe_call(thr, duk__pcall_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
+}
+
+DUK_LOCAL duk_ret_t duk__pcall_method_raw(duk_hthread *thr, void *udata) {
+	duk__pcall_method_args *args;
 	duk_idx_t idx_func;
-	duk_int_t rc;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	idx_func = duk_get_top(ctx) - nargs - 2;  /* must work for nargs <= 0 */
-	if (idx_func < 0 || nargs < 0) {
-		/* See comments in duk_pcall(). */
+	duk_int_t ret;
+
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_ASSERT(udata != NULL);
+
+	args = (duk__pcall_method_args *) udata;
+
+	idx_func = duk__call_get_idx_func_unvalidated(thr, args->nargs, 2);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+
+	ret = duk_handle_call_unprotected(thr, idx_func, args->call_flags);
+	DUK_ASSERT(ret == 0);
+	DUK_UNREF(ret);
+
+	return 1;
+}
+
+DUK_INTERNAL duk_int_t duk_pcall_method_flags(duk_hthread *thr, duk_idx_t nargs, duk_small_uint_t call_flags) {
+	duk__pcall_method_args args;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	args.nargs = nargs;
+	if (DUK_UNLIKELY(nargs < 0)) {
 		DUK_ERROR_TYPE_INVALID_ARGS(thr);
 		return DUK_EXEC_ERROR;  /* unreachable */
 	}
-
-	call_flags = 0;  /* respect reclimit, not constructor */
-
-	rc = duk_handle_call_protected(thr,           /* thread */
-	                               nargs,         /* num_stack_args */
-	                               call_flags);   /* call_flags */
-
-	return rc;
-}
-
-struct duk__pcall_prop_args {
+	args.call_flags = call_flags;
+
+	return duk_safe_call(thr, duk__pcall_method_raw, (void *) &args /*udata*/, nargs + 2 /*nargs*/, 1 /*nrets*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_pcall_method(duk_hthread *thr, duk_idx_t nargs) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_pcall_method_flags(thr, nargs, 0);
+}
+
+DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_hthread *thr, void *udata) {
+	duk__pcall_prop_args *args;
 	duk_idx_t obj_idx;
-	duk_idx_t nargs;
-};
-typedef struct duk__pcall_prop_args duk__pcall_prop_args;
-
-DUK_LOCAL duk_ret_t duk__pcall_prop_raw(duk_context *ctx, void *udata) {
-	duk_idx_t obj_idx;
-	duk_idx_t nargs;
-	duk__pcall_prop_args *args;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	duk_int_t ret;
+
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(udata != NULL);
 
 	args = (duk__pcall_prop_args *) udata;
-	obj_idx = args->obj_idx;
-	nargs = args->nargs;
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);  /* make absolute */
-	duk__call_prop_prep_stack(ctx, obj_idx, nargs);
-	duk_call_method(ctx, nargs);
-	return 1;
-}
-
-DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs) {
+
+	obj_idx = duk_require_normalize_index(thr, args->obj_idx);  /* make absolute */
+	duk__call_prop_prep_stack(thr, obj_idx, args->nargs);
+
+	ret = duk_handle_call_unprotected_nargs(thr, args->nargs, args->call_flags);
+	DUK_ASSERT(ret == 0);
+	DUK_UNREF(ret);
+	return 1;
+}
+
+DUK_EXTERNAL duk_int_t duk_pcall_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t nargs) {
 	duk__pcall_prop_args args;
 
-	/*
-	 *  Must be careful to catch errors related to value stack manipulation
-	 *  and property lookup, not just the call itself.
-	 */
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	args.obj_idx = obj_idx;
 	args.nargs = nargs;
-
-	/* Inputs: explicit arguments (nargs), +1 for key.  If the value stack
-	 * does not contain enough args, an error is thrown; this matches
-	 * behavior of the other protected call API functions.
-	 */
-	return duk_safe_call(ctx, duk__pcall_prop_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
-}
-
-DUK_EXTERNAL duk_int_t duk_safe_call(duk_context *ctx, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	if (DUK_UNLIKELY(nargs < 0)) {
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
+		return DUK_EXEC_ERROR;  /* unreachable */
+	}
+	args.call_flags = 0;
+
+	return duk_safe_call(thr, duk__pcall_prop_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_safe_call(duk_hthread *thr, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) {
 	duk_int_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	if (duk_get_top(ctx) < nargs || nrets < 0) {
-		/* See comments in duk_pcall(). */
+	DUK_ASSERT_API_ENTRY(thr);
+
+	/* nargs condition; fail if: top - bottom < nargs
+	 *                      <=>  top < bottom + nargs
+	 * nrets condition; fail if: end - (top - nargs) < nrets
+	 *                      <=>  end - top + nargs < nrets
+	 *                      <=>  end + nargs < top + nrets
+	 */
+	/* XXX: check for any reserve? */
+
+	if (DUK_UNLIKELY((nargs | nrets) < 0 ||  /* nargs < 0 || nrets < 0; OR sign bits */
+	                 thr->valstack_top < thr->valstack_bottom + nargs ||        /* nargs too large compared to top */
+	                 thr->valstack_end + nargs < thr->valstack_top + nrets)) {  /* nrets too large compared to reserve */
+		DUK_D(DUK_DPRINT("not enough stack reserve for safe call or invalid arguments: "
+		                 "nargs=%ld < 0 (?), nrets=%ld < 0 (?), top=%ld < bottom=%ld + nargs=%ld (?), "
+		                 "end=%ld + nargs=%ld < top=%ld + nrets=%ld (?)",
+		                  (long) nargs,
+		                  (long) nrets,
+		                  (long) (thr->valstack_top - thr->valstack),
+		                  (long) (thr->valstack_bottom - thr->valstack),
+		                  (long) nargs,
+		                  (long) (thr->valstack_end - thr->valstack),
+		                  (long) nargs,
+		                  (long) (thr->valstack_top - thr->valstack),
+		                  (long) nrets));
 		DUK_ERROR_TYPE_INVALID_ARGS(thr);
 		return DUK_EXEC_ERROR;  /* unreachable */
 	}
@@ -13985,248 +14460,53 @@
 	return rc;
 }
 
-DUK_EXTERNAL void duk_new(duk_context *ctx, duk_idx_t nargs) {
-	/*
-	 *  There are two [[Construct]] operations in the specification:
-	 *
-	 *    - E5 Section 13.2.2: for Function objects
-	 *    - E5 Section 15.3.4.5.2: for "bound" Function objects
-	 *
-	 *  The chain of bound functions is resolved in Section 15.3.4.5.2,
-	 *  with arguments "piling up" until the [[Construct]] internal
-	 *  method is called on the final, actual Function object.  Note
-	 *  that the "prototype" property is looked up *only* from the
-	 *  final object, *before* calling the constructor.
-	 *
-	 *  Currently we follow the bound function chain here to get the
-	 *  "prototype" property value from the final, non-bound function.
-	 *  However, we let duk_handle_call() handle the argument "piling"
-	 *  when the constructor is called.  The bound function chain is
-	 *  thus now processed twice.
-	 *
-	 *  When constructing new Array instances, an unnecessary object is
-	 *  created and discarded now: the standard [[Construct]] creates an
-	 *  object, and calls the Array constructor.  The Array constructor
-	 *  returns an Array instance, which is used as the result value for
-	 *  the "new" operation; the object created before the Array constructor
-	 *  call is discarded.
-	 *
-	 *  This would be easy to fix, e.g. by knowing that the Array constructor
-	 *  will always create a replacement object and skip creating the fallback
-	 *  object in that case.
-	 *
-	 *  Note: functions called via "new" need to know they are called as a
-	 *  constructor.  For instance, built-in constructors behave differently
-	 *  depending on how they are called.
-	 */
-
-	/* XXX: merge this with duk_js_call.c, as this function implements
-	 * core semantics (or perhaps merge the two files altogether).
-	 */
-
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *proto;
-	duk_hobject *cons;
-	duk_hobject *fallback;
-	duk_idx_t idx_cons;
-	duk_small_uint_t call_flags;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	/* [... constructor arg1 ... argN] */
-
-	idx_cons = duk_require_normalize_index(ctx, -nargs - 1);
-
-	DUK_DDD(DUK_DDDPRINT("top=%ld, nargs=%ld, idx_cons=%ld",
-	                     (long) duk_get_top(ctx), (long) nargs, (long) idx_cons));
-
-	/* XXX: code duplication */
-
-	/*
-	 *  Figure out the final, non-bound constructor, to get "prototype"
-	 *  property.
-	 */
-
-	duk_dup(ctx, idx_cons);
-	for (;;) {
-		duk_tval *tv;
-		tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
-		DUK_ASSERT(tv != NULL);
-
-		if (DUK_TVAL_IS_OBJECT(tv)) {
-			cons = DUK_TVAL_GET_OBJECT(tv);
-			DUK_ASSERT(cons != NULL);
-			if (!DUK_HOBJECT_IS_CALLABLE(cons) || !DUK_HOBJECT_HAS_CONSTRUCTABLE(cons)) {
-				/* Checking callability of the immediate target
-				 * is important, same for constructability.
-				 * Checking it for functions down the bound
-				 * function chain is not strictly necessary
-				 * because .bind() should normally reject them.
-				 * But it's good to check anyway because it's
-				 * technically possible to edit the bound function
-				 * chain via internal keys.
-				 */
-				goto not_constructable;
-			}
-			if (!DUK_HOBJECT_HAS_BOUNDFUNC(cons)) {
-				break;
-			}
-		} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
-			/* Lightfuncs cannot be bound. */
-			break;
-		} else {
-			/* Anything else is not constructable. */
-			goto not_constructable;
-		}
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TARGET);  /* -> [... cons target] */
-		duk_remove_m2(ctx);                                         /* -> [... target] */
-	}
-	DUK_ASSERT(duk_is_callable(ctx, -1));
-	DUK_ASSERT(duk_is_lightfunc(ctx, -1) ||
-	           (duk_get_hobject(ctx, -1) != NULL && !DUK_HOBJECT_HAS_BOUNDFUNC(duk_get_hobject(ctx, -1))));
-
-	/* [... constructor arg1 ... argN final_cons] */
-
-	/*
-	 *  Create "fallback" object to be used as the object instance,
-	 *  unless the constructor returns a replacement value.
-	 *  Its internal prototype needs to be set based on "prototype"
-	 *  property of the constructor.
-	 */
-
-	duk_push_object(ctx);  /* class Object, extensible */
-
-	/* [... constructor arg1 ... argN final_cons fallback] */
-
-	duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_PROTOTYPE);
-	proto = duk_get_hobject(ctx, -1);
-	if (!proto) {
-		DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object "
-		                     "-> leave standard Object prototype as fallback prototype"));
-	} else {
-		DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value "
-		                     "-> set fallback prototype to that value: %!iO", (duk_heaphdr *) proto));
-		fallback = duk_get_hobject(ctx, -2);
-		DUK_ASSERT(fallback != NULL);
-		DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto);
-	}
-	duk_pop(ctx);
-
-	/* [... constructor arg1 ... argN final_cons fallback] */
-
-	/*
-	 *  Manipulate callstack for the call.
-	 */
-
-	duk_dup_top(ctx);
-	duk_insert(ctx, idx_cons + 1);  /* use fallback as 'this' value */
-	duk_insert(ctx, idx_cons);      /* also stash it before constructor,
-	                                 * in case we need it (as the fallback value)
-	                                 */
-	duk_pop(ctx);                   /* pop final_cons */
-
-
-	/* [... fallback constructor fallback(this) arg1 ... argN];
-	 * Note: idx_cons points to first 'fallback', not 'constructor'.
-	 */
-
-	DUK_DDD(DUK_DDDPRINT("before call, idx_cons+1 (constructor) -> %!T, idx_cons+2 (fallback/this) -> %!T, "
-	                     "nargs=%ld, top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, idx_cons + 1),
-	                     (duk_tval *) duk_get_tval(ctx, idx_cons + 2),
-	                     (long) nargs,
-	                     (long) duk_get_top(ctx)));
-
-	/*
-	 *  Call the constructor function (called in "constructor mode").
-	 */
-
-	call_flags = DUK_CALL_FLAG_CONSTRUCTOR_CALL;  /* not protected, respect reclimit, is a constructor call */
-
-	duk_handle_call_unprotected(thr,           /* thread */
-	                            nargs,         /* num_stack_args */
-	                            call_flags);   /* call_flags */
-
-	/* [... fallback retval] */
-
-	DUK_DDD(DUK_DDDPRINT("constructor call finished, fallback=%!iT, retval=%!iT",
-	                     (duk_tval *) duk_get_tval(ctx, -2),
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-	/*
-	 *  Determine whether to use the constructor return value as the created
-	 *  object instance or not.
-	 */
-
-	if (duk_check_type_mask(ctx, -1, DUK_TYPE_MASK_OBJECT |
-	                                 DUK_TYPE_MASK_BUFFER |
-	                                 DUK_TYPE_MASK_LIGHTFUNC)) {
-		duk_remove_m2(ctx);
-	} else {
-		duk_pop(ctx);
-	}
-
-	/*
-	 *  Augment created errors upon creation (not when they are thrown or
-	 *  rethrown).  __FILE__ and __LINE__ are not desirable here; the call
-	 *  stack reflects the caller which is correct.
-	 */
-
-#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-	duk_hthread_sync_currpc(thr);
-	duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/);
-#endif
-
-	/* [... retval] */
-
-	return;
-
- not_constructable:
-#if defined(DUK_USE_VERBOSE_ERRORS)
-#if defined(DUK_USE_PARANOID_ERRORS)
-	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_get_type_name(ctx, -1));
-#else
-	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_readable(ctx, -1));
-#endif
-#else
-	DUK_ERROR_TYPE(thr, "not constructable");
-#endif
-}
-
-DUK_LOCAL duk_ret_t duk__pnew_helper(duk_context *ctx, void *udata) {
+DUK_EXTERNAL void duk_new(duk_hthread *thr, duk_idx_t nargs) {
+	duk_idx_t idx_func;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx_func = duk__call_get_idx_func(thr, nargs, 1);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+
+	duk_push_object(thr);  /* default instance; internal proto updated by call handling */
+	duk_insert(thr, idx_func + 1);
+
+	duk_handle_call_unprotected(thr, idx_func, DUK_CALL_FLAG_CONSTRUCT);
+}
+
+DUK_LOCAL duk_ret_t duk__pnew_helper(duk_hthread *thr, void *udata) {
 	duk_idx_t nargs;
 
 	DUK_ASSERT(udata != NULL);
 	nargs = *((duk_idx_t *) udata);
 
-	duk_new(ctx, nargs);
-	return 1;
-}
-
-DUK_EXTERNAL duk_int_t duk_pnew(duk_context *ctx, duk_idx_t nargs) {
+	duk_new(thr, nargs);
+	return 1;
+}
+
+DUK_EXTERNAL duk_int_t duk_pnew(duk_hthread *thr, duk_idx_t nargs) {
 	duk_int_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* For now, just use duk_safe_call() to wrap duk_new().  We can't
-	 * simply use a protected duk_handle_call() because there's post
-	 * processing which might throw.  It should be possible to ensure
-	 * the post processing never throws (except in internal errors and
-	 * out of memory etc which are always allowed) and then remove this
-	 * wrapper.
-	 */
-
-	rc = duk_safe_call(ctx, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
+	 * simply use a protected duk_handle_call() because pushing the
+	 * default instance might throw.
+	 */
+
+	if (DUK_UNLIKELY(nargs < 0)) {
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
+		return DUK_EXEC_ERROR;  /* unreachable */
+	}
+
+	rc = duk_safe_call(thr, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/);
 	return rc;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_hthread *thr) {
 	duk_activation *act;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	act = thr->callstack_curr;
 	if (act != NULL) {
@@ -14238,14 +14518,15 @@
 /* XXX: Make this obsolete by adding a function flag for rejecting a
  * non-constructor call automatically?
  */
-DUK_INTERNAL void duk_require_constructor_call(duk_context *ctx) {
-	if (!duk_is_constructor_call(ctx)) {
-		DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CONSTRUCT_ONLY);
-	}
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_require_constructor_call(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (!duk_is_constructor_call(thr)) {
+		DUK_ERROR_TYPE(thr, DUK_STR_CONSTRUCT_ONLY);
+	}
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_hthread *thr) {
 	duk_activation *act;
 
 	/* For user code this could just return 1 (strict) always
@@ -14257,9 +14538,7 @@
 	 * the internal call sites.
 	 */
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	act = thr->callstack_curr;
 	if (act != NULL) {
@@ -14274,14 +14553,11 @@
  *  Duktape/C function magic
  */
 
-DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_hthread *thr) {
 	duk_activation *act;
 	duk_hobject *func;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	act = thr->callstack_curr;
 	if (act) {
@@ -14302,14 +14578,13 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_int_t duk_get_magic(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	if (DUK_TVAL_IS_OBJECT(tv)) {
 		h = DUK_TVAL_GET_OBJECT(tv);
 		DUK_ASSERT(h != NULL);
@@ -14328,12 +14603,12 @@
 	return 0;
 }
 
-DUK_EXTERNAL void duk_set_magic(duk_context *ctx, duk_idx_t idx, duk_int_t magic) {
+DUK_EXTERNAL void duk_set_magic(duk_hthread *thr, duk_idx_t idx, duk_int_t magic) {
 	duk_hnatfunc *nf;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	nf = duk_require_hnatfunc(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	nf = duk_require_hnatfunc(thr, idx);
 	DUK_ASSERT(nf != NULL);
 	nf->magic = (duk_int16_t) magic;
 }
@@ -14342,30 +14617,40 @@
  *  Misc helpers
  */
 
-DUK_INTERNAL void duk_resolve_nonbound_function(duk_context *ctx) {
-	duk_uint_t sanity;
-	duk_tval *tv;
-
-	sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
-	do {
-		tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
-		if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
-			/* Lightweight function: never bound, so terminate. */
-			break;
-		} else if (DUK_TVAL_IS_OBJECT(tv)) {
-			duk_hobject *func;
-
-			func = DUK_TVAL_GET_OBJECT(tv);
-			DUK_ASSERT(func != NULL);
-			if (!DUK_HOBJECT_IS_CALLABLE(func) || !DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
-				break;
-			}
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TARGET);
-			duk_replace(ctx, -2);
-		} else {
-			break;
-		}
-	} while (--sanity > 0);
+/* Resolve a bound function on value stack top to a non-bound target
+ * (leave other values as is).
+ */
+DUK_INTERNAL void duk_resolve_nonbound_function(duk_hthread *thr) {
+	duk_tval *tv;
+
+	DUK_ASSERT_HTHREAD_VALID(thr);
+
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		duk_hobject *h;
+
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) {
+			duk_push_tval(thr, &((duk_hboundfunc *) h)->target);
+			duk_replace(thr, -2);
+#if 0
+			DUK_TVAL_SET_TVAL(tv, &((duk_hboundfunc *) h)->target);
+			DUK_TVAL_INCREF(thr, tv);
+			DUK_HOBJECT_DECREF_NORZ(thr, h);
+#endif
+			/* Rely on Function.prototype.bind() on never creating a bound
+			 * function whose target is not proper.  This is now safe
+			 * because the target is not even an internal property but a
+			 * struct member.
+			 */
+			DUK_ASSERT(duk_is_lightfunc(thr, -1) || duk_is_callable(thr, -1));
+		}
+	}
+
+	/* Lightfuncs cannot be bound but are always callable and
+	 * constructable.
+	 */
 }
 #line 1 "duk_api_codec.c"
 /*
@@ -14382,21 +14667,21 @@
  * buffer and string values because they're the most common.  In particular,
  * avoid creating a temporary string or buffer when possible.
  */
-DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
+DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
 	void *ptr;
 	duk_bool_t isbuffer;
 
-	DUK_ASSERT(duk_is_valid_index(ctx, idx));  /* checked by caller */
+	DUK_ASSERT(duk_is_valid_index(thr, idx));  /* checked by caller */
 
 	/* XXX: with def_ptr set to a stack related pointer, isbuffer could
 	 * be removed from the helper?
 	 */
-	ptr = duk_get_buffer_data_raw(ctx, idx, out_len, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, &isbuffer);
+	ptr = duk_get_buffer_data_raw(thr, idx, out_len, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, &isbuffer);
 	if (isbuffer) {
 		DUK_ASSERT(*out_len == 0 || ptr != NULL);
 		return (const duk_uint8_t *) ptr;
 	}
-	return (const duk_uint8_t *) duk_to_lstring(ctx, idx, out_len);
+	return (const duk_uint8_t *) duk_to_lstring(thr, idx, out_len);
 }
 
 #if defined(DUK_USE_BASE64_FASTPATH)
@@ -14745,22 +15030,21 @@
 }
 #endif  /* DUK_USE_BASE64_FASTPATH */
 
-DUK_EXTERNAL const char *duk_base64_encode(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL const char *duk_base64_encode(duk_hthread *thr, duk_idx_t idx) {
 	const duk_uint8_t *src;
 	duk_size_t srclen;
 	duk_size_t dstlen;
 	duk_uint8_t *dst;
 	const char *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* XXX: optimize for string inputs: no need to coerce to a buffer
 	 * which makes a copy of the input.
 	 */
 
-	idx = duk_require_normalize_index(ctx, idx);
-	src = duk__prep_codec_arg(ctx, idx, &srclen);
+	idx = duk_require_normalize_index(thr, idx);
+	src = duk__prep_codec_arg(thr, idx, &srclen);
 	/* Note: for srclen=0, src may be NULL */
 
 	/* Computation must not wrap; this limit works for 32-bit size_t:
@@ -14772,21 +15056,20 @@
 		goto type_error;
 	}
 	dstlen = (srclen + 2) / 3 * 4;
-	dst = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, dstlen);
+	dst = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, dstlen);
 
 	duk__base64_encode_helper((const duk_uint8_t *) src, srclen, dst);
 
-	ret = duk_buffer_to_string(ctx, -1);  /* Safe, result is ASCII. */
-	duk_replace(ctx, idx);
+	ret = duk_buffer_to_string(thr, -1);  /* Safe, result is ASCII. */
+	duk_replace(thr, idx);
 	return ret;
 
  type_error:
-	DUK_ERROR_TYPE(thr, DUK_STR_ENCODE_FAILED);
+	DUK_ERROR_TYPE(thr, DUK_STR_BASE64_ENCODE_FAILED);
 	return NULL;  /* never here */
 }
 
-DUK_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_base64_decode(duk_hthread *thr, duk_idx_t idx) {
 	const duk_uint8_t *src;
 	duk_size_t srclen;
 	duk_size_t dstlen;
@@ -14794,14 +15077,14 @@
 	duk_uint8_t *dst_final;
 	duk_bool_t retval;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* XXX: optimize for buffer inputs: no need to coerce to a string
 	 * which causes an unnecessary interning.
 	 */
 
-	idx = duk_require_normalize_index(ctx, idx);
-	src = duk__prep_codec_arg(ctx, idx, &srclen);
+	idx = duk_require_normalize_index(thr, idx);
+	src = duk__prep_codec_arg(thr, idx, &srclen);
 
 	/* Computation must not wrap, only srclen + 3 is at risk of
 	 * wrapping because after that the number gets smaller.
@@ -14812,7 +15095,7 @@
 		goto type_error;
 	}
 	dstlen = (srclen + 3) / 4 * 3;  /* upper limit, assuming no whitespace etc */
-	dst = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, dstlen);
+	dst = (duk_uint8_t *) duk_push_dynamic_buffer(thr, dstlen);
 	/* Note: for dstlen=0, dst may be NULL */
 
 	retval = duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final);
@@ -14821,15 +15104,15 @@
 	}
 
 	/* XXX: convert to fixed buffer? */
-	(void) duk_resize_buffer(ctx, -1, (duk_size_t) (dst_final - dst));
-	duk_replace(ctx, idx);
+	(void) duk_resize_buffer(thr, -1, (duk_size_t) (dst_final - dst));
+	duk_replace(thr, idx);
 	return;
 
  type_error:
-	DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED);
-}
-
-DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx) {
+	DUK_ERROR_TYPE(thr, DUK_STR_BASE64_DECODE_FAILED);
+}
+
+DUK_EXTERNAL const char *duk_hex_encode(duk_hthread *thr, duk_idx_t idx) {
 	const duk_uint8_t *inp;
 	duk_size_t len;
 	duk_size_t i;
@@ -14840,14 +15123,14 @@
 	duk_uint16_t *p16;
 #endif
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	inp = duk__prep_codec_arg(ctx, idx, &len);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	inp = duk__prep_codec_arg(thr, idx, &len);
 	DUK_ASSERT(inp != NULL || len == 0);
 
 	/* Fixed buffer, no zeroing because we'll fill all the data. */
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, len * 2);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len * 2);
 	DUK_ASSERT(buf != NULL);
 
 #if defined(DUK_USE_HEX_FASTPATH)
@@ -14880,13 +15163,12 @@
 	 * caller coerce to string if necessary?
 	 */
 
-	ret = duk_buffer_to_string(ctx, -1);  /* Safe, result is ASCII. */
-	duk_replace(ctx, idx);
+	ret = duk_buffer_to_string(thr, -1);  /* Safe, result is ASCII. */
+	duk_replace(thr, idx);
 	return ret;
 }
 
-DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_hex_decode(duk_hthread *thr, duk_idx_t idx) {
 	const duk_uint8_t *inp;
 	duk_size_t len;
 	duk_size_t i;
@@ -14898,10 +15180,10 @@
 	duk_size_t len_safe;
 #endif
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	inp = duk__prep_codec_arg(ctx, idx, &len);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	inp = duk__prep_codec_arg(thr, idx, &len);
 	DUK_ASSERT(inp != NULL || len == 0);
 
 	if (len & 0x01) {
@@ -14909,7 +15191,7 @@
 	}
 
 	/* Fixed buffer, no zeroing because we'll fill all the data. */
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, len / 2);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len / 2);
 	DUK_ASSERT(buf != NULL);
 
 #if defined(DUK_USE_HEX_FASTPATH)
@@ -14962,68 +15244,70 @@
 	}
 #endif  /* DUK_USE_HEX_FASTPATH */
 
-	duk_replace(ctx, idx);
+	duk_replace(thr, idx);
 	return;
 
  type_error:
-	DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED);
+	DUK_ERROR_TYPE(thr, DUK_STR_HEX_DECODE_FAILED);
 }
 
 #if defined(DUK_USE_JSON_SUPPORT)
-DUK_EXTERNAL const char *duk_json_encode(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) {
 #if defined(DUK_USE_ASSERTIONS)
 	duk_idx_t top_at_entry;
 #endif
 	const char *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-#if defined(DUK_USE_ASSERTIONS)
-	top_at_entry = duk_get_top(ctx);
-#endif
-
-	idx = duk_require_normalize_index(ctx, idx);
-	duk_bi_json_stringify_helper(ctx,
+	DUK_ASSERT_API_ENTRY(thr);
+#if defined(DUK_USE_ASSERTIONS)
+	top_at_entry = duk_get_top(thr);
+#endif
+
+	idx = duk_require_normalize_index(thr, idx);
+	duk_bi_json_stringify_helper(thr,
 	                             idx /*idx_value*/,
 	                             DUK_INVALID_INDEX /*idx_replacer*/,
 	                             DUK_INVALID_INDEX /*idx_space*/,
 	                             0 /*flags*/);
-	DUK_ASSERT(duk_is_string(ctx, -1));
-	duk_replace(ctx, idx);
-	ret = duk_get_string(ctx, idx);
-
-	DUK_ASSERT(duk_get_top(ctx) == top_at_entry);
+	DUK_ASSERT(duk_is_string(thr, -1));
+	duk_replace(thr, idx);
+	ret = duk_get_string(thr, idx);
+
+	DUK_ASSERT(duk_get_top(thr) == top_at_entry);
 
 	return ret;
 }
 
-DUK_EXTERNAL void duk_json_decode(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) {
 #if defined(DUK_USE_ASSERTIONS)
 	duk_idx_t top_at_entry;
 #endif
 
-	DUK_ASSERT_CTX_VALID(ctx);
-#if defined(DUK_USE_ASSERTIONS)
-	top_at_entry = duk_get_top(ctx);
-#endif
-
-	idx = duk_require_normalize_index(ctx, idx);
-	duk_bi_json_parse_helper(ctx,
+	DUK_ASSERT_API_ENTRY(thr);
+#if defined(DUK_USE_ASSERTIONS)
+	top_at_entry = duk_get_top(thr);
+#endif
+
+	idx = duk_require_normalize_index(thr, idx);
+	duk_bi_json_parse_helper(thr,
 	                         idx /*idx_value*/,
 	                         DUK_INVALID_INDEX /*idx_reviver*/,
 	                         0 /*flags*/);
-	duk_replace(ctx, idx);
-
-	DUK_ASSERT(duk_get_top(ctx) == top_at_entry);
+	duk_replace(thr, idx);
+
+	DUK_ASSERT(duk_get_top(thr) == top_at_entry);
 }
 #else  /* DUK_USE_JSON_SUPPORT */
-DUK_EXTERNAL const char *duk_json_encode(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL const char *duk_json_encode(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(idx);
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
-}
-
-DUK_EXTERNAL void duk_json_decode(duk_context *ctx, duk_idx_t idx) {
+	DUK_ERROR_UNSUPPORTED(thr);
+}
+
+DUK_EXTERNAL void duk_json_decode(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(idx);
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+	DUK_ERROR_UNSUPPORTED(thr);
 }
 #endif  /* DUK_USE_JSON_SUPPORT */
 #line 1 "duk_api_compile.c"
@@ -15041,10 +15325,10 @@
 };
 
 /* Eval is just a wrapper now. */
-DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
+DUK_EXTERNAL duk_int_t duk_eval_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
 	duk_int_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Note: strictness is *not* inherited from the current Duktape/C.
 	 * This would be confusing because the current strictness state
@@ -15055,7 +15339,7 @@
 
 	/* [ ... source? filename? ] (depends on flags) */
 
-	rc = duk_compile_raw(ctx, src_buffer, src_length, flags | DUK_COMPILE_EVAL);  /* may be safe, or non-safe depending on flags */
+	rc = duk_compile_raw(thr, src_buffer, src_length, flags | DUK_COMPILE_EVAL);  /* may be safe, or non-safe depending on flags */
 
 	/* [ ... closure/error ] */
 
@@ -15064,12 +15348,12 @@
 		goto got_rc;
 	}
 
-	duk_push_global_object(ctx);  /* explicit 'this' binding, see GH-164 */
+	duk_push_global_object(thr);  /* explicit 'this' binding, see GH-164 */
 
 	if (flags & DUK_COMPILE_SAFE) {
-		rc = duk_pcall_method(ctx, 0);
-	} else {
-		duk_call_method(ctx, 0);
+		rc = duk_pcall_method(thr, 0);
+	} else {
+		duk_call_method(thr, 0);
 		rc = DUK_EXEC_SUCCESS;
 	}
 
@@ -15077,20 +15361,19 @@
 
  got_rc:
 	if (flags & DUK_COMPILE_NORESULT) {
-		duk_pop(ctx);
+		duk_pop(thr);
 	}
 
 	return rc;
 }
 
 /* Helper which can be called both directly and with duk_safe_call(). */
-DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_ret_t duk__do_compile(duk_hthread *thr, void *udata) {
 	duk__compile_raw_args *comp_args;
 	duk_uint_t flags;
 	duk_hcompfunc *h_templ;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(udata != NULL);
 
 	/* Note: strictness is not inherited from the current Duktape/C
@@ -15107,7 +15390,7 @@
 
 	if (flags & DUK_COMPILE_NOFILENAME) {
 		/* Automatic filename: 'eval' or 'input'. */
-		duk_push_hstring_stridx(ctx, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT);
+		duk_push_hstring_stridx(thr, (flags & DUK_COMPILE_EVAL) ? DUK_STRIDX_EVAL : DUK_STRIDX_INPUT);
 	}
 
 	/* [ ... source? filename ] */
@@ -15115,7 +15398,7 @@
 	if (!comp_args->src_buffer) {
 		duk_hstring *h_sourcecode;
 
-		h_sourcecode = duk_get_hstring(ctx, -2);
+		h_sourcecode = duk_get_hstring(thr, -2);
 		if ((flags & DUK_COMPILE_NOSOURCE) ||  /* args incorrect */
 		    (h_sourcecode == NULL)) {          /* e.g. duk_push_string_file_raw() pushed undefined */
 			DUK_ERROR_TYPE(thr, DUK_STR_NO_SOURCECODE);
@@ -15139,29 +15422,29 @@
 	if (flags & DUK_COMPILE_NOSOURCE) {
 		;
 	} else {
-		duk_remove_m2(ctx);
+		duk_remove_m2(thr);
 	}
 
 	/* [ ... func_template ] */
 
-	h_templ = (duk_hcompfunc *) duk_known_hobject(ctx, -1);
+	h_templ = (duk_hcompfunc *) duk_known_hobject(thr, -1);
 	duk_js_push_closure(thr,
 	                   h_templ,
 	                   thr->builtins[DUK_BIDX_GLOBAL_ENV],
 	                   thr->builtins[DUK_BIDX_GLOBAL_ENV],
 	                   1 /*add_auto_proto*/);
-	duk_remove_m2(ctx);   /* -> [ ... closure ] */
+	duk_remove_m2(thr);   /* -> [ ... closure ] */
 
 	/* [ ... closure ] */
 
 	return 1;
 }
 
-DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
+DUK_EXTERNAL duk_int_t duk_compile_raw(duk_hthread *thr, const char *src_buffer, duk_size_t src_length, duk_uint_t flags) {
 	duk__compile_raw_args comp_args_alloc;
 	duk__compile_raw_args *comp_args = &comp_args_alloc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	if ((flags & DUK_COMPILE_STRLEN) && (src_buffer != NULL)) {
 		/* String length is computed here to avoid multiple evaluation
@@ -15188,13 +15471,13 @@
 		nargs = flags & 0x07;
 		DUK_ASSERT(nargs == ((flags & DUK_COMPILE_NOSOURCE) ? 0 : 1) +
 		                    ((flags & DUK_COMPILE_NOFILENAME) ? 0 : 1));
-		rc = duk_safe_call(ctx, duk__do_compile, (void *) comp_args, nargs, nrets);
+		rc = duk_safe_call(thr, duk__do_compile, (void *) comp_args, nargs, nrets);
 
 		/* [ ... closure ] */
 		return rc;
 	}
 
-	(void) duk__do_compile(ctx, (void *) comp_args);
+	(void) duk__do_compile(thr, (void *) comp_args);
 
 	/* [ ... closure ] */
 	return DUK_EXEC_SUCCESS;
@@ -15207,48 +15490,49 @@
 /* #include duk_internal.h -> already included */
 
 #if defined(DUK_USE_JSON_SUPPORT)
-DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) {
+DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
 	duk_idx_t idx;
 	duk_idx_t top;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* We don't duk_require_stack() here now, but rely on the caller having
 	 * enough space.
 	 */
 
-	top = duk_get_top(ctx);
-	duk_push_array(ctx);
+	top = duk_get_top(thr);
+	duk_push_array(thr);
 	for (idx = 0; idx < top; idx++) {
-		duk_dup(ctx, idx);
-		duk_put_prop_index(ctx, -2, idx);
+		duk_dup(thr, idx);
+		duk_put_prop_index(thr, -2, (duk_uarridx_t) idx);
 	}
 
 	/* XXX: conversion errors should not propagate outwards.
 	 * Perhaps values need to be coerced individually?
 	 */
-	duk_bi_json_stringify_helper(ctx,
-	                             duk_get_top_index(ctx),  /*idx_value*/
+	duk_bi_json_stringify_helper(thr,
+	                             duk_get_top_index(thr),  /*idx_value*/
 	                             DUK_INVALID_INDEX,  /*idx_replacer*/
 	                             DUK_INVALID_INDEX,  /*idx_space*/
 	                             DUK_JSON_FLAG_EXT_CUSTOM |
 	                             DUK_JSON_FLAG_ASCII_ONLY |
 	                             DUK_JSON_FLAG_AVOID_KEY_QUOTES /*flags*/);
 
-	duk_push_sprintf(ctx, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(ctx, -1));
-	duk_replace(ctx, -3);  /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
-	duk_pop(ctx);
-	DUK_ASSERT(duk_is_string(ctx, -1));
+	duk_push_sprintf(thr, "ctx: top=%ld, stack=%s", (long) top, (const char *) duk_safe_to_string(thr, -1));
+	duk_replace(thr, -3);  /* [ ... arr jsonx(arr) res ] -> [ ... res jsonx(arr) ] */
+	duk_pop(thr);
+	DUK_ASSERT(duk_is_string(thr, -1));
 }
 #else  /* DUK_USE_JSON_SUPPORT */
-DUK_EXTERNAL void duk_push_context_dump(duk_context *ctx) {
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+DUK_EXTERNAL void duk_push_context_dump(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ERROR_UNSUPPORTED(thr);
 }
 #endif  /* DUK_USE_JSON_SUPPORT */
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 
-DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx,
+DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                       duk_debug_read_function read_cb,
                                       duk_debug_write_function write_cb,
                                       duk_debug_peek_function peek_cb,
@@ -15257,7 +15541,6 @@
                                       duk_debug_request_function request_cb,
                                       duk_debug_detached_function detached_cb,
                                       void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_heap *heap;
 	const char *str;
 	duk_size_t len;
@@ -15268,7 +15551,7 @@
 
 	DUK_D(DUK_DPRINT("application called duk_debugger_attach()"));
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(read_cb != NULL);
 	DUK_ASSERT(write_cb != NULL);
 	/* Other callbacks are optional. */
@@ -15288,10 +15571,9 @@
 	heap->dbg_processing = 0;
 	heap->dbg_state_dirty = 0;
 	heap->dbg_force_restart = 0;
-	heap->dbg_step_type = DUK_STEP_TYPE_NONE;
-	heap->dbg_step_thread = NULL;
-	heap->dbg_step_csindex = 0;
-	heap->dbg_step_startline = 0;
+	heap->dbg_pause_flags = 0;
+	heap->dbg_pause_act = NULL;
+	heap->dbg_pause_startline = 0;
 	heap->dbg_exec_counter = 0;
 	heap->dbg_last_counter = 0;
 	heap->dbg_last_time = 0.0;
@@ -15300,45 +15582,38 @@
 	/* Send version identification and flush right afterwards.  Note that
 	 * we must write raw, unframed bytes here.
 	 */
-	duk_push_sprintf(ctx, "%ld %ld %s %s\n",
+	duk_push_sprintf(thr, "%ld %ld %s %s\n",
 	                 (long) DUK_DEBUG_PROTOCOL_VERSION,
 	                 (long) DUK_VERSION,
 	                 (const char *) DUK_GIT_DESCRIBE,
 	                 (const char *) DUK_USE_TARGET_INFO);
-	str = duk_get_lstring(ctx, -1, &len);
+	str = duk_get_lstring(thr, -1, &len);
 	DUK_ASSERT(str != NULL);
 	duk_debug_write_bytes(thr, (const duk_uint8_t *) str, len);
 	duk_debug_write_flush(thr);
-	duk_pop(ctx);
-}
-
-DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
-	duk_hthread *thr;
-
+	duk_pop(thr);
+}
+
+DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
 	DUK_D(DUK_DPRINT("application called duk_debugger_detach()"));
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 
 	/* Can be called multiple times with no harm. */
 	duk_debug_do_detach(thr->heap);
 }
 
-DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
 	duk_bool_t processed_messages;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 
 	if (!duk_debug_is_attached(thr->heap)) {
 		return;
 	}
-	if (thr->callstack_top > 0 || thr->heap->dbg_processing) {
+	if (thr->callstack_curr != NULL || thr->heap->dbg_processing) {
 		/* Calling duk_debugger_cooperate() while Duktape is being
 		 * called into is not supported.  This is not a 100% check
 		 * but prevents any damage in most cases.
@@ -15350,20 +15625,17 @@
 	DUK_UNREF(processed_messages);
 }
 
-DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
-	duk_hthread *thr;
+DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
 	duk_idx_t top;
 	duk_idx_t idx;
 	duk_bool_t ret = 0;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 
 	DUK_D(DUK_DPRINT("application called duk_debugger_notify() with nvalues=%ld", (long) nvalues));
 
-	top = duk_get_top(ctx);
+	top = duk_get_top(thr);
 	if (top < nvalues) {
 		DUK_ERROR_RANGE(thr, "not enough stack values for notify");
 		return ret;  /* unreachable */
@@ -15371,7 +15643,7 @@
 	if (duk_debug_is_attached(thr->heap)) {
 		duk_debug_write_notify(thr, DUK_DBG_CMD_APPNOTIFY);
 		for (idx = top - nvalues; idx < top; idx++) {
-			duk_tval *tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+			duk_tval *tv = DUK_GET_TVAL_POSIDX(thr, idx);
 			duk_debug_write_tval(thr, tv);
 		}
 		duk_debug_write_eom(thr);
@@ -15385,16 +15657,12 @@
 			ret = 1;
 		}
 	}
-	duk_pop_n(ctx, nvalues);
+	duk_pop_n(thr, nvalues);
 	return ret;
 }
 
-DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
-	duk_hthread *thr;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
+DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 
 	DUK_D(DUK_DPRINT("application called duk_debugger_pause()"));
@@ -15418,7 +15686,7 @@
 
 #else  /* DUK_USE_DEBUGGER_SUPPORT */
 
-DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx,
+DUK_EXTERNAL void duk_debugger_attach(duk_hthread *thr,
                                       duk_debug_read_function read_cb,
                                       duk_debug_write_function write_cb,
                                       duk_debug_peek_function peek_cb,
@@ -15427,7 +15695,7 @@
                                       duk_debug_request_function request_cb,
                                       duk_debug_detached_function detached_cb,
                                       void *udata) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(read_cb);
 	DUK_UNREF(write_cb);
 	DUK_UNREF(peek_cb);
@@ -15436,40 +15704,40 @@
 	DUK_UNREF(request_cb);
 	DUK_UNREF(detached_cb);
 	DUK_UNREF(udata);
-	DUK_ERROR_TYPE((duk_hthread *) ctx, "no debugger support");
-}
-
-DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ERROR_TYPE((duk_hthread *) ctx, "no debugger support");
-}
-
-DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) {
+	DUK_ERROR_TYPE(thr, "no debugger support");
+}
+
+DUK_EXTERNAL void duk_debugger_detach(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ERROR_TYPE(thr, "no debugger support");
+}
+
+DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) {
 	/* nop */
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(ctx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(thr);
+}
+
+DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) {
 	duk_idx_t top;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	top = duk_get_top(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	top = duk_get_top(thr);
 	if (top < nvalues) {
-		DUK_ERROR_RANGE_INVALID_COUNT((duk_hthread *) ctx);
+		DUK_ERROR_RANGE_INVALID_COUNT(thr);
 		return 0;  /* unreachable */
 	}
 
 	/* No debugger support, just pop values. */
-	duk_pop_n(ctx, nvalues);
-	return 0;
-}
-
-DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) {
+	duk_pop_n(thr, nvalues);
+	return 0;
+}
+
+DUK_EXTERNAL void duk_debugger_pause(duk_hthread *thr) {
 	/* Treat like debugger statement: nop */
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(thr);
 }
 
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
@@ -15489,14 +15757,13 @@
 	duk_int_t call_recursion_depth;
 };
 
-DUK_EXTERNAL
-duk_context *duk_create_heap(duk_alloc_function alloc_func,
-                             duk_realloc_function realloc_func,
-                             duk_free_function free_func,
-                             void *heap_udata,
-                             duk_fatal_function fatal_handler) {
+DUK_EXTERNAL duk_hthread *duk_create_heap(duk_alloc_function alloc_func,
+                                          duk_realloc_function realloc_func,
+                                          duk_free_function free_func,
+                                          void *heap_udata,
+                                          duk_fatal_function fatal_handler) {
 	duk_heap *heap = NULL;
-	duk_context *ctx;
+	duk_hthread *thr;
 
 	/* Assume that either all memory funcs are NULL or non-NULL, mixed
 	 * cases will now be unsafe.
@@ -15535,33 +15802,31 @@
 	if (!heap) {
 		return NULL;
 	}
-	ctx = (duk_context *) heap->heap_thread;
-	DUK_ASSERT(ctx != NULL);
-	DUK_ASSERT(((duk_hthread *) ctx)->heap != NULL);
-	return ctx;
-}
-
-DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	thr = heap->heap_thread;
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(thr->heap != NULL);
+	return thr;
+}
+
+DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) {
 	duk_heap *heap;
 
-	if (!ctx) {
-		return;
-	}
+	if (!thr) {
+		return;
+	}
+	DUK_ASSERT_API_ENTRY(thr);
 	heap = thr->heap;
 	DUK_ASSERT(heap != NULL);
 
 	duk_heap_free(heap);
 }
 
-DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_suspend(duk_hthread *thr, duk_thread_state *state) {
 	duk_internal_thread_state *snapshot = (duk_internal_thread_state *) (void *) state;
 	duk_heap *heap;
 	duk_ljstate *lj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 	DUK_ASSERT(state != NULL);  /* unvalidated */
 
@@ -15580,8 +15845,8 @@
 	heap = thr->heap;
 	lj = &heap->lj;
 
-	duk_push_tval(ctx, &lj->value1);
-	duk_push_tval(ctx, &lj->value2);
+	duk_push_tval(thr, &lj->value1);
+	duk_push_tval(thr, &lj->value2);
 
 	/* XXX: creating_error == 0 is asserted above, so no need to store. */
 	DUK_MEMCPY((void *) &snapshot->lj, (const void *) lj, sizeof(duk_ljstate));
@@ -15598,13 +15863,11 @@
 	heap->call_recursion_depth = 0;
 }
 
-DUK_EXTERNAL void duk_resume(duk_context *ctx, const duk_thread_state *state) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_resume(duk_hthread *thr, const duk_thread_state *state) {
 	const duk_internal_thread_state *snapshot = (const duk_internal_thread_state *) (const void *) state;
 	duk_heap *heap;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->heap != NULL);
 	DUK_ASSERT(state != NULL);  /* unvalidated */
 
@@ -15621,20 +15884,21 @@
 	heap->curr_thread = snapshot->curr_thread;
 	heap->call_recursion_depth = snapshot->call_recursion_depth;
 
-	duk_pop_2(ctx);
+	duk_pop_2(thr);
 }
 
 /* XXX: better place for this */
-DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_set_global_object(duk_hthread *thr) {
 	duk_hobject *h_glob;
 	duk_hobject *h_prev_glob;
 	duk_hobjenv *h_env;
 	duk_hobject *h_prev_env;
 
-	DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(ctx, -1)));
-
-	h_glob = duk_require_hobject(ctx, -1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1)));
+
+	h_glob = duk_require_hobject(thr, -1);
 	DUK_ASSERT(h_glob != NULL);
 
 	/*
@@ -15679,7 +15943,7 @@
 
 	/* [ ... new_glob ] */
 
-	duk_pop(ctx);
+	duk_pop(thr);
 
 	/* [ ... ] */
 }
@@ -15693,7 +15957,7 @@
 /* For footprint efficient multiple value setting: arrays are much better than
  * varargs, format string with parsing is often better than string pointer arrays.
  */
-DUK_LOCAL void duk__inspect_multiple_uint(duk_context *ctx, const char *fmt, duk_int_t *vals) {
+DUK_LOCAL void duk__inspect_multiple_uint(duk_hthread *thr, const char *fmt, duk_int_t *vals) {
 	duk_int_t val;
 	const char *p;
 	const char *p_curr;
@@ -15710,9 +15974,9 @@
 		val = *vals++;
 		if (val >= 0) {
 			/* Negative values are markers to skip key. */
-			duk_push_string(ctx, p_curr);
-			duk_push_uint(ctx, val);
-			duk_put_prop(ctx, -3);
+			duk_push_string(thr, p_curr);
+			duk_push_int(thr, val);
+			duk_put_prop(thr, -3);
 		}
 	}
 }
@@ -15740,8 +16004,7 @@
 #define DUK__IDX_TSTATE   12
 #define DUK__IDX_VARIANT  13
 
-DUK_EXTERNAL void duk_inspect_value(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_inspect_value(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_heaphdr *h;
 	/* The temporary values should be in an array rather than individual
@@ -15750,31 +16013,31 @@
 	 */
 	duk_int_t vals[14];
 
-	DUK_UNREF(thr);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Assume two's complement and set everything to -1. */
 	DUK_MEMSET((void *) &vals, (int) 0xff, sizeof(vals));
 	DUK_ASSERT(vals[DUK__IDX_TYPE] == -1);  /* spot check one */
 
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL);
 
 	vals[DUK__IDX_TYPE] = duk_get_type_tval(tv);
-	vals[DUK__IDX_ITAG] = (duk_uint_t) DUK_TVAL_GET_TAG(tv);
-
-	duk_push_bare_object(ctx);  /* Invalidates 'tv'. */
+	vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv);
+
+	duk_push_bare_object(thr);  /* Invalidates 'tv'. */
 	tv = NULL;
 
 	if (h == NULL) {
 		goto finish;
 	}
-	duk_push_pointer(ctx, (void *) h);
-	duk_put_prop_string(ctx, -2, "hptr");
+	duk_push_pointer(thr, (void *) h);
+	duk_put_prop_string(thr, -2, "hptr");
 
 #if 0
 	/* Covers a lot of information, e.g. buffer and string variants. */
-	duk_push_uint(ctx, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
-	duk_put_prop_string(ctx, -2, "hflags");
+	duk_push_uint(thr, (duk_uint_t) DUK_HEAPHDR_GET_FLAGS(h));
+	duk_put_prop_string(thr, -2, "hflags");
 #endif
 
 #if defined(DUK_USE_REFERENCE_COUNTING)
@@ -15853,62 +16116,61 @@
 				vals[DUK__IDX_VARIANT] = 1;  /* buffer variant 1: dynamic */
 				vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_dynamic));
 			}
-			vals[DUK__IDX_DBYTES] = (duk_uint_t) (DUK_HBUFFER_GET_SIZE(h_buf));
+			vals[DUK__IDX_DBYTES] = (duk_int_t) (DUK_HBUFFER_GET_SIZE(h_buf));
 		} else {
 			DUK_ASSERT(vals[DUK__IDX_VARIANT] == 0);  /* buffer variant 0: fixed */
-			vals[DUK__IDX_HBYTES] = (duk_uint_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf));
+			vals[DUK__IDX_HBYTES] = (duk_int_t) (sizeof(duk_hbuffer_fixed) + DUK_HBUFFER_GET_SIZE(h_buf));
 		}
 		break;
 	}
 	}
 
  finish:
-	duk__inspect_multiple_uint(ctx,
+	duk__inspect_multiple_uint(thr,
 	    "type" "\x00" "itag" "\x00" "refc" "\x00" "hbytes" "\x00" "class" "\x00"
 	    "pbytes" "\x00" "esize" "\x00" "enext" "\x00" "asize" "\x00" "hsize" "\x00"
 	    "bcbytes" "\x00" "dbytes" "\x00" "tstate" "\x00" "variant" "\x00" "\x00",
 	    (duk_int_t *) &vals);
 }
 
-DUK_EXTERNAL void duk_inspect_callstack_entry(duk_context *ctx, duk_int_t level) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_inspect_callstack_entry(duk_hthread *thr, duk_int_t level) {
 	duk_activation *act;
 	duk_uint_fast32_t pc;
 	duk_uint_fast32_t line;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	/* -1             = top callstack entry, callstack[callstack_top - 1]
-	 * -callstack_top = bottom callstack entry, callstack[0]
-	 */
-	if (level >= 0 || -level > (duk_int_t) thr->callstack_top) {
-		duk_push_undefined(ctx);
-		return;
-	}
-	duk_push_bare_object(ctx);
-	DUK_ASSERT(level >= -((duk_int_t) thr->callstack_top) && level <= -1);
-
-	act = thr->callstack + thr->callstack_top + level;
+	DUK_ASSERT_API_ENTRY(thr);
+
+	/* -1   = top callstack entry
+	 * -2   = caller of level -1
+	 * etc
+	 */
+	act = duk_hthread_get_activation_for_level(thr, level);
+	if (act == NULL) {
+		duk_push_undefined(thr);
+		return;
+	}
+	duk_push_bare_object(thr);
+
 	/* Relevant PC is just before current one because PC is
 	 * post-incremented.  This should match what error augment
 	 * code does.
 	 */
 	pc = duk_hthread_get_act_prev_pc(thr, act);
 
-	duk_push_tval(ctx, &act->tv_func);
-
-	duk_push_uint(ctx, (duk_uint_t) pc);
-	duk_put_prop_stridx_short(ctx, -3, DUK_STRIDX_PC);
+	duk_push_tval(thr, &act->tv_func);
+
+	duk_push_uint(thr, (duk_uint_t) pc);
+	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_PC);
 
 #if defined(DUK_USE_PC2LINE)
-	line = duk_hobject_pc2line_query(ctx, -1, pc);
+	line = duk_hobject_pc2line_query(thr, -1, pc);
 #else
 	line = 0;
 #endif
-	duk_push_uint(ctx, (duk_uint_t) line);
-	duk_put_prop_stridx_short(ctx, -3, DUK_STRIDX_LINE_NUMBER);
-
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_LC_FUNCTION);
+	duk_push_uint(thr, (duk_uint_t) line);
+	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER);
+
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_LC_FUNCTION);
 	/* Providing access to e.g. act->lex_env would be dangerous: these
 	 * internal structures must never be accessible to the application.
 	 * Duktape relies on them having consistent data, and this consistency
@@ -15938,50 +16200,38 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_EXTERNAL void *duk_alloc_raw(duk_context *ctx, duk_size_t size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void *duk_alloc_raw(duk_hthread *thr, duk_size_t size) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	return DUK_ALLOC_RAW(thr->heap, size);
 }
 
-DUK_EXTERNAL void duk_free_raw(duk_context *ctx, void *ptr) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_free_raw(duk_hthread *thr, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK_FREE_RAW(thr->heap, ptr);
 }
 
-DUK_EXTERNAL void *duk_realloc_raw(duk_context *ctx, void *ptr, duk_size_t size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void *duk_realloc_raw(duk_hthread *thr, void *ptr, duk_size_t size) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	return DUK_REALLOC_RAW(thr->heap, ptr, size);
 }
 
-DUK_EXTERNAL void *duk_alloc(duk_context *ctx, duk_size_t size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void *duk_alloc(duk_hthread *thr, duk_size_t size) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	return DUK_ALLOC(thr->heap, size);
 }
 
-DUK_EXTERNAL void duk_free(duk_context *ctx, void *ptr) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	DUK_FREE(thr->heap, ptr);
-}
-
-DUK_EXTERNAL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_free(duk_hthread *thr, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_FREE_CHECKED(thr, ptr);
+}
+
+DUK_EXTERNAL void *duk_realloc(duk_hthread *thr, void *ptr, duk_size_t size) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/*
 	 *  Note: since this is an exposed API call, there should be
@@ -15996,11 +16246,10 @@
 	return DUK_REALLOC(thr->heap, ptr, size);
 }
 
-DUK_EXTERNAL void duk_get_memory_functions(duk_context *ctx, duk_memory_functions *out_funcs) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_get_memory_functions(duk_hthread *thr, duk_memory_functions *out_funcs) {
 	duk_heap *heap;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(out_funcs != NULL);
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
@@ -16012,12 +16261,11 @@
 	out_funcs->udata = heap->heap_udata;
 }
 
-DUK_EXTERNAL void duk_gc(duk_context *ctx, duk_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_gc(duk_hthread *thr, duk_uint_t flags) {
 	duk_heap *heap;
 	duk_small_uint_t ms_flags;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	heap = thr->heap;
 	DUK_ASSERT(heap != NULL);
 
@@ -16041,94 +16289,98 @@
  *  defineProperty, getOwnPropertyDescriptor).
  */
 
-DUK_EXTERNAL duk_bool_t duk_get_prop(duk_context *ctx, duk_idx_t obj_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_get_prop(duk_hthread *thr, duk_idx_t obj_idx) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_bool_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Note: copying tv_obj and tv_key to locals to shield against a valstack
 	 * resize is not necessary for a property get right now.
 	 */
 
-	tv_obj = duk_require_tval(ctx, obj_idx);
-	tv_key = duk_require_tval(ctx, -1);
+	tv_obj = duk_require_tval(thr, obj_idx);
+	tv_key = duk_require_tval(thr, -1);
 
 	rc = duk_hobject_getprop(thr, tv_obj, tv_key);
 	DUK_ASSERT(rc == 0 || rc == 1);
 	/* a value is left on stack regardless of rc */
 
-	duk_remove_m2(ctx);  /* remove key */
+	duk_remove_m2(thr);  /* remove key */
+	DUK_ASSERT(duk_is_undefined(thr, -1) || rc == 1);
 	return rc;  /* 1 if property found, 0 otherwise */
 }
 
-DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_string(ctx, key);
-	return duk_get_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_get_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_string(thr, key);
+	return duk_get_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_get_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_lstring(ctx, key, key_len);
-	return duk_get_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_uarridx(ctx, arr_idx);
-	return duk_get_prop(ctx, obj_idx);
-}
-
-DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_lstring(thr, key, key_len);
+	return duk_get_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_uarridx(thr, arr_idx);
+	return duk_get_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_get_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
+	return duk_get_prop(thr, obj_idx);
+}
+
+DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
-	DUK_UNREF(thr);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-	return duk_get_prop(ctx, obj_idx);
-}
-
-DUK_INTERNAL duk_bool_t duk_get_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args) {
-	return duk_get_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+	return duk_get_prop(thr, obj_idx);
+}
+
+DUK_INTERNAL duk_bool_t duk_get_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
+	return duk_get_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
 	                                (duk_small_uint_t) (packed_args & 0xffffUL));
 }
 
-DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop) {
+DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_bool_t *out_has_prop) {
 	duk_bool_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
 
-	rc = duk_get_prop_stridx(ctx, obj_idx, stridx);
+	rc = duk_get_prop_stridx(thr, obj_idx, stridx);
 	if (out_has_prop) {
 		*out_has_prop = rc;
 	}
-	rc = duk_to_boolean(ctx, -1);
+	rc = duk_to_boolean(thr, -1);
 	DUK_ASSERT(rc == 0 || rc == 1);
-	duk_pop(ctx);
+	duk_pop(thr);
 	return rc;
 }
 
-DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t idx_key) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_tval *tv_val;
-	duk_small_int_t throw_flag;
+	duk_bool_t throw_flag;
 	duk_bool_t rc;
 
 	/* Note: copying tv_obj and tv_key to locals to shield against a valstack
@@ -16142,202 +16394,216 @@
 	DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) ||
 	           (idx_key == -1 && (idx_key ^ 1) == -2));
 	/* XXX: Direct access; faster validation. */
-	tv_obj = duk_require_tval(ctx, obj_idx);
-	tv_key = duk_require_tval(ctx, idx_key);
-	tv_val = duk_require_tval(ctx, idx_key ^ 1);
-	throw_flag = duk_is_strict_call(ctx);
+	tv_obj = duk_require_tval(thr, obj_idx);
+	tv_key = duk_require_tval(thr, idx_key);
+	tv_val = duk_require_tval(thr, idx_key ^ 1);
+	throw_flag = duk_is_strict_call(thr);
 
 	rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag);
 	DUK_ASSERT(rc == 0 || rc == 1);
 
-	duk_pop_2(ctx);  /* remove key and value */
+	duk_pop_2(thr);  /* remove key and value */
 	return rc;  /* 1 if property found, 0 otherwise */
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__put_prop_shared(ctx, obj_idx, -2);
-}
-
-DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_put_prop(duk_hthread *thr, duk_idx_t obj_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__put_prop_shared(thr, obj_idx, -2);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
 	/* Careful here and with other duk_put_prop_xxx() helpers: the
 	 * target object and the property value may be in the same value
 	 * stack slot (unusual, but still conceptually clear).
 	 */
-	obj_idx = duk_normalize_index(ctx, obj_idx);
-	(void) duk_push_string(ctx, key);
-	return duk__put_prop_shared(ctx, obj_idx, -1);
-}
-
-DUK_EXTERNAL duk_bool_t duk_put_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_normalize_index(thr, obj_idx);
+	(void) duk_push_string(thr, key);
+	return duk__put_prop_shared(thr, obj_idx, -1);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_normalize_index(ctx, obj_idx);
-	(void) duk_push_lstring(ctx, key, key_len);
-	return duk__put_prop_shared(ctx, obj_idx, -1);
-}
-
-DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_uarridx(ctx, arr_idx);
-	return duk__put_prop_shared(ctx, obj_idx, -1);
-}
-
-DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_normalize_index(thr, obj_idx);
+	(void) duk_push_lstring(thr, key, key_len);
+	return duk__put_prop_shared(thr, obj_idx, -1);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_uarridx(thr, arr_idx);
+	return duk__put_prop_shared(thr, obj_idx, -1);
+}
+
+DUK_EXTERNAL duk_bool_t duk_put_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
+	return duk__put_prop_shared(thr, obj_idx, -1);
+}
+
+
+DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
-	DUK_UNREF(thr);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-	return duk__put_prop_shared(ctx, obj_idx, -1);
-}
-
-DUK_INTERNAL duk_bool_t duk_put_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args) {
-	return duk_put_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+	return duk__put_prop_shared(thr, obj_idx, -1);
+}
+
+DUK_INTERNAL duk_bool_t duk_put_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
+	return duk_put_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
 	                                (duk_small_uint_t) (packed_args & 0xffffUL));
 }
 
-DUK_EXTERNAL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_del_prop(duk_hthread *thr, duk_idx_t obj_idx) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
-	duk_small_int_t throw_flag;
+	duk_bool_t throw_flag;
 	duk_bool_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Note: copying tv_obj and tv_key to locals to shield against a valstack
 	 * resize is not necessary for a property delete right now.
 	 */
 
-	tv_obj = duk_require_tval(ctx, obj_idx);
-	tv_key = duk_require_tval(ctx, -1);
-	throw_flag = duk_is_strict_call(ctx);
+	tv_obj = duk_require_tval(thr, obj_idx);
+	tv_key = duk_require_tval(thr, -1);
+	throw_flag = duk_is_strict_call(thr);
 
 	rc = duk_hobject_delprop(thr, tv_obj, tv_key, throw_flag);
 	DUK_ASSERT(rc == 0 || rc == 1);
 
-	duk_pop(ctx);  /* remove key */
+	duk_pop(thr);  /* remove key */
 	return rc;
 }
 
-DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_string(ctx, key);
-	return duk_del_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_del_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_string(thr, key);
+	return duk_del_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_del_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_lstring(ctx, key, key_len);
-	return duk_del_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_uarridx(ctx, arr_idx);
-	return duk_del_prop(ctx, obj_idx);
-}
-
-DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_lstring(thr, key, key_len);
+	return duk_del_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_uarridx(thr, arr_idx);
+	return duk_del_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_del_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
+	return duk_del_prop(thr, obj_idx);
+}
+
+DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
-	DUK_UNREF(thr);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-	return duk_del_prop(ctx, obj_idx);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+	return duk_del_prop(thr, obj_idx);
 }
 
 #if 0
-DUK_INTERNAL duk_bool_t duk_del_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args) {
-	return duk_del_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
+DUK_INTERNAL duk_bool_t duk_del_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
+	return duk_del_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
 	                                (duk_small_uint_t) (packed_args & 0xffffUL));
 }
 #endif
 
-DUK_EXTERNAL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_has_prop(duk_hthread *thr, duk_idx_t obj_idx) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_bool_t rc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Note: copying tv_obj and tv_key to locals to shield against a valstack
 	 * resize is not necessary for a property existence check right now.
 	 */
 
-	tv_obj = duk_require_tval(ctx, obj_idx);
-	tv_key = duk_require_tval(ctx, -1);
+	tv_obj = duk_require_tval(thr, obj_idx);
+	tv_key = duk_require_tval(thr, -1);
 
 	rc = duk_hobject_hasprop(thr, tv_obj, tv_key);
 	DUK_ASSERT(rc == 0 || rc == 1);
 
-	duk_pop(ctx);  /* remove key */
+	duk_pop(thr);  /* remove key */
 	return rc;  /* 1 if property found, 0 otherwise */
 }
 
-DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_hthread *thr, duk_idx_t obj_idx, const char *key) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_string(ctx, key);
-	return duk_has_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_has_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_string(thr, key);
+	return duk_has_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_has_prop_lstring(duk_hthread *thr, duk_idx_t obj_idx, const char *key, duk_size_t key_len) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(key != NULL);
 
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_lstring(ctx, key, key_len);
-	return duk_has_prop(ctx, obj_idx);
-}
-
-DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_uarridx(ctx, arr_idx);
-	return duk_has_prop(ctx, obj_idx);
-}
-
-DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_lstring(thr, key, key_len);
+	return duk_has_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_uarridx(thr, arr_idx);
+	return duk_has_prop(thr, obj_idx);
+}
+
+DUK_EXTERNAL duk_bool_t duk_has_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_heapptr(thr, ptr);  /* NULL -> 'undefined' */
+	return duk_has_prop(thr, obj_idx);
+}
+
+DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
-	DUK_UNREF(thr);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-	return duk_has_prop(ctx, obj_idx);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+	return duk_has_prop(thr, obj_idx);
 }
 
 #if 0
-DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args) {
-	return duk_has_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
+DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
+	return duk_has_prop_stridx(thr, (duk_idx_t) (duk_int16_t) (packed_args >> 16),
 	                                (duk_small_uint_t) (packed_args & 0xffffUL));
 }
 #endif
@@ -16347,103 +16613,103 @@
  * not invoked by this method.  The caller must be careful to invoke any such
  * behaviors if necessary.
  */
-DUK_INTERNAL void duk_xdef_prop(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t desc_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_xdef_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t desc_flags) {
 	duk_hobject *obj;
 	duk_hstring *key;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_require_hobject(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_require_hobject(thr, obj_idx);
 	DUK_ASSERT(obj != NULL);
-	key = duk_to_property_key_hstring(ctx, -2);
+	key = duk_to_property_key_hstring(thr, -2);
 	DUK_ASSERT(key != NULL);
-	DUK_ASSERT(duk_require_tval(ctx, -1) != NULL);
+	DUK_ASSERT(duk_require_tval(thr, -1) != NULL);
 
 	duk_hobject_define_property_internal(thr, obj, key, desc_flags);
 
-	duk_pop(ctx);  /* pop key */
-}
-
-DUK_INTERNAL void duk_xdef_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	duk_pop(thr);  /* pop key */
+}
+
+DUK_INTERNAL void duk_xdef_prop_index(duk_hthread *thr, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags) {
 	duk_hobject *obj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_require_hobject(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_require_hobject(thr, obj_idx);
 	DUK_ASSERT(obj != NULL);
 
 	duk_hobject_define_property_internal_arridx(thr, obj, arr_idx, desc_flags);
 	/* value popped by call */
 }
 
-DUK_INTERNAL void duk_xdef_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_xdef_prop_stridx(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_uint_t desc_flags) {
 	duk_hobject *obj;
 	duk_hstring *key;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
 
-	obj = duk_require_hobject(ctx, obj_idx);
+	obj = duk_require_hobject(thr, obj_idx);
 	DUK_ASSERT(obj != NULL);
 	key = DUK_HTHREAD_GET_STRING(thr, stridx);
 	DUK_ASSERT(key != NULL);
-	DUK_ASSERT(duk_require_tval(ctx, -1) != NULL);
+	DUK_ASSERT(duk_require_tval(thr, -1) != NULL);
 
 	duk_hobject_define_property_internal(thr, obj, key, desc_flags);
 	/* value popped by call */
 }
 
-DUK_INTERNAL void duk_xdef_prop_stridx_short_raw(duk_context *ctx, duk_uint_t packed_args) {
-	duk_xdef_prop_stridx(ctx, (duk_idx_t) (duk_int8_t) (packed_args >> 24),
+DUK_INTERNAL void duk_xdef_prop_stridx_short_raw(duk_hthread *thr, duk_uint_t packed_args) {
+	duk_xdef_prop_stridx(thr, (duk_idx_t) (duk_int8_t) (packed_args >> 24),
 	                          (duk_small_uint_t) (packed_args >> 8) & 0xffffUL,
 	                          (duk_small_uint_t) (packed_args & 0xffL));
 }
 
-DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+#if 0  /*unused*/
+DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) {
 	duk_hobject *obj;
 	duk_hstring *key;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
 	DUK_ASSERT_BIDX_VALID(builtin_idx);
 
-	obj = duk_require_hobject(ctx, obj_idx);
+	obj = duk_require_hobject(thr, obj_idx);
 	DUK_ASSERT(obj != NULL);
 	key = DUK_HTHREAD_GET_STRING(thr, stridx);
 	DUK_ASSERT(key != NULL);
 
-	duk_push_hobject(ctx, thr->builtins[builtin_idx]);
+	duk_push_hobject(thr, thr->builtins[builtin_idx]);
 	duk_hobject_define_property_internal(thr, obj, key, desc_flags);
 	/* value popped by call */
 }
+#endif
 
 /* This is a rare property helper; it sets the global thrower (E5 Section 13.2.3)
  * setter/getter into an object property.  This is needed by the 'arguments'
  * object creation code, function instance creation code, and Function.prototype.bind().
  */
 
-DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx) {
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
-	duk_push_hstring_stridx(ctx, stridx);
-	duk_push_hobject_bidx(ctx, DUK_BIDX_TYPE_ERROR_THROWER);
-	duk_dup_top(ctx);
-	duk_def_prop(ctx, obj_idx, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE);  /* attributes always 0 */
+DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
+	duk_push_hstring_stridx(thr, stridx);
+	duk_push_hobject_bidx(thr, DUK_BIDX_TYPE_ERROR_THROWER);
+	duk_dup_top(thr);
+	duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_SETTER | DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_FORCE);  /* attributes always 0 */
 }
 
 /* Object.getOwnPropertyDescriptor() equivalent C binding. */
-DUK_EXTERNAL void duk_get_prop_desc(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags) {
+DUK_EXTERNAL void duk_get_prop_desc(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(flags);  /* no flags defined yet */
 
-	duk_hobject_object_get_own_property_descriptor(ctx, obj_idx);  /* [ ... key ] -> [ ... desc ] */
+	duk_hobject_object_get_own_property_descriptor(thr, obj_idx);  /* [ ... key ] -> [ ... desc ] */
 }
 
 /* Object.defineProperty() equivalent C binding. */
-DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_def_prop(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t flags) {
 	duk_idx_t idx_base;
 	duk_hobject *obj;
 	duk_hstring *key;
@@ -16453,9 +16719,9 @@
 	duk_uint_t is_data_desc;
 	duk_uint_t is_acc_desc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_require_hobject(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_require_hobject(thr, obj_idx);
 
 	is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE);
 	is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER);
@@ -16467,12 +16733,12 @@
 		goto fail_invalid_desc;
 	}
 
-	idx_base = duk_get_top_index(ctx);
+	idx_base = duk_get_top_index(thr);
 	if (flags & DUK_DEFPROP_HAVE_SETTER) {
-		duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED |
+		duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED |
 		                                     DUK_TYPE_MASK_OBJECT |
 		                                     DUK_TYPE_MASK_LIGHTFUNC);
-		set = duk_get_hobject_promote_lfunc(ctx, idx_base);
+		set = duk_get_hobject_promote_lfunc(thr, idx_base);
 		if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) {
 			goto fail_not_callable;
 		}
@@ -16481,10 +16747,10 @@
 		set = NULL;
 	}
 	if (flags & DUK_DEFPROP_HAVE_GETTER) {
-		duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED |
+		duk_require_type_mask(thr, idx_base, DUK_TYPE_MASK_UNDEFINED |
 		                                     DUK_TYPE_MASK_OBJECT |
 		                                     DUK_TYPE_MASK_LIGHTFUNC);
-		get = duk_get_hobject_promote_lfunc(ctx, idx_base);
+		get = duk_get_hobject_promote_lfunc(thr, idx_base);
 		if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) {
 			goto fail_not_callable;
 		}
@@ -16498,12 +16764,12 @@
 	} else {
 		idx_value = (duk_idx_t) -1;
 	}
-	key = duk_to_property_key_hstring(ctx, idx_base);
+	key = duk_to_property_key_hstring(thr, idx_base);
 	DUK_ASSERT(key != NULL);
 
-	duk_require_valid_index(ctx, idx_base);
-
-	duk_hobject_define_property_helper(ctx,
+	duk_require_valid_index(thr, idx_base);
+
+	duk_hobject_define_property_helper(thr,
 	                                   flags /*defprop_flags*/,
 	                                   obj,
 	                                   key,
@@ -16514,7 +16780,7 @@
 
 	/* Clean up stack */
 
-	duk_set_top(ctx, idx_base);
+	duk_set_top(thr, idx_base);
 
 	/* [ ... obj ... ] */
 
@@ -16536,73 +16802,140 @@
  *  and are not exposed through the API.
  */
 
-DUK_EXTERNAL void duk_compact(duk_context *ctx, duk_idx_t obj_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_compact(duk_hthread *thr, duk_idx_t obj_idx) {
 	duk_hobject *obj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_get_hobject(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_get_hobject(thr, obj_idx);
 	if (obj) {
 		/* Note: this may fail, caller should protect the call if necessary */
 		duk_hobject_compact_props(thr, obj);
 	}
 }
 
-DUK_INTERNAL void duk_compact_m1(duk_context *ctx) {
-	duk_compact(ctx, -1);
+DUK_INTERNAL void duk_compact_m1(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_compact(thr, -1);
 }
 
 /* XXX: the duk_hobject_enum.c stack APIs should be reworked */
 
-DUK_EXTERNAL void duk_enum(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t enum_flags) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_dup(ctx, obj_idx);
-	duk_require_hobject_promote_mask(ctx, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-	duk_hobject_enumerator_create(ctx, enum_flags);   /* [target] -> [enum] */
-}
-
-DUK_EXTERNAL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_index, duk_bool_t get_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_require_hobject(ctx, enum_index);
-	duk_dup(ctx, enum_index);
-	return duk_hobject_enumerator_next(ctx, get_value);
+DUK_EXTERNAL void duk_enum(duk_hthread *thr, duk_idx_t obj_idx, duk_uint_t enum_flags) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_dup(thr, obj_idx);
+	duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	duk_hobject_enumerator_create(thr, enum_flags);   /* [target] -> [enum] */
+}
+
+DUK_EXTERNAL duk_bool_t duk_next(duk_hthread *thr, duk_idx_t enum_index, duk_bool_t get_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_require_hobject(thr, enum_index);
+	duk_dup(thr, enum_index);
+	return duk_hobject_enumerator_next(thr, get_value);
+}
+
+DUK_INTERNAL void duk_seal_freeze_raw(duk_hthread *thr, duk_idx_t obj_idx, duk_bool_t is_freeze) {
+	duk_tval *tv;
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, obj_idx);
+	DUK_ASSERT(tv != NULL);
+
+	/* Seal/freeze are quite rare in practice so it'd be nice to get the
+	 * correct behavior simply via automatic promotion (at the cost of some
+	 * memory churn).  However, the promoted objects don't behave the same,
+	 * e.g. promoted lightfuncs are extensible.
+	 */
+
+	switch (DUK_TVAL_GET_TAG(tv)) {
+	case DUK_TAG_BUFFER:
+		/* Plain buffer: already sealed, but not frozen (and can't be frozen
+		 * because index properties can't be made non-writable.
+		 */
+		if (is_freeze) {
+			goto fail_cannot_freeze;
+		}
+		break;
+	case DUK_TAG_LIGHTFUNC:
+		/* Lightfunc: already sealed and frozen, success. */
+		break;
+	case DUK_TAG_OBJECT:
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) {
+			/* Buffer objects cannot be frozen because there's no internal
+			 * support for making virtual array indices non-writable.
+			 */
+			DUK_DD(DUK_DDPRINT("cannot freeze a buffer object"));
+			goto fail_cannot_freeze;
+		}
+		duk_hobject_object_seal_freeze_helper(thr, h, is_freeze);
+
+		/* Sealed and frozen objects cannot gain any more properties,
+		 * so this is a good time to compact them.
+		 */
+		duk_hobject_compact_props(thr, h);
+		break;
+	default:
+		/* ES2015 Sections 19.1.2.5, 19.1.2.17 */
+		break;
+	}
+	return;
+
+ fail_cannot_freeze:
+	DUK_ERROR_TYPE_INVALID_ARGS(thr);  /* XXX: proper error message */
+}
+
+DUK_EXTERNAL void duk_seal(duk_hthread *thr, duk_idx_t obj_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_seal_freeze_raw(thr, obj_idx, 0 /*is_freeze*/);
+}
+
+DUK_EXTERNAL void duk_freeze(duk_hthread *thr, duk_idx_t obj_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_seal_freeze_raw(thr, obj_idx, 1 /*is_freeze*/);
 }
 
 /*
  *  Helpers for writing multiple properties
  */
 
-DUK_EXTERNAL void duk_put_function_list(duk_context *ctx, duk_idx_t obj_idx, const duk_function_list_entry *funcs) {
+DUK_EXTERNAL void duk_put_function_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_function_list_entry *funcs) {
 	const duk_function_list_entry *ent = funcs;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
 	if (ent != NULL) {
 		while (ent->key != NULL) {
-			duk_push_c_function(ctx, ent->value, ent->nargs);
-			duk_put_prop_string(ctx, obj_idx, ent->key);
+			duk_push_c_function(thr, ent->value, ent->nargs);
+			duk_put_prop_string(thr, obj_idx, ent->key);
 			ent++;
 		}
 	}
 }
 
-DUK_EXTERNAL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_idx, const duk_number_list_entry *numbers) {
+DUK_EXTERNAL void duk_put_number_list(duk_hthread *thr, duk_idx_t obj_idx, const duk_number_list_entry *numbers) {
 	const duk_number_list_entry *ent = numbers;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj_idx = duk_require_normalize_index(ctx, obj_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj_idx = duk_require_normalize_index(thr, obj_idx);
 	if (ent != NULL) {
 		while (ent->key != NULL) {
-			tv = ((duk_hthread *) ctx)->valstack_top++;
+			tv = thr->valstack_top++;
 			DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));  /* value stack init policy */
 			DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(tv, ent->value);  /* no need for decref/incref */
-			duk_put_prop_string(ctx, obj_idx, ent->key);
+			duk_put_prop_string(thr, obj_idx, ent->key);
 			ent++;
 		}
 	}
@@ -16612,65 +16945,61 @@
  *  Shortcut for accessing global object properties
  */
 
-DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_context *ctx, const char *key) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_hthread *thr, const char *key) {
 	duk_bool_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
 
 	/* XXX: direct implementation */
 
-	duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]);
-	ret = duk_get_prop_string(ctx, -1, key);
-	duk_remove_m2(ctx);
+	duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
+	ret = duk_get_prop_string(thr, -1, key);
+	duk_remove_m2(thr);
 	return ret;
 }
 
-DUK_EXTERNAL duk_bool_t duk_get_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_get_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) {
 	duk_bool_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
 
 	/* XXX: direct implementation */
 
-	duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]);
-	ret = duk_get_prop_lstring(ctx, -1, key, key_len);
-	duk_remove_m2(ctx);
+	duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
+	ret = duk_get_prop_lstring(thr, -1, key, key_len);
+	duk_remove_m2(thr);
 	return ret;
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_context *ctx, const char *key) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_hthread *thr, const char *key) {
 	duk_bool_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
 
 	/* XXX: direct implementation */
 
-	duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]);
-	duk_insert(ctx, -2);
-	ret = duk_put_prop_string(ctx, -2, key);  /* [ ... global val ] -> [ ... global ] */
-	duk_pop(ctx);
+	duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
+	duk_insert(thr, -2);
+	ret = duk_put_prop_string(thr, -2, key);  /* [ ... global val ] -> [ ... global ] */
+	duk_pop(thr);
 	return ret;
 }
 
-DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_context *ctx, const char *key, duk_size_t key_len) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_hthread *thr, const char *key, duk_size_t key_len) {
 	duk_bool_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
 
 	/* XXX: direct implementation */
 
-	duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]);
-	duk_insert(ctx, -2);
-	ret = duk_put_prop_lstring(ctx, -2, key, key_len);  /* [ ... global val ] -> [ ... global ] */
-	duk_pop(ctx);
+	duk_push_hobject(thr, thr->builtins[DUK_BIDX_GLOBAL]);
+	duk_insert(thr, -2);
+	ret = duk_put_prop_lstring(thr, -2, key, key_len);  /* [ ... global val ] -> [ ... global ] */
+	duk_pop(thr);
 	return ret;
 }
 
@@ -16678,38 +17007,35 @@
  *  Object prototype
  */
 
-DUK_EXTERNAL void duk_get_prototype(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_get_prototype(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *obj;
 	duk_hobject *proto;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	obj = duk_require_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_require_hobject(thr, idx);
 	DUK_ASSERT(obj != NULL);
 
 	/* XXX: shared helper for duk_push_hobject_or_undefined()? */
 	proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj);
 	if (proto) {
-		duk_push_hobject(ctx, proto);
-	} else {
-		duk_push_undefined(ctx);
-	}
-}
-
-DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+		duk_push_hobject(thr, proto);
+	} else {
+		duk_push_undefined(thr);
+	}
+}
+
+DUK_EXTERNAL void duk_set_prototype(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *obj;
 	duk_hobject *proto;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_require_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_require_hobject(thr, idx);
 	DUK_ASSERT(obj != NULL);
-	duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_UNDEFINED |
+	duk_require_type_mask(thr, -1, DUK_TYPE_MASK_UNDEFINED |
 	                               DUK_TYPE_MASK_OBJECT);
-	proto = duk_get_hobject(ctx, -1);
+	proto = duk_get_hobject(thr, -1);
 	/* proto can also be NULL here (allowed explicitly) */
 
 #if defined(DUK_USE_ROM_OBJECTS)
@@ -16721,7 +17047,7 @@
 
 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto);
 
-	duk_pop(ctx);
+	duk_pop(thr);
 }
 
 /*
@@ -16734,21 +17060,21 @@
  * XXX: same issue as with Duktape.fin: there's no way to delete the property
  * now (just set it to undefined).
  */
-DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_get_prop_stridx(ctx, idx, DUK_STRIDX_INT_FINALIZER);
-}
-
-DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_get_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER);
+}
+
+DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *h;
 	duk_bool_t callable;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_require_hobject(ctx, idx);  /* Get before 'put' so that 'idx' is correct. */
-	callable = duk_is_callable(ctx, -1);
-	duk_put_prop_stridx(ctx, idx, DUK_STRIDX_INT_FINALIZER);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_require_hobject(thr, idx);  /* Get before 'put' so that 'idx' is correct. */
+	callable = duk_is_callable(thr, -1);
+	duk_put_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER);
 
 	/* In addition to setting the finalizer property, keep a "have
 	 * finalizer" flag in duk_hobject in sync so that refzero can do
@@ -16768,16 +17094,16 @@
 	}
 }
 #else  /* DUK_USE_FINALIZER_SUPPORT */
-DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(idx);
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
-}
-
-DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ERROR_UNSUPPORTED(thr);
+}
+
+DUK_EXTERNAL void duk_set_finalizer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(idx);
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+	DUK_ERROR_UNSUPPORTED(thr);
 }
 #endif  /* DUK_USE_FINALIZER_SUPPORT */
 #line 1 "duk_api_stack.c"
@@ -16799,7 +17125,7 @@
  *  Forward declarations
  */
 
-DUK_LOCAL_DECL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags);
+DUK_LOCAL_DECL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx);
 
 /*
  *  Global state for working around missing variadic macros
@@ -16814,6 +17140,10 @@
  *  Misc helpers
  */
 
+DUK_LOCAL const char * const duk__symbol_type_strings[4] = {
+	"hidden", "global", "local", "wellknown"
+};
+
 #if !defined(DUK_USE_PACKED_TVAL)
 DUK_LOCAL const duk_uint_t duk__type_from_tag[] = {
 	DUK_TYPE_NUMBER,
@@ -16843,12 +17173,15 @@
 };
 #endif  /* !DUK_USE_PACKED_TVAL */
 
+/* Assert that there's room for one value. */
+#define DUK__ASSERT_SPACE() do { \
+		DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
+	} while (0)
+
 /* Check that there's room to push one value. */
 #if defined(DUK_USE_VALSTACK_UNSAFE)
 /* Faster but value stack overruns are memory unsafe. */
-#define DUK__CHECK_SPACE() do { \
-		DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
-	} while (0)
+#define DUK__CHECK_SPACE() DUK__ASSERT_SPACE()
 #else
 #define DUK__CHECK_SPACE() do { \
 		if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \
@@ -16857,17 +17190,48 @@
 	} while (0)
 #endif
 
-DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t idx, duk_uint_t tag);
-
-DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t idx, duk_int_t def_value, duk_bool_t require) {
-	duk_hthread *thr;
+DUK_LOCAL duk_small_uint_t duk__get_symbol_type(duk_hstring *h) {
+	const duk_uint8_t *data;
+	duk_size_t len;
+
+	DUK_ASSERT(h != NULL);
+	DUK_ASSERT(DUK_HSTRING_HAS_SYMBOL(h));
+	DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(h) >= 1);  /* always true, symbol prefix */
+
+	data = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
+	len = DUK_HSTRING_GET_BYTELEN(h);
+	DUK_ASSERT(len >= 1);
+
+	/* XXX: differentiate between 0x82 and 0xff (hidden vs. internal?)? */
+
+	if (data[0] == 0xffU) {
+		return DUK_SYMBOL_TYPE_HIDDEN;
+	} else if (data[0] == 0x82U) {
+		return DUK_SYMBOL_TYPE_HIDDEN;
+	} else if (data[0] == 0x80U) {
+		return DUK_SYMBOL_TYPE_GLOBAL;
+	} else if (data[len - 1] != 0xffU) {
+		return DUK_SYMBOL_TYPE_LOCAL;
+	} else {
+		return DUK_SYMBOL_TYPE_WELLKNOWN;
+	}
+}
+
+DUK_LOCAL const char *duk__get_symbol_type_string(duk_hstring *h) {
+	duk_small_uint_t idx;
+	idx = duk__get_symbol_type(h);
+	DUK_ASSERT(idx < sizeof(duk__symbol_type_strings));
+	return duk__symbol_type_strings[idx];
+}
+
+DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag);
+
+DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value, duk_bool_t require) {
 	duk_tval *tv;
 	duk_small_int_t c;
 	duk_double_t d;
 
-	thr = (duk_hthread *) ctx;
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	/*
@@ -16923,17 +17287,14 @@
 	return def_value;
 }
 
-DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value, duk_bool_t require) {
-	duk_hthread *thr;
+DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value, duk_bool_t require) {
 	duk_tval *tv;
 	duk_small_int_t c;
 	duk_double_t d;
 
 	/* Same as above but for unsigned int range. */
 
-	thr = (duk_hthread *) ctx;
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 #if defined(DUK_USE_FASTINT)
@@ -16986,12 +17347,11 @@
  *  There's some repetition because of this; keep the functions in sync.
  */
 
-DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_hthread *thr, duk_idx_t idx) {
 	duk_uidx_t vs_size;
 	duk_uidx_t uidx;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
 	/* Care must be taken to avoid pointer wrapping in the index
@@ -17022,12 +17382,11 @@
 	return DUK_INVALID_INDEX;
 }
 
-DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_hthread *thr, duk_idx_t idx) {
 	duk_uidx_t vs_size;
 	duk_uidx_t uidx;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
@@ -17051,12 +17410,11 @@
 	return 0;  /* unreachable */
 }
 
-DUK_INTERNAL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_tval *duk_get_tval(duk_hthread *thr, duk_idx_t idx) {
 	duk_uidx_t vs_size;
 	duk_uidx_t uidx;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
@@ -17087,21 +17445,23 @@
  */
 DUK_LOCAL const duk_tval_unused duk__const_tval_unused = DUK_TVAL_UNUSED_INITIALIZER();
 
-DUK_INTERNAL duk_tval *duk_get_tval_or_unused(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-	tv = duk_get_tval(ctx, idx);
+DUK_INTERNAL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval(thr, idx);
 	if (tv != NULL) {
 		return tv;
 	}
 	return (duk_tval *) DUK_LOSE_CONST(&duk__const_tval_unused);
 }
 
-DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_tval *duk_require_tval(duk_hthread *thr, duk_idx_t idx) {
 	duk_uidx_t vs_size;
 	duk_uidx_t uidx;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
@@ -17127,21 +17487,19 @@
 }
 
 /* Non-critical. */
-DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
-	return (duk_normalize_index(ctx, idx) >= 0);
+	return (duk_normalize_index(thr, idx) >= 0);
 }
 
 /* Non-critical. */
-DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_require_valid_index(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
-	if (DUK_UNLIKELY(duk_normalize_index(ctx, idx) < 0)) {
+	if (DUK_UNLIKELY(duk_normalize_index(thr, idx) < 0)) {
 		DUK_ERROR_RANGE_INDEX(thr, idx);
 		return;  /* unreachable */
 	}
@@ -17151,10 +17509,8 @@
  *  Value stack top handling
  */
 
-DUK_EXTERNAL duk_idx_t duk_get_top(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_idx_t duk_get_top(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
 }
@@ -17162,11 +17518,10 @@
 /* Internal helper to get current top but to require a minimum top value
  * (TypeError if not met).
  */
-DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_context *ctx, duk_idx_t min_top) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_hthread *thr, duk_idx_t min_top) {
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
 	if (DUK_UNLIKELY(ret < min_top)) {
@@ -17179,14 +17534,13 @@
  * This is performance critical especially for call handling, so whenever
  * changing, profile and look at generated code.
  */
-DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_set_top(duk_hthread *thr, duk_idx_t idx) {
 	duk_uidx_t vs_size;
 	duk_uidx_t vs_limit;
 	duk_uidx_t uidx;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_INVALID_INDEX < 0);
 
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
@@ -17275,11 +17629,98 @@
 	}
 }
 
-DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+/* Internal variant with a non-negative index and no runtime size checks. */
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_set_top(thr, idx);
+}
+#else  /* DUK_USE_PREFER_SIZE */
+DUK_INTERNAL void duk_set_top_unsafe(duk_hthread *thr, duk_idx_t idx) {
+	duk_uidx_t uidx;
+	duk_uidx_t vs_size;
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom);
+	DUK_ASSERT(idx >= 0);
+	DUK_ASSERT(idx <= (duk_idx_t) (thr->valstack_end - thr->valstack_bottom));
+
+	/* XXX: byte arithmetic */
+	uidx = (duk_uidx_t) idx;
+	vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+
+	if (uidx >= vs_size) {
+		/* Stack size increases or stays the same. */
+#if defined(DUK_USE_ASSERTIONS)
+		duk_uidx_t count;
+
+		count = uidx - vs_size;
+		while (count != 0) {
+			count--;
+			tv = thr->valstack_top + count;
+			DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
+		}
+#endif
+		thr->valstack_top = thr->valstack_bottom + uidx;
+	} else {
+		/* Stack size decreases. */
+#if defined(DUK_USE_REFERENCE_COUNTING)
+		duk_uidx_t count;
+		duk_tval *tv_end;
+
+		count = vs_size - uidx;
+		DUK_ASSERT(count > 0);
+		tv = thr->valstack_top;
+		tv_end = tv - count;
+		DUK_ASSERT(tv > tv_end);  /* Because count > 0. */
+		do {
+			tv--;
+			DUK_ASSERT(tv >= thr->valstack_bottom);
+			DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
+		} while (tv != tv_end);
+		thr->valstack_top = tv_end;
+		DUK_REFZERO_CHECK_FAST(thr);
+#else  /* DUK_USE_REFERENCE_COUNTING */
+		duk_uidx_t count;
+		duk_tval *tv_end;
+
+		count = vs_size - uidx;
+		tv = thr->valstack_top;
+		tv_end = tv - count;
+		DUK_ASSERT(tv > tv_end);
+		do {
+			tv--;
+			DUK_TVAL_SET_UNDEFINED(tv);
+		} while (tv != tv_end);
+		thr->valstack_top = tv_end;
+#endif  /* DUK_USE_REFERENCE_COUNTING */
+	}
+}
+#endif  /* DUK_USE_PREFER_SIZE */
+
+/* Internal helper: set top to 'top', and set [idx_wipe_start,top[ to
+ * 'undefined' (doing nothing if idx_wipe_start == top).  Indices are
+ * positive and within value stack reserve.  This is used by call handling.
+ */
+DUK_INTERNAL void duk_set_top_and_wipe(duk_hthread *thr, duk_idx_t top, duk_idx_t idx_wipe_start) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(top >= 0);
+	DUK_ASSERT(idx_wipe_start >= 0);
+	DUK_ASSERT(idx_wipe_start <= top);
+	DUK_ASSERT(thr->valstack_bottom + top <= thr->valstack_end);
+	DUK_ASSERT(thr->valstack_bottom + idx_wipe_start <= thr->valstack_end);
+
+	duk_set_top_unsafe(thr, idx_wipe_start);
+	duk_set_top_unsafe(thr, top);
+}
+
+DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_hthread *thr) {
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
 	if (DUK_UNLIKELY(ret < 0)) {
@@ -17295,21 +17736,19 @@
 /* Internal variant: call assumes there is at least one element on the value
  * stack frame; this is only asserted for.
  */
-DUK_INTERNAL duk_idx_t duk_get_top_index_unsafe(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_idx_t duk_get_top_index_unsafe(duk_hthread *thr) {
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
 	return ret;
 }
 
-DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_hthread *thr) {
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom) - 1;
 	if (DUK_UNLIKELY(ret < 0)) {
@@ -17324,61 +17763,71 @@
  *
  *  This resizing happens above the current "top": the value stack can be
  *  grown or shrunk, but the "top" is not affected.  The value stack cannot
- *  be resized to a size below the current "top".
+ *  be resized to a size below the current reserve.
  *
  *  The low level reallocation primitive must carefully recompute all value
  *  stack pointers, and must also work if ALL pointers are NULL.  The resize
  *  is quite tricky because the valstack realloc may cause a mark-and-sweep,
  *  which may run finalizers.  Running finalizers may resize the valstack
  *  recursively (the same value stack we're working on).  So, after realloc
- *  returns, we know that the valstack "top" should still be the same (there
- *  should not be live values above the "top"), but its underlying size and
- *  pointer may have changed.
- */
-
-/* XXX: perhaps refactor this to allow caller to specify some parameters, or
- * at least a 'compact' flag which skips any spare or round-up .. useful for
- * emergency gc.
- */
-
-DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_ptrdiff_t old_bottom_offset;
-	duk_ptrdiff_t old_top_offset;
-	duk_ptrdiff_t old_end_offset_post;
-#if defined(DUK_USE_DEBUG)
-	duk_ptrdiff_t old_end_offset_pre;
-	duk_tval *old_valstack_pre;
-	duk_tval *old_valstack_post;
-#endif
+ *  returns, we know that the valstack bottom, top, and reserve should still
+ *  be the same (there should not be live values above the "top"), but its
+ *  underlying size, alloc_end, and base pointer may have changed.
+ *
+ *  'new_size' is known to be <= DUK_USE_VALSTACK_LIMIT, which ensures that
+ *  size_t and pointer arithmetic won't wrap in duk__resize_valstack().
+ */
+
+/* Low level valstack resize primitive, used for both grow and shrink.  All
+ * adjustments for slack etc have already been done.  Doesn't throw but does
+ * have allocation side effects.
+ */
+DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__resize_valstack(duk_hthread *thr, duk_size_t new_size) {
+	duk_tval *pre_valstack;
+	duk_tval *pre_bottom;
+	duk_tval *pre_top;
+	duk_tval *pre_end;
+	duk_tval *pre_alloc_end;
+	duk_ptrdiff_t ptr_diff;
 	duk_tval *new_valstack;
 	duk_size_t new_alloc_size;
+	duk_tval *tv_prev_alloc_end;
 	duk_tval *p;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_HTHREAD_VALID(thr);
 	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+	DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
 	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size);  /* can't resize below 'top' */
-	DUK_ASSERT(new_size <= thr->valstack_max);  /* valstack limit caller has check, prevents wrapping */
+	DUK_ASSERT(new_size <= DUK_USE_VALSTACK_LIMIT);  /* valstack limit caller has check, prevents wrapping */
 	DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval));  /* specific assert for wrapping */
 
-	/* get pointer offsets for tweaking below */
-	old_bottom_offset = (((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack));
-	old_top_offset = (((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack));
-#if defined(DUK_USE_DEBUG)
-	old_end_offset_pre = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack));  /* not very useful, used for debugging */
-	old_valstack_pre = thr->valstack;
-#endif
-
-	/* Allocate a new valstack.
-	 *
-	 * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may
-	 * invalidate the original thr->valstack base pointer inside the realloc
-	 * process.  See doc/memory-management.rst.
-	 */
-
+	/* Pre-realloc pointer copies for asserts and debug logs. */
+	pre_valstack = thr->valstack;
+	pre_bottom = thr->valstack_bottom;
+	pre_top = thr->valstack_top;
+	pre_end = thr->valstack_end;
+	pre_alloc_end = thr->valstack_alloc_end;
+
+	DUK_UNREF(pre_valstack);
+	DUK_UNREF(pre_bottom);
+	DUK_UNREF(pre_top);
+	DUK_UNREF(pre_end);
+	DUK_UNREF(pre_alloc_end);
+
+	/* If finalizer torture enabled, force base pointer change every time
+	 * when it would be allowed.
+	 */
+#if defined(DUK_USE_FINALIZER_TORTURE)
+	if (thr->heap->pf_prevent_count == 0) {
+		duk_hthread_valstack_torture_realloc(thr);
+	}
+#endif
+
+	/* Allocate a new valstack using DUK_REALLOC_DIRECT() to deal with
+	 * a side effect changing the base pointer.
+	 */
 	new_alloc_size = sizeof(duk_tval) * new_size;
 	new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size);
 	if (DUK_UNLIKELY(new_valstack == NULL)) {
@@ -17391,69 +17840,78 @@
 		return 0;
 	}
 
-	/* Note: the realloc may have triggered a mark-and-sweep which may
-	 * have resized our valstack internally.  However, the mark-and-sweep
-	 * MUST NOT leave the stack bottom/top in a different state.  Particular
-	 * assumptions and facts:
-	 *
-	 *   - The thr->valstack pointer may be different after realloc,
-	 *     and the offset between thr->valstack_end <-> thr->valstack
-	 *     may have changed.
-	 *   - The offset between thr->valstack_bottom <-> thr->valstack
-	 *     and thr->valstack_top <-> thr->valstack MUST NOT have changed,
-	 *     because mark-and-sweep must adhere to a strict stack policy.
-	 *     In other words, logical bottom and top MUST NOT have changed.
-	 *   - All values above the top are unreachable but are initialized
-	 *     to UNDEFINED, up to the post-realloc valstack_end.
-	 *   - 'old_end_offset' must be computed after realloc to be correct.
-	 */
-
-	DUK_ASSERT((((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack)) == old_bottom_offset);
-	DUK_ASSERT((((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack)) == old_top_offset);
-
-	/* success, fixup pointers */
-	old_end_offset_post = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack));  /* must be computed after realloc */
+	/* Debug log any changes in pointer(s) by side effects.  These don't
+	 * necessarily imply any incorrect behavior, but should be rare in
+	 * practice.
+	 */
 #if defined(DUK_USE_DEBUG)
-	old_valstack_post = thr->valstack;
-#endif
+	if (thr->valstack != pre_valstack) {
+		DUK_D(DUK_DPRINT("valstack base pointer changed during valstack resize: %p -> %p",
+		                 (void *) pre_valstack, (void *) thr->valstack));
+	}
+	if (thr->valstack_bottom != pre_bottom) {
+		DUK_D(DUK_DPRINT("valstack bottom pointer changed during valstack resize: %p -> %p",
+		                 (void *) pre_bottom, (void *) thr->valstack_bottom));
+	}
+	if (thr->valstack_top != pre_top) {
+		DUK_D(DUK_DPRINT("valstack top pointer changed during valstack resize: %p -> %p",
+		                 (void *) pre_top, (void *) thr->valstack_top));
+	}
+	if (thr->valstack_end != pre_end) {
+		DUK_D(DUK_DPRINT("valstack end pointer changed during valstack resize: %p -> %p",
+		                 (void *) pre_end, (void *) thr->valstack_end));
+	}
+	if (thr->valstack_alloc_end != pre_alloc_end) {
+		DUK_D(DUK_DPRINT("valstack alloc_end pointer changed during valstack resize: %p -> %p",
+		                 (void *) pre_alloc_end, (void *) thr->valstack_alloc_end));
+	}
+#endif
+
+	/* Assertions: offsets for bottom, top, and end (reserve) must not
+	 * have changed even with side effects because they are always
+	 * restored in unwind.  For alloc_end there's no guarantee: it may
+	 * have grown or shrunk (but remain above 'end').
+	 */
+	DUK_ASSERT(thr->valstack_bottom - thr->valstack == pre_bottom - pre_valstack);
+	DUK_ASSERT(thr->valstack_top - thr->valstack == pre_top - pre_valstack);
+	DUK_ASSERT(thr->valstack_end - thr->valstack == pre_end - pre_valstack);
+	DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
+
+	/* Write new pointers.  Most pointers can be handled as a pointer
+	 * difference.
+	 */
+	ptr_diff = (duk_ptrdiff_t) ((duk_uint8_t *) new_valstack - (duk_uint8_t *) thr->valstack);
+	tv_prev_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_alloc_end + ptr_diff);
 	thr->valstack = new_valstack;
-	thr->valstack_end = new_valstack + new_size;
-#if !defined(DUK_USE_PREFER_SIZE)
-	thr->valstack_size = new_size;
-#endif
-	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_bottom_offset);
-	thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_top_offset);
-
+	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_bottom + ptr_diff);
+	thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + ptr_diff);
+	thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_end + ptr_diff);
+	thr->valstack_alloc_end = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + new_alloc_size);
+
+	/* Assertions: pointer sanity after pointer updates. */
 	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-	/* useful for debugging */
-#if defined(DUK_USE_DEBUG)
-	if (old_end_offset_pre != old_end_offset_post) {
-		DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; "
-		                 "end offset changed: %lu -> %lu",
-		                 (unsigned long) old_end_offset_pre,
-		                 (unsigned long) old_end_offset_post));
-	}
-	if (old_valstack_pre != old_valstack_post) {
-		DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p",
-		                 (void *) old_valstack_pre,
-		                 (void *) old_valstack_post));
-	}
-#endif
-
-	DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, "
-	                   "new pointers: start=%p end=%p bottom=%p top=%p",
-	                   (unsigned long) new_size, (unsigned long) new_alloc_size,
-	                   (long) (thr->valstack_bottom - thr->valstack),
-	                   (long) (thr->valstack_top - thr->valstack),
-	                   (void *) thr->valstack, (void *) thr->valstack_end,
-	                   (void *) thr->valstack_bottom, (void *) thr->valstack_top));
-
-	/* Init newly allocated slots (only). */
-	p = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + old_end_offset_post);
-	while (p < thr->valstack_end) {
+	DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
+
+	DUK_D(DUK_DPRINT("resized valstack %lu -> %lu elements (%lu -> %lu bytes): "
+	                 "base=%p -> %p, bottom=%p -> %p (%ld), top=%p -> %p (%ld), "
+	                 "end=%p -> %p (%ld), alloc_end=%p -> %p (%ld);"
+	                 " tv_prev_alloc_end=%p (-> %ld inits; <0 means shrink)",
+	                 (unsigned long) (pre_alloc_end - pre_valstack),
+	                 (unsigned long) new_size,
+	                 (unsigned long) ((duk_uint8_t *) pre_alloc_end - (duk_uint8_t *) pre_valstack),
+	                 (unsigned long) new_alloc_size,
+	                 (void *) pre_valstack, (void *) thr->valstack,
+	                 (void *) pre_bottom, (void *) thr->valstack_bottom, (long) (thr->valstack_bottom - thr->valstack),
+	                 (void *) pre_top, (void *) thr->valstack_top, (long) (thr->valstack_top - thr->valstack),
+	                 (void *) pre_end, (void *) thr->valstack_end, (long) (thr->valstack_end - thr->valstack),
+	                 (void *) pre_alloc_end, (void *) thr->valstack_alloc_end, (long) (thr->valstack_alloc_end - thr->valstack),
+	                 (void *) tv_prev_alloc_end, (long) (thr->valstack_alloc_end - tv_prev_alloc_end)));
+
+	/* If allocation grew, init any new slots to 'undefined'. */
+	p = tv_prev_alloc_end;
+	while (p < thr->valstack_alloc_end) {
 		/* Never executed if new size is smaller. */
 		DUK_TVAL_SET_UNDEFINED(p);
 		p++;
@@ -17462,7 +17920,7 @@
 	/* Assert for value stack initialization policy. */
 #if defined(DUK_USE_ASSERTIONS)
 	p = thr->valstack_top;
-	while (p < thr->valstack_end) {
+	while (p < thr->valstack_alloc_end) {
 		DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p));
 		p++;
 	}
@@ -17471,229 +17929,257 @@
 	return 1;
 }
 
-DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__valstack_do_resize(duk_context *ctx,
-                                                                   duk_size_t min_new_size,
-                                                                   duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_size_t old_size;
+DUK_LOCAL DUK_COLD DUK_NOINLINE duk_bool_t duk__valstack_grow(duk_hthread *thr, duk_size_t min_bytes, duk_bool_t throw_on_error) {
+	duk_size_t min_size;
 	duk_size_t new_size;
-	duk_bool_t is_shrink;
-	duk_small_uint_t compact_flag = (flags & DUK_VSRESIZE_FLAG_COMPACT);
-	duk_small_uint_t throw_flag = (flags & DUK_VSRESIZE_FLAG_THROW);
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-#if defined(DUK_USE_PREFER_SIZE)
-	old_size = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-	DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size);
-	old_size = thr->valstack_size;
-#endif
-
-	if (min_new_size <= old_size) {
-		is_shrink = 1;
-	} else {
-		is_shrink = 0;
-	}
-
-	new_size = min_new_size;
-	if (!compact_flag) {
-		if (is_shrink) {
-			/* shrink case; leave some spare */
-			new_size += DUK_VALSTACK_SHRINK_SPARE;
-		}
-
-		/* round up roughly to next 'grow step' */
-		new_size = (new_size / DUK_VALSTACK_GROW_STEP + 1) * DUK_VALSTACK_GROW_STEP;
-	}
-
-	DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)",
-	                   (const char *) (new_size > old_size ? "grow" : "shrink"),
-	                   (unsigned long) old_size, (unsigned long) new_size,
-	                   (unsigned long) min_new_size));
-
-	if (DUK_UNLIKELY(new_size > thr->valstack_max)) {
+
+	DUK_ASSERT(min_bytes / sizeof(duk_tval) * sizeof(duk_tval) == min_bytes);
+	min_size = min_bytes / sizeof(duk_tval);  /* from bytes to slots */
+
+#if defined(DUK_USE_VALSTACK_GROW_SHIFT)
+	/* New size is minimum size plus a proportional slack, e.g. shift of
+	 * 2 means a 25% slack.
+	 */
+	new_size = min_size + (min_size >> DUK_USE_VALSTACK_GROW_SHIFT);
+#else
+	/* New size is tight with no slack.  This is sometimes preferred in
+	 * low memory environments.
+	 */
+	new_size = min_size;
+#endif
+
+	if (DUK_UNLIKELY(new_size > DUK_USE_VALSTACK_LIMIT || new_size < min_size /*wrap*/)) {
 		/* Note: may be triggered even if minimal new_size would not reach the limit,
-		 * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account).
-		 */
-		if (throw_flag) {
+		 * plan limit accordingly.
+		 */
+		if (throw_on_error) {
 			DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT);
-		} else {
-			return 0;
-		}
-	}
-
-	/*
-	 *  When resizing the valstack, a mark-and-sweep may be triggered for
-	 *  the allocation of the new valstack.  If the mark-and-sweep needs
-	 *  to use our thread for something, it may cause *the same valstack*
-	 *  to be resized recursively.  This happens e.g. when mark-and-sweep
-	 *  finalizers are called.  This is taken into account carefully in
-	 *  duk__resize_valstack().
-	 *
-	 *  'new_size' is known to be <= valstack_max, which ensures that
-	 *  size_t and pointer arithmetic won't wrap in duk__resize_valstack().
-	 */
-
-	if (DUK_UNLIKELY(!duk__resize_valstack(ctx, new_size))) {
-		if (is_shrink) {
-			DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore"));
-			return 1;
-		}
-
-		DUK_DD(DUK_DDPRINT("valstack resize failed"));
-
-		if (throw_flag) {
+		}
+		return 0;
+	}
+
+	if (duk__resize_valstack(thr, new_size) == 0) {
+		if (throw_on_error) {
 			DUK_ERROR_ALLOC_FAILED(thr);
-		} else {
-			return 0;
-		}
-	}
-
-	DUK_DDD(DUK_DDDPRINT("valstack resize successful"));
-	return 1;
-}
-
-DUK_INTERNAL duk_bool_t duk_valstack_resize_raw(duk_context *ctx,
-                                                duk_size_t min_new_size,
-                                                duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_size_t old_size;
-
-	DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, "
-	                     "curr_bottom=%ld, flags=%lx",
-	                     (unsigned long) min_new_size,
-	                     (long) (thr->valstack_end - thr->valstack),
-	                     (long) (thr->valstack_top - thr->valstack),
-	                     (long) (thr->valstack_bottom - thr->valstack),
-	                     (unsigned long) flags));
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-#if defined(DUK_USE_PREFER_SIZE)
-	old_size = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-	DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size);
-	old_size = thr->valstack_size;
-#endif
-
-	if (DUK_LIKELY(min_new_size <= old_size)) {
-		if (DUK_LIKELY((flags & DUK_VSRESIZE_FLAG_SHRINK) == 0 ||
-		               old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD)) {
-			DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack"));
-			return 1;
-		}
-	}
-
-	return duk__valstack_do_resize(ctx, min_new_size, flags);
-}
-
-DUK_EXTERNAL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_size_t min_new_size;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	if (DUK_UNLIKELY(extra < 0)) {
-		/* Clamping to zero makes the API more robust to calling code
-		 * calculation errors.
-		 */
-		extra = 0;
-	}
-
-	min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
-	return duk_valstack_resize_raw(ctx,
-	                               min_new_size,         /* min_new_size */
-	                               0 /* no shrink */ |   /* flags */
-	                               0 /* no compact */ |
-	                               0 /* no throw */);
-}
-
-DUK_EXTERNAL void duk_require_stack(duk_context *ctx, duk_idx_t extra) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_size_t min_new_size;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-
-	if (DUK_UNLIKELY(extra < 0)) {
-		/* Clamping to zero makes the API more robust to calling code
-		 * calculation errors.
-		 */
-		extra = 0;
-	}
-
-	min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
-	(void) duk_valstack_resize_raw(ctx,
-	                               min_new_size,  /* min_new_size */
-	                               0 /* no shrink */ |   /* flags */
-	                               0 /* no compact */ |
-	                               DUK_VSRESIZE_FLAG_THROW);
-}
-
-DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top) {
-	duk_size_t min_new_size;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (DUK_UNLIKELY(top < 0)) {
-		/* Clamping to zero makes the API more robust to calling code
-		 * calculation errors.
-		 */
-		top = 0;
-	}
-
-	min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA;
-	return duk_valstack_resize_raw(ctx,
-	                               min_new_size,  /* min_new_size */
-	                               0 /* no shrink */ |   /* flags */
-	                               0 /* no compact */ |
-	                               0 /* no throw */);
-}
-
-DUK_EXTERNAL void duk_require_stack_top(duk_context *ctx, duk_idx_t top) {
-	duk_size_t min_new_size;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (DUK_UNLIKELY(top < 0)) {
-		/* Clamping to zero makes the API more robust to calling code
-		 * calculation errors.
-		 */
-		top = 0;
-	}
-
-	min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA;
-	(void) duk_valstack_resize_raw(ctx,
-	                               min_new_size,  /* min_new_size */
-	                               0 /* no shrink */ |   /* flags */
-	                               0 /* no compact */ |
-	                               DUK_VSRESIZE_FLAG_THROW);
+		}
+		return 0;
+	}
+
+	thr->valstack_end = thr->valstack + min_size;
+	DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end);
+
+	return 1;
+}
+
+/* Hot, inlined value stack grow check.  Because value stack almost never
+ * grows, the actual resize call is in a NOINLINE helper.
+ */
+DUK_INTERNAL DUK_INLINE void duk_valstack_grow_check_throw(duk_hthread *thr, duk_size_t min_bytes) {
+	duk_tval *tv;
+
+	tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes);
+	if (DUK_LIKELY(thr->valstack_end >= tv)) {
+		return;
+	}
+	if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) {
+		/* Values in [valstack_top,valstack_alloc_end[ are initialized
+		 * to 'undefined' so we can just move the end pointer.
+		 */
+		thr->valstack_end = tv;
+		return;
+	}
+	(void) duk__valstack_grow(thr, min_bytes, 1 /*throw_on_error*/);
+}
+
+/* Hot, inlined value stack grow check which doesn't throw. */
+DUK_INTERNAL DUK_INLINE duk_bool_t duk_valstack_grow_check_nothrow(duk_hthread *thr, duk_size_t min_bytes) {
+	duk_tval *tv;
+
+	tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + min_bytes);
+	if (DUK_LIKELY(thr->valstack_end >= tv)) {
+		return 1;
+	}
+	if (DUK_LIKELY(thr->valstack_alloc_end >= tv)) {
+		thr->valstack_end = tv;
+		return 1;
+	}
+	return duk__valstack_grow(thr, min_bytes, 0 /*throw_on_error*/);
+}
+
+/* Value stack shrink check, called from mark-and-sweep. */
+DUK_INTERNAL void duk_valstack_shrink_check_nothrow(duk_hthread *thr, duk_bool_t snug) {
+	duk_size_t alloc_bytes;
+	duk_size_t reserve_bytes;
+	duk_size_t shrink_bytes;
+
+	alloc_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack);
+	reserve_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
+	DUK_ASSERT(alloc_bytes >= reserve_bytes);
+
+	/* We're free to shrink the value stack allocation down to
+	 * reserve_bytes but not more.  If 'snug' (emergency GC)
+	 * shrink whatever we can.  Otherwise only shrink if the new
+	 * size would be considerably smaller.
+	 */
+
+#if defined(DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT)
+	if (snug) {
+		shrink_bytes = reserve_bytes;
+	} else {
+		duk_size_t proportion, slack;
+
+		/* Require that value stack shrinks by at least X% of its
+		 * current size.  For example, shift of 2 means at least
+		 * 25%.  The proportion is computed as bytes and may not
+		 * be a multiple of sizeof(duk_tval); that's OK here.
+		 */
+		proportion = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT;
+		if (alloc_bytes - reserve_bytes < proportion) {
+			/* Too little would be freed, do nothing. */
+			return;
+		}
+
+		/* Keep a slack after shrinking.  The slack is again a
+		 * proportion of the current size (the proportion should
+		 * of course be smaller than the check proportion above).
+		 */
+#if defined(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT)
+		DUK_ASSERT(DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT > DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT);
+		slack = alloc_bytes >> DUK_USE_VALSTACK_SHRINK_SLACK_SHIFT;
+#else
+		slack = 0;
+#endif
+		shrink_bytes = reserve_bytes +
+		               slack / sizeof(duk_tval) * sizeof(duk_tval);  /* multiple of duk_tval */
+	}
+#else  /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */
+	/* Always snug, useful in some low memory environments. */
+	DUK_UNREF(snug);
+	shrink_bytes = reserve_bytes;
+#endif  /* DUK_USE_VALSTACK_SHRINK_CHECK_SHIFT */
+
+	DUK_D(DUK_DPRINT("valstack shrink check: alloc_bytes=%ld, reserve_bytes=%ld, shrink_bytes=%ld (unvalidated)",
+	                 (long) alloc_bytes, (long) reserve_bytes, (long) shrink_bytes));
+	DUK_ASSERT(shrink_bytes >= reserve_bytes);
+	if (shrink_bytes >= alloc_bytes) {
+		/* Skip if shrink target is same as current one (or higher,
+		 * though that shouldn't happen in practice).
+		 */
+		return;
+	}
+	DUK_ASSERT(shrink_bytes / sizeof(duk_tval) * sizeof(duk_tval) == shrink_bytes);
+
+	DUK_D(DUK_DPRINT("valstack shrink check: decided to shrink, snug: %ld", (long) snug));
+
+	duk__resize_valstack(thr, shrink_bytes / sizeof(duk_tval));
+}
+
+DUK_EXTERNAL duk_bool_t duk_check_stack(duk_hthread *thr, duk_idx_t extra) {
+	duk_size_t min_new_bytes;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr != NULL);
+
+	if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) {
+		if (extra < 0) {
+			/* Clamping to zero makes the API more robust to calling code
+			 * calculation errors.
+			 */
+			extra = 0;
+		} else {
+			/* Cause grow check to fail without wrapping arithmetic. */
+			extra = DUK_USE_VALSTACK_LIMIT;
+		}
+	}
+
+	min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) +
+	                sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA);
+	return duk_valstack_grow_check_nothrow(thr, min_new_bytes);
+}
+
+DUK_EXTERNAL void duk_require_stack(duk_hthread *thr, duk_idx_t extra) {
+	duk_size_t min_new_bytes;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr != NULL);
+
+	if (DUK_UNLIKELY(extra < 0 || extra > DUK_USE_VALSTACK_LIMIT)) {
+		if (extra < 0) {
+			/* Clamping to zero makes the API more robust to calling code
+			 * calculation errors.
+			 */
+			extra = 0;
+		} else {
+			/* Cause grow check to fail without wrapping arithmetic. */
+			extra = DUK_USE_VALSTACK_LIMIT;
+		}
+	}
+
+	min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack) +
+	                sizeof(duk_tval) * ((duk_size_t) extra + DUK_VALSTACK_INTERNAL_EXTRA);
+	duk_valstack_grow_check_throw(thr, min_new_bytes);
+}
+
+DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_hthread *thr, duk_idx_t top) {
+	duk_size_t min_new_bytes;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) {
+		if (top < 0) {
+			/* Clamping to zero makes the API more robust to calling code
+			 * calculation errors.
+			 */
+			top = 0;
+		} else {
+			/* Cause grow check to fail without wrapping arithmetic. */
+			top = DUK_USE_VALSTACK_LIMIT;
+		}
+	}
+
+	DUK_ASSERT(top >= 0);
+	min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) +
+	                sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA);
+	return duk_valstack_grow_check_nothrow(thr, min_new_bytes);
+}
+
+DUK_EXTERNAL void duk_require_stack_top(duk_hthread *thr, duk_idx_t top) {
+	duk_size_t min_new_bytes;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (DUK_UNLIKELY(top < 0 || top > DUK_USE_VALSTACK_LIMIT)) {
+		if (top < 0) {
+			/* Clamping to zero makes the API more robust to calling code
+			 * calculation errors.
+			 */
+			top = 0;
+		} else {
+			/* Cause grow check to fail without wrapping arithmetic. */
+			top = DUK_USE_VALSTACK_LIMIT;
+		}
+	}
+
+	DUK_ASSERT(top >= 0);
+	min_new_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) +
+	                sizeof(duk_tval) * ((duk_size_t) top + DUK_VALSTACK_INTERNAL_EXTRA);
+	duk_valstack_grow_check_throw(thr, min_new_bytes);
 }
 
 /*
  *  Basic stack manipulation: swap, dup, insert, replace, etc
  */
 
-DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) {
+DUK_EXTERNAL void duk_swap(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
 	duk_tval *tv1;
 	duk_tval *tv2;
 	duk_tval tv_tmp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv1 = duk_require_tval(ctx, idx1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_require_tval(thr, idx1);
 	DUK_ASSERT(tv1 != NULL);
-	tv2 = duk_require_tval(ctx, idx2);
+	tv2 = duk_require_tval(thr, idx2);
 	DUK_ASSERT(tv2 != NULL);
 
 	/* If tv1==tv2 this is a NOP, no check is needed */
@@ -17702,22 +18188,20 @@
 	DUK_TVAL_SET_TVAL(tv2, &tv_tmp);
 }
 
-DUK_EXTERNAL void duk_swap_top(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_swap(ctx, idx, -1);
-}
-
-DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_idx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_swap_top(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_swap(thr, idx, -1);
+}
+
+DUK_EXTERNAL void duk_dup(duk_hthread *thr, duk_idx_t from_idx) {
 	duk_tval *tv_from;
 	duk_tval *tv_to;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 
-	tv_from = duk_require_tval(ctx, from_idx);
+	tv_from = duk_require_tval(thr, from_idx);
 	tv_to = thr->valstack_top++;
 	DUK_ASSERT(tv_from != NULL);
 	DUK_ASSERT(tv_to != NULL);
@@ -17725,16 +18209,14 @@
 	DUK_TVAL_INCREF(thr, tv_to);  /* no side effects */
 }
 
-DUK_EXTERNAL void duk_dup_top(duk_context *ctx) {
+DUK_EXTERNAL void duk_dup_top(duk_hthread *thr) {
 #if defined(DUK_USE_PREFER_SIZE)
-	duk_dup(ctx, -1);
-#else
-	duk_hthread *thr;
+	duk_dup(thr, -1);
+#else
 	duk_tval *tv_from;
 	duk_tval *tv_to;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 
 	if (DUK_UNLIKELY(thr->valstack_top - thr->valstack_bottom <= 0)) {
@@ -17750,36 +18232,42 @@
 #endif
 }
 
-DUK_INTERNAL void duk_dup_0(duk_context *ctx) {
-	duk_dup(ctx, 0);
-}
-DUK_INTERNAL void duk_dup_1(duk_context *ctx) {
-	duk_dup(ctx, 1);
-}
-DUK_INTERNAL void duk_dup_2(duk_context *ctx) {
-	duk_dup(ctx, 2);
-}
-DUK_INTERNAL void duk_dup_m2(duk_context *ctx) {
-	duk_dup(ctx, -2);
-}
-DUK_INTERNAL void duk_dup_m3(duk_context *ctx) {
-	duk_dup(ctx, -3);
-}
-DUK_INTERNAL void duk_dup_m4(duk_context *ctx) {
-	duk_dup(ctx, -4);
-}
-
-DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_idx) {
+DUK_INTERNAL void duk_dup_0(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, 0);
+}
+DUK_INTERNAL void duk_dup_1(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, 1);
+}
+DUK_INTERNAL void duk_dup_2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, 2);
+}
+DUK_INTERNAL void duk_dup_m2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, -2);
+}
+DUK_INTERNAL void duk_dup_m3(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, -3);
+}
+DUK_INTERNAL void duk_dup_m4(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_dup(thr, -4);
+}
+
+DUK_EXTERNAL void duk_insert(duk_hthread *thr, duk_idx_t to_idx) {
 	duk_tval *p;
 	duk_tval *q;
 	duk_tval tv_tmp;
 	duk_size_t nbytes;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	p = duk_require_tval(ctx, to_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	p = duk_require_tval(thr, to_idx);
 	DUK_ASSERT(p != NULL);
-	q = duk_require_tval(ctx, -1);
+	q = duk_require_tval(thr, -1);
 	DUK_ASSERT(q != NULL);
 
 	DUK_ASSERT(q >= p);
@@ -17809,21 +18297,43 @@
 	}
 }
 
-DUK_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_insert_undefined(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(idx >= 0);  /* Doesn't support negative indices. */
+
+	duk_push_undefined(thr);
+	duk_insert(thr, idx);
+}
+
+DUK_INTERNAL void duk_insert_undefined_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
+	duk_tval *tv, *tv_end;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(idx >= 0);  /* Doesn't support negative indices or count. */
+	DUK_ASSERT(count >= 0);
+
+	tv = duk_reserve_gap(thr, idx, count);
+	tv_end = tv + count;
+	while (tv != tv_end) {
+		DUK_TVAL_SET_UNDEFINED(tv);
+		tv++;
+	}
+}
+
+DUK_EXTERNAL void duk_replace(duk_hthread *thr, duk_idx_t to_idx) {
 	duk_tval *tv1;
 	duk_tval *tv2;
 	duk_tval tv_tmp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv1 = duk_require_tval(ctx, -1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_require_tval(thr, -1);
 	DUK_ASSERT(tv1 != NULL);
-	tv2 = duk_require_tval(ctx, to_idx);
+	tv2 = duk_require_tval(thr, to_idx);
 	DUK_ASSERT(tv2 != NULL);
 
 	/* For tv1 == tv2, both pointing to stack top, the end result
-	 * is same as duk_pop(ctx).
+	 * is same as duk_pop(thr).
 	 */
 	DUK_TVAL_SET_TVAL(&tv_tmp, tv2);
 	DUK_TVAL_SET_TVAL(tv2, tv1);
@@ -17832,25 +18342,22 @@
 	DUK_TVAL_DECREF(thr, &tv_tmp);  /* side effects */
 }
 
-DUK_EXTERNAL void duk_copy(duk_context *ctx, duk_idx_t from_idx, duk_idx_t to_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_copy(duk_hthread *thr, duk_idx_t from_idx, duk_idx_t to_idx) {
 	duk_tval *tv1;
 	duk_tval *tv2;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);  /* w/o refcounting */
-
-	tv1 = duk_require_tval(ctx, from_idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_require_tval(thr, from_idx);
 	DUK_ASSERT(tv1 != NULL);
-	tv2 = duk_require_tval(ctx, to_idx);
+	tv2 = duk_require_tval(thr, to_idx);
 	DUK_ASSERT(tv2 != NULL);
 
 	/* For tv1 == tv2, this is a no-op (no explicit check needed). */
 	DUK_TVAL_SET_TVAL_UPDREF(thr, tv2, tv1);  /* side effects */
 }
 
-DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_remove(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *p;
 	duk_tval *q;
 #if defined(DUK_USE_REFERENCE_COUNTING)
@@ -17858,11 +18365,11 @@
 #endif
 	duk_size_t nbytes;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	p = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	p = duk_require_tval(thr, idx);
 	DUK_ASSERT(p != NULL);
-	q = duk_require_tval(ctx, -1);
+	q = duk_require_tval(thr, -1);
 	DUK_ASSERT(q != NULL);
 
 	DUK_ASSERT(q >= p);
@@ -17889,17 +18396,74 @@
 #endif
 }
 
-DUK_INTERNAL_DECL void duk_remove_m2(duk_context *ctx) {
-	duk_remove(ctx, -2);
+DUK_INTERNAL void duk_remove_unsafe(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_remove(thr, idx);  /* XXX: no optimization for now */
+}
+
+DUK_INTERNAL void duk_remove_m2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_remove(thr, -2);
+}
+
+DUK_INTERNAL void duk_remove_n(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
+#if defined(DUK_USE_PREFER_SIZE)
+	/* XXX: maybe too slow even when preferring size? */
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(count >= 0);
+	DUK_ASSERT(idx >= 0);
+
+	while (count-- > 0) {
+		duk_remove(thr, idx);
+	}
+#else  /* DUK_USE_PREFER_SIZE */
+	duk_tval *tv_src;
+	duk_tval *tv_dst;
+	duk_tval *tv_newtop;
+	duk_tval *tv;
+	duk_size_t bytes;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(count >= 0);
+	DUK_ASSERT(idx >= 0);
+
+	tv_dst = thr->valstack_bottom + idx;
+	DUK_ASSERT(tv_dst <= thr->valstack_top);
+	tv_src = tv_dst + count;
+	DUK_ASSERT(tv_src <= thr->valstack_top);
+	bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src);
+
+	for (tv = tv_dst; tv < tv_src; tv++) {
+		DUK_TVAL_DECREF_NORZ(thr, tv);
+	}
+
+	DUK_MEMMOVE((void *) tv_dst, (const void *) tv_src, bytes);
+
+	tv_newtop = thr->valstack_top - count;
+	for (tv = tv_newtop; tv < thr->valstack_top; tv++) {
+		DUK_TVAL_SET_UNDEFINED(tv);
+	}
+	thr->valstack_top = tv_newtop;
+
+	/* When not preferring size, only NORZ macros are used; caller
+	 * is expected to DUK_REFZERO_CHECK().
+	 */
+#endif  /* DUK_USE_PREFER_SIZE */
+}
+
+DUK_INTERNAL void duk_remove_n_unsafe(duk_hthread *thr, duk_idx_t idx, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_remove_n(thr, idx, count);  /* XXX: no optimization for now */
 }
 
 /*
  *  Stack slice primitives
  */
 
-DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy) {
-	duk_hthread *to_thr = (duk_hthread *) to_ctx;
-	duk_hthread *from_thr = (duk_hthread *) from_ctx;
+DUK_EXTERNAL void duk_xcopymove_raw(duk_hthread *to_thr, duk_hthread *from_thr, duk_idx_t count, duk_bool_t is_copy) {
 	void *src;
 	duk_size_t nbytes;
 	duk_tval *p;
@@ -17907,23 +18471,25 @@
 
 	/* XXX: several pointer comparison issues here */
 
-	DUK_ASSERT_CTX_VALID(to_ctx);
-	DUK_ASSERT_CTX_VALID(from_ctx);
-	DUK_ASSERT(to_ctx != NULL);
-	DUK_ASSERT(from_ctx != NULL);
-
-	if (DUK_UNLIKELY(to_ctx == from_ctx)) {
+	DUK_ASSERT_API_ENTRY(to_thr);
+	DUK_ASSERT_CTX_VALID(to_thr);
+	DUK_ASSERT_CTX_VALID(from_thr);
+	DUK_ASSERT(to_thr->heap == from_thr->heap);
+
+	if (DUK_UNLIKELY(to_thr == from_thr)) {
 		DUK_ERROR_TYPE(to_thr, DUK_STR_INVALID_CONTEXT);
 		return;
 	}
-	if (DUK_UNLIKELY((count < 0) ||
-	                 (count > (duk_idx_t) to_thr->valstack_max))) {
-		/* Maximum value check ensures 'nbytes' won't wrap below. */
+	if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) DUK_USE_VALSTACK_LIMIT)) {
+		/* Maximum value check ensures 'nbytes' won't wrap below.
+		 * Also handles negative count.
+		 */
 		DUK_ERROR_RANGE_INVALID_COUNT(to_thr);
 		return;
 	}
-
-	nbytes = sizeof(duk_tval) * count;
+	DUK_ASSERT(count >= 0);
+
+	nbytes = sizeof(duk_tval) * (duk_size_t) count;
 	if (DUK_UNLIKELY(nbytes == 0)) {
 		return;
 	}
@@ -17936,7 +18502,7 @@
 		DUK_ERROR_RANGE_INVALID_COUNT(to_thr);
 	}
 
-	/* copy values (no overlap even if to_ctx == from_ctx; that's not
+	/* copy values (no overlap even if to_thr == from_thr; that's not
 	 * allowed now anyway)
 	 */
 	DUK_ASSERT(nbytes > 0);
@@ -17966,43 +18532,72 @@
 	}
 }
 
+/* Internal helper: reserve a gap of 'count' elements at 'idx_base' and return a
+ * pointer to the gap.  Values in the gap are garbage and MUST be initialized by
+ * the caller before any side effects may occur.  The caller must ensure there's
+ * enough stack reserve for 'count' values.
+ */
+DUK_INTERNAL duk_tval *duk_reserve_gap(duk_hthread *thr, duk_idx_t idx_base, duk_idx_t count) {
+	duk_tval *tv_src;
+	duk_tval *tv_dst;
+	duk_size_t gap_bytes;
+	duk_size_t copy_bytes;
+
+	/* Caller is responsible for ensuring there's enough preallocated
+	 * value stack.
+	 */
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(count >= 0);
+	DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack_top) >= (duk_size_t) count);
+
+	tv_src = thr->valstack_bottom + idx_base;
+	gap_bytes = (duk_size_t) count * sizeof(duk_tval);
+	tv_dst = (duk_tval *) (void *) ((duk_uint8_t *) tv_src + gap_bytes);
+	copy_bytes = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) tv_src);
+	thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack_top + gap_bytes);
+	DUK_MEMMOVE((void *) tv_dst, (const void *) tv_src, copy_bytes);
+
+	/* Values in the gap are left as garbage: caller must fill them in
+	 * and INCREF them before any side effects.
+	 */
+	return tv_src;
+}
+
 /*
  *  Get/opt/require
  */
 
-DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL void duk_require_undefined(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_UNDEFINED(tv))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "undefined", DUK_STR_NOT_UNDEFINED);
 	}
 }
 
-DUK_EXTERNAL void duk_require_null(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL void duk_require_null(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_NULL(tv))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "null", DUK_STR_NOT_NULL);
 	}
 }
 
-DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value) {
+DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
 	duk_bool_t ret;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_BOOLEAN(tv)) {
 		ret = DUK_TVAL_GET_BOOLEAN(tv);
@@ -18015,26 +18610,25 @@
 	return ret;
 }
 
-DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk__get_boolean_raw(ctx, idx, 0);  /* default: false */
-}
-
-DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk__get_boolean_raw(ctx, idx, def_value);
-}
-
-DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk__get_boolean_raw(thr, idx, 0);  /* default: false */
+}
+
+DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk__get_boolean_raw(thr, idx, def_value);
+}
+
+DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_bool_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_LIKELY(DUK_TVAL_IS_BOOLEAN(tv))) {
 		ret = DUK_TVAL_GET_BOOLEAN(tv);
@@ -18045,22 +18639,22 @@
 	}
 }
 
-DUK_EXTERNAL duk_bool_t duk_opt_boolean(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL duk_bool_t duk_opt_boolean(duk_hthread *thr, duk_idx_t idx, duk_bool_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_boolean(ctx, idx);
-}
-
-DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_context *ctx, duk_idx_t idx, duk_double_t def_value) {
+	return duk_require_boolean(thr, idx);
+}
+
+DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
 	duk_double_union ret;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 #if defined(DUK_USE_FASTINT)
 	if (DUK_TVAL_IS_FASTINT(tv)) {
@@ -18083,22 +18677,23 @@
 	return ret.d;
 }
 
-DUK_EXTERNAL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t idx) {
-	return duk__get_number_raw(ctx, idx, DUK_DOUBLE_NAN);  /* default: NaN */
-}
-
-DUK_EXTERNAL duk_double_t duk_get_number_default(duk_context *ctx, duk_idx_t idx, duk_double_t def_value) {
-	return duk__get_number_raw(ctx, idx, def_value);
-}
-
-DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_double_t duk_get_number(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_number_raw(thr, idx, DUK_DOUBLE_NAN);  /* default: NaN */
+}
+
+DUK_EXTERNAL duk_double_t duk_get_number_default(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_number_raw(thr, idx, def_value);
+}
+
+DUK_EXTERNAL duk_double_t duk_require_number(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_double_union ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_NUMBER(tv))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER);
@@ -18114,78 +18709,78 @@
 	return ret.d;
 }
 
-DUK_EXTERNAL duk_double_t duk_opt_number(duk_context *ctx, duk_idx_t idx, duk_double_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL duk_double_t duk_opt_number(duk_hthread *thr, duk_idx_t idx, duk_double_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		/* User provided default is not NaN normalized. */
 		return def_value;
 	}
-	return duk_require_number(ctx, idx);
-}
-
-DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_int_t duk_get_int_default(duk_context *ctx, duk_idx_t idx, duk_int_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_int_t) duk__api_coerce_d2i(ctx, idx, def_value, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, def_value, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 1 /*require*/);
-}
-
-DUK_EXTERNAL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 1 /*require*/);
-}
-
-DUK_EXTERNAL duk_int_t duk_opt_int(duk_context *ctx, duk_idx_t idx, duk_int_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+	return duk_require_number(thr, idx);
+}
+
+DUK_EXTERNAL duk_int_t duk_get_int(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_get_uint(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_get_int_default(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_int_t) duk__api_coerce_d2i(thr, idx, def_value, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, def_value, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_require_int(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 1 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_require_uint(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 1 /*require*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_opt_int(duk_hthread *thr, duk_idx_t idx, duk_int_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_int(ctx, idx);
-}
-
-DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+	return duk_require_int(thr, idx);
+}
+
+DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_hthread *thr, duk_idx_t idx, duk_uint_t def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_uint(ctx, idx);
-}
-
-DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
+	return duk_require_uint(thr, idx);
+}
+
+DUK_EXTERNAL const char *duk_get_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
 	duk_hstring *h;
 	const char *ret;
 	duk_size_t len;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hstring(thr, idx);
 	if (h != NULL) {
 		len = DUK_HSTRING_GET_BYTELEN(h);
 		ret = (const char *) DUK_HSTRING_GET_DATA(h);
@@ -18200,12 +18795,12 @@
 	return ret;
 }
 
-DUK_EXTERNAL const char *duk_require_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
+DUK_EXTERNAL const char *duk_require_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_require_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_require_hstring(thr, idx);
 	DUK_ASSERT(h != NULL);
 	if (out_len) {
 		*out_len = DUK_HSTRING_GET_BYTELEN(h);
@@ -18213,12 +18808,12 @@
 	return (const char *) DUK_HSTRING_GET_DATA(h);
 }
 
-DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
+DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_require_hstring_notsymbol(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_require_hstring_notsymbol(thr, idx);
 	DUK_ASSERT(h != NULL);
 	if (out_len) {
 		*out_len = DUK_HSTRING_GET_BYTELEN(h);
@@ -18226,12 +18821,12 @@
 	return (const char *) DUK_HSTRING_GET_DATA(h);
 }
 
-DUK_EXTERNAL const char *duk_get_string(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL const char *duk_get_string(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hstring(thr, idx);
 	if (h != NULL) {
 		return (const char *) DUK_HSTRING_GET_DATA(h);
 	} else {
@@ -18239,35 +18834,35 @@
 	}
 }
 
-DUK_EXTERNAL const char *duk_opt_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL const char *duk_opt_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		if (out_len != NULL) {
 			*out_len = def_len;
 		}
 		return def_ptr;
 	}
-	return duk_require_lstring(ctx, idx, out_len);
-}
-
-DUK_EXTERNAL const char *duk_opt_string(duk_context *ctx, duk_idx_t idx, const char *def_ptr) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+	return duk_require_lstring(thr, idx, out_len);
+}
+
+DUK_EXTERNAL const char *duk_opt_string(duk_hthread *thr, duk_idx_t idx, const char *def_ptr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_ptr;
 	}
-	return duk_require_string(ctx, idx);
-}
-
-DUK_EXTERNAL const char *duk_get_lstring_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) {
+	return duk_require_string(thr, idx);
+}
+
+DUK_EXTERNAL const char *duk_get_lstring_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) {
 	duk_hstring *h;
 	const char *ret;
 	duk_size_t len;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hstring(thr, idx);
 	if (h != NULL) {
 		len = DUK_HSTRING_GET_BYTELEN(h);
 		ret = (const char *) DUK_HSTRING_GET_DATA(h);
@@ -18282,12 +18877,12 @@
 	return ret;
 }
 
-DUK_EXTERNAL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, const char *def_value) {
+DUK_EXTERNAL const char *duk_get_string_default(duk_hthread *thr, duk_idx_t idx, const char *def_value) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hstring(thr, idx);
 	if (h != NULL) {
 		return (const char *) DUK_HSTRING_GET_DATA(h);
 	} else {
@@ -18295,12 +18890,12 @@
 	}
 }
 
-DUK_INTERNAL const char *duk_get_string_notsymbol(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL const char *duk_get_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hstring_notsymbol(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hstring_notsymbol(thr, idx);
 	if (h) {
 		return (const char *) DUK_HSTRING_GET_DATA(h);
 	} else {
@@ -18308,29 +18903,41 @@
 	}
 }
 
-DUK_EXTERNAL const char *duk_require_string(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk_require_lstring(ctx, idx, NULL);
-}
-
-DUK_INTERNAL const char *duk_require_string_notsymbol(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL const char *duk_require_string(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_require_lstring(thr, idx, NULL);
+}
+
+DUK_INTERNAL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_require_hstring_notsymbol(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_require_hstring_notsymbol(thr, idx);
 	DUK_ASSERT(h != NULL);
 	return (const char *) DUK_HSTRING_GET_DATA(h);
 }
 
-DUK_LOCAL void *duk__get_pointer_raw(duk_context *ctx, duk_idx_t idx, void *def_value) {
+DUK_EXTERNAL void duk_require_object(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
+	DUK_ASSERT(tv != NULL);
+	if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) {
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
+	}
+}
+
+DUK_LOCAL void *duk__get_pointer_raw(duk_hthread *thr, duk_idx_t idx, void *def_value) {
 	duk_tval *tv;
 	void *p;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (!DUK_TVAL_IS_POINTER(tv)) {
 		return def_value;
@@ -18340,34 +18947,35 @@
 	return p;
 }
 
-DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx) {
-	return duk__get_pointer_raw(ctx, idx, NULL /*def_value*/);
-}
-
-DUK_EXTERNAL void *duk_opt_pointer(duk_context *ctx, duk_idx_t idx, void *def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL void *duk_get_pointer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_pointer_raw(thr, idx, NULL /*def_value*/);
+}
+
+DUK_EXTERNAL void *duk_opt_pointer(duk_hthread *thr, duk_idx_t idx, void *def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_pointer(ctx, idx);
-}
-
-DUK_EXTERNAL void *duk_get_pointer_default(duk_context *ctx, duk_idx_t idx, void *def_value) {
-	return duk__get_pointer_raw(ctx, idx, def_value);
-}
-
-DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	return duk_require_pointer(thr, idx);
+}
+
+DUK_EXTERNAL void *duk_get_pointer_default(duk_hthread *thr, duk_idx_t idx, void *def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_pointer_raw(thr, idx, def_value);
+}
+
+DUK_EXTERNAL void *duk_require_pointer(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	void *p;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Note: here we must be wary of the fact that a pointer may be
 	 * valid and be a NULL.
 	 */
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_POINTER(tv))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "pointer", DUK_STR_NOT_POINTER);
@@ -18377,13 +18985,13 @@
 }
 
 #if 0  /*unused*/
-DUK_INTERNAL void *duk_get_voidptr(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL void *duk_get_voidptr(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_heaphdr *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
 		return NULL;
@@ -18395,21 +19003,19 @@
 }
 #endif
 
-DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL void *duk__get_buffer_helper(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag) {
 	duk_hbuffer *h;
 	void *ret;
 	duk_size_t len;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	if (out_size != NULL) {
 		*out_size = 0;
 	}
 
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_LIKELY(DUK_TVAL_IS_BUFFER(tv))) {
 		h = DUK_TVAL_GET_BUFFER(tv);
@@ -18431,34 +19037,34 @@
 	return ret;
 }
 
-DUK_EXTERNAL void *duk_get_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk__get_buffer_helper(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/);
-}
-
-DUK_EXTERNAL void *duk_opt_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL void *duk_get_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/);
+}
+
+DUK_EXTERNAL void *duk_opt_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		if (out_size != NULL) {
 			*out_size = def_size;
 		}
 		return def_ptr;
 	}
-	return duk_require_buffer(ctx, idx, out_size);
-}
-
-DUK_EXTERNAL void *duk_get_buffer_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk__get_buffer_helper(ctx, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/);
-}
-
-DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk__get_buffer_helper(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/);
+	return duk_require_buffer(thr, idx, out_size);
+}
+
+DUK_EXTERNAL void *duk_get_buffer_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk__get_buffer_helper(thr, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/);
+}
+
+DUK_EXTERNAL void *duk_require_buffer(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk__get_buffer_helper(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/);
 }
 
 /* Get the active buffer data area for a plain buffer or a buffer object.
@@ -18466,12 +19072,10 @@
  * have a NULL data pointer when its size is zero, the optional 'out_isbuffer'
  * argument allows caller to detect this reliably.
  */
-DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
+DUK_INTERNAL void *duk_get_buffer_data_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
 
 	if (out_isbuffer != NULL) {
 		*out_isbuffer = 0;
@@ -18480,7 +19084,7 @@
 		*out_size = def_size;
 	}
 
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_BUFFER(tv)) {
@@ -18529,28 +19133,31 @@
 	return def_ptr;
 }
 
-DUK_EXTERNAL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) {
-	return duk_get_buffer_data_raw(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL);
-}
-
-DUK_EXTERNAL void *duk_get_buffer_data_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
-	return duk_get_buffer_data_raw(ctx, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL);
-}
-
-DUK_EXTERNAL void *duk_opt_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL void *duk_get_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL);
+}
+
+DUK_EXTERNAL void *duk_get_buffer_data_default(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_get_buffer_data_raw(thr, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL);
+}
+
+DUK_EXTERNAL void *duk_opt_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		if (out_size != NULL) {
 			*out_size = def_size;
 		}
 		return def_ptr;
 	}
-	return duk_require_buffer_data(ctx, idx, out_size);
-}
-
-DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) {
-	return duk_get_buffer_data_raw(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/, NULL);
+	return duk_require_buffer_data(thr, idx, out_size);
+}
+
+DUK_EXTERNAL void *duk_require_buffer_data(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_get_buffer_data_raw(thr, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/, NULL);
 }
 
 /* Raw helper for getting a value from the stack, checking its tag.
@@ -18558,13 +19165,13 @@
  * tag in the packed representation.
  */
 
-DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t idx, duk_uint_t tag) {
+DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t tag) {
 	duk_tval *tv;
 	duk_heaphdr *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_GET_TAG(tv) != tag) {
 		return (duk_heaphdr *) NULL;
@@ -18576,121 +19183,161 @@
 
 }
 
-DUK_INTERNAL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t idx) {
-	return (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING);
-}
-
-DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_context *ctx, duk_idx_t idx) {
-	duk_hstring *res = (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING);
-	if (DUK_UNLIKELY(res && DUK_HSTRING_HAS_SYMBOL(res))) {
+DUK_INTERNAL duk_hstring *duk_get_hstring(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
+}
+
+DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) {
+	duk_hstring *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
+	if (DUK_UNLIKELY(h && DUK_HSTRING_HAS_SYMBOL(h))) {
 		return NULL;
 	}
-	return res;
-}
-
-DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t idx) {
-	duk_hstring *h;
-	h = (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING);
-	if (DUK_UNLIKELY(h == NULL)) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "string", DUK_STR_NOT_STRING);
-	}
 	return h;
 }
 
-DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL duk_hstring *duk_require_hstring(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
-	h = (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
+	if (DUK_UNLIKELY(h == NULL)) {
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING);
+	}
+	return h;
+}
+
+DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_hthread *thr, duk_idx_t idx) {
+	duk_hstring *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hstring *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_STRING);
 	if (DUK_UNLIKELY(h == NULL || DUK_HSTRING_HAS_SYMBOL(h))) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "string", DUK_STR_NOT_STRING);
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "string", DUK_STR_NOT_STRING);
 	}
 	return h;
 }
 
-DUK_INTERNAL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t idx) {
-	return (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
-}
-
-DUK_INTERNAL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL duk_hobject *duk_get_hobject(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
+}
+
+DUK_INTERNAL duk_hobject *duk_require_hobject(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *h;
-	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(h == NULL)) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "object", DUK_STR_NOT_OBJECT);
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
 	}
 	return h;
 }
 
-DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t idx) {
-	return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_BUFFER);
-}
-
-DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER);
+}
+
+DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_hthread *thr, duk_idx_t idx) {
 	duk_hbuffer *h;
-	h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_BUFFER);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_BUFFER);
 	if (DUK_UNLIKELY(h == NULL)) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "buffer", DUK_STR_NOT_BUFFER);
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER);
 	}
 	return h;
 }
 
-DUK_INTERNAL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t idx) {
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hthread *duk_get_hthread(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_THREAD(h))) {
 		h = NULL;
 	}
 	return (duk_hthread *) h;
 }
 
-DUK_INTERNAL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hthread *duk_require_hthread(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_THREAD(h)))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "thread", DUK_STR_NOT_THREAD);
 	}
 	return (duk_hthread *) h;
 }
 
-DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_context *ctx, duk_idx_t idx) {
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h))) {
 		h = NULL;
 	}
 	return (duk_hcompfunc *) h;
 }
 
-DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_COMPFUNC(h)))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "compiledfunction", DUK_STR_NOT_COMPFUNC);
 	}
 	return (duk_hcompfunc *) h;
 }
 
-DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_context *ctx, duk_idx_t idx) {
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_NATFUNC(h))) {
 		h = NULL;
 	}
 	return (duk_hnatfunc *) h;
 }
 
-DUK_INTERNAL duk_hnatfunc *duk_require_hnatfunc(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+DUK_INTERNAL duk_hnatfunc *duk_require_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
+	duk_hobject *h;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_IS_NATFUNC(h)))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC);
 	}
 	return (duk_hnatfunc *) h;
 }
 
-DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_c_function duk_get_c_function(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_hobject *h;
 	duk_hnatfunc *f;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) {
 		return NULL;
@@ -18707,21 +19354,21 @@
 	return f->func;
 }
 
-DUK_EXTERNAL duk_c_function duk_opt_c_function(duk_context *ctx, duk_idx_t idx, duk_c_function def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL duk_c_function duk_opt_c_function(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_c_function(ctx, idx);
-}
-
-DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value) {
+	return duk_require_c_function(thr, idx);
+}
+
+DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_hthread *thr, duk_idx_t idx, duk_c_function def_value) {
 	duk_c_function ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	ret = duk_get_c_function(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ret = duk_get_c_function(thr, idx);
 	if (ret != NULL) {
 		return ret;
 	}
@@ -18729,62 +19376,64 @@
 	return def_value;
 }
 
-DUK_EXTERNAL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_c_function duk_require_c_function(duk_hthread *thr, duk_idx_t idx) {
 	duk_c_function ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	ret = duk_get_c_function(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ret = duk_get_c_function(thr, idx);
 	if (DUK_UNLIKELY(!ret)) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "nativefunction", DUK_STR_NOT_NATFUNC);
 	}
 	return ret;
 }
 
-DUK_EXTERNAL void duk_require_function(duk_context *ctx, duk_idx_t idx) {
-	if (DUK_UNLIKELY(!duk_is_function(ctx, idx))) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, idx, "function", DUK_STR_NOT_FUNCTION);
-	}
-}
-
-DUK_INTERNAL_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL void duk_require_function(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	if (DUK_UNLIKELY(!duk_is_function(thr, idx))) {
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "function", DUK_STR_NOT_FUNCTION);
+	}
+}
+
+DUK_INTERNAL void duk_require_constructable(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *h;
 
-	h = duk_require_hobject_accept_mask(ctx, idx, DUK_TYPE_MASK_LIGHTFUNC);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_require_hobject_accept_mask(thr, idx, DUK_TYPE_MASK_LIGHTFUNC);
 	if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h))) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE);
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE);
 	}
 	/* Lightfuncs (h == NULL) are constructable. */
 }
 
-DUK_EXTERNAL duk_context *duk_get_context(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_context *) duk_get_hthread(ctx, idx);
-}
-
-DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_context *) duk_require_hthread(ctx, idx);
-}
-
-DUK_EXTERNAL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL duk_hthread *duk_get_context(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_get_hthread(thr, idx);
+}
+
+DUK_EXTERNAL duk_hthread *duk_require_context(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_require_hthread(thr, idx);
+}
+
+DUK_EXTERNAL duk_hthread *duk_opt_context(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_context(ctx, idx);
-}
-
-DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx_t idx, duk_context *def_value) {
-	duk_context *ret;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	ret = duk_get_context(ctx, idx);
+	return duk_require_context(thr, idx);
+}
+
+DUK_EXTERNAL duk_hthread *duk_get_context_default(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) {
+	duk_hthread *ret;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ret = duk_get_context(thr, idx);
 	if (ret != NULL) {
 		return ret;
 	}
@@ -18792,13 +19441,13 @@
 	return def_value;
 }
 
-DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL void *duk_get_heapptr(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	void *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) {
 		return (void *) NULL;
@@ -18809,21 +19458,21 @@
 	return ret;
 }
 
-DUK_EXTERNAL void *duk_opt_heapptr(duk_context *ctx, duk_idx_t idx, void *def_value) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
+DUK_EXTERNAL void *duk_opt_heapptr(duk_hthread *thr, duk_idx_t idx, void *def_value) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) {
 		return def_value;
 	}
-	return duk_require_heapptr(ctx, idx);
-}
-
-DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value) {
+	return duk_require_heapptr(thr, idx);
+}
+
+DUK_EXTERNAL void *duk_get_heapptr_default(duk_hthread *thr, duk_idx_t idx, void *def_value) {
 	void *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	ret = duk_get_heapptr(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ret = duk_get_heapptr(thr, idx);
 	if (ret != NULL) {
 		return ret;
 	}
@@ -18831,14 +19480,13 @@
 	return def_value;
 }
 
-DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void *duk_require_heapptr(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	void *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) {
 		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE);
@@ -18850,22 +19498,22 @@
 }
 
 /* Internal helper for getting/requiring a duk_hobject with possible promotion. */
-DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask) {
+DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
 	duk_uint_t val_mask;
 	duk_hobject *res;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	res = duk_get_hobject(ctx, idx);  /* common case, not promoted */
+	DUK_ASSERT_CTX_VALID(thr);
+
+	res = duk_get_hobject(thr, idx);  /* common case, not promoted */
 	if (DUK_LIKELY(res != NULL)) {
 		DUK_ASSERT(res != NULL);
 		return res;
 	}
 
-	val_mask = duk_get_type_mask(ctx, idx);
+	val_mask = duk_get_type_mask(thr, idx);
 	if (val_mask & type_mask) {
 		if (type_mask & DUK_TYPE_MASK_PROMOTE) {
-			res = duk_to_hobject(ctx, idx);
+			res = duk_to_hobject(thr, idx);
 			DUK_ASSERT(res != NULL);
 			return res;
 		} else {
@@ -18874,7 +19522,7 @@
 	}
 
 	if (type_mask & DUK_TYPE_MASK_THROW) {
-		DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, idx, "object", DUK_STR_NOT_OBJECT);
+		DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "object", DUK_STR_NOT_OBJECT);
 	}
 	return NULL;
 }
@@ -18886,48 +19534,49 @@
  * Return value is NULL if value is neither an object nor a plain type allowed
  * by the mask.
  */
-DUK_INTERNAL duk_hobject *duk_get_hobject_promote_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask) {
-	return duk__get_hobject_promote_mask_raw(ctx, idx, type_mask | DUK_TYPE_MASK_PROMOTE);
+DUK_INTERNAL duk_hobject *duk_get_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_PROMOTE);
 }
 
 /* Like duk_get_hobject_promote_mask() but throw a TypeError instead of
  * returning a NULL.
  */
-DUK_INTERNAL duk_hobject *duk_require_hobject_promote_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask) {
-	return duk__get_hobject_promote_mask_raw(ctx, idx, type_mask | DUK_TYPE_MASK_THROW | DUK_TYPE_MASK_PROMOTE);
+DUK_INTERNAL duk_hobject *duk_require_hobject_promote_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW | DUK_TYPE_MASK_PROMOTE);
 }
 
 /* Require a duk_hobject * at 'idx'; if the value is not an object but matches the
  * supplied 'type_mask', return a NULL instead.  Otherwise throw a TypeError.
  */
-DUK_INTERNAL duk_hobject *duk_require_hobject_accept_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t type_mask) {
-	return duk__get_hobject_promote_mask_raw(ctx, idx, type_mask | DUK_TYPE_MASK_THROW);
-}
-
-DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t idx, duk_small_uint_t classnum) {
+DUK_INTERNAL duk_hobject *duk_require_hobject_accept_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t type_mask) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__get_hobject_promote_mask_raw(thr, idx, type_mask | DUK_TYPE_MASK_THROW);
+}
+
+DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) {
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_DISABLE(classnum >= 0);  /* unsigned */
 	DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);
 
-	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum)) {
 		h = NULL;
 	}
 	return h;
 }
 
-DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_idx_t idx, duk_small_uint_t classnum) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t classnum) {
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_DISABLE(classnum >= 0);  /* unsigned */
 	DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);
-	thr = (duk_hthread *) ctx;
-
-	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT);
+
+	h = (duk_hobject *) duk__get_tagged_heaphdr_raw(thr, idx, DUK_TAG_OBJECT);
 	if (DUK_UNLIKELY(!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum))) {
 		duk_hstring *h_class;
 		h_class = DUK_HTHREAD_GET_STRING(thr, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum));
@@ -18938,12 +19587,12 @@
 	return h;
 }
 
-DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_size_t duk_get_length(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
@@ -18953,17 +19602,20 @@
 	case DUK_TAG_POINTER:
 		return 0;
 #if defined(DUK_USE_PREFER_SIZE)
-	/* All of these types (besides object) have a virtual, non-configurable
-	 * .length property which is within size_t range so we can just look it
-	 * up without specific type checks.
+	/* String and buffer have a virtual non-configurable .length property
+	 * which is within size_t range so it can be looked up without specific
+	 * type checks.  Lightfuncs inherit from %NativeFunctionPrototype%
+	 * which provides an inherited .length accessor; it could be overwritten
+	 * to produce unexpected types or values, but just number convert and
+	 * duk_size_t cast for now.
 	 */
 	case DUK_TAG_STRING:
 	case DUK_TAG_BUFFER:
 	case DUK_TAG_LIGHTFUNC: {
 		duk_size_t ret;
-		duk_get_prop_stridx(ctx, idx, DUK_STRIDX_LENGTH);
-		ret = (duk_size_t) duk_to_number_m1(ctx);
-		duk_pop(ctx);
+		duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
+		ret = (duk_size_t) duk_to_number_m1(thr);
+		duk_pop_unsafe(thr);
 		return ret;
 	}
 #else  /* DUK_USE_PREFER_SIZE */
@@ -18981,15 +19633,22 @@
 		return (duk_size_t) DUK_HBUFFER_GET_SIZE(h);
 	}
 	case DUK_TAG_LIGHTFUNC: {
-		duk_small_uint_t lf_flags;
-		lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
-		return (duk_size_t) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
+		/* We could look up the length from the lightfunc duk_tval,
+		 * but since Duktape 2.2 lightfunc .length comes from
+		 * %NativeFunctionPrototype% which can be overridden, so
+		 * look up the property explicitly.
+		 */
+		duk_size_t ret;
+		duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
+		ret = (duk_size_t) duk_to_number_m1(thr);
+		duk_pop_unsafe(thr);
+		return ret;
 	}
 #endif  /* DUK_USE_PREFER_SIZE */
 	case DUK_TAG_OBJECT: {
 		duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
 		DUK_ASSERT(h != NULL);
-		return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h);
+		return (duk_size_t) duk_hobject_get_length(thr, h);
 	}
 #if defined(DUK_USE_FASTINT)
 	case DUK_TAG_FASTINT:
@@ -19008,17 +19667,16 @@
  *
  *  Used internally when we're 100% sure that a certain index is valid and
  *  contains an object of a certain type.  For example, if we duk_push_object()
- *  we can then safely duk_known_hobject(ctx, -1).  These helpers just assert
+ *  we can then safely duk_known_hobject(thr, -1).  These helpers just assert
  *  for the index and type, and if the assumptions are not valid, memory unsafe
  *  behavior happens.
  */
 
-DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_heaphdr *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	if (idx < 0) {
 		tv = thr->valstack_top + idx;
 	} else {
@@ -19031,37 +19689,42 @@
 	return h;
 }
 
-DUK_INTERNAL duk_hstring *duk_known_hstring(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT(duk_get_hstring(ctx, idx) != NULL);
-	return (duk_hstring *) duk__known_heaphdr(ctx, idx);
-}
-
-DUK_INTERNAL duk_hobject *duk_known_hobject(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT(duk_get_hobject(ctx, idx) != NULL);
-	return (duk_hobject *) duk__known_heaphdr(ctx, idx);
-}
-
-DUK_INTERNAL duk_hbuffer *duk_known_hbuffer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT(duk_get_hbuffer(ctx, idx) != NULL);
-	return (duk_hbuffer *) duk__known_heaphdr(ctx, idx);
-}
-
-DUK_INTERNAL duk_hcompfunc *duk_known_hcompfunc(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT(duk_get_hcompfunc(ctx, idx) != NULL);
-	return (duk_hcompfunc *) duk__known_heaphdr(ctx, idx);
-}
-
-DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT(duk_get_hnatfunc(ctx, idx) != NULL);
-	return (duk_hnatfunc *) duk__known_heaphdr(ctx, idx);
-}
-
-DUK_EXTERNAL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_normalize_index(ctx, idx);
-	duk_push_uint(ctx, (duk_uint_t) len);
-	duk_put_prop_stridx(ctx, idx, DUK_STRIDX_LENGTH);
+DUK_INTERNAL duk_hstring *duk_known_hstring(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_hstring(thr, idx) != NULL);
+	return (duk_hstring *) duk__known_heaphdr(thr, idx);
+}
+
+DUK_INTERNAL duk_hobject *duk_known_hobject(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_hobject(thr, idx) != NULL);
+	return (duk_hobject *) duk__known_heaphdr(thr, idx);
+}
+
+DUK_INTERNAL duk_hbuffer *duk_known_hbuffer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_hbuffer(thr, idx) != NULL);
+	return (duk_hbuffer *) duk__known_heaphdr(thr, idx);
+}
+
+DUK_INTERNAL duk_hcompfunc *duk_known_hcompfunc(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_hcompfunc(thr, idx) != NULL);
+	return (duk_hcompfunc *) duk__known_heaphdr(thr, idx);
+}
+
+DUK_INTERNAL duk_hnatfunc *duk_known_hnatfunc(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_hnatfunc(thr, idx) != NULL);
+	return (duk_hnatfunc *) duk__known_heaphdr(thr, idx);
+}
+
+DUK_EXTERNAL void duk_set_length(duk_hthread *thr, duk_idx_t idx, duk_size_t len) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_normalize_index(thr, idx);
+	duk_push_uint(thr, (duk_uint_t) len);
+	duk_put_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
 }
 
 /*
@@ -19074,68 +19737,63 @@
 
 /* E5 Section 8.12.8 */
 
-DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_context *ctx, duk_idx_t idx, duk_small_int_t func_stridx) {
-	if (duk_get_prop_stridx(ctx, idx, func_stridx)) {
+DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t func_stridx) {
+	if (duk_get_prop_stridx(thr, idx, func_stridx)) {
 		/* [ ... func ] */
-		if (duk_is_callable(ctx, -1)) {
-			duk_dup(ctx, idx);         /* -> [ ... func this ] */
-			duk_call_method(ctx, 0);     /* -> [ ... retval ] */
-			if (duk_is_primitive(ctx, -1)) {
-				duk_replace(ctx, idx);
+		if (duk_is_callable(thr, -1)) {
+			duk_dup(thr, idx);         /* -> [ ... func this ] */
+			duk_call_method(thr, 0);     /* -> [ ... retval ] */
+			if (duk_is_primitive(thr, -1)) {
+				duk_replace(thr, idx);
 				return 1;
 			}
 			/* [ ... retval ]; popped below */
 		}
 	}
-	duk_pop(ctx);  /* [ ... func/retval ] -> [ ... ] */
-	return 0;
-}
-
-DUK_EXTERNAL void duk_to_undefined(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	tv = duk_require_tval(ctx, idx);
+	duk_pop_unsafe(thr);  /* [ ... func/retval ] -> [ ... ] */
+	return 0;
+}
+
+DUK_EXTERNAL void duk_to_undefined(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
 }
 
-DUK_EXTERNAL void duk_to_null(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	tv = duk_require_tval(ctx, idx);
+DUK_EXTERNAL void duk_to_null(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	DUK_TVAL_SET_NULL_UPDREF(thr, tv);  /* side effects */
 }
 
 /* E5 Section 9.1 */
-DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hint) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_to_primitive(duk_hthread *thr, duk_idx_t idx, duk_int_t hint) {
 	/* inline initializer for coercers[] is not allowed by old compilers like BCC */
-	duk_small_int_t coercers[2];
+	duk_small_uint_t coercers[2];
 	duk_small_uint_t class_number;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING);
 
-	idx = duk_require_normalize_index(ctx, idx);
-
-	if (!duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT |
+	idx = duk_require_normalize_index(thr, idx);
+
+	if (!duk_check_type_mask(thr, idx, DUK_TYPE_MASK_OBJECT |
 	                                   DUK_TYPE_MASK_LIGHTFUNC |
 	                                   DUK_TYPE_MASK_BUFFER)) {
 		/* Any other values stay as is. */
-		DUK_ASSERT(!duk_is_buffer(ctx, idx));  /* duk_to_string() relies on this behavior */
-		return;
-	}
-
-	class_number = duk_get_class_number(ctx, idx);
+		DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
+		return;
+	}
+
+	class_number = duk_get_class_number(thr, idx);
 
 	/* XXX: Symbol objects normally coerce via the ES2015-revised ToPrimitive()
 	 * algorithm which consults value[@@toPrimitive] and avoids calling
@@ -19150,11 +19808,11 @@
 		duk_hstring *h_str;
 
 		/* XXX: pretty awkward, index based API for internal value access? */
-		h_obj = duk_known_hobject(ctx, idx);
+		h_obj = duk_known_hobject(thr, idx);
 		h_str = duk_hobject_get_internal_value_string(thr->heap, h_obj);
 		if (h_str) {
-			duk_push_hstring(ctx, h_str);
-			duk_replace(ctx, idx);
+			duk_push_hstring(thr, h_str);
+			duk_replace(thr, idx);
 			return;
 		}
 	}
@@ -19183,13 +19841,13 @@
 		coercers[1] = DUK_STRIDX_VALUE_OF;
 	}
 
-	if (duk__defaultvalue_coerce_attempt(ctx, idx, coercers[0])) {
-		DUK_ASSERT(!duk_is_buffer(ctx, idx));  /* duk_to_string() relies on this behavior */
-		return;
-	}
-
-	if (duk__defaultvalue_coerce_attempt(ctx, idx, coercers[1])) {
-		DUK_ASSERT(!duk_is_buffer(ctx, idx));  /* duk_to_string() relies on this behavior */
+	if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[0])) {
+		DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
+		return;
+	}
+
+	if (duk__defaultvalue_coerce_attempt(thr, idx, coercers[1])) {
+		DUK_ASSERT(!duk_is_buffer(thr, idx));  /* duk_to_string() relies on this behavior */
 		return;
 	}
 
@@ -19197,16 +19855,14 @@
 }
 
 /* E5 Section 9.2 */
-DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_bool_t val;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	val = duk_js_toboolean(tv);
@@ -19218,43 +19874,64 @@
 	return val;
 }
 
-DUK_EXTERNAL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_double_t d;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* XXX: No need to normalize; the whole operation could be inlined here to
 	 * avoid 'tv' re-lookup.
 	 */
-	idx = duk_require_normalize_index(ctx, idx);
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+	idx = duk_require_normalize_index(thr, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	d = duk_js_tonumber(thr, tv);  /* XXX: fastint coercion? now result will always be a non-fastint */
 
 	/* ToNumber() may have side effects so must relookup 'tv'. */
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d);  /* side effects */
 	return d;
 }
 
-DUK_INTERNAL duk_double_t duk_to_number_m1(duk_context *ctx) {
-	return duk_to_number(ctx, -1);
-}
-DUK_INTERNAL duk_double_t duk_to_number_m2(duk_context *ctx) {
-	return duk_to_number(ctx, -2);
-}
-
-DUK_INTERNAL duk_double_t duk_to_number_tval(duk_context *ctx, duk_tval *tv) {
+DUK_INTERNAL duk_double_t duk_to_number_m1(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_to_number(thr, -1);
+}
+DUK_INTERNAL duk_double_t duk_to_number_m2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_to_number(thr, -2);
+}
+
+DUK_INTERNAL duk_double_t duk_to_number_tval(duk_hthread *thr, duk_tval *tv) {
+#if defined(DUK_USE_PREFER_SIZE)
 	duk_double_t res;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_push_tval(ctx, tv);
-	res = duk_to_number(ctx, -1);
-	duk_pop(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_push_tval(thr, tv);
+	res = duk_to_number_m1(thr);
+	duk_pop_unsafe(thr);
 	return res;
+#else
+	duk_double_t res;
+	duk_tval *tv_dst;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK__ASSERT_SPACE();
+
+	tv_dst = thr->valstack_top++;
+	DUK_TVAL_SET_TVAL(tv_dst, tv);
+	DUK_TVAL_INCREF(thr, tv_dst);  /* decref not necessary */
+	res = duk_to_number_m1(thr);  /* invalidates tv_dst */
+
+	tv_dst = --thr->valstack_top;
+	DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_dst));
+	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_dst));  /* plain number */
+	DUK_TVAL_SET_UNDEFINED(tv_dst);  /* valstack init policy */
+
+	return res;
+#endif
 }
 
 /* XXX: combine all the integer conversions: they share everything
@@ -19263,14 +19940,13 @@
 
 typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv);
 
-DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t idx, duk__toint_coercer coerce_func) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_hthread *thr, duk_idx_t idx, duk__toint_coercer coerce_func) {
 	duk_tval *tv;
 	duk_double_t d;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 #if defined(DUK_USE_FASTINT)
@@ -19287,93 +19963,92 @@
 	/* XXX: fastint? */
 
 	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
-	tv = duk_require_tval(ctx, idx);
+	tv = duk_require_tval(thr, idx);
 	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d);  /* side effects */
 	return d;
 }
 
-DUK_EXTERNAL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_int_t duk_to_int(duk_hthread *thr, duk_idx_t idx) {
 	/* Value coercion (in stack): ToInteger(), E5 Section 9.4,
 	 * API return value coercion: custom.
 	 */
-	DUK_ASSERT_CTX_VALID(ctx);
-	(void) duk__to_int_uint_helper(ctx, idx, duk_js_tointeger);
-	return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	(void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger);
+	return (duk_int_t) duk__api_coerce_d2i(thr, idx, 0 /*def_value*/, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_to_uint(duk_hthread *thr, duk_idx_t idx) {
 	/* Value coercion (in stack): ToInteger(), E5 Section 9.4,
 	 * API return value coercion: custom.
 	 */
-	DUK_ASSERT_CTX_VALID(ctx);
-	(void) duk__to_int_uint_helper(ctx, idx, duk_js_tointeger);
-	return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 0 /*require*/);
-}
-
-DUK_EXTERNAL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
+	(void) duk__to_int_uint_helper(thr, idx, duk_js_tointeger);
+	return (duk_uint_t) duk__api_coerce_d2ui(thr, idx, 0 /*def_value*/, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_int32_t duk_to_int32(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_int32_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	ret = duk_js_toint32(thr, tv);
 
 	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
-	tv = duk_require_tval(ctx, idx);
+	tv = duk_require_tval(thr, idx);
 	DUK_TVAL_SET_I32_UPDREF(thr, tv, ret);  /* side effects */
 	return ret;
 }
 
-DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_uint32_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	ret = duk_js_touint32(thr, tv);
 
 	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
-	tv = duk_require_tval(ctx, idx);
+	tv = duk_require_tval(thr, idx);
 	DUK_TVAL_SET_U32_UPDREF(thr, tv, ret);  /* side effects */
 	return ret;
 }
 
-DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_uint16_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	ret = duk_js_touint16(thr, tv);
 
 	/* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
-	tv = duk_require_tval(ctx, idx);
+	tv = duk_require_tval(thr, idx);
 	DUK_TVAL_SET_U32_UPDREF(thr, tv, ret);  /* side effects */
 	return ret;
 }
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 /* Special coercion for Uint8ClampedArray. */
-DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_hthread *thr, duk_idx_t idx) {
 	duk_double_t d;
 	duk_double_t t;
 	duk_uint8_t ret;
 
+	DUK_ASSERT_API_ENTRY(thr);
+
 	/* XXX: Simplify this algorithm, should be possible to come up with
 	 * a shorter and faster algorithm by inspecting IEEE representation
 	 * directly.
 	 */
 
-	d = duk_to_number(ctx, idx);
+	d = duk_to_number(thr, idx);
 	if (d <= 0.0) {
 		return 0;
 	} else if (d >= 255) {
@@ -19398,41 +20073,41 @@
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
-DUK_EXTERNAL const char *duk_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	(void) duk_to_string(ctx, idx);
-	DUK_ASSERT(duk_is_string(ctx, idx));
-	return duk_require_lstring(ctx, idx, out_len);
-}
-
-DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_context *ctx, void *udata) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL const char *duk_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	(void) duk_to_string(thr, idx);
+	DUK_ASSERT(duk_is_string(thr, idx));
+	return duk_require_lstring(thr, idx, out_len);
+}
+
+DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_hthread *thr, void *udata) {
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_UNREF(udata);
 
-	duk_to_string(ctx, -1);
-	return 1;
-}
-
-DUK_EXTERNAL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);
+	duk_to_string(thr, -1);
+	return 1;
+}
+
+DUK_EXTERNAL const char *duk_safe_to_lstring(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_len) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
 
 	/* We intentionally ignore the duk_safe_call() return value and only
 	 * check the output type.  This way we don't also need to check that
 	 * the returned value is indeed a string in the success case.
 	 */
 
-	duk_dup(ctx, idx);
-	(void) duk_safe_call(ctx, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
-	if (!duk_is_string(ctx, -1)) {
+	duk_dup(thr, idx);
+	(void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
+	if (!duk_is_string(thr, -1)) {
 		/* Error: try coercing error to string once. */
-		(void) duk_safe_call(ctx, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
-		if (!duk_is_string(ctx, -1)) {
+		(void) duk_safe_call(thr, duk__safe_to_string_raw, NULL /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
+		if (!duk_is_string(thr, -1)) {
 			/* Double error */
-			duk_pop(ctx);
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_ERROR);
+			duk_pop_unsafe(thr);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_UC_ERROR);
 		} else {
 			;
 		}
@@ -19440,50 +20115,49 @@
 		/* String; may be a symbol, accepted. */
 		;
 	}
-	DUK_ASSERT(duk_is_string(ctx, -1));
-
-	duk_replace(ctx, idx);
-	DUK_ASSERT(duk_get_string(ctx, idx) != NULL);
-	return duk_get_lstring(ctx, idx, out_len);
-}
-
-DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_context *ctx, duk_idx_t idx) {
+	DUK_ASSERT(duk_is_string(thr, -1));
+
+	duk_replace(thr, idx);
+	DUK_ASSERT(duk_get_string(thr, idx) != NULL);
+	return duk_get_lstring(thr, idx, out_len);
+}
+
+DUK_INTERNAL duk_hstring *duk_to_property_key_hstring(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_to_primitive(ctx, idx, DUK_HINT_STRING);  /* needed for e.g. Symbol objects */
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_to_primitive(thr, idx, DUK_HINT_STRING);  /* needed for e.g. Symbol objects */
+	h = duk_get_hstring(thr, idx);
 	if (h == NULL) {
 		/* The "is string?" check may seem unnecessary, but as things
 		 * are duk_to_hstring() invokes ToString() which fails for
 		 * symbols.  But since symbols are already strings for Duktape
 		 * C API, we check for that before doing the coercion.
 		 */
-		h = duk_to_hstring(ctx, idx);
+		h = duk_to_hstring(thr, idx);
 	}
 	DUK_ASSERT(h != NULL);
 	return h;
 }
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)  /* only needed by debugger for now */
-DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t idx) {
-	(void) duk_safe_to_string(ctx, idx);
-	DUK_ASSERT(duk_is_string(ctx, idx));
-	DUK_ASSERT(duk_get_hstring(ctx, idx) != NULL);
-	return duk_known_hstring(ctx, idx);
+DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	(void) duk_safe_to_string(thr, idx);
+	DUK_ASSERT(duk_is_string(thr, idx));
+	DUK_ASSERT(duk_get_hstring(thr, idx) != NULL);
+	return duk_known_hstring(thr, idx);
 }
 #endif
 
 /* Push Object.prototype.toString() output for 'tv'. */
-DUK_INTERNAL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv) {
-	duk_hthread *thr;
+DUK_INTERNAL void duk_push_class_string_tval(duk_hthread *thr, duk_tval *tv) {
 	duk_small_uint_t stridx;
 	duk_hstring *h_strclass;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
 	case DUK_TAG_UNUSED:  /* Treat like 'undefined', shouldn't happen. */
@@ -19555,21 +20229,20 @@
 	h_strclass = DUK_HTHREAD_GET_STRING(thr, stridx);
 	DUK_ASSERT(h_strclass != NULL);
 
-	duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
+	duk_push_sprintf(thr, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
 }
 
 /* XXX: other variants like uint, u32 etc */
-DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) {
 	duk_tval *tv;
 	duk_tval tv_tmp;
 	duk_double_t d, dmin, dmax;
 	duk_int_t res;
 	duk_bool_t clamped = 0;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_require_tval(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	d = duk_js_tointeger(thr, tv);  /* E5 Section 9.4, ToInteger() */
 
@@ -19591,7 +20264,7 @@
 	/* 'd' and 'res' agree here */
 
 	/* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
-	tv = duk_get_tval(ctx, idx);
+	tv = duk_get_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);  /* not popped by side effect */
 	DUK_TVAL_SET_TVAL(&tv_tmp, tv);
 #if defined(DUK_USE_FASTINT)
@@ -19622,40 +20295,42 @@
 	return res;
 }
 
-DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t idx, duk_idx_t minval, duk_idx_t maxval) {
+DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_hthread *thr, duk_idx_t idx, duk_idx_t minval, duk_idx_t maxval) {
 	duk_bool_t dummy;
-	return duk_to_int_clamped_raw(ctx, idx, minval, maxval, &dummy);
-}
-
-DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_context *ctx, duk_idx_t idx, duk_int_t minval, duk_int_t maxval) {
-	return duk_to_int_clamped_raw(ctx, idx, minval, maxval, NULL);  /* out_clamped==NULL -> RangeError if outside range */
-}
-
-DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_to_int_clamped_raw(thr, idx, minval, maxval, &dummy);
+}
+
+DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_hthread *thr, duk_idx_t idx, duk_int_t minval, duk_int_t maxval) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_to_int_clamped_raw(thr, idx, minval, maxval, NULL);  /* out_clamped==NULL -> RangeError if outside range */
+}
+
+DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
 	case DUK_TAG_UNDEFINED: {
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_UNDEFINED);
+		duk_push_hstring_stridx(thr, DUK_STRIDX_LC_UNDEFINED);
 		break;
 	}
 	case DUK_TAG_NULL: {
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
+		duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL);
 		break;
 	}
 	case DUK_TAG_BOOLEAN: {
 		if (DUK_TVAL_GET_BOOLEAN(tv)) {
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_TRUE);
-		} else {
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_FALSE);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_TRUE);
+		} else {
+			duk_push_hstring_stridx(thr, DUK_STRIDX_FALSE);
 		}
 		break;
 	}
@@ -19670,7 +20345,7 @@
 		h = DUK_TVAL_GET_STRING(tv);
 		DUK_ASSERT(h != NULL);
 		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
-			DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CANNOT_STRING_COERCE_SYMBOL);
+			DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_STRING_COERCE_SYMBOL);
 		} else {
 			goto skip_replace;
 		}
@@ -19686,27 +20361,27 @@
 		 * Symbol objects: duk_to_primitive() results in a plain symbol
 		 * value, and duk_to_string() then causes a TypeError.
 		 */
-		duk_to_primitive(ctx, idx, DUK_HINT_STRING);
-		DUK_ASSERT(!duk_is_buffer(ctx, idx));  /* ToPrimitive() must guarantee */
-		DUK_ASSERT(!duk_is_object(ctx, idx));
-		return duk_to_string(ctx, idx);  /* Note: recursive call */
+		duk_to_primitive(thr, idx, DUK_HINT_STRING);
+		DUK_ASSERT(!duk_is_buffer(thr, idx));  /* ToPrimitive() must guarantee */
+		DUK_ASSERT(!duk_is_object(thr, idx));
+		return duk_to_string(thr, idx);  /* Note: recursive call */
 	}
 	case DUK_TAG_POINTER: {
 		void *ptr = DUK_TVAL_GET_POINTER(tv);
 		if (ptr != NULL) {
-			duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) ptr);
+			duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) ptr);
 		} else {
 			/* Represent a null pointer as 'null' to be consistent with
 			 * the JX format variant.  Native '%p' format for a NULL
 			 * pointer may be e.g. '(nil)'.
 			 */
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_LC_NULL);
 		}
 		break;
 	}
 	case DUK_TAG_LIGHTFUNC: {
 		/* Should match Function.prototype.toString() */
-		duk_push_lightfunc_tostring(ctx, tv);
+		duk_push_lightfunc_tostring(thr, tv);
 		break;
 	}
 #if defined(DUK_USE_FASTINT)
@@ -19716,8 +20391,8 @@
 		/* number */
 		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
 		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-		duk_push_tval(ctx, tv);
-		duk_numconv_stringify(ctx,
+		duk_push_tval(thr, tv);
+		duk_numconv_stringify(thr,
 		                      10 /*radix*/,
 		                      0 /*precision:shortest*/,
 		                      0 /*force_exponential*/);
@@ -19725,34 +20400,39 @@
 	}
 	}
 
-	duk_replace(ctx, idx);
+	duk_replace(thr, idx);
 
  skip_replace:
-	DUK_ASSERT(duk_is_string(ctx, idx));
-	return duk_require_string(ctx, idx);
-}
-
-DUK_INTERNAL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t idx) {
+	DUK_ASSERT(duk_is_string(thr, idx));
+	return duk_require_string(thr, idx);
+}
+
+DUK_INTERNAL duk_hstring *duk_to_hstring(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *ret;
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_to_string(ctx, idx);
-	ret = duk_get_hstring(ctx, idx);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_to_string(thr, idx);
+	ret = duk_get_hstring(thr, idx);
 	DUK_ASSERT(ret != NULL);
 	return ret;
 }
 
-DUK_INTERNAL duk_hstring *duk_to_hstring_m1(duk_context *ctx) {
-	return duk_to_hstring(ctx, -1);
-}
-
-DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL duk_hstring *duk_to_hstring_m1(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_to_hstring(thr, -1);
+}
+
+DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *ret;
-	DUK_ASSERT_CTX_VALID(ctx);
-	ret = duk_get_hstring(ctx, idx);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ret = duk_get_hstring(thr, idx);
 	if (DUK_UNLIKELY(ret && DUK_HSTRING_HAS_SYMBOL(ret))) {
 		return ret;
 	}
-	return duk_to_hstring(ctx, idx);
+	return duk_to_hstring(thr, idx);
 }
 
 /* Convert a plain buffer or any buffer object into a string, using the buffer
@@ -19762,34 +20442,34 @@
  * string with the same bytes as in the buffer but rather (usually)
  * '[object ArrayBuffer]'.
  */
-DUK_EXTERNAL const char *duk_buffer_to_string(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL const char *duk_buffer_to_string(duk_hthread *thr, duk_idx_t idx) {
 	void *ptr_src;
 	duk_size_t len;
 	const char *res;
 
-	idx = duk_require_normalize_index(ctx, idx);
-
-	ptr_src = duk_require_buffer_data(ctx, idx, &len);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+
+	ptr_src = duk_require_buffer_data(thr, idx, &len);
 	DUK_ASSERT(ptr_src != NULL || len == 0);
 
-	res = duk_push_lstring(ctx, (const char *) ptr_src, len);
-	duk_replace(ctx, idx);
+	res = duk_push_lstring(thr, (const char *) ptr_src, len);
+	duk_replace(thr, idx);
 	return res;
 }
 
-DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, duk_uint_t mode) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void *duk_to_buffer_raw(duk_hthread *thr, duk_idx_t idx, duk_size_t *out_size, duk_uint_t mode) {
 	duk_hbuffer *h_buf;
 	const duk_uint8_t *src_data;
 	duk_size_t src_size;
 	duk_uint8_t *dst_data;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(thr);
-
-	idx = duk_require_normalize_index(ctx, idx);
-
-	h_buf = duk_get_hbuffer(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+
+	h_buf = duk_get_hbuffer(thr, idx);
 	if (h_buf != NULL) {
 		/* Buffer is kept as is, with the fixed/dynamic nature of the
 		 * buffer only changed if requested.  An external buffer
@@ -19818,10 +20498,10 @@
 		 * explicitly requested).  Symbols are rejected with a TypeError.
 		 * XXX: C API could maybe allow symbol-to-buffer coercion?
 		 */
-		src_data = (const duk_uint8_t *) duk_to_lstring(ctx, idx, &src_size);
-	}
-
-	dst_data = (duk_uint8_t *) duk_push_buffer(ctx, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/);
+		src_data = (const duk_uint8_t *) duk_to_lstring(thr, idx, &src_size);
+	}
+
+	dst_data = (duk_uint8_t *) duk_push_buffer(thr, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/);
 	if (DUK_LIKELY(src_size > 0)) {
 		/* When src_size == 0, src_data may be NULL (if source
 		 * buffer is dynamic), and dst_data may be NULL (if
@@ -19830,7 +20510,7 @@
 		 */
 		DUK_MEMCPY((void *) dst_data, (const void *) src_data, (size_t) src_size);
 	}
-	duk_replace(ctx, idx);
+	duk_replace(thr, idx);
  skip_copy:
 
 	if (out_size) {
@@ -19839,14 +20519,14 @@
 	return dst_data;
 }
 
-DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL void *duk_to_pointer(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	void *res;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
@@ -19884,12 +20564,12 @@
 		break;
 	}
 
-	duk_push_pointer(ctx, res);
-	duk_replace(ctx, idx);
+	duk_push_pointer(thr, res);
+	duk_replace(thr, idx);
 	return res;
 }
 
-DUK_LOCAL void duk__push_func_from_lightfunc(duk_context *ctx, duk_c_function func, duk_small_uint_t lf_flags) {
+DUK_LOCAL void duk__push_func_from_lightfunc(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) {
 	duk_idx_t nargs;
 	duk_uint_t flags = 0;   /* shared flags for a subset of types */
 	duk_small_uint_t lf_len;
@@ -19902,44 +20582,40 @@
 
 	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
 	        DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+	        DUK_HOBJECT_FLAG_CALLABLE |
 	        DUK_HOBJECT_FLAG_FASTREFS |
 	        DUK_HOBJECT_FLAG_NATFUNC |
 	        DUK_HOBJECT_FLAG_NEWENV |
 	        DUK_HOBJECT_FLAG_STRICT |
 	        DUK_HOBJECT_FLAG_NOTAIL |
-	        /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
 	        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
-	(void) duk__push_c_function_raw(ctx, func, nargs, flags);
+	(void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE);
 
 	lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
 	if ((duk_idx_t) lf_len != nargs) {
 		/* Explicit length is only needed if it differs from 'nargs'. */
-		duk_push_int(ctx, (duk_int_t) lf_len);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_int(thr, (duk_int_t) lf_len);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
 	}
 
 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
-	duk_push_lightfunc_name_raw(ctx, func, lf_flags);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
-#endif
-
-	nf = duk_known_hnatfunc(ctx, -1);
+	duk_push_lightfunc_name_raw(thr, func, lf_flags);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
+#endif
+
+	nf = duk_known_hnatfunc(thr, -1);
 	nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
-
-	/* Enable DUKFUNC exotic behavior once properties are set up. */
-	DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject *) nf);
-}
-
-DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+}
+
+DUK_EXTERNAL void duk_to_object(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_uint_t flags = 0;   /* shared flags for a subset of types */
 	duk_small_int_t proto = 0;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);
+	tv = DUK_GET_TVAL_POSIDX(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
@@ -20018,7 +20694,7 @@
 		duk_c_function func;
 
 		DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
-		duk__push_func_from_lightfunc(ctx, func, lf_flags);
+		duk__push_func_from_lightfunc(thr, func, lf_flags);
 		goto replace_value;
 	}
 #if defined(DUK_USE_FASTINT)
@@ -20034,11 +20710,11 @@
 		goto create_object;
 	}
 	}
-	DUK_ASSERT(duk_is_object(ctx, idx));
+	DUK_ASSERT(duk_is_object(thr, idx));
 	return;
 
  create_object:
-	(void) duk_push_object_helper(ctx, flags, proto);
+	(void) duk_push_object_helper(thr, flags, proto);
 
 	/* Note: Boolean prototype's internal value property is not writable,
 	 * but duk_xdef_prop_stridx() disregards the write protection.  Boolean
@@ -20047,19 +20723,21 @@
 	 * String and buffer special behaviors are already enabled which is not
 	 * ideal, but a write to the internal value is not affected by them.
 	 */
-	duk_dup(ctx, idx);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
+	duk_dup(thr, idx);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
 
  replace_value:
-	duk_replace(ctx, idx);
-	DUK_ASSERT(duk_is_object(ctx, idx));
-}
-
-DUK_INTERNAL duk_hobject *duk_to_hobject(duk_context *ctx, duk_idx_t idx) {
+	duk_replace(thr, idx);
+	DUK_ASSERT(duk_is_object(thr, idx));
+}
+
+DUK_INTERNAL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *ret;
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_to_object(ctx, idx);
-	ret = duk_known_hobject(ctx, idx);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_to_object(thr, idx);
+	ret = duk_known_hobject(thr, idx);
 	return ret;
 }
 
@@ -20067,20 +20745,20 @@
  *  Type checking
  */
 
-DUK_LOCAL duk_bool_t duk__tag_check(duk_context *ctx, duk_idx_t idx, duk_small_uint_t tag) {
-	duk_tval *tv;
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_LOCAL duk_bool_t duk__tag_check(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t tag) {
+	duk_tval *tv;
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	return (DUK_TVAL_GET_TAG(tv) == tag);
 }
 
-DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_context *ctx, duk_idx_t idx, duk_uint_t flag_mask) {
+DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_hthread *thr, duk_idx_t idx, duk_uint_t flag_mask) {
 	duk_hobject *obj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_get_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_get_hobject(thr, idx);
 	if (obj) {
 		return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0);
 	}
@@ -20126,19 +20804,19 @@
 #endif  /* DUK_USE_PACKED_TVAL */
 }
 
-DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_int_t duk_get_type(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	return duk_get_type_tval(tv);
 }
 
 #if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
-DUK_LOCAL const char *duk__type_names[] = {
+DUK_LOCAL const char * const duk__type_names[] = {
 	"none",
 	"undefined",
 	"null",
@@ -20151,22 +20829,26 @@
 	"lightfunc"
 };
 
-DUK_INTERNAL const char *duk_get_type_name(duk_context *ctx, duk_idx_t idx) {
+DUK_INTERNAL const char *duk_get_type_name(duk_hthread *thr, duk_idx_t idx) {
 	duk_int_t type_tag;
 
-	type_tag = duk_get_type(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	type_tag = duk_get_type(thr, idx);
 	DUK_ASSERT(type_tag >= DUK_TYPE_MIN && type_tag <= DUK_TYPE_MAX);
 	DUK_ASSERT(DUK_TYPE_MIN == 0 && sizeof(duk__type_names) / sizeof(const char *) == DUK_TYPE_MAX + 1);
 
 	return duk__type_names[type_tag];
 }
-#endif
-
-DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_context *ctx, duk_idx_t idx) {
+#endif  /* DUK_USE_VERBOSE_ERRORS && DUK_USE_PARANOID_ERRORS */
+
+DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_hobject *obj;
 
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
@@ -20186,10 +20868,10 @@
 	}
 }
 
-DUK_EXTERNAL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t idx, duk_int_t type) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return (duk_get_type(ctx, idx) == type) ? 1 : 0;
+DUK_EXTERNAL duk_bool_t duk_check_type(duk_hthread *thr, duk_idx_t idx, duk_int_t type) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return (duk_get_type(thr, idx) == type) ? 1 : 0;
 }
 
 DUK_INTERNAL duk_uint_t duk_get_type_mask_tval(duk_tval *tv) {
@@ -20227,27 +20909,25 @@
 #else  /* DUK_USE_PACKED_TVAL */
 	DUK_ASSERT(DUK_TVAL_IS_VALID_TAG(tv));
 	DUK_ASSERT(sizeof(duk__type_mask_from_tag) / sizeof(duk_uint_t) == DUK_TAG_MAX - DUK_TAG_MIN + 1);
-	return (duk_int_t) duk__type_mask_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN];
+	return duk__type_mask_from_tag[DUK_TVAL_GET_TAG(tv) - DUK_TAG_MIN];
 #endif  /* DUK_USE_PACKED_TVAL */
 }
 
-DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	return duk_get_type_mask_tval(tv);
 }
 
-DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t idx, duk_uint_t mask) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	if (DUK_LIKELY(duk_get_type_mask(ctx, idx) & mask)) {
+DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_hthread *thr, duk_idx_t idx, duk_uint_t mask) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	if (DUK_LIKELY((duk_get_type_mask(thr, idx) & mask) != 0U)) {
 		return 1;
 	}
 	if (mask & DUK_TYPE_MASK_THROW) {
@@ -20257,25 +20937,25 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_UNDEFINED);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_NULL);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_BOOLEAN);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_UNDEFINED);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_null(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_NULL);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_BOOLEAN);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_number(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/*
 	 *  Number is special because it doesn't have a specific
@@ -20284,12 +20964,12 @@
 
 	/* XXX: shorter version for unpacked representation? */
 
-	tv = duk_get_tval_or_unused(ctx, idx);
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	return DUK_TVAL_IS_NUMBER(tv);
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_bool_t duk_is_nan(duk_hthread *thr, duk_idx_t idx) {
 	/* XXX: This will now return false for non-numbers, even though they would
 	 * coerce to NaN (as a general rule).  In particular, duk_get_number()
 	 * returns a NaN for non-numbers, so should this function also return
@@ -20298,45 +20978,45 @@
 
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 
 	/* XXX: for packed duk_tval an explicit "is number" check is unnecessary */
 	if (!DUK_TVAL_IS_NUMBER(tv)) {
 		return 0;
 	}
-	return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv));
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_STRING);
-}
-
-DUK_INTERNAL duk_bool_t duk_is_string_notsymbol(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk_get_hstring_notsymbol(ctx, idx) != NULL;
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_OBJECT);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_BUFFER);
+	return (duk_bool_t) DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv));
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_string(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_STRING);
+}
+
+DUK_INTERNAL duk_bool_t duk_is_string_notsymbol(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_get_hstring_notsymbol(thr, idx) != NULL;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_object(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_OBJECT);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_BUFFER);
 }
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_BUFFER(tv)) {
 		return 1;
@@ -20350,29 +21030,29 @@
 	return 0;
 }
 #else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
-DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	return duk_is_buffer(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	return duk_is_buffer(thr, idx);
 }
 
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
-DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_POINTER);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__tag_check(ctx, idx, DUK_TAG_LIGHTFUNC);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_POINTER);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__tag_check(thr, idx, DUK_TAG_LIGHTFUNC);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	h = duk_get_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+	h = duk_get_hstring(thr, idx);
 	/* Use DUK_LIKELY() here because caller may be more likely to type
 	 * check an expected symbol than not.
 	 */
@@ -20382,73 +21062,110 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_bool_t duk_is_array(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *obj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_get_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_get_hobject(thr, idx);
 	if (obj) {
 		return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0);
 	}
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_function(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		duk_hobject *h;
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0;
+	}
 	if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
 		return 1;
 	}
-	return duk__obj_flag_any_default_false(ctx,
-	                                       idx,
-	                                       DUK_HOBJECT_FLAG_COMPFUNC |
-	                                       DUK_HOBJECT_FLAG_NATFUNC |
-	                                       DUK_HOBJECT_FLAG_BOUNDFUNC);
-}
-
-DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__obj_flag_any_default_false(ctx,
+	return 0;
+}
+
+DUK_INTERNAL duk_bool_t duk_is_callable_tval(duk_hthread *thr, duk_tval *tv) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_UNREF(thr);
+
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		duk_hobject *h;
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		return DUK_HOBJECT_HAS_CALLABLE(h) ? 1 : 0;
+	}
+	if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+		return 1;
+	}
+	return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_constructable(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		duk_hobject *h;
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		return DUK_HOBJECT_HAS_CONSTRUCTABLE(h) ? 1 : 0;
+	}
+	if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+		return 1;
+	}
+	return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__obj_flag_any_default_false(thr,
 	                                       idx,
 	                                       DUK_HOBJECT_FLAG_NATFUNC);
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__obj_flag_any_default_false(ctx,
+DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__obj_flag_any_default_false(thr,
 	                                       idx,
 	                                       DUK_HOBJECT_FLAG_COMPFUNC);
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__obj_flag_any_default_false(ctx,
+DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__obj_flag_any_default_false(thr,
 	                                       idx,
 	                                       DUK_HOBJECT_FLAG_BOUNDFUNC);
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx) {
+DUK_EXTERNAL duk_bool_t duk_is_thread(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *obj;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	obj = duk_get_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	obj = duk_get_hobject(thr, idx);
 	if (obj) {
 		return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_THREAD ? 1 : 0);
 	}
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_BUFFER(tv)) {
 		duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
@@ -20458,12 +21175,12 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_BUFFER(tv)) {
 		duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
@@ -20473,12 +21190,12 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx) {
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv = duk_get_tval_or_unused(ctx, idx);
+DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_get_tval_or_unused(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_BUFFER(tv)) {
 		duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
@@ -20488,20 +21205,22 @@
 	return 0;
 }
 
-DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_hthread *thr, duk_idx_t idx) {
 	duk_hobject *h;
 	duk_uint_t sanity;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_get_hobject(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_get_hobject(thr, idx);
 
 	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
 	do {
 		if (!h) {
 			return DUK_ERR_NONE;
 		}
+
+		/* XXX: something more convenient? */
+
 		if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) {
 			return DUK_ERR_EVAL_ERROR;
 		}
@@ -20534,24 +21253,21 @@
  *  Pushers
  */
 
-DUK_INTERNAL void duk_push_tval(duk_context *ctx, duk_tval *tv) {
-	duk_hthread *thr;
+DUK_INTERNAL void duk_push_tval(duk_hthread *thr, duk_tval *tv) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(tv != NULL);
-	thr = (duk_hthread *) ctx;
+
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_TVAL(tv_slot, tv);
 	DUK_TVAL_INCREF(thr, tv);  /* no side effects */
 }
 
-DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) {
-	duk_hthread *thr;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_push_undefined(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
 	DUK__CHECK_SPACE();
 
 	/* Because value stack init policy is 'undefined above top',
@@ -20561,60 +21277,50 @@
 	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
 }
 
-DUK_EXTERNAL void duk_push_null(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_null(duk_hthread *thr) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_NULL(tv_slot);
 }
 
-DUK_EXTERNAL void duk_push_boolean(duk_context *ctx, duk_bool_t val) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_boolean(duk_hthread *thr, duk_bool_t val) {
 	duk_tval *tv_slot;
 	duk_small_int_t b;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	b = (val ? 1 : 0);  /* ensure value is 1 or 0 (not other non-zero) */
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_BOOLEAN(tv_slot, b);
 }
 
-DUK_EXTERNAL void duk_push_true(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_true(duk_hthread *thr) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot);
 }
 
-DUK_EXTERNAL void duk_push_false(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_false(duk_hthread *thr) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot);
 }
 
 /* normalize NaN which may not match our canonical internal NaN */
-DUK_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_number(duk_hthread *thr, duk_double_t val) {
 	duk_tval *tv_slot;
 	duk_double_union du;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	du.d = val;
 	DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
@@ -20622,13 +21328,11 @@
 	DUK_TVAL_SET_NUMBER(tv_slot, du.d);
 }
 
-DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) {
+DUK_EXTERNAL void duk_push_int(duk_hthread *thr, duk_int_t val) {
 #if defined(DUK_USE_FASTINT)
-	duk_hthread *thr;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 #if DUK_INT_MAX <= 0x7fffffffL
@@ -20642,12 +21346,10 @@
 	}
 #endif
 #else  /* DUK_USE_FASTINT */
-	duk_hthread *thr;
 	duk_tval *tv_slot;
 	duk_double_t d;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	d = (duk_double_t) val;
 	tv_slot = thr->valstack_top++;
@@ -20655,13 +21357,11 @@
 #endif  /* DUK_USE_FASTINT */
 }
 
-DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) {
+DUK_EXTERNAL void duk_push_uint(duk_hthread *thr, duk_uint_t val) {
 #if defined(DUK_USE_FASTINT)
-	duk_hthread *thr;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 #if DUK_UINT_MAX <= 0xffffffffUL
@@ -20676,12 +21376,10 @@
 	}
 #endif
 #else  /* DUK_USE_FASTINT */
-	duk_hthread *thr;
 	duk_tval *tv_slot;
 	duk_double_t d;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	d = (duk_double_t) val;
 	tv_slot = thr->valstack_top++;
@@ -20689,13 +21387,11 @@
 #endif  /* DUK_USE_FASTINT */
 }
 
-DUK_EXTERNAL void duk_push_nan(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_nan(duk_hthread *thr) {
 	duk_tval *tv_slot;
 	duk_double_union du;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	DUK_DBLUNION_SET_NAN(&du);
 	DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
@@ -20703,12 +21399,11 @@
 	DUK_TVAL_SET_NUMBER(tv_slot, du.d);
 }
 
-DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL const char *duk_push_lstring(duk_hthread *thr, const char *str, duk_size_t len) {
 	duk_hstring *h;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* check stack before interning (avoid hanging temp) */
 	DUK__CHECK_SPACE();
@@ -20737,52 +21432,47 @@
 	return (const char *) DUK_HSTRING_GET_DATA(h);
 }
 
-DUK_EXTERNAL const char *duk_push_string(duk_context *ctx, const char *str) {
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL const char *duk_push_string(duk_hthread *thr, const char *str) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	if (str) {
-		return duk_push_lstring(ctx, str, DUK_STRLEN(str));
-	} else {
-		duk_push_null(ctx);
+		return duk_push_lstring(thr, str, DUK_STRLEN(str));
+	} else {
+		duk_push_null(thr);
 		return NULL;
 	}
 }
 
-DUK_EXTERNAL void duk_push_pointer(duk_context *ctx, void *val) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_pointer(duk_hthread *thr, void *val) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__CHECK_SPACE();
 	tv_slot = thr->valstack_top++;
 	DUK_TVAL_SET_POINTER(tv_slot, val);
 }
 
-DUK_INTERNAL duk_hstring *duk_push_uint_to_hstring(duk_context *ctx, duk_uint_t i) {
+DUK_INTERNAL duk_hstring *duk_push_uint_to_hstring(duk_hthread *thr, duk_uint_t i) {
 	duk_hstring *h_tmp;
 
+	DUK_ASSERT_API_ENTRY(thr);
+
 	/* XXX: this could be a direct DUK_SPRINTF to a buffer followed by duk_push_string() */
-	duk_push_uint(ctx, (duk_uint_t) i);
-	h_tmp = duk_to_hstring_m1(ctx);
+	duk_push_uint(thr, (duk_uint_t) i);
+	h_tmp = duk_to_hstring_m1(thr);
 	DUK_ASSERT(h_tmp != NULL);
 	return h_tmp;
 }
 
-DUK_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t check_object_coercible) {
-	duk_hthread *thr;
+DUK_LOCAL void duk__push_this_helper(duk_hthread *thr, duk_small_uint_t check_object_coercible) {
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);  /* avoid warning (unsigned) */
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
 	DUK__CHECK_SPACE();
 
 	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* because of valstack init policy */
 	tv_slot = thr->valstack_top++;
 
-	if (DUK_UNLIKELY(thr->callstack_top == 0)) {
+	if (DUK_UNLIKELY(thr->callstack_curr == NULL)) {
 		if (check_object_coercible) {
 			goto type_error;
 		}
@@ -20808,132 +21498,119 @@
 	DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE);
 }
 
-DUK_EXTERNAL void duk_push_this(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__push_this_helper(ctx, 0 /*check_object_coercible*/);
-}
-
-DUK_INTERNAL void duk_push_this_check_object_coercible(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
-}
-
-DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx) {
+DUK_EXTERNAL void duk_push_this(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__push_this_helper(thr, 0 /*check_object_coercible*/);
+}
+
+DUK_INTERNAL void duk_push_this_check_object_coercible(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__push_this_helper(thr, 1 /*check_object_coercible*/);
+}
+
+DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr) {
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
-	h = duk_to_hobject(ctx, -1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__push_this_helper(thr, 1 /*check_object_coercible*/);
+	h = duk_to_hobject(thr, -1);
 	DUK_ASSERT(h != NULL);
 	return h;
 }
 
-DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
-	return duk_to_hstring_m1(ctx);  /* This will reject all Symbol values; accepts Symbol objects. */
-}
-
-DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx) {
-	duk_hthread *thr;
-
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__push_this_helper(thr, 1 /*check_object_coercible*/);
+	return duk_to_hstring_m1(thr);  /* This will reject all Symbol values; accepts Symbol objects. */
+}
+
+DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK_ASSERT(thr->callstack_top > 0);  /* caller required to know */
-	DUK_ASSERT(thr->callstack_curr != NULL);
+	DUK_ASSERT(thr->callstack_curr != NULL);  /* caller required to know */
 	DUK_ASSERT(thr->valstack_bottom > thr->valstack);  /* consequence of above */
 	DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack);  /* 'this' binding exists */
 
 	return thr->valstack_bottom - 1;
 }
 
-DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_push_current_function(duk_hthread *thr) {
 	duk_activation *act;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
-	DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	act = thr->callstack_curr;
 	if (act != NULL) {
-		duk_push_tval(ctx, &act->tv_func);
-	} else {
-		duk_push_undefined(ctx);
-	}
-}
-
-DUK_EXTERNAL void duk_push_current_thread(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
+		duk_push_tval(thr, &act->tv_func);
+	} else {
+		duk_push_undefined(thr);
+	}
+}
+
+DUK_EXTERNAL void duk_push_current_thread(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
 
 	if (thr->heap->curr_thread) {
-		duk_push_hobject(ctx, (duk_hobject *) thr->heap->curr_thread);
-	} else {
-		duk_push_undefined(ctx);
-	}
-}
-
-DUK_EXTERNAL void duk_push_global_object(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL);
+		duk_push_hobject(thr, (duk_hobject *) thr->heap->curr_thread);
+	} else {
+		duk_push_undefined(thr);
+	}
+}
+
+DUK_EXTERNAL void duk_push_global_object(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL);
 }
 
 /* XXX: size optimize */
-DUK_LOCAL void duk__push_stash(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE)) {
+DUK_LOCAL void duk__push_stash(duk_hthread *thr) {
+	if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE)) {
 		DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
-		duk_pop(ctx);
-		duk_push_bare_object(ctx);
-		duk_dup_top(ctx);
-		duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C);  /* [ ... parent stash stash ] -> [ ... parent stash ] */
-	}
-	duk_remove_m2(ctx);
-}
-
-DUK_EXTERNAL void duk_push_heap_stash(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+		duk_pop_unsafe(thr);
+		duk_push_bare_object(thr);
+		duk_dup_top(thr);
+		duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C);  /* [ ... parent stash stash ] -> [ ... parent stash ] */
+	}
+	duk_remove_m2(thr);
+}
+
+DUK_EXTERNAL void duk_push_heap_stash(duk_hthread *thr) {
 	duk_heap *heap;
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	heap = thr->heap;
 	DUK_ASSERT(heap->heap_object != NULL);
-	duk_push_hobject(ctx, heap->heap_object);
-	duk__push_stash(ctx);
-}
-
-DUK_EXTERNAL void duk_push_global_stash(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_push_global_object(ctx);
-	duk__push_stash(ctx);
-}
-
-DUK_EXTERNAL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	DUK_ASSERT_CTX_VALID(ctx);
-	if (DUK_UNLIKELY(target_ctx == NULL)) {
+	duk_push_hobject(thr, heap->heap_object);
+	duk__push_stash(thr);
+}
+
+DUK_EXTERNAL void duk_push_global_stash(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_push_global_object(thr);
+	duk__push_stash(thr);
+}
+
+DUK_EXTERNAL void duk_push_thread_stash(duk_hthread *thr, duk_hthread *target_thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	if (DUK_UNLIKELY(target_thr == NULL)) {
 		DUK_ERROR_TYPE_INVALID_ARGS(thr);
 		return;  /* not reached */
 	}
-	duk_push_hobject(ctx, (duk_hobject *) target_ctx);
-	duk__push_stash(ctx);
+	duk_push_hobject(thr, (duk_hobject *) target_thr);
+	duk__push_stash(thr);
 }
 
 /* XXX: duk_ssize_t would be useful here */
-DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size_t sz, const char *fmt, va_list ap) {
+DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_hthread *thr, void *buf, duk_size_t sz, const char *fmt, va_list ap) {
 	duk_int_t len;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_UNREF(thr);
 
 	/* NUL terminator handling doesn't matter here */
 	len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap);
@@ -20946,8 +21623,7 @@
 	return -1;
 }
 
-DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL const char *duk_push_vsprintf(duk_hthread *thr, const char *fmt, va_list ap) {
 	duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE];
 	duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
 	duk_bool_t pushed_buf = 0;
@@ -20955,13 +21631,13 @@
 	duk_int_t len;  /* XXX: duk_ssize_t */
 	const char *res;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* special handling of fmt==NULL */
 	if (!fmt) {
 		duk_hstring *h_str;
-		duk_push_hstring_empty(ctx);
-		h_str = duk_known_hstring(ctx, -1);
+		duk_push_hstring_empty(thr);
+		h_str = duk_known_hstring(thr, -1);
 		return (const char *) DUK_HSTRING_GET_DATA(h_str);
 	}
 
@@ -20982,14 +21658,14 @@
 			buf = stack_buf;
 		} else if (!pushed_buf) {
 			pushed_buf = 1;
-			buf = duk_push_dynamic_buffer(ctx, sz);
-		} else {
-			buf = duk_resize_buffer(ctx, -1, sz);
+			buf = duk_push_dynamic_buffer(thr, sz);
+		} else {
+			buf = duk_resize_buffer(thr, -1, sz);
 		}
 		DUK_ASSERT(buf != NULL);
 
 		DUK_VA_COPY(ap_copy, ap);
-		len = duk__try_push_vsprintf(ctx, buf, sz, fmt, ap_copy);
+		len = duk__try_push_vsprintf(thr, buf, sz, fmt, ap_copy);
 		va_end(ap_copy);
 		if (len >= 0) {
 			break;
@@ -21005,33 +21681,32 @@
 	/* Cannot use duk_buffer_to_string() on the buffer because it is
 	 * usually larger than 'len'; 'buf' is also usually a stack buffer.
 	 */
-	res = duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len);  /* [ buf? res ] */
+	res = duk_push_lstring(thr, (const char *) buf, (duk_size_t) len);  /* [ buf? res ] */
 	if (pushed_buf) {
-		duk_remove_m2(ctx);
+		duk_remove_m2(thr);
 	}
 	return res;
 }
 
-DUK_EXTERNAL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL const char *duk_push_sprintf(duk_hthread *thr, const char *fmt, ...) {
 	va_list ap;
 	const char *ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* allow fmt==NULL */
 	va_start(ap, fmt);
-	ret = duk_push_vsprintf(ctx, fmt, ap);
+	ret = duk_push_vsprintf(thr, fmt, ap);
 	va_end(ap);
 
 	return ret;
 }
 
-DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
 	duk_tval *tv_slot;
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(prototype_bidx == -1 ||
 	           (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS));
 
@@ -21050,7 +21725,7 @@
 	/* object is now reachable */
 
 	if (prototype_bidx >= 0) {
-		DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[prototype_bidx]);
+		DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, thr->builtins[prototype_bidx]);
 	} else {
 		DUK_ASSERT(prototype_bidx == -1);
 		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
@@ -21059,38 +21734,35 @@
 	return h;
 }
 
-DUK_INTERNAL duk_hobject *duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hobject *duk_push_object_helper_proto(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_hobject *proto) {
 	duk_hobject *h;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h = duk_push_object_helper(ctx, hobject_flags_and_class, -1);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h = duk_push_object_helper(thr, hobject_flags_and_class, -1);
 	DUK_ASSERT(h != NULL);
-	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, proto);
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, h, proto);
 	return h;
 }
 
-DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	(void) duk_push_object_helper(ctx,
+DUK_EXTERNAL duk_idx_t duk_push_object(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
 	                              DUK_BIDX_OBJECT_PROTOTYPE);
-	return duk_get_top_index_unsafe(ctx);
-}
-
-DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	return duk_get_top_index_unsafe(thr);
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_array(duk_hthread *thr) {
 	duk_uint_t flags;
 	duk_harray *obj;
 	duk_idx_t ret;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
 	        DUK_HOBJECT_FLAG_FASTREFS |
@@ -21101,8 +21773,7 @@
 	obj = duk_harray_alloc(thr, flags);
 	DUK_ASSERT(obj != NULL);
 
-	/* XXX: since prototype is NULL, could save a check */
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]);
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_ARRAY_PROTOTYPE]);
 
 	tv_slot = thr->valstack_top;
 	DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
@@ -21114,13 +21785,13 @@
 	return ret;
 }
 
-DUK_INTERNAL duk_harray *duk_push_harray(duk_context *ctx) {
+DUK_INTERNAL duk_harray *duk_push_harray(duk_hthread *thr) {
 	/* XXX: API call could do this directly, cast to void in API macro. */
-	duk_hthread *thr;
 	duk_harray *a;
 
-	thr = (duk_hthread *) ctx;
-	(void) duk_push_array(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	(void) duk_push_array(thr);
 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(thr->valstack_top - 1));
 	a = (duk_harray *) DUK_TVAL_GET_OBJECT(thr->valstack_top - 1);
 	DUK_ASSERT(a != NULL);
@@ -21130,12 +21801,14 @@
 /* Push a duk_harray with preallocated size (.length also set to match size).
  * Caller may then populate array part of the duk_harray directly.
  */
-DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_context *ctx, duk_uint32_t size) {
+DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_hthread *thr, duk_uint32_t size) {
 	duk_harray *a;
 
-	a = duk_push_harray(ctx);
-
-	duk_hobject_realloc_props((duk_hthread *) ctx,
+	DUK_ASSERT_API_ENTRY(thr);
+
+	a = duk_push_harray(thr);
+
+	duk_hobject_realloc_props(thr,
 	                          (duk_hobject *) a,
 	                          0,
 	                          size,
@@ -21145,13 +21818,22 @@
 	return a;
 }
 
-DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_tval *duk_push_harray_with_size_outptr(duk_hthread *thr, duk_uint32_t size) {
+	duk_harray *a;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	a = duk_push_harray_with_size(thr, size);
+	DUK_ASSERT(a != NULL);
+	return DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_hthread *thr, duk_uint_t flags) {
 	duk_hthread *obj;
 	duk_idx_t ret;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK__CHECK_SPACE();
 
@@ -21190,11 +21872,10 @@
 		duk_hthread_copy_builtin_objects(thr, obj);
 	}
 
-	/* default prototype (Note: 'obj' must be reachable) */
-	/* XXX: since prototype is NULL, could save a check */
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
-
-	/* Initial stack size satisfies the stack spare constraints so there
+	/* default prototype */
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
+
+	/* Initial stack size satisfies the stack slack constraints so there
 	 * is no need to require stack here.
 	 */
 	DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >=
@@ -21203,12 +21884,11 @@
 	return ret;
 }
 
-DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_hthread *thr) {
 	duk_hcompfunc *obj;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK__CHECK_SPACE();
 
@@ -21219,6 +21899,7 @@
 
 	obj = duk_hcompfunc_alloc(thr,
 	                          DUK_HOBJECT_FLAG_EXTENSIBLE |
+	                          DUK_HOBJECT_FLAG_CALLABLE |
 	                          DUK_HOBJECT_FLAG_COMPFUNC |
 	                          DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
 	if (DUK_UNLIKELY(obj == NULL)) {
@@ -21232,19 +21913,49 @@
 	DUK_HOBJECT_INCREF(thr, obj);
 	thr->valstack_top++;
 
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+	/* default prototype */
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL);
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
 
 	return obj;
 }
 
-DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hboundfunc *duk_push_hboundfunc(duk_hthread *thr) {
+	duk_hboundfunc *obj;
+	duk_tval *tv_slot;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK__CHECK_SPACE();
+	obj = duk_hboundfunc_alloc(thr->heap,
+	                           DUK_HOBJECT_FLAG_EXTENSIBLE |
+	                           DUK_HOBJECT_FLAG_BOUNDFUNC |
+	                           DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+	                           DUK_HOBJECT_FLAG_CALLABLE |
+	                           DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
+	if (!obj) {
+		DUK_ERROR_ALLOC_FAILED(thr);
+	}
+
+	tv_slot = thr->valstack_top++;
+	DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+	DUK_HOBJECT_INCREF(thr, obj);
+
+	/* Prototype is left as NULL because the caller always sets it (and
+	 * it depends on the target function).
+	 */
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) obj) == NULL);
+
+	return obj;
+}
+
+DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_uint_t flags, duk_small_uint_t proto_bidx) {
 	duk_hnatfunc *obj;
 	duk_idx_t ret;
 	duk_tval *tv_slot;
 	duk_int16_t func_nargs;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	DUK__CHECK_SPACE();
 
@@ -21274,9 +21985,8 @@
 	ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
 	thr->valstack_top++;
 
-	/* default prototype (Note: 'obj' must be reachable) */
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
-
+	DUK_ASSERT_BIDX_VALID(proto_bidx);
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[proto_bidx]);
 	return ret;
 
  api_error:
@@ -21284,31 +21994,14 @@
 	return 0;  /* not reached */
 }
 
-DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
+DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
 	duk_uint_t flags;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
 	        DUK_HOBJECT_FLAG_CONSTRUCTABLE |
-	        DUK_HOBJECT_FLAG_FASTREFS |
-	        DUK_HOBJECT_FLAG_NATFUNC |
-	        DUK_HOBJECT_FLAG_NEWENV |
-	        DUK_HOBJECT_FLAG_STRICT |
-	        DUK_HOBJECT_FLAG_NOTAIL |
-	        DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC |
-	        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
-
-	return duk__push_c_function_raw(ctx, func, nargs, flags);
-}
-
-DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
-	duk_uint_t flags;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
-	        DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+	        DUK_HOBJECT_FLAG_CALLABLE |
 	        DUK_HOBJECT_FLAG_FASTREFS |
 	        DUK_HOBJECT_FLAG_NATFUNC |
 	        DUK_HOBJECT_FLAG_NEWENV |
@@ -21316,15 +22009,20 @@
 	        DUK_HOBJECT_FLAG_NOTAIL |
 	        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
 
-	(void) duk__push_c_function_raw(ctx, func, nargs, flags);
-}
-
-DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
+	/* Default prototype is a Duktape specific %NativeFunctionPrototype%
+	 * which provides .length and .name getters.
+	 */
+	return duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE);
+}
+
+DUK_INTERNAL void duk_push_c_function_builtin(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
 	duk_uint_t flags;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+	        DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+	        DUK_HOBJECT_FLAG_CALLABLE |
 	        DUK_HOBJECT_FLAG_FASTREFS |
 	        DUK_HOBJECT_FLAG_NATFUNC |
 	        DUK_HOBJECT_FLAG_NEWENV |
@@ -21332,15 +22030,33 @@
 	        DUK_HOBJECT_FLAG_NOTAIL |
 	        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
 
-	(void) duk__push_c_function_raw(ctx, func, nargs, flags);
-}
-
-DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval tv_tmp;
+	/* Must use Function.prototype for standard built-in functions. */
+	(void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE);
+}
+
+DUK_INTERNAL void duk_push_c_function_builtin_noconstruct(duk_hthread *thr, duk_c_function func, duk_int_t nargs) {
+	duk_uint_t flags;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+	        DUK_HOBJECT_FLAG_CALLABLE |
+	        DUK_HOBJECT_FLAG_FASTREFS |
+	        DUK_HOBJECT_FLAG_NATFUNC |
+	        DUK_HOBJECT_FLAG_NEWENV |
+	        DUK_HOBJECT_FLAG_STRICT |
+	        DUK_HOBJECT_FLAG_NOTAIL |
+	        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
+
+	/* Must use Function.prototype for standard built-in functions. */
+	(void) duk__push_c_function_raw(thr, func, nargs, flags, DUK_BIDX_FUNCTION_PROTOTYPE);
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_hthread *thr, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) {
 	duk_small_uint_t lf_flags;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	duk_tval *tv_slot;
+
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK__CHECK_SPACE();
 
@@ -21358,11 +22074,12 @@
 		goto api_error;
 	}
 
-	lf_flags = DUK_LFUNC_FLAGS_PACK(magic, length, nargs);
-	DUK_TVAL_SET_LIGHTFUNC(&tv_tmp, func, lf_flags);
-	duk_push_tval(ctx, &tv_tmp);  /* XXX: direct valstack write */
-	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
-	return ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
+	lf_flags = DUK_LFUNC_FLAGS_PACK((duk_small_int_t) magic, (duk_small_uint_t) length, (duk_small_uint_t) nargs);
+	tv_slot = thr->valstack_top++;
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_slot));
+	DUK_TVAL_SET_LIGHTFUNC(tv_slot, func, lf_flags);
+	DUK_ASSERT(tv_slot >= thr->valstack_bottom);
+	return (duk_idx_t) (tv_slot - thr->valstack_bottom);
 
  api_error:
 	DUK_ERROR_TYPE_INVALID_ARGS(thr);
@@ -21370,12 +22087,11 @@
 }
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_hthread *thr, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
 	duk_hbufobj *obj;
 	duk_tval *tv_slot;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(prototype_bidx >= 0);
 
 	DUK__CHECK_SPACE();
@@ -21383,7 +22099,7 @@
 	obj = duk_hbufobj_alloc(thr, hobject_flags_and_class);
 	DUK_ASSERT(obj != NULL);
 
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]);
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]);
 	DUK_ASSERT_HBUFOBJ_VALID(obj);
 
 	tv_slot = thr->valstack_top;
@@ -21420,23 +22136,20 @@
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
-	duk_hthread *thr;
+DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
 	duk_hbufobj *h_bufobj;
 	duk_hbuffer *h_val;
+	duk_hobject *h_arraybuf;
 	duk_uint32_t tmp;
 	duk_uint_t classnum;
 	duk_uint_t protobidx;
 	duk_uint_t lookupidx;
 	duk_uint_t uint_offset, uint_length, uint_added;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* The underlying types for offset/length in duk_hbufobj is
-	 * duk_uint_t; make sure argument values fit and that
-	 * offset + length does not wrap.
+	 * duk_uint_t; make sure argument values fit.
 	 */
 	uint_offset = (duk_uint_t) byte_offset;
 	uint_length = (duk_uint_t) byte_length;
@@ -21445,11 +22158,6 @@
 			goto range_error;
 		}
 	}
-	uint_added = uint_offset + uint_length;
-	if (DUK_UNLIKELY(uint_added < uint_offset)) {
-		goto range_error;
-	}
-	DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length);
 
 	DUK_ASSERT_DISABLE(flags >= 0);  /* flags is unsigned */
 	lookupidx = flags;
@@ -21460,18 +22168,56 @@
 	classnum = tmp >> 24;
 	protobidx = (tmp >> 16) & 0xff;
 
-	h_val = duk_require_hbuffer(ctx, idx_buffer);
+	h_arraybuf = duk_get_hobject(thr, idx_buffer);
+	if (h_arraybuf != NULL &&  /* argument is an object */
+	    flags != DUK_BUFOBJ_ARRAYBUFFER &&  /* creating a view */
+	    DUK_HOBJECT_GET_CLASS_NUMBER(h_arraybuf) == DUK_HOBJECT_CLASS_ARRAYBUFFER  /* argument is ArrayBuffer */) {
+		duk_uint_t tmp_offset;
+
+		DUK_ASSERT_HBUFOBJ_VALID((duk_hbufobj *) h_arraybuf);
+		h_val = ((duk_hbufobj *) h_arraybuf)->buf;
+		if (DUK_UNLIKELY(h_val == NULL)) {
+			goto arg_error;
+		}
+
+		tmp_offset = uint_offset + ((duk_hbufobj *) h_arraybuf)->offset;
+		if (DUK_UNLIKELY(tmp_offset < uint_offset)) {
+			goto range_error;
+		}
+		uint_offset = tmp_offset;
+
+		/* Note intentional difference to new TypedArray(): we allow
+		 * caller to create an uncovered typed array (which is memory
+		 * safe); new TypedArray() rejects it.
+		 */
+	} else {
+		/* Handle unexpected object arguments here too, for nice error
+		 * messages.
+		 */
+		h_arraybuf = NULL;
+		h_val = duk_require_hbuffer(thr, idx_buffer);
+	}
+
+	/* Wrap check for offset+length. */
+	uint_added = uint_offset + uint_length;
+	if (DUK_UNLIKELY(uint_added < uint_offset)) {
+		goto range_error;
+	}
+	DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length);
+
 	DUK_ASSERT(h_val != NULL);
 
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(classnum),
-	                               protobidx);
+	                               (duk_small_int_t) protobidx);
 	DUK_ASSERT(h_bufobj != NULL);
 
 	h_bufobj->buf = h_val;
 	DUK_HBUFFER_INCREF(thr, h_val);
+	h_bufobj->buf_prop = h_arraybuf;
+	DUK_HOBJECT_INCREF_ALLOWNULL(thr, h_arraybuf);
 	h_bufobj->offset = uint_offset;
 	h_bufobj->length = uint_length;
 	h_bufobj->shift = (tmp >> 4) & 0x0f;
@@ -21483,7 +22229,7 @@
 	 * provided as .buffer property of the view.  The ArrayBuffer is
 	 * referenced via duk_hbufobj->buf_prop and an inherited .buffer
 	 * accessor returns it.  The ArrayBuffer is created lazily on first
-	 * access so we don't need to do anything more here.
+	 * access if necessary so we don't need to do anything more here.
 	 */
 	return;
 
@@ -21496,36 +22242,39 @@
 	return;  /* not reached */
 }
 #else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
-DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
+DUK_EXTERNAL void duk_push_buffer_object(duk_hthread *thr, duk_idx_t idx_buffer, duk_size_t byte_offset, duk_size_t byte_length, duk_uint_t flags) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_UNREF(idx_buffer);
 	DUK_UNREF(byte_offset);
 	DUK_UNREF(byte_length);
 	DUK_UNREF(flags);
-	DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+	DUK_ERROR_UNSUPPORTED(thr);
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
-DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
 	duk_hobject *proto;
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-	duk_bool_t noblame_fileline;
-#endif
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	duk_small_uint_t augment_flags;
+#endif
+
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr != NULL);
 	DUK_UNREF(filename);
 	DUK_UNREF(line);
 
 	/* Error code also packs a tracedata related flag. */
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-	noblame_fileline = err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE;
+	augment_flags = 0;
+	if (err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE) {
+		augment_flags = DUK_AUGMENT_FLAG_NOBLAME_FILELINE;
+	}
 #endif
 	err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE);
 
 	/* error gets its 'name' from the prototype */
 	proto = duk_error_prototype_from_code(thr, err_code);
-	(void) duk_push_object_helper_proto(ctx,
+	(void) duk_push_object_helper_proto(thr,
 	                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                                    DUK_HOBJECT_FLAG_FASTREFS |
 	                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR),
@@ -21533,8 +22282,8 @@
 
 	/* ... and its 'message' from an instance property */
 	if (fmt) {
-		duk_push_vsprintf(ctx, fmt, ap);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
+		duk_push_vsprintf(thr, fmt, ap);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
 	} else {
 		/* If no explicit message given, put error code into message field
 		 * (as a number).  This is not fully in keeping with the Ecmascript
@@ -21542,8 +22291,8 @@
 		 * constructors use ToString() on their argument).  However, it's
 		 * probably more useful than having a separate 'code' property.
 		 */
-		duk_push_int(ctx, err_code);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
+		duk_push_int(thr, err_code);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
 	}
 
 	/* XXX: .code = err_code disabled, not sure if useful */
@@ -21551,49 +22300,48 @@
 	/* Creation time error augmentation */
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
 	/* filename may be NULL in which case file/line is not recorded */
-	duk_err_augment_error_create(thr, thr, filename, line, noblame_fileline);  /* may throw an error */
-#endif
-
-	return duk_get_top_index_unsafe(ctx);
-}
-
-DUK_EXTERNAL duk_idx_t duk_push_error_object_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
+	duk_err_augment_error_create(thr, thr, filename, line, augment_flags);  /* may throw an error */
+#endif
+
+	return duk_get_top_index_unsafe(thr);
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_error_object_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
 	va_list ap;
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	va_start(ap, fmt);
-	ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+	ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
 	va_end(ap);
 	return ret;
 }
 
 #if !defined(DUK_USE_VARIADIC_MACROS)
-DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
+DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) {
 	const char *filename = duk_api_global_filename;
 	duk_int_t line = duk_api_global_line;
 	va_list ap;
 	duk_idx_t ret;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	duk_api_global_filename = NULL;
 	duk_api_global_line = 0;
 	va_start(ap, fmt);
-	ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+	ret = duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
 	va_end(ap);
 	return ret;
 }
 #endif  /* DUK_USE_VARIADIC_MACROS */
 
-DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void *duk_push_buffer_raw(duk_hthread *thr, duk_size_t size, duk_small_uint_t flags) {
 	duk_tval *tv_slot;
 	duk_hbuffer *h;
 	void *buf_data;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	DUK__CHECK_SPACE();
 
@@ -21615,13 +22363,17 @@
 	return (void *) buf_data;
 }
 
-DUK_INTERNAL void *duk_push_fixed_buffer_nozero(duk_context *ctx, duk_size_t len) {
-	return duk_push_buffer_raw(ctx, len, DUK_BUF_FLAG_NOZERO);
-}
-
-DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_context *ctx, duk_size_t len) {
+DUK_INTERNAL void *duk_push_fixed_buffer_nozero(duk_hthread *thr, duk_size_t len) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_push_buffer_raw(thr, len, DUK_BUF_FLAG_NOZERO);
+}
+
+DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_hthread *thr, duk_size_t len) {
 	void *ptr;
-	ptr = duk_push_buffer_raw(ctx, len, 0);
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	ptr = duk_push_buffer_raw(thr, len, 0);
 #if !defined(DUK_USE_ZERO_BUFFER_DATA)
 	/* ES2015 requires zeroing even when DUK_USE_ZERO_BUFFER_DATA
 	 * is not set.
@@ -21631,14 +22383,113 @@
 	return ptr;
 }
 
-#if defined(DUK_USE_ASSERTIONS)
-DUK_LOCAL void duk__validate_push_heapptr(duk_context *ctx, void *ptr) {
+#if defined(DUK_USE_ES6_PROXY)
+DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) {
+	duk_hobject *h_target;
+	duk_hobject *h_handler;
+	duk_hproxy *h_proxy;
+	duk_tval *tv_slot;
+	duk_uint_t flags;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(proxy_flags);
+
+	/* DUK__CHECK_SPACE() unnecessary because the Proxy is written to
+	 * value stack in-place.
+	 */
+#if 0
+	DUK__CHECK_SPACE();
+#endif
+
+	/* Reject a proxy object as the target because it would need
+	 * special handling in property lookups.  (ES2015 has no such
+	 * restriction.)
+	 */
+	h_target = duk_require_hobject_promote_mask(thr, -2, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	DUK_ASSERT(h_target != NULL);
+	if (DUK_HOBJECT_IS_PROXY(h_target)) {
+		goto fail_args;
+	}
+
+	/* Reject a proxy object as the handler because it would cause
+	 * potentially unbounded recursion.  (ES2015 has no such
+	 * restriction.)
+	 *
+	 * There's little practical reason to use a lightfunc or a plain
+	 * buffer as the handler table: one could only provide traps via
+	 * their prototype objects (Function.prototype and ArrayBuffer.prototype).
+	 * Even so, as lightfuncs and plain buffers mimic their object
+	 * counterparts, they're promoted and accepted here.
+	 */
+	h_handler = duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	DUK_ASSERT(h_handler != NULL);
+	if (DUK_HOBJECT_IS_PROXY(h_handler)) {
+		goto fail_args;
+	}
+
+	/* XXX: Proxy object currently has no prototype, so ToPrimitive()
+	 * coercion fails which is a bit confusing.
+	 */
+
+	/* CALLABLE and CONSTRUCTABLE flags are copied from the (initial)
+	 * target, see ES2015 Sections 9.5.15 and 9.5.13.
+	 */
+	flags = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) h_target) &
+	        (DUK_HOBJECT_FLAG_CALLABLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE);
+	flags |= DUK_HOBJECT_FLAG_EXTENSIBLE |
+	         DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ;
+	if (flags & DUK_HOBJECT_FLAG_CALLABLE) {
+		flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION) |
+		         DUK_HOBJECT_FLAG_SPECIAL_CALL;
+	} else {
+		flags |= DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT);
+	}
+
+	h_proxy = duk_hproxy_alloc(thr, flags);
+	DUK_ASSERT(h_proxy != NULL);
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_proxy) == NULL);
+
+	/* Initialize Proxy target and handler references; avoid INCREF
+	 * by stealing the value stack refcounts via direct value stack
+	 * manipulation.  INCREF is needed for the Proxy itself however.
+	 */
+	DUK_ASSERT(h_target != NULL);
+	h_proxy->target = h_target;
+	DUK_ASSERT(h_handler != NULL);
+	h_proxy->handler = h_handler;
+	DUK_ASSERT_HPROXY_VALID(h_proxy);
+
+	DUK_ASSERT(duk_get_hobject(thr, -2) == h_target);
+	DUK_ASSERT(duk_get_hobject(thr, -1) == h_handler);
+	tv_slot = thr->valstack_top - 2;
+	DUK_ASSERT(tv_slot >= thr->valstack_bottom);
+	DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) h_proxy);
+	DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_proxy);
+	tv_slot++;
+	DUK_TVAL_SET_UNDEFINED(tv_slot);  /* [ ... target handler ] -> [ ... proxy undefined ] */
+	thr->valstack_top = tv_slot;      /* -> [ ... proxy ] */
+
+	DUK_DD(DUK_DDPRINT("created Proxy: %!iT", duk_get_tval(thr, -1)));
+
+	return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom - 1);
+
+ fail_args:
+	DUK_ERROR_TYPE_INVALID_ARGS(thr);
+}
+#else  /* DUK_USE_ES6_PROXY */
+DUK_EXTERNAL duk_idx_t duk_push_proxy(duk_hthread *thr, duk_uint_t proxy_flags) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(proxy_flags);
+	DUK_ERROR_UNSUPPORTED(thr);
+}
+#endif  /* DUK_USE_ES6_PROXY */
+
+#if defined(DUK_USE_ASSERTIONS)
+DUK_LOCAL void duk__validate_push_heapptr(duk_hthread *thr, void *ptr) {
 	duk_heaphdr *h;
 	duk_heaphdr *curr;
-	duk_hthread *thr;
 	duk_bool_t found = 0;
 
-	thr = (duk_hthread *) ctx;
 	h = (duk_heaphdr *) ptr;
 	if (h == NULL) {
 		/* Allowed. */
@@ -21731,12 +22582,11 @@
 }
 #endif  /* DUK_USE_ASSERTIONS */
 
-DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_hthread *thr, void *ptr) {
 	duk_idx_t ret;
 	duk_tval *tv;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Reviving an object using a heap pointer is a dangerous API
 	 * operation: if the application doesn't guarantee that the
@@ -21746,7 +22596,7 @@
 	 */
 
 #if defined(DUK_USE_ASSERTIONS)
-	duk__validate_push_heapptr(ctx, ptr);
+	duk__validate_push_heapptr(thr, ptr);
 #endif
 
 	DUK__CHECK_SPACE();
@@ -21824,118 +22674,78 @@
 }
 
 /* Push object with no prototype, i.e. a "bare" object. */
-DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_context *ctx) {
-	(void) duk_push_object_helper(ctx,
+DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
 	                              -1);  /* no prototype */
-	return duk_get_top_index_unsafe(ctx);
-}
-
-DUK_INTERNAL void duk_push_hstring(duk_context *ctx, duk_hstring *h) {
+	return duk_get_top_index_unsafe(thr);
+}
+
+DUK_INTERNAL void duk_push_hstring(duk_hthread *thr, duk_hstring *h) {
 	duk_tval tv;
-	DUK_ASSERT_CTX_VALID(ctx);
+
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(h != NULL);
+
 	DUK_TVAL_SET_STRING(&tv, h);
-	duk_push_tval(ctx, &tv);
-}
-
-DUK_INTERNAL void duk_push_hstring_stridx(duk_context *ctx, duk_small_uint_t stridx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
+	duk_push_tval(thr, &tv);
+}
+
+DUK_INTERNAL void duk_push_hstring_stridx(duk_hthread *thr, duk_small_uint_t stridx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT_STRIDX_VALID(stridx);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
-}
-
-DUK_INTERNAL void duk_push_hstring_empty(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-	duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, DUK_STRIDX_EMPTY_STRING));
-}
-
-DUK_INTERNAL void duk_push_hobject(duk_context *ctx, duk_hobject *h) {
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+}
+
+DUK_INTERNAL void duk_push_hstring_empty(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_push_hstring(thr, DUK_HTHREAD_GET_STRING(thr, DUK_STRIDX_EMPTY_STRING));
+}
+
+DUK_INTERNAL void duk_push_hobject(duk_hthread *thr, duk_hobject *h) {
 	duk_tval tv;
-	DUK_ASSERT_CTX_VALID(ctx);
+
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(h != NULL);
+
 	DUK_TVAL_SET_OBJECT(&tv, h);
-	duk_push_tval(ctx, &tv);
-}
-
-DUK_INTERNAL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h) {
+	duk_push_tval(thr, &tv);
+}
+
+DUK_INTERNAL void duk_push_hbuffer(duk_hthread *thr, duk_hbuffer *h) {
 	duk_tval tv;
-	DUK_ASSERT_CTX_VALID(ctx);
+
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(h != NULL);
+
 	DUK_TVAL_SET_BUFFER(&tv, h);
-	duk_push_tval(ctx, &tv);
-}
-
-DUK_INTERNAL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr != NULL);
+	duk_push_tval(thr, &tv);
+}
+
+DUK_INTERNAL void duk_push_hobject_bidx(duk_hthread *thr, duk_small_int_t builtin_idx) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS);
 	DUK_ASSERT(thr->builtins[builtin_idx] != NULL);
-	duk_push_hobject(ctx, thr->builtins[builtin_idx]);
+
+	duk_push_hobject(thr, thr->builtins[builtin_idx]);
 }
 
 /*
  *  Poppers
  */
 
-DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_n_unsafe_raw(duk_hthread *thr, duk_idx_t count) {
 	duk_tval *tv;
 #if defined(DUK_USE_REFERENCE_COUNTING)
 	duk_tval *tv_end;
 #endif
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-
-	if (DUK_UNLIKELY(count < 0)) {
-		DUK_ERROR_RANGE_INVALID_COUNT(thr);
-		return;
-	}
-
-	if (DUK_UNLIKELY((duk_size_t) (thr->valstack_top - thr->valstack_bottom) < (duk_size_t) count)) {
-		DUK_ERROR_RANGE_INVALID_COUNT(thr);
-	}
-
-#if defined(DUK_USE_REFERENCE_COUNTING)
-	tv = thr->valstack_top;
-	tv_end = tv - count;
-	while (tv != tv_end) {
-		tv--;
-		DUK_ASSERT(tv >= thr->valstack_bottom);
-		DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv);
-	}
-	thr->valstack_top = tv;
-	DUK_REFZERO_CHECK_FAST(thr);
-#else
-	tv = thr->valstack_top;
-	while (count > 0) {
-		count--;
-		tv--;
-		DUK_ASSERT(tv >= thr->valstack_bottom);
-		DUK_TVAL_SET_UNDEFINED(tv);
-	}
-	thr->valstack_top = tv;
-#endif
-
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-}
-
-DUK_INTERNAL void duk_pop_n_unsafe(duk_context *ctx, duk_idx_t count) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-#if defined(DUK_USE_REFERENCE_COUNTING)
-	duk_tval *tv_end;
-#endif
-
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(count >= 0);
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count);
 
 #if defined(DUK_USE_REFERENCE_COUNTING)
@@ -21962,13 +22772,37 @@
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 }
 
-/* Pop N elements without DECREF (in effect "stealing" the refcounts). */
+DUK_EXTERNAL void duk_pop_n(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+
+	if (DUK_UNLIKELY((duk_uidx_t) (thr->valstack_top - thr->valstack_bottom) < (duk_uidx_t) count)) {
+		DUK_ERROR_RANGE_INVALID_COUNT(thr);
+		return;
+	}
+	DUK_ASSERT(count >= 0);
+
+	duk__pop_n_unsafe_raw(thr, count);
+}
+
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n(thr, count);
+}
+#else  /* DUK_USE_PREFER_SIZE */
+DUK_INTERNAL void duk_pop_n_unsafe(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk__pop_n_unsafe_raw(thr, count);
+}
+#endif  /* DUK_USE_PREFER_SIZE */
+
+/* Pop N elements without DECREF (in effect "stealing" any actual refcounts). */
 #if defined(DUK_USE_REFERENCE_COUNTING)
-DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(count >= 0);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count);
@@ -21985,8 +22819,9 @@
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 }
 #else  /* DUK_USE_REFERENCE_COUNTING */
-DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count) {
-	duk_pop_n_unsafe(ctx, count);
+DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_unsafe(thr, count);
 }
 #endif  /* DUK_USE_REFERENCE_COUNTING */
 
@@ -21994,51 +22829,28 @@
  * compile a specialized function for it.
  */
 #if defined(DUK_USE_PREFER_SIZE)
-DUK_EXTERNAL void duk_pop(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_pop_n(ctx, 1);
-}
-#else
-DUK_EXTERNAL void duk_pop(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-	if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) {
-		DUK_ERROR_RANGE_INVALID_COUNT(thr);
-	}
-
-	tv = --thr->valstack_top;  /* tv points to element just below prev top */
-	DUK_ASSERT(tv >= thr->valstack_bottom);
-#if defined(DUK_USE_REFERENCE_COUNTING)
-	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
-#else
-	DUK_TVAL_SET_UNDEFINED(tv);
-#endif
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-}
-#endif  /* !DUK_USE_PREFER_SIZE */
-
-/* Unsafe internal variant which assumes there are enough values on the value
- * stack so that a top check can be skipped safely.
- */
-#if defined(DUK_USE_PREFER_SIZE)
-DUK_INTERNAL void duk_pop_unsafe(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_pop_n_unsafe(ctx, 1);
-}
-#else
-DUK_INTERNAL void duk_pop_unsafe(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_tval *tv;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_pop(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n(thr, 1);
+}
+DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_unsafe(thr, 1);
+}
+DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_nodecref_unsafe(thr, 1);
+}
+#else  /* DUK_USE_PREFER_SIZE */
+DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_unsafe_raw(duk_hthread *thr) {
+	duk_tval *tv;
+
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);
 
-	tv = --thr->valstack_top;  /* tv points to element just below prev top */
+	tv = --thr->valstack_top;
 	DUK_ASSERT(tv >= thr->valstack_bottom);
 #if defined(DUK_USE_REFERENCE_COUNTING)
 	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
@@ -22048,16 +22860,135 @@
 
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 }
+DUK_EXTERNAL void duk_pop(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) {
+		DUK_ERROR_RANGE_INVALID_COUNT(thr);
+	}
+
+	duk__pop_unsafe_raw(thr);
+}
+DUK_INTERNAL void duk_pop_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk__pop_unsafe_raw(thr);
+}
+DUK_INTERNAL void duk_pop_nodecref_unsafe(duk_hthread *thr) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);
+
+	tv = --thr->valstack_top;
+	DUK_ASSERT(tv >= thr->valstack_bottom);
+	DUK_TVAL_SET_UNDEFINED(tv);
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
 #endif  /* !DUK_USE_PREFER_SIZE */
 
-DUK_EXTERNAL void duk_pop_2(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_pop_n(ctx, 2);
-}
-
-DUK_EXTERNAL void duk_pop_3(duk_context *ctx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	duk_pop_n(ctx, 3);
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_nodecref_unsafe(thr);
+}
+#else  /* DUK_USE_PREFER_SIZE */
+DUK_INTERNAL void duk_pop_undefined(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1);
+
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
+	thr->valstack_top--;
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
+#endif  /* !DUK_USE_PREFER_SIZE */
+
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n(thr, 2);
+}
+DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_unsafe(thr, 2);
+}
+DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_nodecref_unsafe(thr, 2);
+}
+#else
+DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_2_unsafe_raw(duk_hthread *thr) {
+	duk_tval *tv;
+
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2);
+
+	tv = --thr->valstack_top;
+	DUK_ASSERT(tv >= thr->valstack_bottom);
+#if defined(DUK_USE_REFERENCE_COUNTING)
+	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
+#else
+	DUK_TVAL_SET_UNDEFINED(tv);
+#endif
+	tv = --thr->valstack_top;
+	DUK_ASSERT(tv >= thr->valstack_bottom);
+#if defined(DUK_USE_REFERENCE_COUNTING)
+	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);  /* side effects */
+#else
+	DUK_TVAL_SET_UNDEFINED(tv);
+#endif
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
+DUK_EXTERNAL void duk_pop_2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	if (DUK_UNLIKELY(thr->valstack_top - 2 < thr->valstack_bottom)) {
+		DUK_ERROR_RANGE_INVALID_COUNT(thr);
+	}
+
+	duk__pop_2_unsafe_raw(thr);
+}
+DUK_INTERNAL void duk_pop_2_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk__pop_2_unsafe_raw(thr);
+}
+DUK_INTERNAL void duk_pop_2_nodecref_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2);
+
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 2));
+	thr->valstack_top -= 2;
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
+#endif  /* !DUK_USE_PREFER_SIZE */
+
+DUK_EXTERNAL void duk_pop_3(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n(thr, 3);
+}
+
+DUK_INTERNAL void duk_pop_3_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_unsafe(thr, 3);
+}
+
+DUK_INTERNAL void duk_pop_3_nodecref_unsafe(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_pop_n_nodecref_unsafe(thr, 3);
 }
 
 /*
@@ -22065,43 +22996,40 @@
  */
 
 /* XXX: pack index range? array index offset? */
-DUK_INTERNAL void duk_pack(duk_context *ctx, duk_idx_t count) {
-	duk_hthread *thr;
-	duk_harray *a;
+DUK_INTERNAL void duk_pack(duk_hthread *thr, duk_idx_t count) {
 	duk_tval *tv_src;
 	duk_tval *tv_dst;
 	duk_tval *tv_curr;
 	duk_tval *tv_limit;
 	duk_idx_t top;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-
+	DUK_ASSERT_API_ENTRY(thr);
+
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
-	if (DUK_UNLIKELY(count < 0 || count > top)) {
+	DUK_ASSERT(top >= 0);
+	if (DUK_UNLIKELY((duk_uidx_t) count > (duk_uidx_t) top)) {
+		/* Also handles negative count. */
 		DUK_ERROR_RANGE_INVALID_COUNT(thr);
 		return;
 	}
+	DUK_ASSERT(count >= 0);
 
 	/* Wrapping is controlled by the check above: value stack top can be
-	 * at most thr->valstack_max which is low enough so that multiplying
-	 * with sizeof(duk_tval) won't wrap.
-	 */
-	DUK_ASSERT(count >= 0 && count <= (duk_idx_t) thr->valstack_max);
+	 * at most DUK_USE_VALSTACK_LIMIT which is low enough so that
+	 * multiplying with sizeof(duk_tval) won't wrap.
+	 */
+	DUK_ASSERT(count >= 0 && count <= (duk_idx_t) DUK_USE_VALSTACK_LIMIT);
 	DUK_ASSERT((duk_size_t) count <= DUK_SIZE_MAX / sizeof(duk_tval));  /* no wrapping */
 
-	a = duk_push_harray_with_size(ctx, (duk_uint32_t) count);  /* XXX: uninitialized would be OK */
-	DUK_ASSERT(a != NULL);
-	DUK_ASSERT(DUK_HOBJECT_GET_ASIZE((duk_hobject *) a) == (duk_uint32_t) count);
-	DUK_ASSERT(count == 0 || DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a) != NULL);
-	DUK_ASSERT((duk_idx_t) a->length == count);
+	tv_dst = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count);  /* XXX: uninitialized would be OK */
+	DUK_ASSERT(count == 0 || tv_dst != NULL);
 
 	/* Copy value stack values directly to the array part without
 	 * any refcount updates: net refcount changes are zero.
 	 */
 
 	tv_src = thr->valstack_top - count - 1;
-	tv_dst = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
 	DUK_MEMCPY((void *) tv_dst, (const void *) tv_src, (size_t) count * sizeof(duk_tval));
 
 	/* Overwrite result array to final value stack location and wipe
@@ -22123,26 +23051,118 @@
 	thr->valstack_top = tv_dst + 1;
 }
 
-#if 0
-/* XXX: unpack to position? */
-DUK_INTERNAL void duk_unpack(duk_context *ctx) {
-	/* - dense with length <= a_part
-	 * - dense with length > a_part
-	 * - sparse
-	 * - array-like but not actually an array?
-	 * - how to deal with 'unused' values (gaps); inherit or ignore?
-	 */
-}
-#endif
+DUK_INTERNAL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv = duk_require_tval(thr, idx);
+	if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv))) {
+		duk_hobject *h;
+		duk_uint32_t len;
+		duk_uint32_t i;
+
+		h = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		DUK_UNREF(h);
+
+#if defined(DUK_USE_ARRAY_FASTPATH)  /* close enough */
+		if (DUK_LIKELY(DUK_HOBJECT_IS_ARRAY(h) &&
+		               ((duk_harray *) h)->length <= DUK_HOBJECT_GET_ASIZE(h))) {
+			duk_harray *h_arr;
+			duk_tval *tv_src;
+			duk_tval *tv_dst;
+
+			h_arr = (duk_harray *) h;
+			len = h_arr->length;
+			if (DUK_UNLIKELY(len >= 0x80000000UL)) {
+				goto fail_over_2g;
+			}
+			duk_require_stack(thr, (duk_idx_t) len);
+
+			/* The potential allocation in duk_require_stack() may
+			 * run a finalizer which modifies the argArray so that
+			 * e.g. becomes sparse.  So, we need to recheck that the
+			 * array didn't change size and that there's still a
+			 * valid backing array part.
+			 *
+			 * XXX: alternatively, could prevent finalizers for the
+			 * duration.
+			 */
+			if (DUK_UNLIKELY(len != h_arr->length ||
+			                 h_arr->length > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr))) {
+				goto skip_fast;
+			}
+
+			/* Main fast path: arguments array is almost always
+			 * an actual array (though it might also be an arguments
+			 * object).
+			 */
+
+			DUK_DDD(DUK_DDDPRINT("fast path for %ld elements", (long) h_arr->length));
+			tv_src = DUK_HOBJECT_A_GET_BASE(thr->heap, h);
+			tv_dst = thr->valstack_top;
+			while (len-- > 0) {
+				DUK_ASSERT(tv_dst < thr->valstack_end);
+				if (DUK_UNLIKELY(DUK_TVAL_IS_UNUSED(tv_src))) {
+					/* Gaps are very unlikely.  Skip over them,
+					 * without an ancestor lookup (technically
+					 * not compliant).
+					 */
+					DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_dst));  /* valstack policy */
+				} else {
+					DUK_TVAL_SET_TVAL(tv_dst, tv_src);
+					DUK_TVAL_INCREF(thr, tv_dst);
+				}
+				tv_src++;
+				tv_dst++;
+			}
+			DUK_ASSERT(tv_dst <= thr->valstack_end);
+			thr->valstack_top = tv_dst;
+			return (duk_idx_t) h_arr->length;
+		}
+	 skip_fast:
+#endif  /* DUK_USE_ARRAY_FASTPATH */
+
+		/* Slow path: actual lookups.  The initial 'length' lookup
+		 * decides the output length, regardless of side effects that
+		 * may resize or change the argArray while we read the
+		 * indices.
+		 */
+		idx = duk_normalize_index(thr, idx);
+		duk_get_prop_stridx(thr, idx, DUK_STRIDX_LENGTH);
+		len = duk_to_uint32(thr, -1);  /* ToUint32() coercion required */
+		if (DUK_UNLIKELY(len >= 0x80000000UL)) {
+			goto fail_over_2g;
+		}
+		duk_pop_unsafe(thr);
+		DUK_DDD(DUK_DDDPRINT("slow path for %ld elements", (long) len));
+
+		duk_require_stack(thr, (duk_idx_t) len);
+		for (i = 0; i < len; i++) {
+			duk_get_prop_index(thr, idx, (duk_uarridx_t) i);
+		}
+		return (duk_idx_t) len;
+	} else if (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv)) {
+		return 0;
+	}
+
+	DUK_ERROR_TYPE_INVALID_ARGS(thr);
+	return 0;
+
+ fail_over_2g:
+	DUK_ERROR_RANGE_INVALID_LENGTH(thr);
+	return 0;
+}
 
 /*
  *  Error throwing
  */
 
-DUK_EXTERNAL void duk_throw_raw(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_throw_raw(duk_hthread *thr) {
 	duk_tval *tv_val;
 
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
@@ -22163,12 +23183,12 @@
 	duk_hthread_sync_and_null_currpc(thr);
 
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
-	DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
+	DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(thr, -1)));
 	duk_err_augment_error_throw(thr);
 #endif
-	DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
-
-	tv_val = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1)));
+
+	tv_val = DUK_GET_TVAL_NEGIDX(thr, -1);
 	duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, tv_val);
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 	duk_err_check_debugger_integration(thr);
@@ -22183,10 +23203,8 @@
 	DUK_UNREACHABLE();
 }
 
-DUK_EXTERNAL void duk_fatal_raw(duk_context *ctx, const char *err_msg) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT_CTX_VALID(ctx);
+DUK_EXTERNAL void duk_fatal_raw(duk_hthread *thr, const char *err_msg) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
 	DUK_ASSERT(thr->heap->fatal_func != NULL);
@@ -22209,72 +23227,80 @@
 	}
 }
 
-DUK_EXTERNAL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
-	(void) duk_throw(ctx);
-}
-
-DUK_EXTERNAL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
+DUK_EXTERNAL void duk_error_va_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
+	(void) duk_throw(thr);
+}
+
+DUK_EXTERNAL void duk_error_raw(duk_hthread *thr, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
 	va_list ap;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	va_start(ap, fmt);
-	duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+	duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
 	va_end(ap);
-	(void) duk_throw(ctx);
+	(void) duk_throw(thr);
 }
 
 #if !defined(DUK_USE_VARIADIC_MACROS)
-DUK_NORETURN(DUK_LOCAL_DECL void duk__throw_error_from_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, va_list ap));
-
-DUK_LOCAL void duk__throw_error_from_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, va_list ap) {
+DUK_NORETURN(DUK_LOCAL_DECL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap));
+
+DUK_LOCAL void duk__throw_error_from_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, va_list ap) {
 	const char *filename;
 	duk_int_t line;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	filename = duk_api_global_filename;
 	line = duk_api_global_line;
 	duk_api_global_filename = NULL;
 	duk_api_global_line = 0;
 
-	duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
-	(void) duk_throw(ctx);
+	duk_push_error_object_va_raw(thr, err_code, filename, line, fmt, ap);
+	(void) duk_throw(thr);
 }
 
 #define DUK__ERROR_STASH_SHARED(code) do { \
 		va_list ap; \
 		va_start(ap, fmt); \
-		duk__throw_error_from_stash(ctx, (code), fmt, ap); \
+		duk__throw_error_from_stash(thr, (code), fmt, ap); \
 		va_end(ap); \
 		/* Never reached; if return 0 here, gcc/clang will complain. */ \
 	} while (0)
 
-DUK_EXTERNAL duk_ret_t duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_error_stash(duk_hthread *thr, duk_errcode_t err_code, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(err_code);
 }
-DUK_EXTERNAL duk_ret_t duk_generic_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_generic_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_eval_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_eval_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_EVAL_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_range_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_range_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_RANGE_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_reference_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_reference_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_REFERENCE_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_syntax_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_syntax_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_SYNTAX_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_type_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_type_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_TYPE_ERROR);
 }
-DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_context *ctx, const char *fmt, ...) {
+DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_hthread *thr, const char *fmt, ...) {
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK__ERROR_STASH_SHARED(DUK_ERR_URI_ERROR);
 }
 #endif  /* DUK_USE_VARIADIC_MACROS */
@@ -22283,14 +23309,13 @@
  *  Comparison
  */
 
-DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL duk_bool_t duk_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
 	duk_tval *tv1, *tv2;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv1 = duk_get_tval(ctx, idx1);
-	tv2 = duk_get_tval(ctx, idx2);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_get_tval(thr, idx1);
+	tv2 = duk_get_tval(thr, idx2);
 	if ((tv1 == NULL) || (tv2 == NULL)) {
 		return 0;
 	}
@@ -22301,13 +23326,13 @@
 	return duk_js_equals(thr, tv1, tv2);
 }
 
-DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) {
+DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
 	duk_tval *tv1, *tv2;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv1 = duk_get_tval(ctx, idx1);
-	tv2 = duk_get_tval(ctx, idx2);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_get_tval(thr, idx1);
+	tv2 = duk_get_tval(thr, idx2);
 	if ((tv1 == NULL) || (tv2 == NULL)) {
 		return 0;
 	}
@@ -22316,13 +23341,13 @@
 	return duk_js_strict_equals(tv1, tv2);
 }
 
-DUK_EXTERNAL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) {
+DUK_EXTERNAL duk_bool_t duk_samevalue(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
 	duk_tval *tv1, *tv2;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	tv1 = duk_get_tval(ctx, idx1);
-	tv2 = duk_get_tval(ctx, idx2);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	tv1 = duk_get_tval(thr, idx1);
+	tv2 = duk_get_tval(thr, idx2);
 	if ((tv1 == NULL) || (tv2 == NULL)) {
 		return 0;
 	}
@@ -22335,10 +23360,10 @@
  *  instanceof
  */
 
-DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) {
+DUK_EXTERNAL duk_bool_t duk_instanceof(duk_hthread *thr, duk_idx_t idx1, duk_idx_t idx2) {
 	duk_tval *tv1, *tv2;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* Index validation is strict, which differs from duk_equals().
 	 * The strict behavior mimics how instanceof itself works, e.g.
@@ -22346,19 +23371,19 @@
 	 * be somewhat inconsistent if rval would be allowed to be
 	 * non-existent without a TypeError.
 	 */
-	tv1 = duk_require_tval(ctx, idx1);
+	tv1 = duk_require_tval(thr, idx1);
 	DUK_ASSERT(tv1 != NULL);
-	tv2 = duk_require_tval(ctx, idx2);
+	tv2 = duk_require_tval(thr, idx2);
 	DUK_ASSERT(tv2 != NULL);
 
-	return duk_js_instanceof((duk_hthread *) ctx, tv1, tv2);
+	return duk_js_instanceof(thr, tv1, tv2);
 }
 
 /*
  *  Lightfunc
  */
 
-DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_context *ctx, duk_c_function func, duk_small_uint_t lf_flags) {
+DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_hthread *thr, duk_c_function func, duk_small_uint_t lf_flags) {
 	/* Lightfunc name, includes Duktape/C native function pointer, which
 	 * can often be used to locate the function from a symbol table.
 	 * The name also includes the 16-bit duk_tval flags field because it
@@ -22371,32 +23396,37 @@
 	 * is accessed).
 	 */
 
-	duk_push_sprintf(ctx, "light_");
-	duk_push_string_funcptr(ctx, (duk_uint8_t *) &func, sizeof(func));
-	duk_push_sprintf(ctx, "_%04x", (unsigned int) lf_flags);
-	duk_concat(ctx, 3);
-}
-
-DUK_INTERNAL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk_push_sprintf(thr, "light_");
+	duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func));
+	duk_push_sprintf(thr, "_%04x", (unsigned int) lf_flags);
+	duk_concat(thr, 3);
+}
+
+DUK_INTERNAL void duk_push_lightfunc_name(duk_hthread *thr, duk_tval *tv) {
 	duk_c_function func;
 	duk_small_uint_t lf_flags;
 
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
+
 	DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
-	duk_push_lightfunc_name_raw(ctx, func, lf_flags);
-}
-
-DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) {
+	duk_push_lightfunc_name_raw(thr, func, lf_flags);
+}
+
+DUK_INTERNAL void duk_push_lightfunc_tostring(duk_hthread *thr, duk_tval *tv) {
 	duk_c_function func;
 	duk_small_uint_t lf_flags;
 
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
+
 	DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);  /* read before 'tv' potentially invalidated */
-
-	duk_push_string(ctx, "function ");
-	duk_push_lightfunc_name_raw(ctx, func, lf_flags);
-	duk_push_string(ctx, "() { [lightfunc code] }");
-	duk_concat(ctx, 3);
+	duk_push_string(thr, "function ");
+	duk_push_lightfunc_name_raw(thr, func, lf_flags);
+	duk_push_string(thr, "() { [lightfunc code] }");
+	duk_concat(thr, 3);
 }
 
 /*
@@ -22406,12 +23436,13 @@
  *  bytes from memory.
  */
 
-DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz) {
+DUK_INTERNAL void duk_push_string_funcptr(duk_hthread *thr, duk_uint8_t *ptr, duk_size_t sz) {
 	duk_uint8_t buf[32 * 2];
 	duk_uint8_t *p, *q;
 	duk_small_uint_t i;
 	duk_small_uint_t t;
 
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(sz <= 32);  /* sanity limit for function pointer size */
 
 	p = buf;
@@ -22430,7 +23461,7 @@
 		*p++ = duk_lc_digits[t & 0x0f];
 	}
 
-	duk_push_lstring(ctx, (const char *) buf, sz * 2);
+	duk_push_lstring(thr, (const char *) buf, sz * 2);
 }
 
 /*
@@ -22440,25 +23471,27 @@
  *  and is not intended to be fast (but small and safe).
  */
 
-#define DUK__READABLE_STRING_MAXCHARS 32
+/* String limits for summary strings. */
+#define DUK__READABLE_SUMMARY_MAXCHARS 96  /* maximum supported by helper */
+#define DUK__READABLE_STRING_MAXCHARS  32  /* for strings/symbols */
+#define DUK__READABLE_ERRMSG_MAXCHARS  96  /* for error messages */
 
 /* String sanitizer which escapes ASCII control characters and a few other
  * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with
  * question marks.  No errors are thrown for any input string, except in out
  * of memory situations.
  */
-DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring *h_input) {
-	duk_hthread *thr;
+DUK_LOCAL void duk__push_hstring_readable_unicode(duk_hthread *thr, duk_hstring *h_input, duk_small_uint_t maxchars) {
 	const duk_uint8_t *p, *p_start, *p_end;
-	duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_STRING_MAXCHARS +
+	duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_SUMMARY_MAXCHARS +
 	                2 /*quotes*/ + 3 /*periods*/];
 	duk_uint8_t *q;
 	duk_ucodepoint_t cp;
 	duk_small_uint_t nchars;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(h_input != NULL);
-	thr = (duk_hthread *) ctx;
+	DUK_ASSERT(maxchars <= DUK__READABLE_SUMMARY_MAXCHARS);
 
 	p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
 	p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
@@ -22471,7 +23504,7 @@
 		if (p >= p_end) {
 			break;
 		}
-		if (nchars == DUK__READABLE_STRING_MAXCHARS) {
+		if (nchars == maxchars) {
 			*q++ = (duk_uint8_t) DUK_ASC_PERIOD;
 			*q++ = (duk_uint8_t) DUK_ASC_PERIOD;
 			*q++ = (duk_uint8_t) DUK_ASC_PERIOD;
@@ -22496,24 +23529,32 @@
 	}
 	*q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE;
 
-	duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (q - buf));
-}
-
-DUK_LOCAL const char *duk__push_string_tval_readable(duk_context *ctx, duk_tval *tv, duk_bool_t error_aware) {
-	duk_hthread *thr;
-
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
+	duk_push_lstring(thr, (const char *) buf, (duk_size_t) (q - buf));
+}
+
+DUK_LOCAL const char *duk__push_string_tval_readable(duk_hthread *thr, duk_tval *tv, duk_bool_t error_aware) {
+	DUK_ASSERT_CTX_VALID(thr);
 	/* 'tv' may be NULL */
 
 	if (tv == NULL) {
-		duk_push_string(ctx, "none");
+		duk_push_string(thr, "none");
 	} else {
 		switch (DUK_TVAL_GET_TAG(tv)) {
 		case DUK_TAG_STRING: {
-			/* XXX: symbol support (maybe in summary rework branch) */
-			duk__push_hstring_readable_unicode(ctx, DUK_TVAL_GET_STRING(tv));
+			duk_hstring *h = DUK_TVAL_GET_STRING(tv);
+			if (DUK_HSTRING_HAS_SYMBOL(h)) {
+				/* XXX: string summary produces question marks
+				 * so this is not very ideal.
+				 */
+				duk_push_string(thr, "[Symbol ");
+				duk_push_string(thr, duk__get_symbol_type_string(h));
+				duk_push_string(thr, " ");
+				duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS);
+				duk_push_string(thr, "]");
+				duk_concat(thr, 5);
+				break;
+			}
+			duk__push_hstring_readable_unicode(thr, h, DUK__READABLE_STRING_MAXCHARS);
 			break;
 		}
 		case DUK_TAG_OBJECT: {
@@ -22531,16 +23572,15 @@
 				 */
 				duk_tval *tv_msg;
 				tv_msg = duk_hobject_find_existing_entry_tval_ptr(thr->heap, h, DUK_HTHREAD_STRING_MESSAGE(thr));
-				if (tv_msg) {
-					/* It's important this summarization is
-					 * not error aware to avoid unlimited
-					 * recursion when the .message property
-					 * is e.g. another error.
+				if (tv_msg != NULL && DUK_TVAL_IS_STRING(tv_msg)) {
+					/* It's critical to avoid recursion so
+					 * only summarize a string .message.
 					 */
-					return duk_push_string_tval_readable(ctx, tv_msg);
-				}
-			}
-			duk_push_class_string_tval(ctx, tv);
+					duk__push_hstring_readable_unicode(thr, DUK_TVAL_GET_STRING(tv_msg), DUK__READABLE_ERRMSG_MAXCHARS);
+					break;
+				}
+			}
+			duk_push_class_string_tval(thr, tv);
 			break;
 		}
 		case DUK_TAG_BUFFER: {
@@ -22551,49 +23591,51 @@
 			/* XXX: Hex encoded, length limited buffer summary here? */
 			duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
 			DUK_ASSERT(h != NULL);
-			duk_push_sprintf(ctx, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h));
+			duk_push_sprintf(thr, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h));
 			break;
 		}
 		case DUK_TAG_POINTER: {
 			/* Surround with parentheses like in JX, ensures NULL pointer
 			 * is distinguishable from null value ("(null)" vs "null").
 			 */
-			duk_push_tval(ctx, tv);
-			duk_push_sprintf(ctx, "(%s)", duk_to_string(ctx, -1));
-			duk_remove_m2(ctx);
+			duk_push_tval(thr, tv);
+			duk_push_sprintf(thr, "(%s)", duk_to_string(thr, -1));
+			duk_remove_m2(thr);
 			break;
 		}
 		default: {
-			duk_push_tval(ctx, tv);
-			break;
-		}
-		}
-	}
-
-	return duk_to_string(ctx, -1);
-}
-DUK_INTERNAL const char *duk_push_string_tval_readable(duk_context *ctx, duk_tval *tv) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__push_string_tval_readable(ctx, tv, 0 /*error_aware*/);
-}
-
-DUK_INTERNAL const char *duk_push_string_readable(duk_context *ctx, duk_idx_t idx) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk_push_string_tval_readable(ctx, duk_get_tval(ctx, idx));
-}
-
-DUK_INTERNAL const char *duk_push_string_tval_readable_error(duk_context *ctx, duk_tval *tv) {
-	DUK_ASSERT_CTX_VALID(ctx);
-	return duk__push_string_tval_readable(ctx, tv, 1 /*error_aware*/);
-}
-
-DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_context *ctx, duk_hstring *h) {
+			duk_push_tval(thr, tv);
+			break;
+		}
+		}
+	}
+
+	return duk_to_string(thr, -1);
+}
+DUK_INTERNAL const char *duk_push_string_tval_readable(duk_hthread *thr, duk_tval *tv) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__push_string_tval_readable(thr, tv, 0 /*error_aware*/);
+}
+
+DUK_INTERNAL const char *duk_push_string_readable(duk_hthread *thr, duk_idx_t idx) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk_push_string_tval_readable(thr, duk_get_tval(thr, idx));
+}
+
+DUK_INTERNAL const char *duk_push_string_tval_readable_error(duk_hthread *thr, duk_tval *tv) {
+	DUK_ASSERT_API_ENTRY(thr);
+	return duk__push_string_tval_readable(thr, tv, 1 /*error_aware*/);
+}
+
+DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_hthread *thr, duk_hstring *h) {
 	const duk_uint8_t *p;
 	const duk_uint8_t *p_end;
 	const duk_uint8_t *q;
 
+	DUK_ASSERT_API_ENTRY(thr);
+
 	/* .toString() */
-	duk_push_string(ctx, "Symbol(");
+	duk_push_string(thr, "Symbol(");
 	p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
 	p_end = p + DUK_HSTRING_GET_BYTELEN(h);
 	DUK_ASSERT(p[0] == 0xff || (p[0] & 0xc0) == 0x80);
@@ -22608,16 +23650,60 @@
 			break;
 		}
 	}
-	duk_push_lstring(ctx, (const char *) p, (duk_size_t) (q - p));
-	duk_push_string(ctx, ")");
-	duk_concat(ctx, 3);
+	duk_push_lstring(thr, (const char *) p, (duk_size_t) (q - p));
+	duk_push_string(thr, ")");
+	duk_concat(thr, 3);
+}
+
+/*
+ *  Functions
+ */
+
+#if 0  /* not used yet */
+DUK_INTERNAL void duk_push_hnatfunc_name(duk_hthread *thr, duk_hnatfunc *h) {
+	duk_c_function func;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(h != NULL);
+	DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h));
+
+	duk_push_sprintf(thr, "native_");
+	func = h->func;
+	duk_push_string_funcptr(thr, (duk_uint8_t *) &func, sizeof(func));
+	duk_push_sprintf(thr, "_%04x_%04x",
+	                 (unsigned int) (duk_uint16_t) h->nargs,
+	                 (unsigned int) (duk_uint16_t) h->magic);
+	duk_concat(thr, 3);
+}
+#endif
+
+/*
+ *  duk_tval slice copy
+ */
+
+DUK_INTERNAL void duk_copy_tvals_incref(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_size_t count) {
+	duk_tval *tv;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(thr);
+	DUK_ASSERT(count * sizeof(duk_tval) >= count);  /* no wrap */
+	DUK_MEMCPY((void *) tv_dst, (const void *) tv_src, count * sizeof(duk_tval));
+
+	tv = tv_dst;
+	while (count-- > 0) {
+		DUK_TVAL_INCREF(thr, tv);
+		tv++;
+	}
 }
 
 /* automatic undefs */
+#undef DUK__ASSERT_SPACE
 #undef DUK__CHECK_SPACE
 #undef DUK__ERROR_STASH_SHARED
 #undef DUK__PACK_ARGS
+#undef DUK__READABLE_ERRMSG_MAXCHARS
 #undef DUK__READABLE_STRING_MAXCHARS
+#undef DUK__READABLE_SUMMARY_MAXCHARS
 #line 1 "duk_api_string.c"
 /*
  *  String manipulation
@@ -22625,8 +23711,7 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, duk_bool_t is_join) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL void duk__concat_and_join_helper(duk_hthread *thr, duk_idx_t count_in, duk_bool_t is_join) {
 	duk_uint_t count;
 	duk_uint_t i;
 	duk_size_t idx;
@@ -22634,7 +23719,7 @@
 	duk_hstring *h;
 	duk_uint8_t *buf;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	if (DUK_UNLIKELY(count_in <= 0)) {
 		if (count_in < 0) {
@@ -22642,14 +23727,14 @@
 			return;
 		}
 		DUK_ASSERT(count_in == 0);
-		duk_push_hstring_empty(ctx);
+		duk_push_hstring_empty(thr);
 		return;
 	}
 	count = (duk_uint_t) count_in;
 
 	if (is_join) {
 		duk_size_t t1, t2, limit;
-		h = duk_to_hstring(ctx, -((duk_idx_t) count) - 1);
+		h = duk_to_hstring(thr, -((duk_idx_t) count) - 1);
 		DUK_ASSERT(h != NULL);
 
 		/* A bit tricky overflow test, see doc/code-issues.rst. */
@@ -22667,7 +23752,7 @@
 
 	for (i = count; i >= 1; i--) {
 		duk_size_t new_len;
-		h = duk_to_hstring(ctx, -((duk_idx_t) i));
+		h = duk_to_hstring(thr, -((duk_idx_t) i));
 		new_len = len + (duk_size_t) DUK_HSTRING_GET_BYTELEN(h);
 
 		/* Impose a string maximum length, need to handle overflow
@@ -22686,7 +23771,7 @@
 	/* Use stack allocated buffer to ensure reachability in errors
 	 * (e.g. intern error).
 	 */
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, len);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len);
 	DUK_ASSERT(buf != NULL);
 
 	/* [ ... (sep) str1 str2 ... strN buf ] */
@@ -22694,11 +23779,11 @@
 	idx = 0;
 	for (i = count; i >= 1; i--) {
 		if (is_join && i != count) {
-			h = duk_require_hstring(ctx, -((duk_idx_t) count) - 2);  /* extra -1 for buffer */
+			h = duk_require_hstring(thr, -((duk_idx_t) count) - 2);  /* extra -1 for buffer */
 			DUK_MEMCPY(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
 			idx += DUK_HSTRING_GET_BYTELEN(h);
 		}
-		h = duk_require_hstring(ctx, -((duk_idx_t) i) - 1);  /* extra -1 for buffer */
+		h = duk_require_hstring(thr, -((duk_idx_t) i) - 1);  /* extra -1 for buffer */
 		DUK_MEMCPY(buf + idx, DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
 		idx += DUK_HSTRING_GET_BYTELEN(h);
 	}
@@ -22710,16 +23795,16 @@
 	/* Get rid of the strings early to minimize memory use before intern. */
 
 	if (is_join) {
-		duk_replace(ctx, -((duk_idx_t) count) - 2);  /* overwrite sep */
-		duk_pop_n(ctx, count);
-	} else {
-		duk_replace(ctx, -((duk_idx_t) count) - 1);  /* overwrite str1 */
-		duk_pop_n(ctx, count-1);
+		duk_replace(thr, -((duk_idx_t) count) - 2);  /* overwrite sep */
+		duk_pop_n(thr, (duk_idx_t) count);
+	} else {
+		duk_replace(thr, -((duk_idx_t) count) - 1);  /* overwrite str1 */
+		duk_pop_n(thr, (duk_idx_t) (count - 1));
 	}
 
 	/* [ ... buf ] */
 
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe if inputs are safe. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */
 
 	/* [ ... res ] */
 	return;
@@ -22728,31 +23813,74 @@
 	DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG);
 }
 
-DUK_EXTERNAL void duk_concat(duk_context *ctx, duk_idx_t count) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__concat_and_join_helper(ctx, count, 0 /*is_join*/);
-}
-
-DUK_EXTERNAL void duk_join(duk_context *ctx, duk_idx_t count) {
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	duk__concat_and_join_helper(ctx, count, 1 /*is_join*/);
+DUK_EXTERNAL void duk_concat(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__concat_and_join_helper(thr, count, 0 /*is_join*/);
+}
+
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_INTERNAL void duk_concat_2(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	duk_concat(thr, 2);
+}
+#else  /* DUK_USE_PREFER_SIZE */
+DUK_INTERNAL void duk_concat_2(duk_hthread *thr) {
+	duk_hstring *h1;
+	duk_hstring *h2;
+	duk_uint8_t *buf;
+	duk_size_t len1;
+	duk_size_t len2;
+	duk_size_t len;
+
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_ASSERT(duk_get_top(thr) >= 2);  /* Trusted caller. */
+
+	h1 = duk_to_hstring(thr, -2);
+	h2 = duk_to_hstring(thr, -1);
+	len1 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1);
+	len2 = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2);
+	len = len1 + len2;
+	if (DUK_UNLIKELY(len < len1 ||  /* wrapped */
+	                 len > (duk_size_t) DUK_HSTRING_MAX_BYTELEN)) {
+		goto error_overflow;
+	}
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, len);
+	DUK_ASSERT(buf != NULL);
+
+	DUK_MEMCPY((void *) buf, (const void *) DUK_HSTRING_GET_DATA(h1), (size_t) len1);
+	DUK_MEMCPY((void *) (buf + len1), (const void *) DUK_HSTRING_GET_DATA(h2), (size_t) len2);
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */
+
+	/* [ ... str1 str2 buf ] */
+
+	duk_replace(thr, -3);
+	duk_pop_unsafe(thr);
+	return;
+
+ error_overflow:
+	DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG);
+}
+#endif  /* DUK_USE_PREFER_SIZE */
+
+DUK_EXTERNAL void duk_join(duk_hthread *thr, duk_idx_t count) {
+	DUK_ASSERT_API_ENTRY(thr);
+
+	duk__concat_and_join_helper(thr, count, 1 /*is_join*/);
 }
 
 /* XXX: could map/decode be unified with duk_unicode_support.c code?
  * Case conversion needs also the character surroundings though.
  */
 
-DUK_EXTERNAL void duk_decode_string(duk_context *ctx, duk_idx_t idx, duk_decode_char_function callback, void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_decode_string(duk_hthread *thr, duk_idx_t idx, duk_decode_char_function callback, void *udata) {
 	duk_hstring *h_input;
 	const duk_uint8_t *p, *p_start, *p_end;
 	duk_codepoint_t cp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	h_input = duk_require_hstring(ctx, idx);  /* Accept symbols. */
+	DUK_ASSERT_API_ENTRY(thr);
+
+	h_input = duk_require_hstring(thr, idx);  /* Accept symbols. */
 	DUK_ASSERT(h_input != NULL);
 
 	p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
@@ -22768,19 +23896,18 @@
 	}
 }
 
-DUK_EXTERNAL void duk_map_string(duk_context *ctx, duk_idx_t idx, duk_map_char_function callback, void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_map_string(duk_hthread *thr, duk_idx_t idx, duk_map_char_function callback, void *udata) {
 	duk_hstring *h_input;
 	duk_bufwriter_ctx bw_alloc;
 	duk_bufwriter_ctx *bw;
 	const duk_uint8_t *p, *p_start, *p_end;
 	duk_codepoint_t cp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_normalize_index(ctx, idx);
-
-	h_input = duk_require_hstring(ctx, idx);  /* Accept symbols. */
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_normalize_index(thr, idx);
+
+	h_input = duk_require_hstring(thr, idx);  /* Accept symbols. */
 	DUK_ASSERT(h_input != NULL);
 
 	bw = &bw_alloc;
@@ -22805,22 +23932,21 @@
 	}
 
 	DUK_BW_COMPACT(thr, bw);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe, extended UTF-8 encoded. */
-	duk_replace(ctx, idx);
-}
-
-DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t start_offset, duk_size_t end_offset) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	(void) duk_buffer_to_string(thr, -1);  /* Safe, extended UTF-8 encoded. */
+	duk_replace(thr, idx);
+}
+
+DUK_EXTERNAL void duk_substring(duk_hthread *thr, duk_idx_t idx, duk_size_t start_offset, duk_size_t end_offset) {
 	duk_hstring *h;
 	duk_hstring *res;
 	duk_size_t start_byte_offset;
 	duk_size_t end_byte_offset;
 	duk_size_t charlen;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);  /* Accept symbols. */
-	h = duk_require_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);  /* Accept symbols. */
+	h = duk_require_hstring(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	charlen = DUK_HSTRING_GET_CHARLEN(h);
@@ -22851,24 +23977,23 @@
 	                                       DUK_HSTRING_GET_DATA(h) + start_byte_offset,
 	                                       (duk_uint32_t) (end_byte_offset - start_byte_offset));
 
-	duk_push_hstring(ctx, res);
-	duk_replace(ctx, idx);
+	duk_push_hstring(thr, res);
+	duk_replace(thr, idx);
 }
 
 /* XXX: this is quite clunky.  Add Unicode helpers to scan backwards and
  * forwards with a callback to process codepoints?
  */
-DUK_EXTERNAL void duk_trim(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_EXTERNAL void duk_trim(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 	const duk_uint8_t *p, *p_start, *p_end, *p_tmp1, *p_tmp2;  /* pointers for scanning */
 	const duk_uint8_t *q_start, *q_end;  /* start (incl) and end (excl) of trimmed part */
 	duk_codepoint_t cp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-
-	idx = duk_require_normalize_index(ctx, idx);  /* Accept symbols. */
-	h = duk_require_hstring(ctx, idx);
+	DUK_ASSERT_API_ENTRY(thr);
+
+	idx = duk_require_normalize_index(thr, idx);  /* Accept symbols. */
+	h = duk_require_hstring(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	p_start = DUK_HSTRING_GET_DATA(h);
@@ -22930,22 +24055,21 @@
 		return;
 	}
 
-	duk_push_lstring(ctx, (const char *) q_start, (duk_size_t) (q_end - q_start));
-	duk_replace(ctx, idx);
-}
-
-DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_context *ctx, duk_idx_t idx, duk_size_t char_offset) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	duk_push_lstring(thr, (const char *) q_start, (duk_size_t) (q_end - q_start));
+	duk_replace(thr, idx);
+}
+
+DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_hthread *thr, duk_idx_t idx, duk_size_t char_offset) {
 	duk_hstring *h;
 	duk_ucodepoint_t cp;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_API_ENTRY(thr);
 
 	/* XXX: Share code with String.prototype.charCodeAt?  Main difference
 	 * is handling of clamped offsets.
 	 */
 
-	h = duk_require_hstring(ctx, idx);  /* Accept symbols. */
+	h = duk_require_hstring(thr, idx);  /* Accept symbols. */
 	DUK_ASSERT(h != NULL);
 
 	DUK_ASSERT_DISABLE(char_offset >= 0);  /* Always true, arg is unsigned. */
@@ -22964,18 +24088,56 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_EXTERNAL duk_double_t duk_get_now(duk_context *ctx) {
-	return ((duk_double_t) DUK_USE_DATE_GET_NOW((ctx)));
-}
-
-DUK_EXTERNAL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, duk_time_components *comp) {
+DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time(duk_hthread *thr) {
+	/* Ecmascript time, with millisecond fractions.  Exposed via
+	 * duk_get_now() for example.
+	 */
+	DUK_UNREF(thr);
+	return (duk_double_t) DUK_USE_DATE_GET_NOW(thr);
+}
+
+DUK_INTERNAL duk_double_t duk_time_get_ecmascript_time_nofrac(duk_hthread *thr) {
+	/* Ecmascript time without millisecond fractions.  Exposed via
+	 * the Date built-in which doesn't allow fractions.
+	 */
+	DUK_UNREF(thr);
+	return (duk_double_t) DUK_FLOOR(DUK_USE_DATE_GET_NOW(thr));
+}
+
+DUK_INTERNAL duk_double_t duk_time_get_monotonic_time(duk_hthread *thr) {
+	DUK_UNREF(thr);
+#if defined(DUK_USE_GET_MONOTONIC_TIME)
+	return (duk_double_t) DUK_USE_GET_MONOTONIC_TIME(thr);
+#else
+	return (duk_double_t) DUK_USE_DATE_GET_NOW(thr);
+#endif
+}
+
+DUK_EXTERNAL duk_double_t duk_get_now(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(thr);
+
+	/* This API intentionally allows millisecond fractions. */
+	return duk_time_get_ecmascript_time(thr);
+}
+
+#if 0  /* XXX: worth exposing? */
+DUK_EXTERNAL duk_double_t duk_get_monotonic_time(duk_hthread *thr) {
+	DUK_ASSERT_API_ENTRY(thr);
+	DUK_UNREF(thr);
+
+	return duk_time_get_monotonic_time(thr);
+}
+#endif
+
+DUK_EXTERNAL void duk_time_to_components(duk_hthread *thr, duk_double_t timeval, duk_time_components *comp) {
 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
 	duk_uint_t flags;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(comp != NULL);  /* XXX: or check? */
-	DUK_UNREF(ctx);
+	DUK_UNREF(thr);
 
 	/* Convert as one-based, but change month to zero-based to match the
 	 * Ecmascript Date built-in behavior 1:1.
@@ -22984,6 +24146,8 @@
 
 	duk_bi_date_timeval_to_parts(timeval, parts, dparts, flags);
 
+	/* XXX: sub-millisecond accuracy for the API */
+
 	DUK_ASSERT(dparts[DUK_DATE_IDX_MONTH] >= 1.0 && dparts[DUK_DATE_IDX_MONTH] <= 12.0);
 	comp->year = dparts[DUK_DATE_IDX_YEAR];
 	comp->month = dparts[DUK_DATE_IDX_MONTH] - 1.0;
@@ -22995,14 +24159,14 @@
 	comp->weekday = dparts[DUK_DATE_IDX_WEEKDAY];
 }
 
-DUK_EXTERNAL duk_double_t duk_components_to_time(duk_context *ctx, duk_time_components *comp) {
+DUK_EXTERNAL duk_double_t duk_components_to_time(duk_hthread *thr, duk_time_components *comp) {
 	duk_double_t d;
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
 	duk_uint_t flags;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT_API_ENTRY(thr);
 	DUK_ASSERT(comp != NULL);  /* XXX: or check? */
-	DUK_UNREF(ctx);
+	DUK_UNREF(thr);
 
 	/* Match Date constructor behavior (with UTC time).  Month is given
 	 * as zero-based.  Day-of-month is given as one-based so normalize
@@ -23087,27 +24251,27 @@
  * Note that length is left on stack (it could be popped, but that's not
  * usually necessary because call handling will clean it up automatically).
  */
-DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_context *ctx) {
+DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32(duk_hthread *thr) {
 	duk_uint32_t len;
 
 	/* XXX: push more directly? */
-	(void) duk_push_this_coercible_to_object(ctx);
-	DUK_ASSERT_HOBJECT_VALID(duk_get_hobject(ctx, -1));
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_LENGTH);
-	len = duk_to_uint32(ctx, -1);
+	(void) duk_push_this_coercible_to_object(thr);
+	DUK_ASSERT_HOBJECT_VALID(duk_get_hobject(thr, -1));
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_LENGTH);
+	len = duk_to_uint32(thr, -1);
 
 	/* -> [ ... ToObject(this) ToUint32(length) ] */
 	return len;
 }
 
-DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) {
+DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_hthread *thr) {
 	/* Range limited to [0, 0x7fffffff] range, i.e. range that can be
 	 * represented with duk_int32_t.  Use this when the method doesn't
 	 * handle the full 32-bit unsigned range correctly.
 	 */
-	duk_uint32_t ret = duk__push_this_obj_len_u32(ctx);
+	duk_uint32_t ret = duk__push_this_obj_len_u32(thr);
 	if (DUK_UNLIKELY(ret >= 0x80000000UL)) {
-		DUK_ERROR_RANGE_INVALID_LENGTH((duk_hthread *) ctx);
+		DUK_ERROR_RANGE_INVALID_LENGTH(thr);
 	}
 	return ret;
 }
@@ -23119,13 +24283,11 @@
  * significant fraction to improve performance.  Return a non-NULL duk_harray
  * pointer when all fast path criteria are met, NULL otherwise.
  */
-DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_hthread *thr) {
 	duk_tval *tv;
 	duk_hobject *h;
 	duk_uint_t flags_mask, flags_bits, flags_value;
 
-	thr = (duk_hthread *) ctx;
 	DUK_ASSERT(thr->valstack_bottom > thr->valstack);  /* because call in progress */
 	tv = DUK_GET_THIS_TVAL_PTR(thr);
 
@@ -23174,34 +24336,34 @@
  *  Constructor
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_harray *a;
 	duk_double_t d;
 	duk_uint32_t len;
 	duk_uint32_t len_prealloc;
 
-	nargs = duk_get_top(ctx);
-
-	if (nargs == 1 && duk_is_number(ctx, 0)) {
+	nargs = duk_get_top(thr);
+
+	if (nargs == 1 && duk_is_number(thr, 0)) {
 		/* XXX: expensive check (also shared elsewhere - so add a shared internal API call?) */
-		d = duk_get_number(ctx, 0);
-		len = duk_to_uint32(ctx, 0);
+		d = duk_get_number(thr, 0);
+		len = duk_to_uint32(thr, 0);
 		if (((duk_double_t) len) != d) {
-			DUK_DCERROR_RANGE_INVALID_LENGTH((duk_hthread *) ctx);
+			DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
 		}
 
 		/* For small lengths create a dense preallocated array.
 		 * For large arrays preallocate an initial part.
 		 */
 		len_prealloc = len < 64 ? len : 64;
-		a = duk_push_harray_with_size(ctx, len_prealloc);
+		a = duk_push_harray_with_size(thr, len_prealloc);
 		DUK_ASSERT(a != NULL);
 		a->length = len;
 		return 1;
 	}
 
-	duk_pack(ctx, nargs);
+	duk_pack(thr, nargs);
 	return 1;
 }
 
@@ -23209,11 +24371,11 @@
  *  isArray()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_hthread *thr) {
 	duk_hobject *h;
 
-	h = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_ARRAY);
-	duk_push_boolean(ctx, (h != NULL));
+	h = duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_ARRAY);
+	duk_push_boolean(thr, (h != NULL));
 	return 1;
 }
 
@@ -23221,12 +24383,12 @@
  *  toString()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) {
-	(void) duk_push_this_coercible_to_object(ctx);
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_JOIN);
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_hthread *thr) {
+	(void) duk_push_this_coercible_to_object(thr);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_JOIN);
 
 	/* [ ... this func ] */
-	if (!duk_is_callable(ctx, -1)) {
+	if (!duk_is_callable(thr, -1)) {
 		/* Fall back to the initial (original) Object.toString().  We don't
 		 * currently have pointers to the built-in functions, only the top
 		 * level global objects (like "Array") so this is now done in a bit
@@ -23238,20 +24400,20 @@
 		 * but should have no visible side effects.
 		 */
 		DUK_DDD(DUK_DDDPRINT("this.join is not callable, fall back to (original) Object.toString"));
-		duk_set_top(ctx, 0);
-		return duk_bi_object_prototype_to_string(ctx);  /* has access to 'this' binding */
+		duk_set_top(thr, 0);
+		return duk_bi_object_prototype_to_string(thr);  /* has access to 'this' binding */
 	}
 
 	/* [ ... this func ] */
 
-	duk_insert(ctx, -2);
+	duk_insert(thr, -2);
 
 	/* [ ... func this ] */
 
 	DUK_DDD(DUK_DDDPRINT("calling: func=%!iT, this=%!iT",
-	                     (duk_tval *) duk_get_tval(ctx, -2),
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-	duk_call_method(ctx, 0);
+	                     (duk_tval *) duk_get_tval(thr, -2),
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+	duk_call_method(thr, 0);
 
 	return 1;
 }
@@ -23260,7 +24422,7 @@
  *  concat()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_hthread *thr) {
 	duk_idx_t i, n;
 	duk_uarridx_t idx, idx_last;
 	duk_uarridx_t j, len;
@@ -23271,10 +24433,10 @@
 	 * (as the element is dup()'d anyway).
 	 */
 
-	(void) duk_push_this_coercible_to_object(ctx);
-	duk_insert(ctx, 0);
-	n = duk_get_top(ctx);
-	duk_push_array(ctx);  /* -> [ ToObject(this) item1 ... itemN arr ] */
+	(void) duk_push_this_coercible_to_object(thr);
+	duk_insert(thr, 0);
+	n = duk_get_top(thr);
+	duk_push_array(thr);  /* -> [ ToObject(this) item1 ... itemN arr ] */
 
 	/* NOTE: The Array special behaviors are NOT invoked by duk_xdef_prop_index()
 	 * (which differs from the official algorithm).  If no error is thrown, this
@@ -23286,14 +24448,14 @@
 	idx = 0;
 	idx_last = 0;
 	for (i = 0; i < n; i++) {
-		DUK_ASSERT_TOP(ctx, n + 1);
+		DUK_ASSERT_TOP(thr, n + 1);
 
 		/* [ ToObject(this) item1 ... itemN arr ] */
 
-		duk_dup(ctx, i);
-		h = duk_get_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_ARRAY);
+		duk_dup(thr, i);
+		h = duk_get_hobject_with_class(thr, -1, DUK_HOBJECT_CLASS_ARRAY);
 		if (!h) {
-			duk_xdef_prop_index_wec(ctx, -2, idx++);
+			duk_xdef_prop_index_wec(thr, -2, idx++);
 			idx_last = idx;
 			continue;
 		}
@@ -23303,15 +24465,15 @@
 		/* XXX: an array can have length higher than 32 bits; this is not handled
 		 * correctly now.
 		 */
-		len = (duk_uarridx_t) duk_get_length(ctx, -1);
+		len = (duk_uarridx_t) duk_get_length(thr, -1);
 		for (j = 0; j < len; j++) {
-			if (duk_get_prop_index(ctx, -1, j)) {
+			if (duk_get_prop_index(thr, -1, j)) {
 				/* [ ToObject(this) item1 ... itemN arr item(i) item(i)[j] ] */
-				duk_xdef_prop_index_wec(ctx, -3, idx++);
+				duk_xdef_prop_index_wec(thr, -3, idx++);
 				idx_last = idx;
 			} else {
 				idx++;
-				duk_pop(ctx);
+				duk_pop_undefined(thr);
 #if defined(DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER)
 				/* According to E5.1 Section 15.4.4.4 nonexistent trailing
 				 * elements do not affect 'length' of the result.  Test262
@@ -23325,17 +24487,17 @@
 #endif
 			}
 		}
-		duk_pop(ctx);
+		duk_pop_unsafe(thr);
 	}
 
 	/* The E5.1 Section 15.4.4.4 algorithm doesn't set the length explicitly
 	 * in the end, but because we're operating with an internal value which
 	 * is known to be an array, this should be equivalent.
 	 */
-	duk_push_uarridx(ctx, idx_last);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
-
-	DUK_ASSERT_TOP(ctx, n + 1);
+	duk_push_uarridx(thr, idx_last);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
+
+	DUK_ASSERT_TOP(thr, n + 1);
 	return 1;
 }
 
@@ -23352,39 +24514,39 @@
  *  There is no fancy handling; the prefix gets re-joined multiple times.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_hthread *thr) {
 	duk_uint32_t len, count;
 	duk_uint32_t idx;
-	duk_small_int_t to_locale_string = duk_get_current_magic(ctx);
+	duk_small_int_t to_locale_string = duk_get_current_magic(thr);
 	duk_idx_t valstack_required;
 
 	/* For join(), nargs is 1.  For toLocaleString(), nargs is 0 and
 	 * setting the top essentially pushes an undefined to the stack,
 	 * thus defaulting to a comma separator.
 	 */
-	duk_set_top(ctx, 1);
-	if (duk_is_undefined(ctx, 0)) {
-		duk_pop(ctx);
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_COMMA);
-	} else {
-		duk_to_string(ctx, 0);
-	}
-
-	len = duk__push_this_obj_len_u32(ctx);
+	duk_set_top(thr, 1);
+	if (duk_is_undefined(thr, 0)) {
+		duk_pop_undefined(thr);
+		duk_push_hstring_stridx(thr, DUK_STRIDX_COMMA);
+	} else {
+		duk_to_string(thr, 0);
+	}
+
+	len = duk__push_this_obj_len_u32(thr);
 
 	/* [ sep ToObject(this) len ] */
 
 	DUK_DDD(DUK_DDDPRINT("sep=%!T, this=%!T, len=%lu",
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1),
+	                     (duk_tval *) duk_get_tval(thr, 0),
+	                     (duk_tval *) duk_get_tval(thr, 1),
 	                     (unsigned long) len));
 
 	/* The extra (+4) is tight. */
-	valstack_required = (len >= DUK__ARRAY_MID_JOIN_LIMIT ?
-	                     DUK__ARRAY_MID_JOIN_LIMIT : len) + 4;
-	duk_require_stack(ctx, valstack_required);
-
-	duk_dup_0(ctx);
+	valstack_required = (duk_idx_t) ((len >= DUK__ARRAY_MID_JOIN_LIMIT ?
+	                                  DUK__ARRAY_MID_JOIN_LIMIT : len) + 4);
+	duk_require_stack(thr, valstack_required);
+
+	duk_dup_0(thr);
 
 	/* [ sep ToObject(this) len sep ] */
 
@@ -23397,9 +24559,9 @@
 			/* [ sep ToObject(this) len sep str0 ... str(count-1) ] */
 			DUK_DDD(DUK_DDDPRINT("mid/final join, count=%ld, idx=%ld, len=%ld",
 			                     (long) count, (long) idx, (long) len));
-			duk_join(ctx, (duk_idx_t) count);  /* -> [ sep ToObject(this) len str ] */
-			duk_dup_0(ctx);                    /* -> [ sep ToObject(this) len str sep ] */
-			duk_insert(ctx, -2);               /* -> [ sep ToObject(this) len sep str ] */
+			duk_join(thr, (duk_idx_t) count);  /* -> [ sep ToObject(this) len str ] */
+			duk_dup_0(thr);                    /* -> [ sep ToObject(this) len str sep ] */
+			duk_insert(thr, -2);               /* -> [ sep ToObject(this) len sep str ] */
 			count = 1;
 		}
 		if (idx >= len) {
@@ -23407,18 +24569,18 @@
 			break;
 		}
 
-		duk_get_prop_index(ctx, 1, (duk_uarridx_t) idx);
-		if (duk_is_null_or_undefined(ctx, -1)) {
-			duk_pop(ctx);
-			duk_push_hstring_empty(ctx);
+		duk_get_prop_index(thr, 1, (duk_uarridx_t) idx);
+		if (duk_is_null_or_undefined(thr, -1)) {
+			duk_pop_nodecref_unsafe(thr);
+			duk_push_hstring_empty(thr);
 		} else {
 			if (to_locale_string) {
-				duk_to_object(ctx, -1);
-				duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_TO_LOCALE_STRING);
-				duk_insert(ctx, -2);  /* -> [ ... toLocaleString ToObject(val) ] */
-				duk_call_method(ctx, 0);
-			}
-			duk_to_string(ctx, -1);
+				duk_to_object(thr, -1);
+				duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_LOCALE_STRING);
+				duk_insert(thr, -2);  /* -> [ ... toLocaleString ToObject(val) ] */
+				duk_call_method(thr, 0);
+			}
+			duk_to_string(thr, -1);
 		}
 
 		count++;
@@ -23435,14 +24597,11 @@
  */
 
 #if defined(DUK_USE_ARRAY_FASTPATH)
-DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_context *ctx, duk_harray *h_arr) {
-	duk_hthread *thr;
+DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_hthread *thr, duk_harray *h_arr) {
 	duk_tval *tv_arraypart;
 	duk_tval *tv_val;
 	duk_uint32_t len;
 
-	thr = (duk_hthread *) ctx;
-
 	tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr);
 	len = h_arr->length;
 	if (len <= 0) {
@@ -23478,59 +24637,58 @@
 }
 #endif  /* DUK_USE_ARRAY_FASTPATH */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_pop(duk_hthread *thr) {
 	duk_uint32_t len;
 	duk_uint32_t idx;
 #if defined(DUK_USE_ARRAY_FASTPATH)
 	duk_harray *h_arr;
 #endif
 
-	DUK_ASSERT_TOP(ctx, 0);
+	DUK_ASSERT_TOP(thr, 0);
 
 #if defined(DUK_USE_ARRAY_FASTPATH)
-	h_arr = duk__arraypart_fastpath_this(ctx);
+	h_arr = duk__arraypart_fastpath_this(thr);
 	if (h_arr) {
-		return duk__array_pop_fastpath(ctx, h_arr);
+		return duk__array_pop_fastpath(thr, h_arr);
 	}
 #endif
 
 	/* XXX: Merge fastpath check into a related call (push this, coerce length, etc)? */
 
-	len = duk__push_this_obj_len_u32(ctx);
+	len = duk__push_this_obj_len_u32(thr);
 	if (len == 0) {
-		duk_push_int(ctx, 0);
-		duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LENGTH);
+		duk_push_int(thr, 0);
+		duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
 		return 0;
 	}
 	idx = len - 1;
 
-	duk_get_prop_index(ctx, 0, (duk_uarridx_t) idx);
-	duk_del_prop_index(ctx, 0, (duk_uarridx_t) idx);
-	duk_push_u32(ctx, idx);
-	duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LENGTH);
+	duk_get_prop_index(thr, 0, (duk_uarridx_t) idx);
+	duk_del_prop_index(thr, 0, (duk_uarridx_t) idx);
+	duk_push_u32(thr, idx);
+	duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
 	return 1;
 }
 
 #if defined(DUK_USE_ARRAY_FASTPATH)
-DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_context *ctx, duk_harray *h_arr) {
-	duk_hthread *thr;
+DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_hthread *thr, duk_harray *h_arr) {
 	duk_tval *tv_arraypart;
 	duk_tval *tv_src;
 	duk_tval *tv_dst;
 	duk_uint32_t len;
 	duk_idx_t i, n;
 
-	thr = (duk_hthread *) ctx;
-
 	len = h_arr->length;
 	tv_arraypart = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr);
 
 	n = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
-	if (DUK_UNLIKELY(len + n < len)) {
+	DUK_ASSERT(n >= 0);
+	DUK_ASSERT((duk_uint32_t) n <= DUK_UINT32_MAX);
+	if (DUK_UNLIKELY(len + (duk_uint32_t) n < len)) {
 		DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
 		DUK_DCERROR_RANGE_INVALID_LENGTH(thr);  /* != 0 return value returned as is by caller */
 	}
-	if (len + n > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr)) {
+	if (len + (duk_uint32_t) n > DUK_HOBJECT_GET_ASIZE((duk_hobject *) h_arr)) {
 		/* Array part would need to be extended.  Rely on slow path
 		 * for now.
 		 *
@@ -23551,16 +24709,16 @@
 		tv_dst++;
 	}
 	thr->valstack_top = thr->valstack_bottom;
-	len += n;
+	len += (duk_uint32_t) n;
 	h_arr->length = len;
 
 	DUK_ASSERT((duk_uint_t) len == len);
-	duk_push_uint(ctx, (duk_uint_t) len);
+	duk_push_uint(thr, (duk_uint_t) len);
 	return 1;
 }
 #endif  /* DUK_USE_ARRAY_FASTPATH */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_hthread *thr) {
 	/* Note: 'this' is not necessarily an Array object.  The push()
 	 * algorithm is supposed to work for other kinds of objects too,
 	 * so the algorithm has e.g. an explicit update for the 'length'
@@ -23574,10 +24732,10 @@
 #endif
 
 #if defined(DUK_USE_ARRAY_FASTPATH)
-	h_arr = duk__arraypart_fastpath_this(ctx);
+	h_arr = duk__arraypart_fastpath_this(thr);
 	if (h_arr) {
 		duk_ret_t rc;
-		rc = duk__array_push_fastpath(ctx, h_arr);
+		rc = duk__array_push_fastpath(thr, h_arr);
 		if (rc != 0) {
 			return rc;
 		}
@@ -23585,8 +24743,8 @@
 	}
 #endif
 
-	n = duk_get_top(ctx);
-	len = duk__push_this_obj_len_u32(ctx);
+	n = duk_get_top(thr);
+	len = duk__push_this_obj_len_u32(thr);
 
 	/* [ arg1 ... argN obj length ] */
 
@@ -23602,18 +24760,18 @@
 
 	if (len + (duk_uint32_t) n < len) {
 		DUK_D(DUK_DPRINT("Array.prototype.push() would go beyond 32-bit length, throw"));
-		DUK_DCERROR_RANGE_INVALID_LENGTH((duk_hthread *) ctx);
+		DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
 	}
 
 	for (i = 0; i < n; i++) {
-		duk_dup(ctx, i);
-		duk_put_prop_index(ctx, -3, len + i);
-	}
-	len += n;
-
-	duk_push_u32(ctx, len);
-	duk_dup_top(ctx);
-	duk_put_prop_stridx_short(ctx, -4, DUK_STRIDX_LENGTH);
+		duk_dup(thr, i);
+		duk_put_prop_index(thr, -3, (duk_uarridx_t) (len + (duk_uint32_t) i));
+	}
+	len += (duk_uint32_t) n;
+
+	duk_push_u32(thr, len);
+	duk_dup_top(thr);
+	duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);
 
 	/* [ arg1 ... argN obj length new_length ] */
 	return 1;
@@ -23629,7 +24787,7 @@
  *  may use a negative offset.
  */
 
-DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t idx1, duk_int_t idx2) {
+DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_hthread *thr, duk_int_t idx1, duk_int_t idx2) {
 	duk_bool_t have1, have2;
 	duk_bool_t undef1, undef2;
 	duk_small_int_t ret;
@@ -23658,12 +24816,12 @@
 		return 0;
 	}
 
-	have1 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx1);
-	have2 = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) idx2);
+	have1 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx1);
+	have2 = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) idx2);
 
 	DUK_DDD(DUK_DDDPRINT("duk__array_sort_compare: idx1=%ld, idx2=%ld, have1=%ld, have2=%ld, val1=%!T, val2=%!T",
 	                     (long) idx1, (long) idx2, (long) have1, (long) have2,
-	                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
 
 	if (have1) {
 		if (have2) {
@@ -23682,8 +24840,8 @@
 		}
 	}
 
-	undef1 = duk_is_undefined(ctx, -2);
-	undef2 = duk_is_undefined(ctx, -1);
+	undef1 = duk_is_undefined(thr, -2);
+	undef2 = duk_is_undefined(thr, -1);
 	if (undef1) {
 		if (undef2) {
 			ret = 0;
@@ -23701,20 +24859,20 @@
 		}
 	}
 
-	if (!duk_is_undefined(ctx, idx_fn)) {
+	if (!duk_is_undefined(thr, idx_fn)) {
 		duk_double_t d;
 
 		/* No need to check callable; duk_call() will do that. */
-		duk_dup(ctx, idx_fn);    /* -> [ ... x y fn ] */
-		duk_insert(ctx, -3);     /* -> [ ... fn x y ] */
-		duk_call(ctx, 2);        /* -> [ ... res ] */
+		duk_dup(thr, idx_fn);    /* -> [ ... x y fn ] */
+		duk_insert(thr, -3);     /* -> [ ... fn x y ] */
+		duk_call(thr, 2);        /* -> [ ... res ] */
 
 		/* ES5 is a bit vague about what to do if the return value is
 		 * not a number.  ES2015 provides a concrete description:
 		 * http://www.ecma-international.org/ecma-262/6.0/#sec-sortcompare.
 		 */
 
-		d = duk_to_number_m1(ctx);
+		d = duk_to_number_m1(thr);
 		if (d < 0.0) {
 			ret = -1;
 		} else if (d > 0.0) {
@@ -23726,7 +24884,7 @@
 			ret = 0;
 		}
 
-		duk_pop(ctx);
+		duk_pop_nodecref_unsafe(thr);
 		DUK_DDD(DUK_DDDPRINT("-> result %ld (from comparefn, after coercion)", (long) ret));
 		return ret;
 	}
@@ -23734,8 +24892,8 @@
 	/* string compare is the default (a bit oddly) */
 
 	/* XXX: any special handling for plain array; causes repeated coercion now? */
-	h1 = duk_to_hstring(ctx, -2);
-	h2 = duk_to_hstring_m1(ctx);
+	h1 = duk_to_hstring(thr, -2);
+	h2 = duk_to_hstring_m1(thr);
 	DUK_ASSERT(h1 != NULL);
 	DUK_ASSERT(h2 != NULL);
 
@@ -23743,12 +24901,12 @@
 	goto pop_ret;
 
  pop_ret:
-	duk_pop_2(ctx);
+	duk_pop_2_unsafe(thr);
 	DUK_DDD(DUK_DDDPRINT("-> result %ld", (long) ret));
 	return ret;
 }
 
-DUK_LOCAL void duk__array_sort_swap(duk_context *ctx, duk_int_t l, duk_int_t r) {
+DUK_LOCAL void duk__array_sort_swap(duk_hthread *thr, duk_int_t l, duk_int_t r) {
 	duk_bool_t have_l, have_r;
 	duk_idx_t idx_obj = 1;  /* fixed offset in valstack */
 
@@ -23757,32 +24915,32 @@
 	}
 
 	/* swap elements; deal with non-existent elements correctly */
-	have_l = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
-	have_r = duk_get_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
+	have_l = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) l);
+	have_r = duk_get_prop_index(thr, idx_obj, (duk_uarridx_t) r);
 
 	if (have_r) {
 		/* right exists, [[Put]] regardless whether or not left exists */
-		duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
-	} else {
-		duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) l);
-		duk_pop(ctx);
+		duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) l);
+	} else {
+		duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) l);
+		duk_pop_undefined(thr);
 	}
 
 	if (have_l) {
-		duk_put_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
-	} else {
-		duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) r);
-		duk_pop(ctx);
+		duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) r);
+	} else {
+		duk_del_prop_index(thr, idx_obj, (duk_uarridx_t) r);
+		duk_pop_undefined(thr);
 	}
 }
 
 #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
 /* Debug print which visualizes the qsort partitioning process. */
-DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int_t hi, duk_int_t pivot) {
+DUK_LOCAL void duk__debuglog_qsort_state(duk_hthread *thr, duk_int_t lo, duk_int_t hi, duk_int_t pivot) {
 	char buf[4096];
 	char *ptr = buf;
 	duk_int_t i, n;
-	n = (duk_int_t) duk_get_length(ctx, 1);
+	n = (duk_int_t) duk_get_length(thr, 1);
 	if (n > 4000) {
 		n = 4000;
 	}
@@ -23808,15 +24966,15 @@
 }
 #endif
 
-DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) {
+DUK_LOCAL void duk__array_qsort(duk_hthread *thr, duk_int_t lo, duk_int_t hi) {
 	duk_int_t p, l, r;
 
 	/* The lo/hi indices may be crossed and hi < 0 is possible at entry. */
 
 	DUK_DDD(DUK_DDDPRINT("duk__array_qsort: lo=%ld, hi=%ld, obj=%!T",
-	                     (long) lo, (long) hi, (duk_tval *) duk_get_tval(ctx, 1)));
-
-	DUK_ASSERT_TOP(ctx, 3);
+	                     (long) lo, (long) hi, (duk_tval *) duk_get_tval(thr, 1)));
+
+	DUK_ASSERT_TOP(thr, 3);
 
 	/* In some cases it may be that lo > hi, or hi < 0; these
 	 * degenerate cases happen e.g. for empty arrays, and in
@@ -23832,14 +24990,14 @@
 	DUK_ASSERT(hi - lo + 1 >= 2);
 
 	/* randomized pivot selection */
-	p = lo + (duk_int_t) (DUK_UTIL_GET_RANDOM_DOUBLE((duk_hthread *) ctx) * (duk_double_t) (hi - lo + 1));
+	p = lo + (duk_int_t) (DUK_UTIL_GET_RANDOM_DOUBLE(thr) * (duk_double_t) (hi - lo + 1));
 	DUK_ASSERT(p >= lo && p <= hi);
 	DUK_DDD(DUK_DDDPRINT("lo=%ld, hi=%ld, chose pivot p=%ld", (long) lo, (long) hi, (long) p));
 
 	/* move pivot out of the way */
-	duk__array_sort_swap(ctx, p, lo);
+	duk__array_sort_swap(thr, p, lo);
 	p = lo;
-	DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
+	DUK_DDD(DUK_DDDPRINT("pivot moved out of the way: %!T", (duk_tval *) duk_get_tval(thr, 1)));
 
 	l = lo + 1;
 	r = hi;
@@ -23851,7 +25009,7 @@
 			if (l >= hi) {
 				break;
 			}
-			if (duk__array_sort_compare(ctx, l, p) >= 0) {  /* !(l < p) */
+			if (duk__array_sort_compare(thr, l, p) >= 0) {  /* !(l < p) */
 				break;
 			}
 			l++;
@@ -23862,7 +25020,7 @@
 			if (r <= lo) {
 				break;
 			}
-			if (duk__array_sort_compare(ctx, p, r) >= 0) {  /* !(p < r) */
+			if (duk__array_sort_compare(thr, p, r) >= 0) {  /* !(p < r) */
 				break;
 			}
 			r--;
@@ -23874,9 +25032,9 @@
 
 		DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r));
 
-		duk__array_sort_swap(ctx, l, r);
-
-		DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
+		duk__array_sort_swap(thr, l, r);
+
+		DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(thr, 1)));
 		l++;
 		r--;
 	}
@@ -23892,25 +25050,25 @@
 	 */
 
 	/* move pivot to its final place */
-	DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(ctx, 1)));
-	duk__array_sort_swap(ctx, lo, r);
+	DUK_DDD(DUK_DDDPRINT("before final pivot swap: %!T", (duk_tval *) duk_get_tval(thr, 1)));
+	duk__array_sort_swap(thr, lo, r);
 
 #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
-	duk__debuglog_qsort_state(ctx, lo, hi, r);
-#endif
-
-	DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(ctx, 1)));
-	duk__array_qsort(ctx, lo, r - 1);
-	duk__array_qsort(ctx, r + 1, hi);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) {
+	duk__debuglog_qsort_state(thr, lo, hi, r);
+#endif
+
+	DUK_DDD(DUK_DDDPRINT("recurse: pivot=%ld, obj=%!T", (long) r, (duk_tval *) duk_get_tval(thr, 1)));
+	duk__array_qsort(thr, lo, r - 1);
+	duk__array_qsort(thr, r + 1, hi);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_hthread *thr) {
 	duk_uint32_t len;
 
 	/* XXX: len >= 0x80000000 won't work below because a signed type
 	 * is needed by qsort.
 	 */
-	len = duk__push_this_obj_len_u32_limited(ctx);
+	len = duk__push_this_obj_len_u32_limited(thr);
 
 	/* stack[0] = compareFn
 	 * stack[1] = ToObject(this)
@@ -23919,11 +25077,11 @@
 
 	if (len > 0) {
 		/* avoid degenerate cases, so that (len - 1) won't underflow */
-		duk__array_qsort(ctx, (duk_int_t) 0, (duk_int_t) (len - 1));
-	}
-
-	DUK_ASSERT_TOP(ctx, 3);
-	duk_pop(ctx);
+		duk__array_qsort(thr, (duk_int_t) 0, (duk_int_t) (len - 1));
+	}
+
+	DUK_ASSERT_TOP(thr, 3);
+	duk_pop_nodecref_unsafe(thr);
 	return 1;  /* return ToObject(this) */
 }
 
@@ -23940,9 +25098,10 @@
  *   unshift is (close to?) <--> splice(0, 0, [items])?
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_hthread *thr) {
 	duk_idx_t nargs;
-	duk_uint32_t len;
+	duk_uint32_t len_u32;
+	duk_int_t len;
 	duk_bool_t have_delcount;
 	duk_int_t item_count;
 	duk_int_t act_start;
@@ -23951,9 +25110,9 @@
 
 	DUK_UNREF(have_delcount);
 
-	nargs = duk_get_top(ctx);
+	nargs = duk_get_top(thr);
 	if (nargs < 2) {
-		duk_set_top(ctx, 2);
+		duk_set_top(thr, 2);
 		nargs = 2;
 		have_delcount = 0;
 	} else {
@@ -23963,18 +25122,20 @@
 	/* XXX: len >= 0x80000000 won't work below because we need to be
 	 * able to represent -len.
 	 */
-	len = duk__push_this_obj_len_u32_limited(ctx);
-
-	act_start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len);
+	len_u32 = duk__push_this_obj_len_u32_limited(thr);
+	len = (duk_int_t) len_u32;
+	DUK_ASSERT(len >= 0);
+
+	act_start = duk_to_int_clamped(thr, 0, -len, len);
 	if (act_start < 0) {
 		act_start = len + act_start;
 	}
-	DUK_ASSERT(act_start >= 0 && act_start <= (duk_int_t) len);
+	DUK_ASSERT(act_start >= 0 && act_start <= len);
 
 #if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT)
 	if (have_delcount) {
 #endif
-		del_count = duk_to_int_clamped(ctx, 1, 0, len - act_start);
+		del_count = duk_to_int_clamped(thr, 1, 0, len - act_start);
 #if defined(DUK_USE_NONSTD_ARRAY_SPLICE_DELCOUNT)
 	} else {
 		/* E5.1 standard behavior when deleteCount is not given would be
@@ -23989,16 +25150,16 @@
 	DUK_ASSERT(nargs >= 2);
 	item_count = (duk_int_t) (nargs - 2);
 
-	DUK_ASSERT(del_count >= 0 && del_count <= (duk_int_t) len - act_start);
-	DUK_ASSERT(del_count + act_start <= (duk_int_t) len);
+	DUK_ASSERT(del_count >= 0 && del_count <= len - act_start);
+	DUK_ASSERT(del_count + act_start <= len);
 
 	/* For now, restrict result array into 32-bit length range. */
 	if (((duk_double_t) len) - ((duk_double_t) del_count) + ((duk_double_t) item_count) > (duk_double_t) DUK_UINT32_MAX) {
 		DUK_D(DUK_DPRINT("Array.prototype.splice() would go beyond 32-bit length, throw"));
-		DUK_DCERROR_RANGE_INVALID_LENGTH((duk_hthread *) ctx);
-	}
-
-	duk_push_array(ctx);
+		DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
+	}
+
+	duk_push_array(thr);
 
 	/* stack[0] = start
 	 * stack[1] = deleteCount
@@ -24008,19 +25169,19 @@
 	 * stack[nargs+2] = result array               -1
 	 */
 
-	DUK_ASSERT_TOP(ctx, nargs + 3);
+	DUK_ASSERT_TOP(thr, nargs + 3);
 
 	/* Step 9: copy elements-to-be-deleted into the result array */
 
 	for (i = 0; i < del_count; i++) {
-		if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (act_start + i))) {
-			duk_xdef_prop_index_wec(ctx, -2, i);  /* throw flag irrelevant (false in std alg) */
-		} else {
-			duk_pop(ctx);
-		}
-	}
-	duk_push_u32(ctx, (duk_uint32_t) del_count);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
+		if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (act_start + i))) {
+			duk_xdef_prop_index_wec(thr, -2, (duk_uarridx_t) i);  /* throw flag irrelevant (false in std alg) */
+		} else {
+			duk_pop_undefined(thr);
+		}
+	}
+	duk_push_u32(thr, (duk_uint32_t) del_count);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
 
 	/* Steps 12 and 13: reorganize elements to make room for itemCount elements */
 
@@ -24031,27 +25192,27 @@
 		 *    [ A B C F G H ]        (actual result at this point, C will be replaced)
 		 */
 
-		DUK_ASSERT_TOP(ctx, nargs + 3);
+		DUK_ASSERT_TOP(thr, nargs + 3);
 
 		n = len - del_count;
 		for (i = act_start; i < n; i++) {
-			if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) {
-				duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count));
-			} else {
-				duk_pop(ctx);
-				duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count));
-			}
-		}
-
-		DUK_ASSERT_TOP(ctx, nargs + 3);
+			if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) {
+				duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count));
+			} else {
+				duk_pop_undefined(thr);
+				duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count));
+			}
+		}
+
+		DUK_ASSERT_TOP(thr, nargs + 3);
 
 		/* loop iterator init and limit changed from standard algorithm */
 		n = len - del_count + item_count;
 		for (i = len - 1; i >= n; i--) {
-			duk_del_prop_index(ctx, -3, (duk_uarridx_t) i);
-		}
-
-		DUK_ASSERT_TOP(ctx, nargs + 3);
+			duk_del_prop_index(thr, -3, (duk_uarridx_t) i);
+		}
+
+		DUK_ASSERT_TOP(thr, nargs + 3);
 	} else if (item_count > del_count) {
 		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 4
 		 * -> [ A B F G H ]          (conceptual intermediate step)
@@ -24059,19 +25220,19 @@
 		 *    [ A B C D E F F G H ]  (actual result at this point)
 		 */
 
-		DUK_ASSERT_TOP(ctx, nargs + 3);
+		DUK_ASSERT_TOP(thr, nargs + 3);
 
 		/* loop iterator init and limit changed from standard algorithm */
 		for (i = len - del_count - 1; i >= act_start; i--) {
-			if (duk_get_prop_index(ctx, -3, (duk_uarridx_t) (i + del_count))) {
-				duk_put_prop_index(ctx, -4, (duk_uarridx_t) (i + item_count));
-			} else {
-				duk_pop(ctx);
-				duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count));
-			}
-		}
-
-		DUK_ASSERT_TOP(ctx, nargs + 3);
+			if (duk_get_prop_index(thr, -3, (duk_uarridx_t) (i + del_count))) {
+				duk_put_prop_index(thr, -4, (duk_uarridx_t) (i + item_count));
+			} else {
+				duk_pop_undefined(thr);
+				duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count));
+			}
+		}
+
+		DUK_ASSERT_TOP(thr, nargs + 3);
 	} else {
 		/*    [ A B C D E F G H ]    rel_index = 2, del_count 3, item count 3
 		 * -> [ A B F G H ]          (conceptual intermediate step)
@@ -24079,24 +25240,24 @@
 		 *    [ A B C D E F G H ]    (actual result at this point)
 		 */
 	}
-	DUK_ASSERT_TOP(ctx, nargs + 3);
+	DUK_ASSERT_TOP(thr, nargs + 3);
 
 	/* Step 15: insert itemCount elements into the hole made above */
 
 	for (i = 0; i < item_count; i++) {
-		duk_dup(ctx, i + 2);  /* args start at index 2 */
-		duk_put_prop_index(ctx, -4, (duk_uarridx_t) (act_start + i));
+		duk_dup(thr, i + 2);  /* args start at index 2 */
+		duk_put_prop_index(thr, -4, (duk_uarridx_t) (act_start + i));
 	}
 
 	/* Step 16: update length; note that the final length may be above 32 bit range
 	 * (but we checked above that this isn't the case here)
 	 */
 
-	duk_push_u32(ctx, len - del_count + item_count);
-	duk_put_prop_stridx_short(ctx, -4, DUK_STRIDX_LENGTH);
+	duk_push_u32(thr, (duk_uint32_t) (len - del_count + item_count));
+	duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);
 
 	/* result array is already at the top of stack */
-	DUK_ASSERT_TOP(ctx, nargs + 3);
+	DUK_ASSERT_TOP(thr, nargs + 3);
 	return 1;
 }
 
@@ -24104,13 +25265,13 @@
  *  reverse()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_hthread *thr) {
 	duk_uint32_t len;
 	duk_uint32_t middle;
 	duk_uint32_t lower, upper;
 	duk_bool_t have_lower, have_upper;
 
-	len = duk__push_this_obj_len_u32(ctx);
+	len = duk__push_this_obj_len_u32(thr);
 	middle = len / 2;
 
 	/* If len <= 1, middle will be 0 and for-loop bails out
@@ -24119,35 +25280,35 @@
 
 	for (lower = 0; lower < middle; lower++) {
 		DUK_ASSERT(len >= 2);
-		DUK_ASSERT_TOP(ctx, 2);
+		DUK_ASSERT_TOP(thr, 2);
 
 		DUK_ASSERT(len >= lower + 1);
 		upper = len - lower - 1;
 
-		have_lower = duk_get_prop_index(ctx, -2, (duk_uarridx_t) lower);
-		have_upper = duk_get_prop_index(ctx, -3, (duk_uarridx_t) upper);
+		have_lower = duk_get_prop_index(thr, -2, (duk_uarridx_t) lower);
+		have_upper = duk_get_prop_index(thr, -3, (duk_uarridx_t) upper);
 
 		/* [ ToObject(this) ToUint32(length) lowerValue upperValue ] */
 
 		if (have_upper) {
-			duk_put_prop_index(ctx, -4, (duk_uarridx_t) lower);
-		} else {
-			duk_del_prop_index(ctx, -4, (duk_uarridx_t) lower);
-			duk_pop(ctx);
+			duk_put_prop_index(thr, -4, (duk_uarridx_t) lower);
+		} else {
+			duk_del_prop_index(thr, -4, (duk_uarridx_t) lower);
+			duk_pop_undefined(thr);
 		}
 
 		if (have_lower) {
-			duk_put_prop_index(ctx, -3, (duk_uarridx_t) upper);
-		} else {
-			duk_del_prop_index(ctx, -3, (duk_uarridx_t) upper);
-			duk_pop(ctx);
-		}
-
-		DUK_ASSERT_TOP(ctx, 2);
-	}
-
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_pop(ctx);  /* -> [ ToObject(this) ] */
+			duk_put_prop_index(thr, -3, (duk_uarridx_t) upper);
+		} else {
+			duk_del_prop_index(thr, -3, (duk_uarridx_t) upper);
+			duk_pop_undefined(thr);
+		}
+
+		DUK_ASSERT_TOP(thr, 2);
+	}
+
+	DUK_ASSERT_TOP(thr, 2);
+	duk_pop_unsafe(thr);  /* -> [ ToObject(this) ] */
 	return 1;
 }
 
@@ -24155,8 +25316,9 @@
  *  slice()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) {
-	duk_uint32_t len;
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_hthread *thr) {
+	duk_uint32_t len_u32;
+	duk_int_t len;
 	duk_int_t start, end;
 	duk_int_t i;
 	duk_uarridx_t idx;
@@ -24165,8 +25327,11 @@
 	/* XXX: len >= 0x80000000 won't work below because we need to be
 	 * able to represent -len.
 	 */
-	len = duk__push_this_obj_len_u32_limited(ctx);
-	duk_push_array(ctx);
+	len_u32 = duk__push_this_obj_len_u32_limited(thr);
+	len = (duk_int_t) len_u32;
+	DUK_ASSERT(len >= 0);
+
+	duk_push_array(thr);
 
 	/* stack[0] = start
 	 * stack[1] = end
@@ -24175,41 +25340,41 @@
 	 * stack[4] = result array
 	 */
 
-	start = duk_to_int_clamped(ctx, 0, -((duk_int_t) len), (duk_int_t) len);
+	start = duk_to_int_clamped(thr, 0, -len, len);
 	if (start < 0) {
 		start = len + start;
 	}
 	/* XXX: could duk_is_undefined() provide defaulting undefined to 'len'
 	 * (the upper limit)?
 	 */
-	if (duk_is_undefined(ctx, 1)) {
+	if (duk_is_undefined(thr, 1)) {
 		end = len;
 	} else {
-		end = duk_to_int_clamped(ctx, 1, -((duk_int_t) len), (duk_int_t) len);
+		end = duk_to_int_clamped(thr, 1, -len, len);
 		if (end < 0) {
 			end = len + end;
 		}
 	}
-	DUK_ASSERT(start >= 0 && (duk_uint32_t) start <= len);
-	DUK_ASSERT(end >= 0 && (duk_uint32_t) end <= len);
+	DUK_ASSERT(start >= 0 && start <= len);
+	DUK_ASSERT(end >= 0 && end <= len);
 
 	idx = 0;
 	for (i = start; i < end; i++) {
-		DUK_ASSERT_TOP(ctx, 5);
-		if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
-			duk_xdef_prop_index_wec(ctx, 4, idx);
+		DUK_ASSERT_TOP(thr, 5);
+		if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
+			duk_xdef_prop_index_wec(thr, 4, idx);
 			res_length = idx + 1;
 		} else {
-			duk_pop(ctx);
+			duk_pop_undefined(thr);
 		}
 		idx++;
-		DUK_ASSERT_TOP(ctx, 5);
-	}
-
-	duk_push_u32(ctx, res_length);
-	duk_xdef_prop_stridx_short(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
-
-	DUK_ASSERT_TOP(ctx, 5);
+		DUK_ASSERT_TOP(thr, 5);
+	}
+
+	duk_push_u32(thr, res_length);
+	duk_xdef_prop_stridx_short(thr, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
+
+	DUK_ASSERT_TOP(thr, 5);
 	return 1;
 }
 
@@ -24217,18 +25382,18 @@
  *  shift()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_hthread *thr) {
 	duk_uint32_t len;
 	duk_uint32_t i;
 
-	len = duk__push_this_obj_len_u32(ctx);
+	len = duk__push_this_obj_len_u32(thr);
 	if (len == 0) {
-		duk_push_int(ctx, 0);
-		duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LENGTH);
-		return 0;
-	}
-
-	duk_get_prop_index(ctx, 0, 0);
+		duk_push_int(thr, 0);
+		duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
+		return 0;
+	}
+
+	duk_get_prop_index(thr, 0, 0);
 
 	/* stack[0] = object (this)
 	 * stack[1] = ToUint32(length)
@@ -24236,22 +25401,22 @@
 	 */
 
 	for (i = 1; i < len; i++) {
-		DUK_ASSERT_TOP(ctx, 3);
-		if (duk_get_prop_index(ctx, 0, (duk_uarridx_t) i)) {
+		DUK_ASSERT_TOP(thr, 3);
+		if (duk_get_prop_index(thr, 0, (duk_uarridx_t) i)) {
 			/* fromPresent = true */
-			duk_put_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
+			duk_put_prop_index(thr, 0, (duk_uarridx_t) (i - 1));
 		} else {
 			/* fromPresent = false */
-			duk_del_prop_index(ctx, 0, (duk_uarridx_t) (i - 1));
-			duk_pop(ctx);
-		}
-	}
-	duk_del_prop_index(ctx, 0, (duk_uarridx_t) (len - 1));
-
-	duk_push_u32(ctx, (duk_uint32_t) (len - 1));
-	duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LENGTH);
-
-	DUK_ASSERT_TOP(ctx, 3);
+			duk_del_prop_index(thr, 0, (duk_uarridx_t) (i - 1));
+			duk_pop_undefined(thr);
+		}
+	}
+	duk_del_prop_index(thr, 0, (duk_uarridx_t) (len - 1));
+
+	duk_push_u32(thr, (duk_uint32_t) (len - 1));
+	duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
+
+	DUK_ASSERT_TOP(thr, 3);
 	return 1;
 }
 
@@ -24259,20 +25424,20 @@
  *  unshift()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_uint32_t len;
 	duk_uint32_t i;
 
-	nargs = duk_get_top(ctx);
-	len = duk__push_this_obj_len_u32(ctx);
+	nargs = duk_get_top(thr);
+	len = duk__push_this_obj_len_u32(thr);
 
 	/* stack[0...nargs-1] = unshift args (vararg)
 	 * stack[nargs] = ToObject(this)
 	 * stack[nargs+1] = ToUint32(length)
 	 */
 
-	DUK_ASSERT_TOP(ctx, nargs + 2);
+	DUK_ASSERT_TOP(thr, nargs + 2);
 
 	/* Note: unshift() may operate on indices above unsigned 32-bit range
 	 * and the final length may be >= 2**32.  However, we restrict the
@@ -24281,39 +25446,39 @@
 
 	if (len + (duk_uint32_t) nargs < len) {
 		DUK_D(DUK_DPRINT("Array.prototype.unshift() would go beyond 32-bit length, throw"));
-		DUK_DCERROR_RANGE_INVALID_LENGTH((duk_hthread *) ctx);
+		DUK_DCERROR_RANGE_INVALID_LENGTH(thr);
 	}
 
 	i = len;
 	while (i > 0) {
-		DUK_ASSERT_TOP(ctx, nargs + 2);
+		DUK_ASSERT_TOP(thr, nargs + 2);
 		i--;
 		/* k+argCount-1; note that may be above 32-bit range */
 
-		if (duk_get_prop_index(ctx, -2, (duk_uarridx_t) i)) {
+		if (duk_get_prop_index(thr, -2, (duk_uarridx_t) i)) {
 			/* fromPresent = true */
 			/* [ ... ToObject(this) ToUint32(length) val ] */
-			duk_put_prop_index(ctx, -3, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
+			duk_put_prop_index(thr, -3, (duk_uarridx_t) (i + (duk_uint32_t) nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
 		} else {
 			/* fromPresent = false */
 			/* [ ... ToObject(this) ToUint32(length) val ] */
-			duk_pop(ctx);
-			duk_del_prop_index(ctx, -2, (duk_uarridx_t) (i + nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
-		}
-		DUK_ASSERT_TOP(ctx, nargs + 2);
+			duk_pop_undefined(thr);
+			duk_del_prop_index(thr, -2, (duk_uarridx_t) (i + (duk_uint32_t) nargs));  /* -> [ ... ToObject(this) ToUint32(length) ] */
+		}
+		DUK_ASSERT_TOP(thr, nargs + 2);
 	}
 
 	for (i = 0; i < (duk_uint32_t) nargs; i++) {
-		DUK_ASSERT_TOP(ctx, nargs + 2);
-		duk_dup(ctx, i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
-		duk_put_prop_index(ctx, -3, (duk_uarridx_t) i);
-		DUK_ASSERT_TOP(ctx, nargs + 2);
-	}
-
-	DUK_ASSERT_TOP(ctx, nargs + 2);
-	duk_push_u32(ctx, len + nargs);
-	duk_dup_top(ctx);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
-	duk_put_prop_stridx_short(ctx, -4, DUK_STRIDX_LENGTH);
+		DUK_ASSERT_TOP(thr, nargs + 2);
+		duk_dup(thr, (duk_idx_t) i);  /* -> [ ... ToObject(this) ToUint32(length) arg[i] ] */
+		duk_put_prop_index(thr, -3, (duk_uarridx_t) i);
+		DUK_ASSERT_TOP(thr, nargs + 2);
+	}
+
+	DUK_ASSERT_TOP(thr, nargs + 2);
+	duk_push_u32(thr, len + (duk_uint32_t) nargs);
+	duk_dup_top(thr);  /* -> [ ... ToObject(this) ToUint32(length) final_len final_len ] */
+	duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_LENGTH);
 	return 1;
 }
 
@@ -24321,22 +25486,22 @@
  *  indexOf(), lastIndexOf()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_int_t i, len;
 	duk_int_t from_idx;
-	duk_small_int_t idx_step = duk_get_current_magic(ctx);  /* idx_step is +1 for indexOf, -1 for lastIndexOf */
+	duk_small_int_t idx_step = duk_get_current_magic(thr);  /* idx_step is +1 for indexOf, -1 for lastIndexOf */
 
 	/* lastIndexOf() needs to be a vararg function because we must distinguish
 	 * between an undefined fromIndex and a "not given" fromIndex; indexOf() is
 	 * made vararg for symmetry although it doesn't strictly need to be.
 	 */
 
-	nargs = duk_get_top(ctx);
-	duk_set_top(ctx, 2);
+	nargs = duk_get_top(thr);
+	duk_set_top(thr, 2);
 
 	/* XXX: must be able to represent -len */
-	len = (duk_int_t) duk__push_this_obj_len_u32_limited(ctx);
+	len = (duk_int_t) duk__push_this_obj_len_u32_limited(thr);
 	if (len == 0) {
 		goto not_found;
 	}
@@ -24363,7 +25528,7 @@
 		 * lastIndexOf: clamp fromIndex to [-len - 1, len - 1]
 		 * (if clamped to -len-1 -> fromIndex becomes -1, terminates for-loop directly)
 		 */
-		from_idx = duk_to_int_clamped(ctx,
+		from_idx = duk_to_int_clamped(thr,
 		                              1,
 		                              (idx_step > 0 ? -len : -len - 1),
 		                              (idx_step > 0 ? len : len - 1));
@@ -24389,21 +25554,21 @@
 	 */
 
 	for (i = from_idx; i >= 0 && i < len; i += idx_step) {
-		DUK_ASSERT_TOP(ctx, 4);
-
-		if (duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
-			DUK_ASSERT_TOP(ctx, 5);
-			if (duk_strict_equals(ctx, 0, 4)) {
-				duk_push_int(ctx, i);
+		DUK_ASSERT_TOP(thr, 4);
+
+		if (duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
+			DUK_ASSERT_TOP(thr, 5);
+			if (duk_strict_equals(thr, 0, 4)) {
+				duk_push_int(thr, i);
 				return 1;
 			}
 		}
 
-		duk_pop(ctx);
+		duk_pop_unsafe(thr);
 	}
 
  not_found:
-	duk_push_int(ctx, -1);
+	duk_push_int(thr, -1);
 	return 1;
 }
 
@@ -24422,25 +25587,25 @@
  * 5 callers the net result is about 100 bytes / caller.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_hthread *thr) {
 	duk_uint32_t len;
 	duk_uint32_t i;
 	duk_uarridx_t k;
 	duk_bool_t bval;
-	duk_small_int_t iter_type = duk_get_current_magic(ctx);
+	duk_small_int_t iter_type = duk_get_current_magic(thr);
 	duk_uint32_t res_length = 0;
 
 	/* each call this helper serves has nargs==2 */
-	DUK_ASSERT_TOP(ctx, 2);
-
-	len = duk__push_this_obj_len_u32(ctx);
-	duk_require_callable(ctx, 0);
+	DUK_ASSERT_TOP(thr, 2);
+
+	len = duk__push_this_obj_len_u32(thr);
+	duk_require_callable(thr, 0);
 	/* if thisArg not supplied, behave as if undefined was supplied */
 
 	if (iter_type == DUK__ITER_MAP || iter_type == DUK__ITER_FILTER) {
-		duk_push_array(ctx);
-	} else {
-		duk_push_undefined(ctx);
+		duk_push_array(thr);
+	} else {
+		duk_push_undefined(thr);
 	}
 
 	/* stack[0] = callback
@@ -24452,9 +25617,9 @@
 
 	k = 0;  /* result index for filter() */
 	for (i = 0; i < len; i++) {
-		DUK_ASSERT_TOP(ctx, 5);
-
-		if (!duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) {
+		DUK_ASSERT_TOP(thr, 5);
+
+		if (!duk_get_prop_index(thr, 2, (duk_uarridx_t) i)) {
 #if defined(DUK_USE_NONSTD_ARRAY_MAP_TRAILER)
 			/* Real world behavior for map(): trailing non-existent
 			 * elements don't invoke the user callback, but are still
@@ -24469,7 +25634,7 @@
 			 * counted towards result 'length'.
 			 */
 #endif
-			duk_pop(ctx);
+			duk_pop_undefined(thr);
 			continue;
 		}
 
@@ -24478,23 +25643,23 @@
 		 * effects.
 		 */
 
-		duk_dup_0(ctx);
-		duk_dup_1(ctx);
-		duk_dup_m3(ctx);
-		duk_push_u32(ctx, i);
-		duk_dup_2(ctx);  /* [ ... val callback thisArg val i obj ] */
-		duk_call_method(ctx, 3); /* -> [ ... val retval ] */
+		duk_dup_0(thr);
+		duk_dup_1(thr);
+		duk_dup_m3(thr);
+		duk_push_u32(thr, i);
+		duk_dup_2(thr);  /* [ ... val callback thisArg val i obj ] */
+		duk_call_method(thr, 3); /* -> [ ... val retval ] */
 
 		switch (iter_type) {
 		case DUK__ITER_EVERY:
-			bval = duk_to_boolean(ctx, -1);
+			bval = duk_to_boolean(thr, -1);
 			if (!bval) {
 				/* stack top contains 'false' */
 				return 1;
 			}
 			break;
 		case DUK__ITER_SOME:
-			bval = duk_to_boolean(ctx, -1);
+			bval = duk_to_boolean(thr, -1);
 			if (bval) {
 				/* stack top contains 'true' */
 				return 1;
@@ -24504,15 +25669,15 @@
 			/* nop */
 			break;
 		case DUK__ITER_MAP:
-			duk_dup_top(ctx);
-			duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) i);  /* retval to result[i] */
+			duk_dup_top(thr);
+			duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) i);  /* retval to result[i] */
 			res_length = i + 1;
 			break;
 		case DUK__ITER_FILTER:
-			bval = duk_to_boolean(ctx, -1);
+			bval = duk_to_boolean(thr, -1);
 			if (bval) {
-				duk_dup_m2(ctx);  /* orig value */
-				duk_xdef_prop_index_wec(ctx, 4, (duk_uarridx_t) k);
+				duk_dup_m2(thr);  /* orig value */
+				duk_xdef_prop_index_wec(thr, 4, (duk_uarridx_t) k);
 				k++;
 				res_length = k;
 			}
@@ -24521,27 +25686,27 @@
 			DUK_UNREACHABLE();
 			break;
 		}
-		duk_pop_2(ctx);
-
-		DUK_ASSERT_TOP(ctx, 5);
+		duk_pop_2_unsafe(thr);
+
+		DUK_ASSERT_TOP(thr, 5);
 	}
 
 	switch (iter_type) {
 	case DUK__ITER_EVERY:
-		duk_push_true(ctx);
+		duk_push_true(thr);
 		break;
 	case DUK__ITER_SOME:
-		duk_push_false(ctx);
+		duk_push_false(thr);
 		break;
 	case DUK__ITER_FOREACH:
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 		break;
 	case DUK__ITER_MAP:
 	case DUK__ITER_FILTER:
-		DUK_ASSERT_TOP(ctx, 5);
-		DUK_ASSERT(duk_is_array(ctx, -1));  /* topmost element is the result array already */
-		duk_push_u32(ctx, res_length);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
+		DUK_ASSERT_TOP(thr, 5);
+		DUK_ASSERT(duk_is_array(thr, -1));  /* topmost element is the result array already */
+		duk_push_u32(thr, res_length);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W);
 		break;
 	default:
 		DUK_UNREACHABLE();
@@ -24555,21 +25720,21 @@
  *  reduce(), reduceRight()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_bool_t have_acc;
 	duk_uint32_t i, len;
-	duk_small_int_t idx_step = duk_get_current_magic(ctx);  /* idx_step is +1 for reduce, -1 for reduceRight */
+	duk_small_int_t idx_step = duk_get_current_magic(thr);  /* idx_step is +1 for reduce, -1 for reduceRight */
 
 	/* We're a varargs function because we need to detect whether
 	 * initialValue was given or not.
 	 */
-	nargs = duk_get_top(ctx);
+	nargs = duk_get_top(thr);
 	DUK_DDD(DUK_DDDPRINT("nargs=%ld", (long) nargs));
 
-	duk_set_top(ctx, 2);
-	len = duk__push_this_obj_len_u32(ctx);
-	duk_require_callable(ctx, 0);
+	duk_set_top(thr, 2);
+	len = duk__push_this_obj_len_u32(thr);
+	duk_require_callable(thr, 0);
 
 	/* stack[0] = callback fn
 	 * stack[1] = initialValue
@@ -24580,11 +25745,11 @@
 
 	have_acc = 0;
 	if (nargs >= 2) {
-		duk_dup_1(ctx);
+		duk_dup_1(thr);
 		have_acc = 1;
 	}
 	DUK_DDD(DUK_DDDPRINT("have_acc=%ld, acc=%!T",
-	                     (long) have_acc, (duk_tval *) duk_get_tval(ctx, 3)));
+	                     (long) have_acc, (duk_tval *) duk_get_tval(thr, 3)));
 
 	/* For len == 0, i is initialized to len - 1 which underflows.
 	 * The condition (i < len) will then exit the for-loop on the
@@ -24594,47 +25759,47 @@
 
 	for (i = (idx_step >= 0 ? 0 : len - 1);
 	     i < len;  /* i >= 0 would always be true */
-	     i += idx_step) {
+	     i += (duk_uint32_t) idx_step) {
 		DUK_DDD(DUK_DDDPRINT("i=%ld, len=%ld, have_acc=%ld, top=%ld, acc=%!T",
 		                     (long) i, (long) len, (long) have_acc,
-		                     (long) duk_get_top(ctx),
-		                     (duk_tval *) duk_get_tval(ctx, 4)));
-
-		DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) ||
-		           (!have_acc && duk_get_top(ctx) == 4));
-
-		if (!duk_has_prop_index(ctx, 2, (duk_uarridx_t) i)) {
+		                     (long) duk_get_top(thr),
+		                     (duk_tval *) duk_get_tval(thr, 4)));
+
+		DUK_ASSERT((have_acc && duk_get_top(thr) == 5) ||
+		           (!have_acc && duk_get_top(thr) == 4));
+
+		if (!duk_has_prop_index(thr, 2, (duk_uarridx_t) i)) {
 			continue;
 		}
 
 		if (!have_acc) {
-			DUK_ASSERT_TOP(ctx, 4);
-			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
+			DUK_ASSERT_TOP(thr, 4);
+			duk_get_prop_index(thr, 2, (duk_uarridx_t) i);
 			have_acc = 1;
-			DUK_ASSERT_TOP(ctx, 5);
-		} else {
-			DUK_ASSERT_TOP(ctx, 5);
-			duk_dup_0(ctx);
-			duk_dup(ctx, 4);
-			duk_get_prop_index(ctx, 2, (duk_uarridx_t) i);
-			duk_push_u32(ctx, i);
-			duk_dup_2(ctx);
+			DUK_ASSERT_TOP(thr, 5);
+		} else {
+			DUK_ASSERT_TOP(thr, 5);
+			duk_dup_0(thr);
+			duk_dup(thr, 4);
+			duk_get_prop_index(thr, 2, (duk_uarridx_t) i);
+			duk_push_u32(thr, i);
+			duk_dup_2(thr);
 			DUK_DDD(DUK_DDDPRINT("calling reduce function: func=%!T, prev=%!T, curr=%!T, idx=%!T, obj=%!T",
-			                     (duk_tval *) duk_get_tval(ctx, -5), (duk_tval *) duk_get_tval(ctx, -4),
-			                     (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2),
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
-			duk_call(ctx, 4);
-			DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
-			duk_replace(ctx, 4);
-			DUK_ASSERT_TOP(ctx, 5);
+			                     (duk_tval *) duk_get_tval(thr, -5), (duk_tval *) duk_get_tval(thr, -4),
+			                     (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2),
+			                     (duk_tval *) duk_get_tval(thr, -1)));
+			duk_call(thr, 4);
+			DUK_DDD(DUK_DDDPRINT("-> result: %!T", (duk_tval *) duk_get_tval(thr, -1)));
+			duk_replace(thr, 4);
+			DUK_ASSERT_TOP(thr, 5);
 		}
 	}
 
 	if (!have_acc) {
-		DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-	}
-
-	DUK_ASSERT_TOP(ctx, 5);
+		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+	}
+
+	DUK_ASSERT_TOP(thr, 5);
 	return 1;
 }
 
@@ -24659,18 +25824,18 @@
 /* Shared helper to provide toString() and valueOf().  Checks 'this', gets
  * the primitive value to stack top, and optionally coerces with ToString().
  */
-DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_hthread *thr) {
 	duk_tval *tv;
 	duk_hobject *h;
-	duk_small_int_t coerce_tostring = duk_get_current_magic(ctx);
+	duk_small_int_t coerce_tostring = duk_get_current_magic(thr);
 
 	/* XXX: there is room to use a shared helper here, many built-ins
 	 * check the 'this' type, and if it's an object, check its class,
 	 * then get its internal value, etc.
 	 */
 
-	duk_push_this(ctx);
-	tv = duk_get_tval(ctx, -1);
+	duk_push_this(thr);
+	tv = duk_get_tval(thr, -1);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_BOOLEAN(tv)) {
@@ -24680,40 +25845,37 @@
 		DUK_ASSERT(h != NULL);
 
 		if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_BOOLEAN) {
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
-			DUK_ASSERT(duk_is_boolean(ctx, -1));
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
+			DUK_ASSERT(duk_is_boolean(thr, -1));
 			goto type_ok;
 		}
 	}
 
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	/* never here */
 
  type_ok:
 	if (coerce_tostring) {
-		duk_to_string(ctx, -1);
-	}
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+		duk_to_string(thr, -1);
+	}
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_hthread *thr) {
 	duk_hobject *h_this;
 
-	DUK_UNREF(thr);
-
-	duk_to_boolean(ctx, 0);
-
-	if (duk_is_constructor_call(ctx)) {
+	duk_to_boolean(thr, 0);
+
+	if (duk_is_constructor_call(thr)) {
 		/* XXX: helper; rely on Boolean.prototype as being non-writable, non-configurable */
-		duk_push_this(ctx);
-		h_this = duk_known_hobject(ctx, -1);
+		duk_push_this(thr);
+		h_this = duk_known_hobject(thr, -1);
 		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_BOOLEAN_PROTOTYPE]);
 
 		DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_BOOLEAN);
 
-		duk_dup_0(ctx);  /* -> [ val obj val ] */
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);  /* XXX: proper flags? */
+		duk_dup_0(thr);  /* -> [ val obj val ] */
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);  /* XXX: proper flags? */
 	}  /* unbalanced stack */
 
 	return 1;
@@ -24837,21 +25999,19 @@
 };
 #endif  /* !DUK_USE_PREFER_SIZE */
 
-DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_hthread *thr) {
 	duk_tval *tv_dst;
 	duk_hbufobj *res;
 
-	thr = (duk_hthread *) ctx;
-	duk_push_this(ctx);
-	DUK_ASSERT(duk_is_buffer(ctx, -1));
-	res = (duk_hbufobj *) duk_to_hobject(ctx, -1);
+	duk_push_this(thr);
+	DUK_ASSERT(duk_is_buffer(thr, -1));
+	res = (duk_hbufobj *) duk_to_hobject(thr, -1);
 	DUK_ASSERT_HBUFOBJ_VALID(res);
-	DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(ctx, -1)));
-
-	tv_dst = duk_get_borrowed_this_tval(ctx);
+	DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(thr, -1)));
+
+	tv_dst = duk_get_borrowed_this_tval(thr);
 	DUK_TVAL_SET_OBJECT_UPDREF(thr, tv_dst, (duk_hobject *) res);
-	duk_pop(ctx);
+	duk_pop(thr);
 
 	return res;
 }
@@ -24863,15 +26023,13 @@
  * always a duk_hbufobj *.  Without the flag the return value can also be a
  * plain buffer, and the caller must check for it using DUK_HEAPHDR_IS_BUFFER().
  */
-DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_context *ctx, duk_small_uint_t flags) {
-	duk_hthread *thr;
+DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_hthread *thr, duk_small_uint_t flags) {
 	duk_tval *tv;
 	duk_hbufobj *h_this;
 
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
-
-	tv = duk_get_borrowed_this_tval(ctx);
+	DUK_ASSERT(thr != NULL);
+
+	tv = duk_get_borrowed_this_tval(thr);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_OBJECT(tv)) {
@@ -24890,7 +26048,7 @@
 			 * support to avoid promotion.
 			 */
 			/* XXX: make this conditional to a flag if call sites need it? */
-			h_this = duk__hbufobj_promote_this(ctx);
+			h_this = duk__hbufobj_promote_this(thr);
 			DUK_ASSERT(h_this != NULL);
 			DUK_ASSERT_HBUFOBJ_VALID(h_this);
 			return (duk_heaphdr *) h_this;
@@ -24907,29 +26065,26 @@
 }
 
 /* Check that 'this' is a duk_hbufobj and return a pointer to it. */
-DUK_LOCAL duk_hbufobj *duk__get_bufobj_this(duk_context *ctx) {
-	return (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_PROMOTE);
+DUK_LOCAL duk_hbufobj *duk__get_bufobj_this(duk_hthread *thr) {
+	return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_PROMOTE);
 }
 
 /* Check that 'this' is a duk_hbufobj and return a pointer to it
  * (NULL if not).
  */
-DUK_LOCAL duk_hbufobj *duk__require_bufobj_this(duk_context *ctx) {
-	return (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_THROW | DUK__BUFOBJ_FLAG_PROMOTE);
+DUK_LOCAL duk_hbufobj *duk__require_bufobj_this(duk_hthread *thr) {
+	return (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW | DUK__BUFOBJ_FLAG_PROMOTE);
 }
 
 /* Check that value is a duk_hbufobj and return a pointer to it. */
-DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_context *ctx, duk_idx_t idx) {
-	duk_hthread *thr;
+DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_hthread *thr, duk_idx_t idx) {
 	duk_tval *tv;
 	duk_hbufobj *h_obj;
 
-	thr = (duk_hthread *) ctx;
-
 	/* Don't accept relative indices now. */
 	DUK_ASSERT(idx >= 0);
 
-	tv = duk_require_tval(ctx, idx);
+	tv = duk_require_tval(thr, idx);
 	DUK_ASSERT(tv != NULL);
 	if (DUK_TVAL_IS_OBJECT(tv)) {
 		h_obj = (duk_hbufobj *) DUK_TVAL_GET_OBJECT(tv);
@@ -24939,7 +26094,7 @@
 			return h_obj;
 		}
 	} else if (DUK_TVAL_IS_BUFFER(tv)) {
-		h_obj = (duk_hbufobj *) duk_to_hobject(ctx, idx);
+		h_obj = (duk_hbufobj *) duk_to_hobject(thr, idx);
 		DUK_ASSERT(h_obj != NULL);
 		DUK_ASSERT_HBUFOBJ_VALID(h_obj);
 		return h_obj;
@@ -24949,17 +26104,13 @@
 	return NULL;  /* not reachable */
 }
 
-DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufobj *h_bufobj, duk_hbuffer *h_val) {
-	duk_hthread *thr;
-
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	DUK_ASSERT(ctx != NULL);
+DUK_LOCAL void duk__set_bufobj_buffer(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_hbuffer *h_val) {
+	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(h_bufobj != NULL);
 	DUK_ASSERT(h_bufobj->buf == NULL);  /* no need to decref */
 	DUK_ASSERT(h_val != NULL);
 	DUK_ASSERT_HBUFOBJ_VALID(h_bufobj);
+	DUK_UNREF(thr);
 
 	h_bufobj->buf = h_val;
 	DUK_HBUFFER_INCREF(thr, h_val);
@@ -24972,23 +26123,19 @@
 }
 
 /* Shared offset/length coercion helper. */
-DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx,
+DUK_LOCAL void duk__resolve_offset_opt_length(duk_hthread *thr,
                                               duk_hbufobj *h_bufarg,
                                               duk_idx_t idx_offset,
                                               duk_idx_t idx_length,
                                               duk_uint_t *out_offset,
                                               duk_uint_t *out_length,
                                               duk_bool_t throw_flag) {
-	duk_hthread *thr;
 	duk_int_t offset_signed;
 	duk_int_t length_signed;
 	duk_uint_t offset;
 	duk_uint_t length;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	offset_signed = duk_to_int(ctx, idx_offset);
+	offset_signed = duk_to_int(thr, idx_offset);
 	if (offset_signed < 0) {
 		goto fail_range;
 	}
@@ -24999,11 +26146,11 @@
 	DUK_ASSERT_DISABLE(offset >= 0);  /* unsigned */
 	DUK_ASSERT(offset <= h_bufarg->length);
 
-	if (duk_is_undefined(ctx, idx_length)) {
+	if (duk_is_undefined(thr, idx_length)) {
 		DUK_ASSERT(h_bufarg->length >= offset);
 		length = h_bufarg->length - offset;  /* >= 0 */
 	} else {
-		length_signed = duk_to_int(ctx, idx_length);
+		length_signed = duk_to_int(thr, idx_length);
 		if (length_signed < 0) {
 			goto fail_range;
 		}
@@ -25034,7 +26181,7 @@
 /* Shared lenient buffer length clamping helper.  No negative indices, no
  * element/byte shifting.
  */
-DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx,
+DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_hthread *thr,
                                                     duk_int_t buffer_length,
                                                     duk_idx_t idx_start,
                                                     duk_idx_t idx_end,
@@ -25047,11 +26194,11 @@
 	DUK_ASSERT(out_end_offset != NULL);
 
 	/* undefined coerces to zero which is correct */
-	start_offset = duk_to_int_clamped(ctx, idx_start, 0, buffer_length);
-	if (duk_is_undefined(ctx, idx_end)) {
+	start_offset = duk_to_int_clamped(thr, idx_start, 0, buffer_length);
+	if (duk_is_undefined(thr, idx_end)) {
 		end_offset = buffer_length;
 	} else {
-		end_offset = duk_to_int_clamped(ctx, idx_end, start_offset, buffer_length);
+		end_offset = duk_to_int_clamped(thr, idx_end, start_offset, buffer_length);
 	}
 
 	DUK_ASSERT(start_offset >= 0);
@@ -25071,7 +26218,7 @@
  * indices are clamped to zero length; and final indices are clamped
  * against input slice.  Used for e.g. ArrayBuffer slice().
  */
-DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx,
+DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_hthread *thr,
                                                   duk_int_t buffer_length,
                                                   duk_uint8_t buffer_shift,
                                                   duk_idx_t idx_start,
@@ -25091,14 +26238,14 @@
 	 * indices first also avoids potential for wrapping.
 	 */
 
-	start_offset = duk_to_int(ctx, idx_start);
+	start_offset = duk_to_int(thr, idx_start);
 	if (start_offset < 0) {
 		start_offset = buffer_length + start_offset;
 	}
-	if (duk_is_undefined(ctx, idx_end)) {
+	if (duk_is_undefined(thr, idx_end)) {
 		end_offset = buffer_length;
 	} else {
-		end_offset = duk_to_int(ctx, idx_end);
+		end_offset = duk_to_int(thr, idx_end);
 		if (end_offset < 0) {
 			end_offset = buffer_length + end_offset;
 		}
@@ -25129,45 +26276,41 @@
 	*out_end_offset = end_offset;
 }
 
-DUK_INTERNAL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx) {
-	if (duk_is_buffer(ctx, idx)) {
-		duk_to_object(ctx, idx);
+DUK_INTERNAL void duk_hbufobj_promote_plain(duk_hthread *thr, duk_idx_t idx) {
+	if (duk_is_buffer(thr, idx)) {
+		duk_to_object(thr, idx);
 	}
 }
 
 DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_hbuffer *h_buf) {
-	duk_context *ctx;
-
-	ctx = (duk_context *) thr;
-
 	/* Push Uint8Array which will share the same underlying buffer as
 	 * the plain buffer argument.  Also create an ArrayBuffer with the
 	 * same backing for the result .buffer property.
 	 */
 
-	duk_push_hbuffer(ctx, h_buf);
-	duk_push_buffer_object(ctx, -1, 0, (duk_size_t) DUK_HBUFFER_GET_SIZE(h_buf), DUK_BUFOBJ_UINT8ARRAY);
-	duk_remove_m2(ctx);
+	duk_push_hbuffer(thr, h_buf);
+	duk_push_buffer_object(thr, -1, 0, (duk_size_t) DUK_HBUFFER_GET_SIZE(h_buf), DUK_BUFOBJ_UINT8ARRAY);
+	duk_remove_m2(thr);
 
 #if 0
 	/* More verbose equivalent; maybe useful if e.g. .buffer is omitted. */
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY),
 	                               DUK_BIDX_UINT8ARRAY_PROTOTYPE);
 	DUK_ASSERT(h_bufobj != NULL);
-	duk__set_bufobj_buffer(ctx, h_bufobj, h_buf);
+	duk__set_bufobj_buffer(thr, h_bufobj, h_buf);
 	h_bufobj->is_typedarray = 1;
 	DUK_ASSERT_HBUFOBJ_VALID(h_bufobj);
 
-	h_arrbuf = duk_push_bufobj_raw(ctx,
+	h_arrbuf = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
 	                               DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
 	DUK_ASSERT(h_arrbuf != NULL);
-	duk__set_bufobj_buffer(ctx, h_arrbuf, h_buf);
+	duk__set_bufobj_buffer(thr, h_arrbuf, h_buf);
 	DUK_ASSERT(h_arrbuf->is_typedarray == 0);
 	DUK_ASSERT_HBUFOBJ_VALID(h_arrbuf);
 
@@ -25175,12 +26318,12 @@
 	h_bufobj->buf_prop = (duk_hobject *) h_arrbuf;
 	DUK_ASSERT(h_arrbuf != NULL);
 	DUK_HBUFOBJ_INCREF(thr, h_arrbuf);
-	duk_pop(ctx);
+	duk_pop(thr);
 #endif
 }
 
 /* Indexed read helper for buffer objects, also called from outside this file. */
-DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_context *ctx, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
+DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
 	duk_double_union du;
 
 	DUK_MEMCPY((void *) du.uc, (const void *) p, (size_t) elem_size);
@@ -25188,28 +26331,28 @@
 	switch (h_bufobj->elem_type) {
 	case DUK_HBUFOBJ_ELEM_UINT8:
 	case DUK_HBUFOBJ_ELEM_UINT8CLAMPED:
-		duk_push_uint(ctx, (duk_uint_t) du.uc[0]);
+		duk_push_uint(thr, (duk_uint_t) du.uc[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT8:
-		duk_push_int(ctx, (duk_int_t) (duk_int8_t) du.uc[0]);
+		duk_push_int(thr, (duk_int_t) (duk_int8_t) du.uc[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_UINT16:
-		duk_push_uint(ctx, (duk_uint_t) du.us[0]);
+		duk_push_uint(thr, (duk_uint_t) du.us[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT16:
-		duk_push_int(ctx, (duk_int_t) (duk_int16_t) du.us[0]);
+		duk_push_int(thr, (duk_int_t) (duk_int16_t) du.us[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_UINT32:
-		duk_push_uint(ctx, (duk_uint_t) du.ui[0]);
+		duk_push_uint(thr, (duk_uint_t) du.ui[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT32:
-		duk_push_int(ctx, (duk_int_t) (duk_int32_t) du.ui[0]);
+		duk_push_int(thr, (duk_int_t) (duk_int32_t) du.ui[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_FLOAT32:
-		duk_push_number(ctx, (duk_double_t) du.f[0]);
+		duk_push_number(thr, (duk_double_t) du.f[0]);
 		break;
 	case DUK_HBUFOBJ_ELEM_FLOAT64:
-		duk_push_number(ctx, (duk_double_t) du.d);
+		duk_push_number(thr, (duk_double_t) du.d);
 		break;
 	default:
 		DUK_UNREACHABLE();
@@ -25217,7 +26360,7 @@
 }
 
 /* Indexed write helper for buffer objects, also called from outside this file. */
-DUK_INTERNAL void duk_hbufobj_validated_write(duk_context *ctx, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
+DUK_INTERNAL void duk_hbufobj_validated_write(duk_hthread *thr, duk_hbufobj *h_bufobj, duk_uint8_t *p, duk_small_uint_t elem_size) {
 	duk_double_union du;
 
 	/* NOTE! Caller must ensure that any side effects from the
@@ -25230,31 +26373,31 @@
 
 	switch (h_bufobj->elem_type) {
 	case DUK_HBUFOBJ_ELEM_UINT8:
-		du.uc[0] = (duk_uint8_t) duk_to_uint32(ctx, -1);
+		du.uc[0] = (duk_uint8_t) duk_to_uint32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_UINT8CLAMPED:
-		du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(ctx, -1);
+		du.uc[0] = (duk_uint8_t) duk_to_uint8clamped(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT8:
-		du.uc[0] = (duk_uint8_t) duk_to_int32(ctx, -1);
+		du.uc[0] = (duk_uint8_t) duk_to_int32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_UINT16:
-		du.us[0] = (duk_uint16_t) duk_to_uint32(ctx, -1);
+		du.us[0] = (duk_uint16_t) duk_to_uint32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT16:
-		du.us[0] = (duk_uint16_t) duk_to_int32(ctx, -1);
+		du.us[0] = (duk_uint16_t) duk_to_int32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_UINT32:
-		du.ui[0] = (duk_uint32_t) duk_to_uint32(ctx, -1);
+		du.ui[0] = (duk_uint32_t) duk_to_uint32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_INT32:
-		du.ui[0] = (duk_uint32_t) duk_to_int32(ctx, -1);
+		du.ui[0] = (duk_uint32_t) duk_to_int32(thr, -1);
 		break;
 	case DUK_HBUFOBJ_ELEM_FLOAT32:
-		du.f[0] = (duk_float_t) duk_to_number_m1(ctx);
+		du.f[0] = (duk_float_t) duk_to_number_m1(thr);
 		break;
 	case DUK_HBUFOBJ_ELEM_FLOAT64:
-		du.d = (duk_double_t) duk_to_number_m1(ctx);
+		du.d = (duk_double_t) duk_to_number_m1(thr);
 		break;
 	default:
 		DUK_UNREACHABLE();
@@ -25266,16 +26409,16 @@
 /* Helper to create a fixed buffer from argument value at index 0.
  * Node.js and allocPlain() compatible.
  */
-DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_context *ctx) {
+DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_hthread *thr) {
 	duk_int_t len;
 	duk_int_t i;
 	duk_size_t buf_size;
 	duk_uint8_t *buf;
 
-	switch (duk_get_type(ctx, 0)) {
+	switch (duk_get_type(thr, 0)) {
 	case DUK_TYPE_NUMBER: {
-		len = duk_to_int_clamped(ctx, 0, 0, DUK_INT_MAX);
-		(void) duk_push_fixed_buffer_zero(ctx, (duk_size_t) len);
+		len = duk_to_int_clamped(thr, 0, 0, DUK_INT_MAX);
+		(void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len);
 		break;
 	}
 	case DUK_TYPE_BUFFER: { /* Treat like Uint8Array. */
@@ -25290,51 +26433,51 @@
 		 * https://nodejs.org/api/buffer.html#buffer_buffer_from_buffer_alloc_and_buffer_allocunsafe
 		 */
 
-		h = duk_known_hobject(ctx, 0);
+		h = duk_known_hobject(thr, 0);
 		if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAYBUFFER) {
 			DUK_ASSERT(DUK_HOBJECT_IS_BUFOBJ(h));
 			h_bufobj = (duk_hbufobj *) h;
 			if (DUK_UNLIKELY(h_bufobj->buf == NULL)) {
-				DUK_ERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+				DUK_ERROR_TYPE_INVALID_ARGS(thr);
 			}
 			if (DUK_UNLIKELY(h_bufobj->offset != 0 || h_bufobj->length != DUK_HBUFFER_GET_SIZE(h_bufobj->buf))) {
 				/* No support for ArrayBuffers with slice
 				 * offset/length.
 				 */
-				DUK_ERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-			}
-			duk_push_hbuffer(ctx, h_bufobj->buf);
+				DUK_ERROR_TYPE_INVALID_ARGS(thr);
+			}
+			duk_push_hbuffer(thr, h_bufobj->buf);
 			return h_bufobj->buf;
 		}
 		goto slow_copy;
 	}
 	case DUK_TYPE_STRING: {
 		/* ignore encoding for now */
-		duk_require_hstring_notsymbol(ctx, 0);
-		duk_dup_0(ctx);
-		(void) duk_to_buffer(ctx, -1, &buf_size);
+		duk_require_hstring_notsymbol(thr, 0);
+		duk_dup_0(thr);
+		(void) duk_to_buffer(thr, -1, &buf_size);
 		break;
 	}
 	default:
-		DUK_ERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
 	}
 
  done:
-	DUK_ASSERT(duk_is_buffer(ctx, -1));
-	return duk_known_hbuffer(ctx, -1);
+	DUK_ASSERT(duk_is_buffer(thr, -1));
+	return duk_known_hbuffer(thr, -1);
 
  slow_copy:
 	/* XXX: fast path for typed arrays and other buffer objects? */
 
-	(void) duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_LENGTH);
-	len = duk_to_int_clamped(ctx, -1, 0, DUK_INT_MAX);
-	duk_pop(ctx);
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, (duk_size_t) len);  /* no zeroing, all indices get initialized */
+	(void) duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH);
+	len = duk_to_int_clamped(thr, -1, 0, DUK_INT_MAX);
+	duk_pop(thr);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len);  /* no zeroing, all indices get initialized */
 	for (i = 0; i < len; i++) {
 		/* XXX: fast path for array or buffer arguments? */
-		duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
-		buf[i] = (duk_uint8_t) (duk_to_uint32(ctx, -1) & 0xffU);
-		duk_pop(ctx);
+		duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
+		buf[i] = (duk_uint8_t) (duk_to_uint32(thr, -1) & 0xffU);
+		duk_pop(thr);
 	}
 	goto done;
 }
@@ -25349,19 +26492,19 @@
  *  constructor entry point is used.
  */
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_hthread *thr) {
 	duk_hbuffer *h_buf;
 
-	h_buf = duk__hbufobj_fixed_from_argvalue(ctx);
+	h_buf = duk__hbufobj_fixed_from_argvalue(thr);
 	DUK_ASSERT(h_buf != NULL);
 
-	duk_push_buffer_object(ctx,
+	duk_push_buffer_object(thr,
 	                       -1,
 	                       0,
 	                       DUK_HBUFFER_FIXED_GET_SIZE((duk_hbuffer_fixed *) h_buf),
 	                       DUK_BUFOBJ_UINT8ARRAY);
-	duk_push_hobject_bidx(ctx, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
-	duk_set_prototype(ctx, -2);
+	duk_push_hobject_bidx(thr, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
+	duk_set_prototype(thr, -2);
 
 	/* XXX: a more direct implementation */
 
@@ -25374,33 +26517,30 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_hthread *thr) {
 	duk_hbufobj *h_bufobj;
 	duk_hbuffer *h_val;
 	duk_int_t len;
 
-	DUK_ASSERT_CTX_VALID(ctx);
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	duk_require_constructor_call(ctx);
-
-	len = duk_to_int(ctx, 0);
+	DUK_ASSERT_CTX_VALID(thr);
+
+	duk_require_constructor_call(thr);
+
+	len = duk_to_int(thr, 0);
 	if (len < 0) {
 		goto fail_length;
 	}
-	(void) duk_push_fixed_buffer_zero(ctx, (duk_size_t) len);
-	h_val = (duk_hbuffer *) duk_known_hbuffer(ctx, -1);
-
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	(void) duk_push_fixed_buffer_zero(thr, (duk_size_t) len);
+	h_val = (duk_hbuffer *) duk_known_hbuffer(thr, -1);
+
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
 	                               DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
 	DUK_ASSERT(h_bufobj != NULL);
 
-	duk__set_bufobj_buffer(ctx, h_bufobj, h_val);
+	duk__set_bufobj_buffer(thr, h_bufobj, h_val);
 	DUK_ASSERT_HBUFOBJ_VALID(h_bufobj);
 
 	return 1;
@@ -25419,8 +26559,7 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) {
 	duk_tval *tv;
 	duk_hobject *h_obj;
 	duk_hbufobj *h_bufobj = NULL;
@@ -25438,24 +26577,21 @@
 	duk_uint_t byte_length;
 	duk_small_uint_t copy_mode;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	/* XXX: The same copy helpers could be shared with at least some
 	 * buffer functions.
 	 */
 
-	duk_require_constructor_call(ctx);
+	duk_require_constructor_call(thr);
 
 	/* We could fit built-in index into magic but that'd make the magic
 	 * number dependent on built-in numbering (genbuiltins.py doesn't
 	 * handle that yet).  So map both class and prototype from the
 	 * element type.
 	 */
-	magic = duk_get_current_magic(ctx);
-	shift = magic & 0x03;               /* bits 0...1: shift */
-	elem_type = (magic >> 2) & 0x0f;    /* bits 2...5: type */
-	elem_size = 1 << shift;
+	magic = (duk_small_uint_t) duk_get_current_magic(thr);
+	shift = magic & 0x03U;               /* bits 0...1: shift */
+	elem_type = (magic >> 2) & 0x0fU;    /* bits 2...5: type */
+	elem_size = 1U << shift;
 	align_mask = elem_size - 1;
 	DUK_ASSERT(elem_type < sizeof(duk__buffer_proto_from_elemtype) / sizeof(duk_uint8_t));
 	proto_bidx = duk__buffer_proto_from_elemtype[elem_type];
@@ -25477,9 +26613,9 @@
 	 * coerce to an ArrayBuffer object and use that as .buffer.  The underlying
 	 * buffer will be the same but result .buffer !== inputPlainBuffer.
 	 */
-	duk_hbufobj_promote_plain(ctx, 0);
-
-	tv = duk_get_tval(ctx, 0);
+	duk_hbufobj_promote_plain(thr, 0);
+
+	tv = duk_get_tval(thr, 0);
 	DUK_ASSERT(tv != NULL);  /* arg count */
 	if (DUK_TVAL_IS_OBJECT(tv)) {
 		h_obj = DUK_TVAL_GET_OBJECT(tv);
@@ -25495,7 +26631,7 @@
 
 			h_bufarg = (duk_hbufobj *) h_obj;
 
-			byte_offset_signed = duk_to_int(ctx, 1);
+			byte_offset_signed = duk_to_int(thr, 1);
 			if (byte_offset_signed < 0) {
 				goto fail_arguments;
 			}
@@ -25505,7 +26641,7 @@
 				/* Must be >= 0 and multiple of element size. */
 				goto fail_arguments;
 			}
-			if (duk_is_undefined(ctx, 2)) {
+			if (duk_is_undefined(thr, 2)) {
 				DUK_ASSERT(h_bufarg->length >= byte_offset);
 				byte_length = h_bufarg->length - byte_offset;
 				if ((byte_length & align_mask) != 0) {
@@ -25516,7 +26652,7 @@
 				}
 				elem_length = (byte_length >> shift);
 			} else {
-				elem_length_signed = duk_to_int(ctx, 2);
+				elem_length_signed = duk_to_int(thr, 2);
 				if (elem_length_signed < 0) {
 					goto fail_arguments;
 				}
@@ -25540,11 +26676,11 @@
 			DUK_ASSERT(byte_offset + byte_length <= h_bufarg->length);
 			DUK_ASSERT((elem_length << shift) == byte_length);
 
-			h_bufobj = duk_push_bufobj_raw(ctx,
+			h_bufobj = duk_push_bufobj_raw(thr,
 			                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 			                               DUK_HOBJECT_FLAG_BUFOBJ |
 			                               DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
-			                               proto_bidx);
+			                               (duk_small_int_t) proto_bidx);
 			h_val = h_bufarg->buf;
 			if (h_val == NULL) {
 				DUK_DCERROR_TYPE_INVALID_ARGS(thr);
@@ -25609,7 +26745,7 @@
 #endif  /* !DUK_USE_PREFER_SIZE */
 		} else {
 			/* Array or Array-like */
-			elem_length_signed = (duk_int_t) duk_get_length(ctx, 0);
+			elem_length_signed = (duk_int_t) duk_get_length(thr, 0);
 			copy_mode = 2;
 		}
 	} else {
@@ -25617,7 +26753,7 @@
 		 * V8 behavior (except for "null", which we coerce to
 		 * 0 but V8 TypeErrors).
 		 */
-		elem_length_signed = duk_to_int(ctx, 0);
+		elem_length_signed = duk_to_int(thr, 0);
 		copy_mode = 3;
 	}
 	if (elem_length_signed < 0) {
@@ -25643,15 +26779,15 @@
 	 */
 
 	/* Push the resulting view object on top of a plain fixed buffer. */
-	(void) duk_push_fixed_buffer(ctx, byte_length);
-	h_val = duk_known_hbuffer(ctx, -1);
+	(void) duk_push_fixed_buffer(thr, byte_length);
+	h_val = duk_known_hbuffer(thr, -1);
 	DUK_ASSERT(h_val != NULL);
 
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(class_num),
-	                               proto_bidx);
+	                               (duk_small_int_t) proto_bidx);
 
 	h_bufobj->buf = h_val;
 	DUK_HBUFFER_INCREF(thr, h_val);
@@ -25712,7 +26848,7 @@
 		DUK_ASSERT(h_bufarg->buf != NULL);
 		DUK_ASSERT(DUK_HBUFOBJ_VALID_SLICE(h_bufarg));
 
-		src_elem_size = 1 << h_bufarg->shift;
+		src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift);
 		dst_elem_size = elem_size;
 
 		p_src = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_bufarg);
@@ -25731,9 +26867,9 @@
 			/* A validated read() is always a number, so it's write coercion
 			 * is always side effect free an won't invalidate pointers etc.
 			 */
-			duk_hbufobj_push_validated_read(ctx, h_bufarg, p_src, src_elem_size);
-			duk_hbufobj_validated_write(ctx, h_bufobj, p_dst, dst_elem_size);
-			duk_pop(ctx);
+			duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size);
+			duk_hbufobj_validated_write(thr, h_bufobj, p_dst, dst_elem_size);
+			duk_pop(thr);
 			p_src += src_elem_size;
 			p_dst += dst_elem_size;
 		}
@@ -25749,8 +26885,8 @@
 		DUK_DDD(DUK_DDDPRINT("using slow copy"));
 
 		for (i = 0; i < elem_length; i++) {
-			duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
-			duk_put_prop_index(ctx, -2, (duk_uarridx_t) i);
+			duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
+			duk_put_prop_index(thr, -2, (duk_uarridx_t) i);
 		}
 		break;
 	}
@@ -25776,7 +26912,7 @@
  * supported to create a plain fixed buffer.  Disabled for now.
  */
 #if 0
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_hthread *thr) {
 	duk_int_t elem_length_signed;
 	duk_uint_t byte_length;
 
@@ -25784,44 +26920,44 @@
 	 * buffer functions.
 	 */
 
-	duk_require_constructor_call(ctx);
-
-	elem_length_signed = duk_require_int(ctx, 0);
+	duk_require_constructor_call(thr);
+
+	elem_length_signed = duk_require_int(thr, 0);
 	if (elem_length_signed < 0) {
 		goto fail_arguments;
 	}
 	byte_length = (duk_uint_t) elem_length_signed;
 
-	(void) duk_push_fixed_buffer_zero(ctx, (duk_size_t) byte_length);
+	(void) duk_push_fixed_buffer_zero(thr, (duk_size_t) byte_length);
 	return 1;
 
  fail_arguments:
-	DUK_DCERROR_RANGE_INVALID_ARGS((duk_hthread *) ctx);
+	DUK_DCERROR_RANGE_INVALID_ARGS(thr);
 }
 #endif  /* 0 */
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_hthread *thr) {
 	duk_hbufobj *h_bufarg;
 	duk_hbufobj *h_bufobj;
 	duk_hbuffer *h_val;
 	duk_uint_t offset;
 	duk_uint_t length;
 
-	duk_require_constructor_call(ctx);
-
-	h_bufarg = duk__require_bufobj_value(ctx, 0);
+	duk_require_constructor_call(thr);
+
+	h_bufarg = duk__require_bufobj_value(thr, 0);
 	DUK_ASSERT(h_bufarg != NULL);
 	if (DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufarg) != DUK_HOBJECT_CLASS_ARRAYBUFFER) {
-		DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-	}
-
-	duk__resolve_offset_opt_length(ctx, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/);
+		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+	}
+
+	duk__resolve_offset_opt_length(thr, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/);
 	DUK_ASSERT(offset <= h_bufarg->length);
 	DUK_ASSERT(offset + length <= h_bufarg->length);
 
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATAVIEW),
@@ -25829,7 +26965,7 @@
 
 	h_val = h_bufarg->buf;
 	if (h_val == NULL) {
-		DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	}
 	h_bufobj->buf = h_val;
 	DUK_HBUFFER_INCREF(thr, h_val);
@@ -25842,7 +26978,7 @@
 	DUK_ASSERT(h_bufobj->buf_prop == NULL);
 	h_bufobj->buf_prop = (duk_hobject *) h_bufarg;
 	DUK_ASSERT(h_bufarg != NULL);
-	DUK_HBUFOBJ_INCREF((duk_hthread *) ctx, h_bufarg);
+	DUK_HBUFOBJ_INCREF(thr, h_bufarg);
 
 	DUK_ASSERT_HBUFOBJ_VALID(h_bufobj);
 	return 1;
@@ -25854,14 +26990,14 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_hthread *thr) {
 	duk_hobject *h_obj;
 	duk_bool_t ret = 0;
 
-	if (duk_is_buffer(ctx, 0)) {
+	if (duk_is_buffer(thr, 0)) {
 		ret = 1;
 	} else {
-		h_obj = duk_get_hobject(ctx, 0);
+		h_obj = duk_get_hobject(thr, 0);
 		if (h_obj != NULL && DUK_HOBJECT_IS_BUFOBJ(h_obj)) {
 			/* DataView needs special casing: ArrayBuffer.isView() is
 			 * true, but ->is_typedarray is 0.
@@ -25870,7 +27006,7 @@
 			      (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_DATAVIEW);
 		}
 	}
-	duk_push_boolean(ctx, ret);
+	duk_push_boolean(thr, ret);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -25880,8 +27016,8 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_context *ctx) {
-	duk__hbufobj_fixed_from_argvalue(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_hthread *thr) {
+	duk__hbufobj_fixed_from_argvalue(thr);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -25891,12 +27027,12 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_hthread *thr) {
 	duk_hbufobj *h_bufobj;
 
 #if !defined(DUK_USE_PREFER_SIZE)
 	/* Avoid churn if argument is already a plain buffer. */
-	if (duk_is_buffer(ctx, 0)) {
+	if (duk_is_buffer(thr, 0)) {
 		return 1;
 	}
 #endif
@@ -25905,11 +27041,11 @@
 	 * argument we'll create a pointless temporary (but still work
 	 * correctly).
 	 */
-	h_bufobj = duk__require_bufobj_value(ctx, 0);
+	h_bufobj = duk__require_bufobj_value(thr, 0);
 	if (h_bufobj->buf == NULL) {
-		duk_push_undefined(ctx);
-	} else {
-		duk_push_hbuffer(ctx, h_bufobj->buf);
+		duk_push_undefined(thr);
+	} else {
+		duk_push_hbuffer(thr, h_bufobj->buf);
 	}
 	return 1;
 }
@@ -25920,27 +27056,23 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_hthread *thr) {
 	duk_hbufobj *h_this;
 	duk_int_t start_offset, end_offset;
 	duk_uint8_t *buf_slice;
 	duk_size_t slice_length;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	h_this = duk__get_bufobj_this(ctx);
+	h_this = duk__get_bufobj_this(thr);
 	if (h_this == NULL) {
 		/* XXX: happens e.g. when evaluating: String(Buffer.prototype). */
-		duk_push_string(ctx, "[object Object]");
+		duk_push_string(thr, "[object Object]");
 		return 1;
 	}
 	DUK_ASSERT_HBUFOBJ_VALID(h_this);
 
 	/* Ignore encoding for now. */
 
-	duk__clamp_startend_nonegidx_noshift(ctx,
+	duk__clamp_startend_nonegidx_noshift(thr,
 	                                     (duk_int_t) h_this->length,
 	                                     1 /*idx_start*/,
 	                                     2 /*idx_end*/,
@@ -25948,12 +27080,12 @@
 	                                     &end_offset);
 
 	slice_length = (duk_size_t) (end_offset - start_offset);
-	buf_slice = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, slice_length);  /* all bytes initialized below */
+	buf_slice = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, slice_length);  /* all bytes initialized below */
 	DUK_ASSERT(buf_slice != NULL);
 
 	/* Neutered or uncovered, TypeError. */
 	if (h_this->buf == NULL ||
-	    !DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, start_offset + slice_length)) {
+	    !DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length)) {
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	}
 
@@ -25963,7 +27095,7 @@
 	 * its stability is difficult).
 	 */
 
-	DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, start_offset + slice_length));
+	DUK_ASSERT(DUK_HBUFOBJ_VALID_BYTEOFFSET_EXCL(h_this, (duk_size_t) start_offset + slice_length));
 	DUK_MEMCPY((void *) buf_slice,
 	           (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
 	           (size_t) slice_length);
@@ -25972,9 +27104,9 @@
 	 * string.  Result will be valid UTF-8; non-CESU-8 inputs are currently
 	 * interpreted loosely.  Value stack convention is a bit odd for now.
 	 */
-	duk_replace(ctx, 0);
-	duk_set_top(ctx, 1);
-	return duk_textdecoder_decode_utf8_nodejs(ctx);
+	duk_replace(thr, 0);
+	duk_set_top(thr, 1);
+	return duk_textdecoder_decode_utf8_nodejs(thr);
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 
@@ -25983,44 +27115,37 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_hthread *thr) {
 	duk_hbufobj *h_this;
-	duk_harray *h_arr;
 	duk_uint8_t *buf;
 	duk_uint_t i, n;
 	duk_tval *tv;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	h_this = duk__require_bufobj_this(ctx);
+	h_this = duk__require_bufobj_this(thr);
 	DUK_ASSERT(h_this != NULL);
 
 	if (h_this->buf == NULL || !DUK_HBUFOBJ_VALID_SLICE(h_this)) {
 		/* Serialize uncovered backing buffer as a null; doesn't
 		 * really matter as long we're memory safe.
 		 */
-		duk_push_null(ctx);
+		duk_push_null(thr);
 		return 1;
 	}
 
-	duk_push_object(ctx);
-	duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_BUFFER);
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_TYPE);
-
+	duk_push_object(thr);
+	duk_push_hstring_stridx(thr, DUK_STRIDX_UC_BUFFER);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_TYPE);
+
+	/* XXX: uninitialized would be OK */
 	DUK_ASSERT_DISABLE((duk_size_t) h_this->length <= (duk_size_t) DUK_UINT32_MAX);
-	h_arr = duk_push_harray_with_size(ctx, (duk_uint32_t) h_this->length);  /* XXX: needs revision with >4G buffers */
-	DUK_ASSERT(h_arr != NULL);
-	DUK_ASSERT(h_arr->length == h_this->length);
-	tv = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) h_arr);
+	tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) h_this->length);  /* XXX: needs revision with >4G buffers */
 
 	DUK_ASSERT(h_this->buf != NULL);
 	buf = DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this);
 	for (i = 0, n = h_this->length; i < n; i++) {
 		DUK_TVAL_SET_U32(tv + i, (duk_uint32_t) buf[i]);  /* no need for decref or incref */
 	}
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_DATA);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_DATA);
 
 	return 1;
 }
@@ -26033,26 +27158,22 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_hthread *thr) {
 	duk_small_uint_t magic;
 	duk_hbufobj *h_bufarg1;
 	duk_hbufobj *h_bufarg2;
 	duk_small_int_t comp_res;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	/* XXX: keep support for plain buffers and non-Node.js buffers? */
 
-	magic = duk_get_current_magic(ctx);
-	if (magic & 0x02) {
+	magic = (duk_small_uint_t) duk_get_current_magic(thr);
+	if (magic & 0x02U) {
 		/* Static call style. */
-		h_bufarg1 = duk__require_bufobj_value(ctx, 0);
-		h_bufarg2 = duk__require_bufobj_value(ctx, 1);
-	} else {
-		h_bufarg1 = duk__require_bufobj_this(ctx);
-		h_bufarg2 = duk__require_bufobj_value(ctx, 0);
+		h_bufarg1 = duk__require_bufobj_value(thr, 0);
+		h_bufarg2 = duk__require_bufobj_value(thr, 1);
+	} else {
+		h_bufarg1 = duk__require_bufobj_this(thr);
+		h_bufarg2 = duk__require_bufobj_value(thr, 0);
 	}
 	DUK_ASSERT(h_bufarg1 != NULL);
 	DUK_ASSERT(h_bufarg2 != NULL);
@@ -26073,12 +27194,12 @@
 		comp_res = -1;  /* either nonzero value is ok */
 	}
 
-	if (magic & 0x01) {
+	if (magic & 0x01U) {
 		/* compare: similar to string comparison but for buffer data. */
-		duk_push_int(ctx, comp_res);
+		duk_push_int(thr, comp_res);
 	} else {
 		/* equals */
-		duk_push_boolean(ctx, (comp_res == 0));
+		duk_push_boolean(thr, (comp_res == 0));
 	}
 
 	return 1;
@@ -26090,8 +27211,7 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_hthread *thr) {
 	duk_hbufobj *h_this;
 	const duk_uint8_t *fill_str_ptr;
 	duk_size_t fill_str_len;
@@ -26101,10 +27221,7 @@
 	duk_size_t fill_length;
 	duk_uint8_t *p;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	h_this = duk__require_bufobj_this(ctx);
+	h_this = duk__require_bufobj_this(thr);
 	DUK_ASSERT(h_this != NULL);
 	if (h_this->buf == NULL) {
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
@@ -26112,19 +27229,19 @@
 
 	/* [ value offset end ] */
 
-	if (duk_is_string_notsymbol(ctx, 0)) {
-		fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(ctx, 0, &fill_str_len);
+	if (duk_is_string_notsymbol(thr, 0)) {
+		fill_str_ptr = (const duk_uint8_t *) duk_get_lstring(thr, 0, &fill_str_len);
 		DUK_ASSERT(fill_str_ptr != NULL);
 	} else {
 		/* Symbols get ToNumber() coerced and cause TypeError. */
-		fill_value = (duk_uint8_t) duk_to_uint32(ctx, 0);
+		fill_value = (duk_uint8_t) duk_to_uint32(thr, 0);
 		fill_str_ptr = (const duk_uint8_t *) &fill_value;
 		fill_str_len = 1;
 	}
 
 	/* Fill offset handling is more lenient than in Node.js. */
 
-	duk__clamp_startend_nonegidx_noshift(ctx,
+	duk__clamp_startend_nonegidx_noshift(thr,
 	                                     (duk_int_t) h_this->length,
 	                                     1 /*idx_start*/,
 	                                     2 /*idx_end*/,
@@ -26147,7 +27264,7 @@
 	} else if (fill_str_len > 1) {
 		duk_size_t i, n, t;
 
-		for (i = 0, n = (fill_end - fill_offset), t = 0; i < n; i++) {
+		for (i = 0, n = (duk_size_t) (fill_end - fill_offset), t = 0; i < n; i++) {
 			p[i] = fill_str_ptr[t++];
 			if (t >= fill_str_len) {
 				t = 0;
@@ -26158,7 +27275,7 @@
 	}
 
 	/* Return the Buffer to allow chaining: b.fill(0x11).fill(0x22, 3, 5).toString() */
-	duk_push_this(ctx);
+	duk_push_this(thr);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -26168,25 +27285,21 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_hthread *thr) {
 	duk_hbufobj *h_this;
 	duk_uint_t offset;
 	duk_uint_t length;
 	const duk_uint8_t *str_data;
 	duk_size_t str_len;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	/* XXX: very inefficient support for plain buffers */
-	h_this = duk__require_bufobj_this(ctx);
+	h_this = duk__require_bufobj_this(thr);
 	DUK_ASSERT(h_this != NULL);
 
 	/* Argument must be a string, e.g. a buffer is not allowed. */
-	str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(ctx, 0, &str_len);
-
-	duk__resolve_offset_opt_length(ctx, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/);
+	str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(thr, 0, &str_len);
+
+	duk__resolve_offset_opt_length(thr, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/);
 	DUK_ASSERT(offset <= h_this->length);
 	DUK_ASSERT(offset + length <= h_this->length);
 
@@ -26205,7 +27318,7 @@
 		DUK_DDD(DUK_DDDPRINT("write() target buffer is not covered, silent ignore"));
 	}
 
-	duk_push_uint(ctx, length);
+	duk_push_uint(thr, length);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -26215,8 +27328,7 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_hthread *thr) {
 	duk_hbufobj *h_this;
 	duk_hbufobj *h_bufarg;
 	duk_int_t source_length;
@@ -26227,22 +27339,19 @@
 
 	/* [ targetBuffer targetStart sourceStart sourceEnd ] */
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	h_this = duk__require_bufobj_this(ctx);
-	h_bufarg = duk__require_bufobj_value(ctx, 0);
+	h_this = duk__require_bufobj_this(thr);
+	h_bufarg = duk__require_bufobj_value(thr, 0);
 	DUK_ASSERT(h_this != NULL);
 	DUK_ASSERT(h_bufarg != NULL);
 	source_length = (duk_int_t) h_this->length;
 	target_length = (duk_int_t) h_bufarg->length;
 
-	target_start = duk_to_int(ctx, 1);
-	source_start = duk_to_int(ctx, 2);
-	if (duk_is_undefined(ctx, 3)) {
+	target_start = duk_to_int(thr, 1);
+	source_start = duk_to_int(thr, 2);
+	if (duk_is_undefined(thr, 3)) {
 		source_end = source_length;
 	} else {
-		source_end = duk_to_int(ctx, 3);
+		source_end = duk_to_int(thr, 3);
 	}
 
 	DUK_DDD(DUK_DDDPRINT("checking copy args: target_start=%ld, target_length=%ld, "
@@ -26266,7 +27375,7 @@
 	}
 	if (source_uend >= (duk_uint_t) source_length) {
 		/* Source end clamped silently to available length. */
-		source_uend = source_length;
+		source_uend = (duk_uint_t) source_length;
 	}
 	copy_size = source_uend - source_ustart;
 	if (target_ustart + copy_size > (duk_uint_t) target_length) {
@@ -26312,7 +27421,7 @@
 	 * The return value matters because of code like:
 	 * "off += buf.copy(...)".
          */
-	duk_push_uint(ctx, copy_size);
+	duk_push_uint(thr, copy_size);
 	return 1;
 
  fail_bounds:
@@ -26357,8 +27466,7 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_hthread *thr) {
 	duk_hbufobj *h_this;
 	duk_hobject *h_obj;
 	duk_uarridx_t i, n;
@@ -26366,10 +27474,7 @@
 	duk_uint_t offset_elems;
 	duk_uint_t offset_bytes;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	h_this = duk__require_bufobj_this(ctx);
+	h_this = duk__require_bufobj_this(thr);
 	DUK_ASSERT(h_this != NULL);
 	DUK_ASSERT_HBUFOBJ_VALID(h_this);
 
@@ -26378,14 +27483,14 @@
 		return 0;
 	}
 
-	duk_hbufobj_promote_plain(ctx, 0);
-	h_obj = duk_require_hobject(ctx, 0);
+	duk_hbufobj_promote_plain(thr, 0);
+	h_obj = duk_require_hobject(thr, 0);
 
 	/* XXX: V8 throws a TypeError for negative values.  Would it
 	 * be more useful to interpret negative offsets here from the
 	 * end of the buffer too?
 	 */
-	offset_signed = duk_to_int(ctx, 1);
+	offset_signed = duk_to_int(thr, 1);
 	if (offset_signed < 0) {
 		/* For some reason this is a TypeError (at least in V8). */
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
@@ -26533,7 +27638,7 @@
 			duk_uint8_t *p_src_copy;
 
 			DUK_DDD(DUK_DDDPRINT("there is overlap, make a copy of the source"));
-			p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, src_length);
+			p_src_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_length);
 			DUK_ASSERT(p_src_copy != NULL);
 			DUK_MEMCPY((void *) p_src_copy, (const void *) p_src_base, (size_t) src_length);
 
@@ -26545,7 +27650,7 @@
 		                     "p_dst_base=%p, dst_length=%ld, valstack top=%ld",
 		                     (void *) p_src_base, (long) src_length,
 		                     (void *) p_dst_base, (long) dst_length,
-		                     (long) duk_get_top(ctx)));
+		                     (long) duk_get_top(thr)));
 
 		/* Ready to make the copy.  We must proceed element by element
 		 * and must avoid any side effects that might cause the buffer
@@ -26555,8 +27660,8 @@
 		 * numbers are handled which should be side effect safe.
 		 */
 
-		src_elem_size = 1 << h_bufarg->shift;
-		dst_elem_size = 1 << h_this->shift;
+		src_elem_size = (duk_small_uint_t) (1U << h_bufarg->shift);
+		dst_elem_size = (duk_small_uint_t) (1U << h_this->shift);
 		p_src = p_src_base;
 		p_dst = p_dst_base;
 		p_src_end = p_src_base + src_length;
@@ -26568,9 +27673,9 @@
 			/* A validated read() is always a number, so it's write coercion
 			 * is always side effect free an won't invalidate pointers etc.
 			 */
-			duk_hbufobj_push_validated_read(ctx, h_bufarg, p_src, src_elem_size);
-			duk_hbufobj_validated_write(ctx, h_this, p_dst, dst_elem_size);
-			duk_pop(ctx);
+			duk_hbufobj_push_validated_read(thr, h_bufarg, p_src, src_elem_size);
+			duk_hbufobj_validated_write(thr, h_this, p_dst, dst_elem_size);
+			duk_pop(thr);
 			p_src += src_elem_size;
 			p_dst += dst_elem_size;
 		}
@@ -26586,7 +27691,7 @@
 		 * would be needed for every element anyway.
 		 */
 
-		n = (duk_uarridx_t) duk_get_length(ctx, 0);
+		n = (duk_uarridx_t) duk_get_length(thr, 0);
 		DUK_ASSERT(offset_bytes <= h_this->length);
 		if ((n << h_this->shift) > h_this->length - offset_bytes) {
 			/* Overflow not an issue because subtraction is used on the right
@@ -26603,12 +27708,12 @@
 		 * the results anyway.
 		 */
 
-		DUK_ASSERT_TOP(ctx, 2);
-		duk_push_this(ctx);
+		DUK_ASSERT_TOP(thr, 2);
+		duk_push_this(thr);
 
 		for (i = 0; i < n; i++) {
-			duk_get_prop_index(ctx, 0, i);
-			duk_put_prop_index(ctx, 2, offset_elems + i);
+			duk_get_prop_index(thr, 0, i);
+			duk_put_prop_index(thr, 2, offset_elems + i);
 		}
 	}
 
@@ -26640,17 +27745,13 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_LOCAL void duk__arraybuffer_plain_slice(duk_context *ctx, duk_hbuffer *h_val) {
-	duk_hthread *thr;
+DUK_LOCAL void duk__arraybuffer_plain_slice(duk_hthread *thr, duk_hbuffer *h_val) {
 	duk_int_t start_offset, end_offset;
 	duk_uint_t slice_length;
 	duk_uint8_t *p_copy;
 	duk_size_t copy_length;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
-	duk__clamp_startend_negidx_shifted(ctx,
+	duk__clamp_startend_negidx_shifted(thr,
 	                                   (duk_int_t) DUK_HBUFFER_GET_SIZE(h_val),
 	                                   0 /*buffer_shift*/,
 	                                   0 /*idx_start*/,
@@ -26662,7 +27763,7 @@
 	DUK_ASSERT(end_offset >= start_offset);
 	slice_length = (duk_uint_t) (end_offset - start_offset);
 
-	p_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, (duk_size_t) slice_length);
+	p_copy = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) slice_length);
 	DUK_ASSERT(p_copy != NULL);
 	copy_length = slice_length;
 
@@ -26676,8 +27777,7 @@
 /* Shared helper for slice/subarray operation.
  * Magic: 0x01=isView, 0x02=copy, 0x04=Node.js Buffer special handling.
  */
-DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_hthread *thr) {
 	duk_small_int_t magic;
 	duk_small_uint_t res_class_num;
 	duk_small_int_t res_proto_bidx;
@@ -26688,14 +27788,11 @@
 	duk_uint_t slice_length;
 	duk_tval *tv;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	/* [ start end ] */
 
-	magic = duk_get_current_magic(ctx);
-
-	tv = duk_get_borrowed_this_tval(ctx);
+	magic = duk_get_current_magic(thr);
+
+	tv = duk_get_borrowed_this_tval(thr);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_BUFFER(tv)) {
@@ -26705,7 +27802,7 @@
 
 		if (magic & 0x02) {
 			/* Make copy: ArrayBuffer.prototype.slice() uses this. */
-			duk__arraybuffer_plain_slice(ctx, h_val);
+			duk__arraybuffer_plain_slice(thr, h_val);
 			return 1;
 		} else {
 			/* View into existing buffer: cannot be done if the
@@ -26722,7 +27819,7 @@
 	}
 	tv = NULL;  /* No longer valid nor needed. */
 
-	h_this = duk__require_bufobj_this(ctx);
+	h_this = duk__require_bufobj_this(thr);
 
 	/* Slice offsets are element (not byte) offsets, which only matters
 	 * for TypedArray views, Node.js Buffer and ArrayBuffer have shift
@@ -26733,7 +27830,7 @@
 	 * against the underlying buffer here.
 	 */
 
-	duk__clamp_startend_negidx_shifted(ctx,
+	duk__clamp_startend_negidx_shifted(thr,
 	                                   (duk_int_t) h_this->length,
 	                                   (duk_uint8_t) h_this->shift,
 	                                   0 /*idx_start*/,
@@ -26741,6 +27838,8 @@
 	                                   &start_offset,
 	                                   &end_offset);
 	DUK_ASSERT(end_offset >= start_offset);
+	DUK_ASSERT(start_offset >= 0);
+	DUK_ASSERT(end_offset >= 0);
 	slice_length = (duk_uint_t) (end_offset - start_offset);
 
 	/* The resulting buffer object gets the same class and prototype as
@@ -26762,14 +27861,14 @@
 	if (magic & 0x04) {
 		res_proto_bidx = DUK_BIDX_NODEJS_BUFFER_PROTOTYPE;
 	}
-	h_bufobj = duk_push_bufobj_raw(ctx,
+	h_bufobj = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(res_class_num),
 	                               res_proto_bidx);
 	DUK_ASSERT(h_bufobj != NULL);
 
-	h_bufobj->length = slice_length;
+	DUK_ASSERT(h_bufobj->length == 0);
 	h_bufobj->shift = h_this->shift;  /* inherit */
 	h_bufobj->elem_type = h_this->elem_type;  /* inherit */
 	h_bufobj->is_typedarray = magic & 0x01;
@@ -26785,7 +27884,7 @@
 		duk_uint8_t *p_copy;
 		duk_size_t copy_length;
 
-		p_copy = (duk_uint8_t *) duk_push_fixed_buffer_zero(ctx, (duk_size_t) slice_length);  /* must be zeroed, not all bytes always copied */
+		p_copy = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, (duk_size_t) slice_length);  /* must be zeroed, not all bytes always copied */
 		DUK_ASSERT(p_copy != NULL);
 
 		/* Copy slice, respecting underlying buffer limits; remainder
@@ -26796,17 +27895,19 @@
 		           (const void *) (DUK_HBUFOBJ_GET_SLICE_BASE(thr->heap, h_this) + start_offset),
 		           copy_length);
 
-		h_val = duk_known_hbuffer(ctx, -1);
+		h_val = duk_known_hbuffer(thr, -1);
 
 		h_bufobj->buf = h_val;
 		DUK_HBUFFER_INCREF(thr, h_val);
+		h_bufobj->length = slice_length;
 		DUK_ASSERT(h_bufobj->offset == 0);
 
-		duk_pop(ctx);  /* reachable so pop OK */
+		duk_pop(thr);  /* reachable so pop OK */
 	} else {
 		h_bufobj->buf = h_val;
 		DUK_HBUFFER_INCREF(thr, h_val);
-		h_bufobj->offset = (duk_uint_t) (h_this->offset + start_offset);
+		h_bufobj->length = slice_length;
+		h_bufobj->offset = h_this->offset + (duk_uint_t) start_offset;
 
 		/* Copy the .buffer property, needed for TypedArray.prototype.subarray().
 		 *
@@ -26829,14 +27930,14 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_hthread *thr) {
 	const char *encoding;
 
 	/* only accept lowercase 'utf8' now. */
 
-	encoding = duk_to_string(ctx, 0);
-	DUK_ASSERT(duk_is_string(ctx, 0));  /* guaranteed by duk_to_string() */
-	duk_push_boolean(ctx, DUK_STRCMP(encoding, "utf8") == 0);
+	encoding = duk_to_string(thr, 0);
+	DUK_ASSERT(duk_is_string(thr, 0));  /* guaranteed by duk_to_string() */
+	duk_push_boolean(thr, DUK_STRCMP(encoding, "utf8") == 0);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -26846,16 +27947,13 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_hthread *thr) {
 	duk_hobject *h;
 	duk_hobject *h_proto;
 	duk_bool_t ret = 0;
 
-	thr = (duk_hthread *) ctx;
-
-	DUK_ASSERT(duk_get_top(ctx) >= 1);  /* nargs */
-	h = duk_get_hobject(ctx, 0);
+	DUK_ASSERT(duk_get_top(thr) >= 1);  /* nargs */
+	h = duk_get_hobject(thr, 0);
 	if (h != NULL) {
 		h_proto = thr->builtins[DUK_BIDX_NODEJS_BUFFER_PROTOTYPE];
 		DUK_ASSERT(h_proto != NULL);
@@ -26866,7 +27964,7 @@
 		}
 	}
 
-	duk_push_boolean(ctx, ret);
+	duk_push_boolean(thr, ret);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -26876,7 +27974,7 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_hthread *thr) {
 	const char *str;
 	duk_size_t len;
 
@@ -26895,9 +27993,9 @@
 	 * (The 20 comes from '[object Uint32Array]'.length
 	 */
 
-	str = duk_to_lstring(ctx, 0, &len);
+	str = duk_to_lstring(thr, 0, &len);
 	DUK_UNREF(str);
-	duk_push_size_t(ctx, len);
+	duk_push_size_t(thr, len);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -26907,10 +28005,9 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_hthread *thr) {
 	duk_hobject *h_arg;
-	duk_int_t total_length = 0;
+	duk_uint_t total_length;
 	duk_hbufobj *h_bufobj;
 	duk_hbufobj *h_bufres;
 	duk_hbuffer *h_val;
@@ -26919,63 +28016,66 @@
 	duk_size_t space_left;
 	duk_size_t copy_size;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	/* Node.js accepts only actual Arrays. */
-	h_arg = duk_require_hobject(ctx, 0);
+	h_arg = duk_require_hobject(thr, 0);
 	if (DUK_HOBJECT_GET_CLASS_NUMBER(h_arg) != DUK_HOBJECT_CLASS_ARRAY) {
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	}
 
 	/* Compute result length and validate argument buffers. */
-	n = (duk_uint_t) duk_get_length(ctx, 0);
+	n = (duk_uint_t) duk_get_length(thr, 0);
+	total_length = 0;
 	for (i = 0; i < n; i++) {
 		/* Neutered checks not necessary here: neutered buffers have
 		 * zero 'length' so we'll effectively skip them.
 		 */
-		DUK_ASSERT_TOP(ctx, 2);  /* [ array totalLength ] */
-		duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);  /* -> [ array totalLength buf ] */
-		h_bufobj = duk__require_bufobj_value(ctx, 2);
+		DUK_ASSERT_TOP(thr, 2);  /* [ array totalLength ] */
+		duk_get_prop_index(thr, 0, (duk_uarridx_t) i);  /* -> [ array totalLength buf ] */
+		h_bufobj = duk__require_bufobj_value(thr, 2);
 		DUK_ASSERT(h_bufobj != NULL);
 		total_length += h_bufobj->length;
-		duk_pop(ctx);
+		if (DUK_UNLIKELY(total_length < h_bufobj->length)) {
+			DUK_DCERROR_RANGE_INVALID_ARGS(thr);  /* Wrapped. */
+		}
+		duk_pop(thr);
 	}
 	/* In Node.js v0.12.1 a 1-element array is special and won't create a
 	 * copy, this was fixed later so an explicit check no longer needed.
 	 */
 
 	/* User totalLength overrides a computed length, but we'll check
-	 * every copy in the copy loop.  Note that duk_to_uint() can
+	 * every copy in the copy loop.  Note that duk_to_int() can
 	 * technically have arbitrary side effects so we need to recheck
 	 * the buffers in the copy loop.
 	 */
-	if (!duk_is_undefined(ctx, 1) && n > 0) {
+	if (!duk_is_undefined(thr, 1) && n > 0) {
 		/* For n == 0, Node.js ignores totalLength argument and
 		 * returns a zero length buffer.
 		 */
-		total_length = duk_to_int(ctx, 1);
-	}
-	if (total_length < 0) {
-		DUK_DCERROR_RANGE_INVALID_ARGS(thr);
-	}
-
-	h_bufres = duk_push_bufobj_raw(ctx,
+		duk_int_t total_length_signed;
+		total_length_signed = duk_to_int(thr, 1);
+		if (total_length_signed < 0) {
+			DUK_DCERROR_RANGE_INVALID_ARGS(thr);
+		}
+		total_length = (duk_uint_t) total_length_signed;
+	}
+
+	h_bufres = duk_push_bufobj_raw(thr,
 	                               DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                               DUK_HOBJECT_FLAG_BUFOBJ |
 	                               DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_UINT8ARRAY),
 	                               DUK_BIDX_NODEJS_BUFFER_PROTOTYPE);
 	DUK_ASSERT(h_bufres != NULL);
 
-	p = (duk_uint8_t *) duk_push_fixed_buffer_zero(ctx, total_length);  /* must be zeroed, all bytes not necessarily written over */
+	p = (duk_uint8_t *) duk_push_fixed_buffer_zero(thr, total_length);  /* must be zeroed, all bytes not necessarily written over */
 	DUK_ASSERT(p != NULL);
-	space_left = total_length;
+	space_left = (duk_size_t) total_length;
 
 	for (i = 0; i < n; i++) {
-		DUK_ASSERT_TOP(ctx, 4);  /* [ array totalLength bufres buf ] */
-
-		duk_get_prop_index(ctx, 0, (duk_uarridx_t) i);
-		h_bufobj = duk__require_bufobj_value(ctx, 4);
+		DUK_ASSERT_TOP(thr, 4);  /* [ array totalLength bufres buf ] */
+
+		duk_get_prop_index(thr, 0, (duk_uarridx_t) i);
+		h_bufobj = duk__require_bufobj_value(thr, 4);
 		DUK_ASSERT(h_bufobj != NULL);
 
 		copy_size = h_bufobj->length;
@@ -26995,16 +28095,16 @@
 		p += copy_size;
 		space_left -= copy_size;
 
-		duk_pop(ctx);
-	}
-
-	h_val = duk_known_hbuffer(ctx, -1);
-
-	duk__set_bufobj_buffer(ctx, h_bufres, h_val);
+		duk_pop(thr);
+	}
+
+	h_val = duk_known_hbuffer(thr, -1);
+
+	duk__set_bufobj_buffer(thr, h_bufres, h_val);
 	h_bufres->is_typedarray = 1;
 	DUK_ASSERT_HBUFOBJ_VALID(h_bufres);
 
-	duk_pop(ctx);  /* pop plain buffer, now reachable through h_bufres */
+	duk_pop(thr);  /* pop plain buffer, now reachable through h_bufres */
 
 	return 1;  /* return h_bufres */
 }
@@ -27035,9 +28135,8 @@
 #define  DUK__FLD_TYPEDARRAY   (1 << 5)
 
 /* XXX: split into separate functions for each field type? */
-DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) {
-	duk_hthread *thr;
-	duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_hthread *thr) {
+	duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(thr);
 	duk_small_int_t magic_ftype;
 	duk_small_int_t magic_bigendian;
 	duk_small_int_t magic_signed;
@@ -27052,15 +28151,12 @@
 	duk_uint8_t *buf;
 	duk_double_union du;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	magic_ftype = magic & 0x0007;
 	magic_bigendian = magic & 0x0008;
 	magic_signed = magic & 0x0010;
 	magic_typedarray = magic & 0x0020;
 
-	h_this = duk__require_bufobj_this(ctx);  /* XXX: very inefficient for plain buffers */
+	h_this = duk__require_bufobj_this(thr);  /* XXX: very inefficient for plain buffers */
 	DUK_ASSERT(h_this != NULL);
 	buffer_length = h_this->length;
 
@@ -27072,12 +28168,12 @@
 	if (magic_typedarray) {
 		no_assert = 0;
 #if defined(DUK_USE_INTEGER_LE)
-		endswap = !duk_to_boolean(ctx, 1);  /* 1=little endian */
-#else
-		endswap = duk_to_boolean(ctx, 1);  /* 1=little endian */
-#endif
-	} else {
-		no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1);
+		endswap = !duk_to_boolean(thr, 1);  /* 1=little endian */
+#else
+		endswap = duk_to_boolean(thr, 1);  /* 1=little endian */
+#endif
+	} else {
+		no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1);
 #if defined(DUK_USE_INTEGER_LE)
 		endswap = magic_bigendian;
 #else
@@ -27089,7 +28185,7 @@
 	 * This ensures we can add a small byte length (1-8) to the offset in
 	 * bound checks and not wrap.
 	 */
-	offset_signed = duk_to_int(ctx, 0);
+	offset_signed = duk_to_int(thr, 0);
 	offset = (duk_uint_t) offset_signed;
 	if (offset_signed < 0) {
 		goto fail_bounds;
@@ -27130,9 +28226,9 @@
 		}
 		tmp = buf[offset];
 		if (magic_signed) {
-			duk_push_int(ctx, (duk_int_t) ((duk_int8_t) tmp));
-		} else {
-			duk_push_uint(ctx, (duk_uint_t) tmp);
+			duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp));
+		} else {
+			duk_push_uint(thr, (duk_uint_t) tmp);
 		}
 		break;
 	}
@@ -27147,9 +28243,9 @@
 			tmp = DUK_BSWAP16(tmp);
 		}
 		if (magic_signed) {
-			duk_push_int(ctx, (duk_int_t) ((duk_int16_t) tmp));
-		} else {
-			duk_push_uint(ctx, (duk_uint_t) tmp);
+			duk_push_int(thr, (duk_int_t) ((duk_int16_t) tmp));
+		} else {
+			duk_push_uint(thr, (duk_uint_t) tmp);
 		}
 		break;
 	}
@@ -27164,9 +28260,9 @@
 			tmp = DUK_BSWAP32(tmp);
 		}
 		if (magic_signed) {
-			duk_push_int(ctx, (duk_int_t) ((duk_int32_t) tmp));
-		} else {
-			duk_push_uint(ctx, (duk_uint_t) tmp);
+			duk_push_int(thr, (duk_int_t) ((duk_int32_t) tmp));
+		} else {
+			duk_push_uint(thr, (duk_uint_t) tmp);
 		}
 		break;
 	}
@@ -27181,7 +28277,7 @@
 			tmp = DUK_BSWAP32(tmp);
 			du.ui[0] = tmp;
 		}
-		duk_push_number(ctx, (duk_double_t) du.f[0]);
+		duk_push_number(thr, (duk_double_t) du.f[0]);
 		break;
 	}
 	case DUK__FLD_DOUBLE: {
@@ -27192,7 +28288,7 @@
 		if (endswap) {
 			DUK_DBLUNION_BSWAP64(&du);
 		}
-		duk_push_number(ctx, (duk_double_t) du.d);
+		duk_push_number(thr, (duk_double_t) du.d);
 		break;
 	}
 	case DUK__FLD_VARINT: {
@@ -27210,7 +28306,7 @@
 #endif
 		const duk_uint8_t *p;
 
-		field_bytelen = duk_get_int(ctx, 1);  /* avoid side effects! */
+		field_bytelen = duk_get_int(thr, 1);  /* avoid side effects! */
 		if (field_bytelen < 1 || field_bytelen > 6) {
 			goto fail_field_length;
 		}
@@ -27246,11 +28342,11 @@
 
 		if (magic_signed) {
 			/* Shift to sign extend. */
-			shift_tmp = 64 - (field_bytelen * 8);
+			shift_tmp = (duk_small_uint_t) (64U - (duk_small_uint_t) field_bytelen * 8U);
 			tmp = (tmp << shift_tmp) >> shift_tmp;
 		}
 
-		duk_push_i64(ctx, tmp);
+		duk_push_i64(thr, tmp);
 #else
 		highbyte = p[i];
 		if (magic_signed && (highbyte & 0x80) != 0) {
@@ -27268,7 +28364,7 @@
 			tmp = (tmp * 256.0) + (duk_double_t) p[i];
 		}
 
-		duk_push_number(ctx, tmp);
+		duk_push_number(thr, tmp);
 #endif
 		break;
 	}
@@ -27286,7 +28382,7 @@
 		/* Node.js return value for noAssert out-of-bounds reads is
 		 * usually (but not always) NaN.  Return NaN consistently.
 		 */
-		duk_push_nan(ctx);
+		duk_push_nan(thr);
 		return 1;
 	}
 	DUK_DCERROR_RANGE_INVALID_ARGS(thr);
@@ -27295,9 +28391,8 @@
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 /* XXX: split into separate functions for each field type? */
-DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) {
-	duk_hthread *thr;
-	duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_hthread *thr) {
+	duk_small_int_t magic = (duk_small_int_t) duk_get_current_magic(thr);
 	duk_small_int_t magic_ftype;
 	duk_small_int_t magic_bigendian;
 	duk_small_int_t magic_signed;
@@ -27313,16 +28408,13 @@
 	duk_double_union du;
 	duk_int_t nbytes = 0;
 
-	thr = (duk_hthread *) ctx;
-	DUK_UNREF(thr);
-
 	magic_ftype = magic & 0x0007;
 	magic_bigendian = magic & 0x0008;
 	magic_signed = magic & 0x0010;
 	magic_typedarray = magic & 0x0020;
 	DUK_UNREF(magic_signed);
 
-	h_this = duk__require_bufobj_this(ctx);  /* XXX: very inefficient for plain buffers */
+	h_this = duk__require_bufobj_this(thr);  /* XXX: very inefficient for plain buffers */
 	DUK_ASSERT(h_this != NULL);
 	buffer_length = h_this->length;
 
@@ -27334,13 +28426,13 @@
 	if (magic_typedarray) {
 		no_assert = 0;
 #if defined(DUK_USE_INTEGER_LE)
-		endswap = !duk_to_boolean(ctx, 2);  /* 1=little endian */
-#else
-		endswap = duk_to_boolean(ctx, 2);  /* 1=little endian */
-#endif
-		duk_swap(ctx, 0, 1);  /* offset/value order different from Node.js */
-	} else {
-		no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2);
+		endswap = !duk_to_boolean(thr, 2);  /* 1=little endian */
+#else
+		endswap = duk_to_boolean(thr, 2);  /* 1=little endian */
+#endif
+		duk_swap(thr, 0, 1);  /* offset/value order different from Node.js */
+	} else {
+		no_assert = duk_to_boolean(thr, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2);
 #if defined(DUK_USE_INTEGER_LE)
 		endswap = magic_bigendian;
 #else
@@ -27352,7 +28444,7 @@
 	 * This ensures we can add a small byte length (1-8) to the offset in
 	 * bound checks and not wrap.
 	 */
-	offset_signed = duk_to_int(ctx, 1);
+	offset_signed = duk_to_int(thr, 1);
 	offset = (duk_uint_t) offset_signed;
 
 	/* We need 'nbytes' even for a failed offset; return value must be
@@ -27362,7 +28454,7 @@
 		DUK_ASSERT(magic_ftype >= 0 && magic_ftype < (duk_small_int_t) (sizeof(duk__buffer_nbytes_from_fldtype) / sizeof(duk_uint8_t)));
 		nbytes = duk__buffer_nbytes_from_fldtype[magic_ftype];
 	} else {
-		nbytes = duk_get_int(ctx, 2);
+		nbytes = duk_get_int(thr, 2);
 		if (nbytes < 1 || nbytes > 6) {
 			goto fail_field_length;
 		}
@@ -27377,7 +28469,7 @@
 	DUK_DDD(DUK_DDDPRINT("writefield, value=%!T, buffer_length=%ld, offset=%ld, no_assert=%d, "
 	                     "magic=%04x, magic_fieldtype=%d, magic_bigendian=%d, magic_signed=%d, "
 	                     "endswap=%d",
-	                     duk_get_tval(ctx, 0), (long) buffer_length, (long) offset, (int) no_assert,
+	                     duk_get_tval(thr, 0), (long) buffer_length, (long) offset, (int) no_assert,
 	                     (unsigned int) magic, (int) magic_ftype, (int) (magic_bigendian >> 3),
 	                     (int) (magic_signed >> 4), (int) endswap));
 
@@ -27385,7 +28477,7 @@
 	 * the field type specific coercion below can't have side effects
 	 * that would invalidate check_length.
 	 */
-	duk_to_number(ctx, 0);
+	duk_to_number(thr, 0);
 
 	/* Update 'buffer_length' to be the effective, safe limit which
 	 * takes into account the underlying buffer.  This value will be
@@ -27413,7 +28505,7 @@
 			goto fail_bounds;
 		}
 		/* sign doesn't matter when writing */
-		buf[offset] = (duk_uint8_t) duk_to_uint32(ctx, 0);
+		buf[offset] = (duk_uint8_t) duk_to_uint32(thr, 0);
 		break;
 	}
 	case DUK__FLD_16BIT: {
@@ -27421,7 +28513,7 @@
 		if (offset + 2U > check_length) {
 			goto fail_bounds;
 		}
-		tmp = (duk_uint16_t) duk_to_uint32(ctx, 0);
+		tmp = (duk_uint16_t) duk_to_uint32(thr, 0);
 		if (endswap) {
 			tmp = DUK_BSWAP16(tmp);
 		}
@@ -27435,7 +28527,7 @@
 		if (offset + 4U > check_length) {
 			goto fail_bounds;
 		}
-		tmp = (duk_uint32_t) duk_to_uint32(ctx, 0);
+		tmp = (duk_uint32_t) duk_to_uint32(thr, 0);
 		if (endswap) {
 			tmp = DUK_BSWAP32(tmp);
 		}
@@ -27449,7 +28541,7 @@
 		if (offset + 4U > check_length) {
 			goto fail_bounds;
 		}
-		du.f[0] = (duk_float_t) duk_to_number(ctx, 0);
+		du.f[0] = (duk_float_t) duk_to_number(thr, 0);
 		if (endswap) {
 			tmp = du.ui[0];
 			tmp = DUK_BSWAP32(tmp);
@@ -27463,7 +28555,7 @@
 		if (offset + 8U > check_length) {
 			goto fail_bounds;
 		}
-		du.d = (duk_double_t) duk_to_number(ctx, 0);
+		du.d = (duk_double_t) duk_to_number(thr, 0);
 		if (endswap) {
 			DUK_DBLUNION_BSWAP64(&du);
 		}
@@ -27513,7 +28605,7 @@
 		 */
 
 #if defined(DUK_USE_64BIT_OPS)
-		tmp = (duk_int64_t) duk_to_number(ctx, 0);
+		tmp = (duk_int64_t) duk_to_number(thr, 0);
 		p = (duk_uint8_t *) (buf + offset);
 		do {
 			i += i_step;
@@ -27522,7 +28614,7 @@
 			tmp = tmp >> 8;  /* unnecessary shift for last byte */
 		} while (i != i_end);
 #else
-		tmp = duk_to_number(ctx, 0);
+		tmp = duk_to_number(thr, 0);
 		p = (duk_uint8_t *) (buf + offset);
 		do {
 			i += i_step;
@@ -27548,7 +28640,7 @@
 		 */
 		return 0;
 	}
-	duk_push_uint(ctx, offset + nbytes);
+	duk_push_uint(thr, offset + (duk_uint_t) nbytes);
 	return 1;
 
  fail_neutered:
@@ -27564,7 +28656,7 @@
 		if (magic_typedarray) {
 			return 0;
 		}
-		duk_push_uint(ctx, offset + nbytes);
+		duk_push_uint(thr, offset + (duk_uint_t) nbytes);
 		return 1;
 	}
 	DUK_DCERROR_RANGE_INVALID_ARGS(thr);
@@ -27576,10 +28668,10 @@
  */
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-DUK_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_context *ctx, duk_hbuffer *h_buf) {
+DUK_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_hthread *thr, duk_hbuffer *h_buf) {
 	duk_hbufobj *h_res;
 
-	h_res = duk_push_bufobj_raw(ctx,
+	h_res = duk_push_bufobj_raw(thr,
 	                            DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                            DUK_HOBJECT_FLAG_BUFOBJ |
 	                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
@@ -27587,20 +28679,20 @@
 	DUK_ASSERT(h_res != NULL);
 	DUK_UNREF(h_res);
 
-	duk__set_bufobj_buffer(ctx, h_res, h_buf);
+	duk__set_bufobj_buffer(thr, h_res, h_buf);
 	DUK_ASSERT_HBUFOBJ_VALID(h_res);
 	DUK_ASSERT(h_res->buf_prop == NULL);
 	return h_res;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) {
 	duk_hbufobj *h_bufobj;
 
-	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_THROW /*flags*/);
+	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
 	DUK_ASSERT(h_bufobj != NULL);
 	if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
 		DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for plain buffer"));
-		(void) duk__autospawn_arraybuffer(ctx, (duk_hbuffer *) h_bufobj);
+		(void) duk__autospawn_arraybuffer(thr, (duk_hbuffer *) h_bufobj);
 		return 1;
 	} else {
 		if (h_bufobj->buf_prop == NULL &&
@@ -27609,7 +28701,7 @@
 			duk_hbufobj *h_arrbuf;
 
 			DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for typed array or DataView"));
-			h_arrbuf = duk__autospawn_arraybuffer(ctx, h_bufobj->buf);
+			h_arrbuf = duk__autospawn_arraybuffer(thr, h_bufobj->buf);
 
 			if (h_bufobj->buf_prop == NULL) {
 				/* Must recheck buf_prop, in case ArrayBuffer
@@ -27634,68 +28726,68 @@
 			/* Left on stack; pushed for the second time below (OK). */
 		}
 		if (h_bufobj->buf_prop) {
-			duk_push_hobject(ctx, h_bufobj->buf_prop);
+			duk_push_hobject(thr, h_bufobj->buf_prop);
 			return 1;
 		}
 	}
 	return 0;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) {
 	duk_hbufobj *h_bufobj;
 
-	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_THROW /*flags*/);
+	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
 	DUK_ASSERT(h_bufobj != NULL);
 	if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
-		duk_push_uint(ctx, 0);
+		duk_push_uint(thr, 0);
 	} else {
 		/* If neutered must return 0; offset is zeroed during
 		 * neutering.
 		 */
-		duk_push_uint(ctx, h_bufobj->offset);
-	}
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx) {
+		duk_push_uint(thr, h_bufobj->offset);
+	}
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) {
 	duk_hbufobj *h_bufobj;
 
-	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_THROW /*flags*/);
+	h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(thr, DUK__BUFOBJ_FLAG_THROW /*flags*/);
 	DUK_ASSERT(h_bufobj != NULL);
 	if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) {
 		duk_hbuffer *h_buf;
 
 		h_buf = (duk_hbuffer *) h_bufobj;
 		DUK_ASSERT(DUK_HBUFFER_GET_SIZE(h_buf) <= DUK_UINT_MAX);  /* Buffer limits. */
-		duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf));
+		duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_buf));
 	} else {
 		/* If neutered must return 0; length is zeroed during
 		 * neutering.
 		 */
-		duk_push_uint(ctx, h_bufobj->length);
+		duk_push_uint(thr, h_bufobj->length);
 	}
 	return 1;
 }
 #else  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 /* No .buffer getter without ArrayBuffer support. */
 #if 0
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx) {
-	return 0;
-}
-#endif
-
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_context *ctx) {
-	duk_push_uint(ctx, 0);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_hthread *thr) {
+	return 0;
+}
+#endif
+
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_byteoffset_getter(duk_hthread *thr) {
+	duk_push_uint(thr, 0);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_hthread *thr) {
 	duk_hbuffer *h_buf;
 
 	/* XXX: helper? */
-	duk_push_this(ctx);
-	h_buf = duk_require_hbuffer(ctx, -1);
-	duk_push_uint(ctx, DUK_HBUFFER_GET_SIZE(h_buf));
+	duk_push_this(thr);
+	h_buf = duk_require_hbuffer(thr, -1);
+	duk_push_uint(thr, DUK_HBUFFER_GET_SIZE(h_buf));
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -27734,10 +28826,10 @@
  *  Forward declarations
  */
 
-DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk_small_uint_t flags, duk_int_t *out_tzoffset);
-DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_context *ctx, duk_small_uint_t flags);
-DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val);
-DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk_double_t *dparts, duk_small_uint_t flags);
+DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset);
+DUK_LOCAL_DECL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags);
+DUK_LOCAL_DECL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val);
+DUK_LOCAL_DECL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags);
 
 /*
  *  Other file level defines
@@ -27901,7 +28993,7 @@
 	 */
 };
 
-DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const char *str) {
+DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_hthread *thr, const char *str) {
 	duk_int_t parts[DUK__NUM_ISO8601_PARSER_PARTS];
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
 	duk_double_t d;
@@ -27949,7 +29041,7 @@
 			}
 		} else {
 			duk_uint_fast32_t match_val;
-			duk_small_int_t sep_idx;
+			duk_small_uint_t sep_idx;
 
 			if (ndigits <= 0) {
 				goto reject;
@@ -28073,7 +29165,7 @@
 	}
 
 	d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
-	duk_push_number(ctx, d);
+	duk_push_number(thr, d);
 	return 1;
 }
 
@@ -28096,7 +29188,7 @@
  *  UTC and '2012/01/01' as local time.
  */
 
-DUK_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) {
+DUK_LOCAL duk_ret_t duk__parse_string(duk_hthread *thr, const char *str) {
 	/* XXX: there is a small risk here: because the ISO 8601 parser is
 	 * very loose, it may end up parsing some datetime values which
 	 * would be better parsed with a platform specific parser.
@@ -28105,7 +29197,7 @@
 	DUK_ASSERT(str != NULL);
 	DUK_DDD(DUK_DDDPRINT("parse datetime from string '%s'", (const char *) str));
 
-	if (duk__parse_string_iso8601_subset(ctx, str) != 0) {
+	if (duk__parse_string_iso8601_subset(thr, str) != 0) {
 		return 1;
 	}
 
@@ -28115,14 +29207,14 @@
 	 * - Don't push anything on stack and return 0
 	 */
 
-	if (DUK_USE_DATE_PARSE_STRING(ctx, str) != 0) {
+	if (DUK_USE_DATE_PARSE_STRING(thr, str) != 0) {
 		return 1;
 	}
 #else
 	/* No platform-specific parsing, this is not an error. */
 #endif
 
-	duk_push_nan(ctx);
+	duk_push_nan(thr);
 	return 1;
 }
 
@@ -28315,9 +29407,9 @@
 	return (duk_double_t) day_num + day;
 }
 
-/* Split time value into parts.  The time value is assumed to be an internal
- * one, i.e. finite, no fractions.  Possible local time adjustment has already
- * been applied when reading the time value.
+/* Split time value into parts.  The time value may contain fractions (it may
+ * come from duk_time_to_components() API call) which are truncated.  Possible
+ * local time adjustment has already been applied when reading the time value.
  */
 DUK_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, duk_double_t *dparts, duk_small_uint_t flags) {
 	duk_double_t d1, d2;
@@ -28336,7 +29428,8 @@
 	duk_small_int_t arridx;
 
 	DUK_ASSERT(DUK_ISFINITE(d));    /* caller checks */
-	DUK_ASSERT(DUK_FLOOR(d) == d);  /* no fractions in internal time */
+	d = DUK_FLOOR(d);  /* remove fractions if present */
+	DUK_ASSERT(DUK_FLOOR(d) == d);
 
 	/* The timevalue must be in valid Ecmascript range, but since a local
 	 * time offset can be applied, we need to allow a +/- 24h leeway to
@@ -28346,7 +29439,7 @@
 	DUK_UNREF(duk_bi_date_timeval_in_leeway_range);
 	DUK_ASSERT(duk_bi_date_timeval_in_leeway_range(d));
 
-	/* these computations are guaranteed to be exact for the valid
+	/* These computations are guaranteed to be exact for the valid
 	 * E5 time value range, assuming milliseconds without fractions.
 	 */
 	d1 = (duk_double_t) DUK_FMOD(d, (double) DUK_DATE_MSEC_DAY);
@@ -28602,21 +29695,20 @@
  * internal time value.  At the end, stack is: [ ... this timeval ].
  * Returns the time value.  Local time adjustment is done if requested.
  */
-DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk_small_uint_t flags, duk_int_t *out_tzoffset) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_hthread *thr, duk_small_uint_t flags, duk_int_t *out_tzoffset) {
 	duk_hobject *h;
 	duk_double_t d;
 	duk_int_t tzoffset = 0;
 
-	duk_push_this(ctx);
-	h = duk_get_hobject(ctx, -1);  /* XXX: getter with class check, useful in built-ins */
+	duk_push_this(thr);
+	h = duk_get_hobject(thr, -1);  /* XXX: getter with class check, useful in built-ins */
 	if (h == NULL || DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_DATE) {
 		DUK_ERROR_TYPE(thr, "expected Date");
 	}
 
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
-	d = duk_to_number_m1(ctx);
-	duk_pop(ctx);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
+	d = duk_to_number_m1(thr);
+	duk_pop(thr);
 
 	if (DUK_ISNAN(d)) {
 		if (flags & DUK_DATE_FLAG_NAN_TO_ZERO) {
@@ -28644,23 +29736,23 @@
 	return d;
 }
 
-DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_context *ctx, duk_small_uint_t flags) {
-	return duk__push_this_get_timeval_tzoffset(ctx, flags, NULL);
+DUK_LOCAL duk_double_t duk__push_this_get_timeval(duk_hthread *thr, duk_small_uint_t flags) {
+	return duk__push_this_get_timeval_tzoffset(thr, flags, NULL);
 }
 
 /* Set timeval to 'this' from dparts, push the new time value onto the
  * value stack and return 1 (caller can then tail call us).  Expects
  * the value stack to contain 'this' on the stack top.
  */
-DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_context *ctx, duk_double_t *dparts, duk_small_uint_t flags) {
+DUK_LOCAL duk_ret_t duk__set_this_timeval_from_dparts(duk_hthread *thr, duk_double_t *dparts, duk_small_uint_t flags) {
 	duk_double_t d;
 
 	/* [ ... this ] */
 
 	d = duk_bi_date_get_timeval_from_dparts(dparts, flags);
-	duk_push_number(ctx, d);  /* -> [ ... this timeval_new ] */
-	duk_dup_top(ctx);         /* -> [ ... this timeval_new timeval_new ] */
-	duk_put_prop_stridx_short(ctx, -3, DUK_STRIDX_INT_VALUE);
+	duk_push_number(thr, d);  /* -> [ ... this timeval_new ] */
+	duk_dup_top(thr);         /* -> [ ... this timeval_new timeval_new ] */
+	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE);
 
 	/* stack top: new time value, return 1 to allow tail calls */
 	return 1;
@@ -28690,13 +29782,23 @@
 		/* tzoffset seconds are dropped; 16 bits suffice for
 		 * time offset in minutes
 		 */
+		const char *fmt;
+		duk_small_int_t tmp, arg_hours, arg_minutes;
+
 		if (tzoffset >= 0) {
-			duk_small_int_t tmp = tzoffset / 60;
-			DUK_SNPRINTF(tzstr, sizeof(tzstr), "+%02d:%02d", (int) (tmp / 60), (int) (tmp % 60));
-		} else {
-			duk_small_int_t tmp = -tzoffset / 60;
-			DUK_SNPRINTF(tzstr, sizeof(tzstr), "-%02d:%02d", (int) (tmp / 60), (int) (tmp % 60));
-		}
+			tmp = tzoffset;
+			fmt = "+%02d:%02d";
+		} else {
+			tmp = -tzoffset;
+			fmt = "-%02d:%02d";
+		}
+		tmp = tmp / 60;
+		arg_hours = tmp / 60;
+		arg_minutes = tmp % 60;
+		DUK_ASSERT(arg_hours <= 24);  /* Even less is actually guaranteed for a valid tzoffset. */
+		arg_hours = arg_hours & 0x3f;  /* For [0,24] this is a no-op, but fixes GCC 7 warning, see https://github.com/svaarala/duktape/issues/1602. */
+
+		DUK_SNPRINTF(tzstr, sizeof(tzstr), fmt, (int) arg_hours, (int) arg_minutes);
 		tzstr[sizeof(tzstr) - 1] = (char) 0;
 	} else {
 		tzstr[0] = DUK_ASC_UC_Z;
@@ -28727,7 +29829,7 @@
  * internal time value, and format date and/or time in a few formats.
  * Return value allows tail calls.
  */
-DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t flags) {
+DUK_LOCAL duk_ret_t duk__to_string_helper(duk_hthread *thr, duk_small_uint_t flags) {
 	duk_double_t d;
 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
 	duk_int_t tzoffset;  /* seconds, doesn't fit into 16 bits */
@@ -28736,9 +29838,9 @@
 
 	DUK_UNREF(rc);  /* unreferenced with some options */
 
-	d = duk__push_this_get_timeval_tzoffset(ctx, flags, &tzoffset);
+	d = duk__push_this_get_timeval_tzoffset(thr, flags, &tzoffset);
 	if (DUK_ISNAN(d)) {
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_INVALID_DATE);
+		duk_push_hstring_stridx(thr, DUK_STRIDX_INVALID_DATE);
 		return 1;
 	}
 	DUK_ASSERT(DUK_ISFINITE(d));
@@ -28759,7 +29861,7 @@
 		 * - Don't push anything and return 0
 		 */
 
-		rc = DUK_USE_DATE_FORMAT_STRING(ctx, parts, tzoffset, flags);
+		rc = DUK_USE_DATE_FORMAT_STRING(thr, parts, tzoffset, flags);
 		if (rc != 0) {
 			return 1;
 		}
@@ -28774,7 +29876,7 @@
 	 * is shared.
 	 */
 	duk__format_parts_iso8601(parts, tzoffset, flags, buf);
-	duk_push_string(ctx, (const char *) buf);
+	duk_push_string(thr, (const char *) buf);
 	return 1;
 }
 
@@ -28783,7 +29885,7 @@
  * local time), push a specified component as a return value to the
  * value stack and return 1 (caller can then tail call us).
  */
-DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flags_and_idx) {
+DUK_LOCAL duk_ret_t duk__get_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_idx) {
 	duk_double_t d;
 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
 	duk_small_uint_t idx_part = (duk_small_uint_t) (flags_and_idx >> DUK_DATE_FLAG_VALUE_SHIFT);  /* unpack args */
@@ -28791,9 +29893,9 @@
 	DUK_ASSERT_DISABLE(idx_part >= 0);  /* unsigned */
 	DUK_ASSERT(idx_part < DUK_DATE_IDX_NUM_PARTS);
 
-	d = duk__push_this_get_timeval(ctx, flags_and_idx);
+	d = duk__push_this_get_timeval(thr, flags_and_idx);
 	if (DUK_ISNAN(d)) {
-		duk_push_nan(ctx);
+		duk_push_nan(thr);
 		return 1;
 	}
 	DUK_ASSERT(DUK_ISFINITE(d));
@@ -28804,7 +29906,7 @@
 	 * only in certain cases.  The legacy getYear() getter applies -1900
 	 * unconditionally.
 	 */
-	duk_push_int(ctx, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]);
+	duk_push_int(thr, (flags_and_idx & DUK_DATE_FLAG_SUB1900) ? parts[idx_part] - 1900 : parts[idx_part]);
 	return 1;
 }
 
@@ -28815,7 +29917,7 @@
  * new time value as a return value to the value stack and return 1
  * (caller can then tail call us).
  */
-DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flags_and_maxnargs) {
+DUK_LOCAL duk_ret_t duk__set_part_helper(duk_hthread *thr, duk_small_uint_t flags_and_maxnargs) {
 	duk_double_t d;
 	duk_int_t parts[DUK_DATE_IDX_NUM_PARTS];
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
@@ -28824,8 +29926,8 @@
 	duk_small_uint_t idx_first, idx;
 	duk_small_uint_t i;
 
-	nargs = duk_get_top(ctx);
-	d = duk__push_this_get_timeval(ctx, flags_and_maxnargs);
+	nargs = duk_get_top(thr);
+	d = duk__push_this_get_timeval(thr, flags_and_maxnargs);
 	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));
 
 	if (DUK_ISFINITE(d)) {
@@ -28880,10 +29982,10 @@
 		DUK_ASSERT(idx < DUK_DATE_IDX_NUM_PARTS);
 
 		if (idx == DUK_DATE_IDX_YEAR && (flags_and_maxnargs & DUK_DATE_FLAG_YEAR_FIXUP)) {
-			duk__twodigit_year_fixup(ctx, (duk_idx_t) i);
-		}
-
-		dparts[idx] = duk_to_number(ctx, i);
+			duk__twodigit_year_fixup(thr, (duk_idx_t) i);
+		}
+
+		dparts[idx] = duk_to_number(thr, (duk_idx_t) i);
 
 		if (idx == DUK_DATE_IDX_DAY) {
 			/* Day-of-month is one-based in the API, but zero-based
@@ -28903,10 +30005,10 @@
 	 * for part setters.
 	 */
 	if (DUK_ISFINITE(d)) {
-		return duk__set_this_timeval_from_dparts(ctx, dparts, flags_and_maxnargs);
+		return duk__set_this_timeval_from_dparts(thr, dparts, flags_and_maxnargs);
 	} else {
 		/* Internal timevalue is already NaN, so don't touch it. */
-		duk_push_nan(ctx);
+		duk_push_nan(thr);
 		return 1;
 	}
 }
@@ -28914,7 +30016,7 @@
 /* Apply ToNumber() to specified index; if ToInteger(val) in [0,99], add
  * 1900 and replace value at idx_val.
  */
-DUK_LOCAL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val) {
+DUK_LOCAL void duk__twodigit_year_fixup(duk_hthread *thr, duk_idx_t idx_val) {
 	duk_double_t d;
 
 	/* XXX: idx_val would fit into 16 bits, but using duk_small_uint_t
@@ -28922,25 +30024,25 @@
 	 */
 
 	/* E5 Sections 15.9.3.1, B.2.4, B.2.5 */
-	duk_to_number(ctx, idx_val);
-	if (duk_is_nan(ctx, idx_val)) {
-		return;
-	}
-	duk_dup(ctx, idx_val);
-	duk_to_int(ctx, -1);
-	d = duk_get_number(ctx, -1);  /* get as double to handle huge numbers correctly */
+	duk_to_number(thr, idx_val);
+	if (duk_is_nan(thr, idx_val)) {
+		return;
+	}
+	duk_dup(thr, idx_val);
+	duk_to_int(thr, -1);
+	d = duk_get_number(thr, -1);  /* get as double to handle huge numbers correctly */
 	if (d >= 0.0 && d <= 99.0) {
 		d += 1900.0;
-		duk_push_number(ctx, d);
-		duk_replace(ctx, idx_val);
-	}
-	duk_pop(ctx);
+		duk_push_number(thr, d);
+		duk_replace(thr, idx_val);
+	}
+	duk_pop(thr);
 }
 
 /* Set datetime parts from stack arguments, defaulting any missing values.
  * Day-of-week is not set; it is not required when setting the time value.
  */
-DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, duk_idx_t nargs) {
+DUK_LOCAL void duk__set_parts_from_args(duk_hthread *thr, duk_double_t *dparts, duk_idx_t nargs) {
 	duk_double_t d;
 	duk_small_uint_t i;
 	duk_small_uint_t idx;
@@ -28948,7 +30050,7 @@
 	/* Causes a ToNumber() coercion, but doesn't break coercion order since
 	 * year is coerced first anyway.
 	 */
-	duk__twodigit_year_fixup(ctx, 0);
+	duk__twodigit_year_fixup(thr, 0);
 
 	/* There are at most 7 args, but we use 8 here so that also
 	 * DUK_DATE_IDX_WEEKDAY gets initialized (to zero) to avoid the potential
@@ -28958,7 +30060,7 @@
 		/* Note: rely on index ordering */
 		idx = DUK_DATE_IDX_YEAR + i;
 		if ((duk_idx_t) i < nargs) {
-			d = duk_to_number(ctx, (duk_idx_t) i);
+			d = duk_to_number(thr, (duk_idx_t) i);
 			if (idx == DUK_DATE_IDX_DAY) {
 				/* Convert day from one-based to zero-based (internal).  This may
 				 * cause the day part to be negative, which is OK.
@@ -29115,9 +30217,9 @@
 	DUK_DATE_FLAG_NAN_TO_ZERO + DUK_DATE_FLAG_YEAR_FIXUP + (3 << DUK_DATE_FLAG_VALUE_SHIFT),
 };
 
-DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_context *ctx) {
-	duk_small_int_t magicidx = (duk_small_uint_t) duk_get_current_magic(ctx);
-	DUK_ASSERT(magicidx >= 0 && magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t)));
+DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_hthread *thr) {
+	duk_small_uint_t magicidx = (duk_small_uint_t) duk_get_current_magic(thr);
+	DUK_ASSERT(magicidx < (duk_small_int_t) (sizeof(duk__date_magics) / sizeof(duk_uint16_t)));
 	return (duk_small_uint_t) duk__date_magics[magicidx];
 }
 
@@ -29126,15 +30228,15 @@
  *  Constructor calls
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_context *ctx) {
-	duk_idx_t nargs = duk_get_top(ctx);
-	duk_bool_t is_cons = duk_is_constructor_call(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_hthread *thr) {
+	duk_idx_t nargs = duk_get_top(thr);
+	duk_bool_t is_cons = duk_is_constructor_call(thr);
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
 	duk_double_t d;
 
 	DUK_DDD(DUK_DDDPRINT("Date constructor, nargs=%ld, is_cons=%ld", (long) nargs, (long) is_cons));
 
-	(void) duk_push_object_helper(ctx,
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE),
@@ -29145,43 +30247,43 @@
 	 */
 
 	if (nargs == 0 || !is_cons) {
-		d = duk__timeclip(DUK_USE_DATE_GET_NOW(ctx));
-		duk_push_number(ctx, d);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
+		d = duk__timeclip(duk_time_get_ecmascript_time_nofrac(thr));
+		duk_push_number(thr, d);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
 		if (!is_cons) {
 			/* called as a normal function: return new Date().toString() */
-			duk_to_string(ctx, -1);
+			duk_to_string(thr, -1);
 		}
 		return 1;
 	} else if (nargs == 1) {
 		const char *str;
-		duk_to_primitive(ctx, 0, DUK_HINT_NONE);
-		str = duk_get_string_notsymbol(ctx, 0);
+		duk_to_primitive(thr, 0, DUK_HINT_NONE);
+		str = duk_get_string_notsymbol(thr, 0);
 		if (str) {
-			duk__parse_string(ctx, str);
-			duk_replace(ctx, 0);  /* may be NaN */
-		}
-		d = duk__timeclip(duk_to_number(ctx, 0));  /* symbols fail here */
-		duk_push_number(ctx, d);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
+			duk__parse_string(thr, str);
+			duk_replace(thr, 0);  /* may be NaN */
+		}
+		d = duk__timeclip(duk_to_number(thr, 0));  /* symbols fail here */
+		duk_push_number(thr, d);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_W);
 		return 1;
 	}
 
-	duk__set_parts_from_args(ctx, dparts, nargs);
+	duk__set_parts_from_args(thr, dparts, nargs);
 
 	/* Parts are in local time, convert when setting. */
 
-	(void) duk__set_this_timeval_from_dparts(ctx, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/);  /* -> [ ... this timeval ] */
-	duk_pop(ctx);  /* -> [ ... this ] */
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_context *ctx) {
-	return duk__parse_string(ctx, duk_to_string(ctx, 0));
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx) {
-	duk_idx_t nargs = duk_get_top(ctx);
+	(void) duk__set_this_timeval_from_dparts(thr, dparts, DUK_DATE_FLAG_LOCALTIME /*flags*/);  /* -> [ ... this timeval ] */
+	duk_pop(thr);  /* -> [ ... this ] */
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_constructor_parse(duk_hthread *thr) {
+	return duk__parse_string(thr, duk_to_string(thr, 0));
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_hthread *thr) {
+	duk_idx_t nargs = duk_get_top(thr);
 	duk_double_t dparts[DUK_DATE_IDX_NUM_PARTS];
 	duk_double_t d;
 
@@ -29190,21 +30292,21 @@
 	 */
 
 	if (nargs < 2) {
-		duk_push_nan(ctx);
-	} else {
-		duk__set_parts_from_args(ctx, dparts, nargs);
+		duk_push_nan(thr);
+	} else {
+		duk__set_parts_from_args(thr, dparts, nargs);
 		d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/);
-		duk_push_number(ctx, d);
-	}
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) {
+		duk_push_number(thr, d);
+	}
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_hthread *thr) {
 	duk_double_t d;
 
-	d = DUK_USE_DATE_GET_NOW(ctx);
+	d = duk_time_get_ecmascript_time_nofrac(thr);
 	DUK_ASSERT(duk__timeclip(d) == d);  /* TimeClip() should never be necessary */
-	duk_push_number(ctx, d);
+	duk_push_number(thr, d);
 	return 1;
 }
 
@@ -29242,44 +30344,44 @@
  *      toISOString() requires a RangeError for invalid date values.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_context *ctx) {
-	duk_small_uint_t flags = duk__date_get_indirect_magic(ctx);
-	return duk__to_string_helper(ctx, flags);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_tostring_shared(duk_hthread *thr) {
+	duk_small_uint_t flags = duk__date_get_indirect_magic(thr);
+	return duk__to_string_helper(thr, flags);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_value_of(duk_hthread *thr) {
 	/* This native function is also used for Date.prototype.getTime()
 	 * as their behavior is identical.
 	 */
 
-	duk_double_t d = duk__push_this_get_timeval(ctx, 0 /*flags*/);  /* -> [ this ] */
+	duk_double_t d = duk__push_this_get_timeval(thr, 0 /*flags*/);  /* -> [ this ] */
 	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));
-	duk_push_number(ctx, d);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) {
+	duk_push_number(thr, d);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_hthread *thr) {
 	/* Note: toJSON() is a generic function which works even if 'this'
 	 * is not a Date.  The sole argument is ignored.
 	 */
 
-	duk_push_this(ctx);
-	duk_to_object(ctx, -1);
-
-	duk_dup_top(ctx);
-	duk_to_primitive(ctx, -1, DUK_HINT_NUMBER);
-	if (duk_is_number(ctx, -1)) {
-		duk_double_t d = duk_get_number(ctx, -1);
+	duk_push_this(thr);
+	duk_to_object(thr, -1);
+
+	duk_dup_top(thr);
+	duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
+	if (duk_is_number(thr, -1)) {
+		duk_double_t d = duk_get_number(thr, -1);
 		if (!DUK_ISFINITE(d)) {
-			duk_push_null(ctx);
+			duk_push_null(thr);
 			return 1;
 		}
 	}
-	duk_pop(ctx);
-
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_TO_ISO_STRING);
-	duk_dup_m2(ctx);  /* -> [ O toIsoString O ] */
-	duk_call_method(ctx, 0);
+	duk_pop(thr);
+
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_ISO_STRING);
+	duk_dup_m2(thr);  /* -> [ O toIsoString O ] */
+	duk_call_method(thr, 0);
 	return 1;
 }
 
@@ -29324,12 +30426,12 @@
  *      function (duk_bi_date_prototype_value_of).
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_context *ctx) {
-	duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(ctx);
-	return duk__get_part_helper(ctx, flags_and_idx);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_shared(duk_hthread *thr) {
+	duk_small_uint_t flags_and_idx = duk__date_get_indirect_magic(thr);
+	return duk__get_part_helper(thr, flags_and_idx);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_hthread *thr) {
 	/*
 	 *  Return (t - LocalTime(t)) in minutes:
 	 *
@@ -29348,14 +30450,14 @@
 	duk_int_t tzoffset;
 
 	/* Note: DST adjustment is determined using UTC time. */
-	d = duk__push_this_get_timeval(ctx, 0 /*flags*/);
+	d = duk__push_this_get_timeval(thr, 0 /*flags*/);
 	DUK_ASSERT(DUK_ISFINITE(d) || DUK_ISNAN(d));
 	if (DUK_ISNAN(d)) {
-		duk_push_nan(ctx);
+		duk_push_nan(thr);
 	} else {
 		DUK_ASSERT(DUK_ISFINITE(d));
 		tzoffset = DUK_USE_DATE_GET_LOCAL_TZOFFSET(d);
-		duk_push_int(ctx, -tzoffset / 60);
+		duk_push_int(thr, -tzoffset / 60);
 	}
 	return 1;
 }
@@ -29409,19 +30511,19 @@
  *      the year will be set regardless of actual argument count.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_context *ctx) {
-	duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(ctx);
-	return duk__set_part_helper(ctx, flags_and_maxnargs);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_shared(duk_hthread *thr) {
+	duk_small_uint_t flags_and_maxnargs = duk__date_get_indirect_magic(thr);
+	return duk__set_part_helper(thr, flags_and_maxnargs);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_hthread *thr) {
 	duk_double_t d;
 
-	(void) duk__push_this_get_timeval(ctx, 0 /*flags*/); /* -> [ timeval this ] */
-	d = duk__timeclip(duk_to_number(ctx, 0));
-	duk_push_number(ctx, d);
-	duk_dup_top(ctx);
-	duk_put_prop_stridx_short(ctx, -3, DUK_STRIDX_INT_VALUE); /* -> [ timeval this timeval ] */
+	(void) duk__push_this_get_timeval(thr, 0 /*flags*/); /* -> [ timeval this ] */
+	d = duk__timeclip(duk_to_number(thr, 0));
+	duk_push_number(thr, d);
+	duk_dup_top(thr);
+	duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_INT_VALUE); /* -> [ timeval this timeval ] */
 
 	return 1;
 }
@@ -29495,18 +30597,18 @@
 
 #if defined(DUK_USE_DATE_NOW_GETTIMEOFDAY)
 /* Get current Ecmascript time (= UNIX/Posix time, but in milliseconds). */
-DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(void) {
 	struct timeval tv;
 	duk_double_t d;
 
 	if (gettimeofday(&tv, NULL) != 0) {
-		DUK_ERROR_INTERNAL(thr);
-	}
-
+		DUK_D(DUK_DPRINT("gettimeofday() failed"));
+		return 0.0;
+	}
+
+	/* As of Duktape 2.2.0 allow fractions. */
 	d = ((duk_double_t) tv.tv_sec) * 1000.0 +
-	    ((duk_double_t) (tv.tv_usec / 1000));
-	DUK_ASSERT(DUK_FLOOR(d) == d);  /* no fractions */
+	    ((duk_double_t) tv.tv_usec) / 1000.0;
 
 	return d;
 }
@@ -29514,11 +30616,14 @@
 
 #if defined(DUK_USE_DATE_NOW_TIME)
 /* Not a very good provider: only full seconds are available. */
-DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(duk_context *ctx) {
+DUK_INTERNAL duk_double_t duk_bi_date_get_now_time(void) {
 	time_t t;
 
-	DUK_UNREF(ctx);
 	t = time(NULL);
+	if (t == (time_t) -1) {
+		DUK_D(DUK_DPRINT("time() failed"));
+		return 0.0;
+	}
 	return ((duk_double_t) t) * 1000.0;
 }
 #endif  /* DUK_USE_DATE_NOW_TIME */
@@ -29674,12 +30779,12 @@
 #endif  /* DUK_USE_DATE_TZO_GMTIME */
 
 #if defined(DUK_USE_DATE_PRS_STRPTIME)
-DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, const char *str) {
+DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_hthread *thr, const char *str) {
 	struct tm tm;
 	time_t t;
 	char buf[DUK__STRPTIME_BUF_SIZE];
 
-	/* copy to buffer with spare to avoid Valgrind gripes from strptime */
+	/* Copy to buffer with slack to avoid Valgrind gripes from strptime. */
 	DUK_ASSERT(str != NULL);
 	DUK_MEMZERO(buf, sizeof(buf));  /* valgrind whine without this */
 	DUK_SNPRINTF(buf, sizeof(buf), "%s", (const char *) str);
@@ -29699,7 +30804,7 @@
 		t = mktime(&tm);
 		DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t));
 		if (t >= 0) {
-			duk_push_number(ctx, ((duk_double_t) t) * 1000.0);
+			duk_push_number(thr, ((duk_double_t) t) * 1000.0);
 			return 1;
 		}
 	}
@@ -29709,7 +30814,7 @@
 #endif  /* DUK_USE_DATE_PRS_STRPTIME */
 
 #if defined(DUK_USE_DATE_PRS_GETDATE)
-DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const char *str) {
+DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_hthread *thr, const char *str) {
 	struct tm tm;
 	duk_small_int_t rc;
 	time_t t;
@@ -29726,7 +30831,7 @@
 		t = mktime(&tm);
 		DUK_DDD(DUK_DDDPRINT("mktime() -> %ld", (long) t));
 		if (t >= 0) {
-			duk_push_number(ctx, (duk_double_t) t);
+			duk_push_number(thr, (duk_double_t) t);
 			return 1;
 		}
 	}
@@ -29736,7 +30841,7 @@
 #endif  /* DUK_USE_DATE_PRS_GETDATE */
 
 #if defined(DUK_USE_DATE_FMT_STRFTIME)
-DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags) {
+DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_hthread *thr, duk_int_t *parts, duk_int_t tzoffset, duk_small_uint_t flags) {
 	char buf[DUK__STRFTIME_BUF_SIZE];
 	struct tm tm;
 	const char *fmt;
@@ -29782,11 +30887,24 @@
 	(void) strftime(buf, sizeof(buf) - 1, fmt, &tm);
 	DUK_ASSERT(buf[sizeof(buf) - 1] == 0);
 
-	duk_push_string(ctx, buf);
+	duk_push_string(thr, buf);
 	return 1;
 }
 #endif  /* DUK_USE_DATE_FMT_STRFTIME */
 
+#if defined(DUK_USE_GET_MONOTONIC_TIME_CLOCK_GETTIME)
+DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_clock_gettime(void) {
+	struct timespec ts;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+		return (duk_double_t) ts.tv_sec * 1000.0 + (duk_double_t) ts.tv_nsec / 1000000.0;
+	} else {
+		DUK_D(DUK_DPRINT("clock_gettime(CLOCK_MONOTONIC) failed"));
+		return 0.0;
+	}
+}
+#endif
+
 /* automatic undefs */
 #undef DUK__STRFTIME_BUF_SIZE
 #undef DUK__STRPTIME_BUF_SIZE
@@ -29815,6 +30933,12 @@
 		res->HighPart = ft.dwHighDateTime;
 	}
 }
+
+DUK_LOCAL void duk__convert_filetime_to_ularge(const FILETIME *ft, ULARGE_INTEGER *res) {
+	res->LowPart = ft->dwLowDateTime;
+	res->HighPart = ft->dwHighDateTime;
+}
+
 DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) {
 	DUK_MEMZERO((void *) st, sizeof(*st));
 	st->wYear = 1970;
@@ -29829,29 +30953,51 @@
 #endif  /* defined(DUK_USE_DATE_NOW_WINDOWS) || defined(DUK_USE_DATE_TZO_WINDOWS) */
 
 #if defined(DUK_USE_DATE_NOW_WINDOWS)
-DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(duk_context *ctx) {
+DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows(void) {
 	/* Suggested step-by-step method from documentation of RtlTimeToSecondsSince1970:
 	 * http://msdn.microsoft.com/en-us/library/windows/desktop/ms724928(v=vs.85).aspx
 	 */
 	SYSTEMTIME st1, st2;
 	ULARGE_INTEGER tmp1, tmp2;
 
-	DUK_UNREF(ctx);
-
 	GetSystemTime(&st1);
 	duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1);
 
 	duk__set_systime_jan1970(&st2);
 	duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);
 
-	/* Difference is in 100ns units, convert to milliseconds w/o fractions */
-	return (duk_double_t) ((tmp1.QuadPart - tmp2.QuadPart) / 10000LL);
+	/* Difference is in 100ns units, convert to milliseconds, keeping
+	 * fractions since Duktape 2.2.0.  This is only theoretical because
+	 * SYSTEMTIME is limited to milliseconds.
+	 */
+	return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0;
 }
 #endif  /* DUK_USE_DATE_NOW_WINDOWS */
 
+#if defined(DUK_USE_DATE_NOW_WINDOWS_SUBMS)
+DUK_INTERNAL duk_double_t duk_bi_date_get_now_windows_subms(void) {
+	/* Variant of the basic algorithm using GetSystemTimePreciseAsFileTime()
+	 * for more accuracy.
+	 */
+	FILETIME ft1;
+	SYSTEMTIME st2;
+	ULARGE_INTEGER tmp1, tmp2;
+
+	GetSystemTimePreciseAsFileTime(&ft1);
+	duk__convert_filetime_to_ularge((const FILETIME *) &ft1, &tmp1);
+
+	duk__set_systime_jan1970(&st2);
+	duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);
+
+	/* Difference is in 100ns units, convert to milliseconds, keeping
+	 * fractions since Duktape 2.2.0.
+	 */
+	return (duk_double_t) ((LONGLONG) tmp1.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000.0;
+}
+#endif  /* DUK_USE_DATE_NOW_WINDOWS */
 
 #if defined(DUK_USE_DATE_TZO_WINDOWS)
-DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) {
+DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d) {
 	SYSTEMTIME st1;
 	SYSTEMTIME st2;
 	SYSTEMTIME st3;
@@ -29886,12 +31032,12 @@
 	duk__convert_systime_to_ularge((const SYSTEMTIME *) &st3, &tmp3);
 
 	/* Positive if local time ahead of UTC. */
-	return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000000LL);  /* seconds */
+	return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / DUK_I64_CONSTANT(10000000));  /* seconds */
 }
 #endif  /* DUK_USE_DATE_TZO_WINDOWS */
 
 #if defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST)
-DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d) {
+DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_double_t d) {
 	SYSTEMTIME st1;
 	SYSTEMTIME st2;
 	FILETIME ft1;
@@ -29918,9 +31064,34 @@
 	FileTimeToSystemTime((const FILETIME *) &ft2, &st2);
 	duk__convert_systime_to_ularge((const SYSTEMTIME *) &st2, &tmp2);
 
-	return (duk_int_t) (((LONGLONG) tmp2.QuadPart - (LONGLONG) tmp1.QuadPart) / 10000000LL);  /* seconds */
+	return (duk_int_t) (((LONGLONG) tmp2.QuadPart - (LONGLONG) tmp1.QuadPart) / DUK_I64_CONSTANT(10000000));  /* seconds */
 }
 #endif  /* DUK_USE_DATE_TZO_WINDOWS_NO_DST */
+
+#if defined(DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC)
+DUK_INTERNAL duk_double_t duk_bi_date_get_monotonic_time_windows_qpc(void) {
+	LARGE_INTEGER count, freq;
+
+	/* There are legacy issues with QueryPerformanceCounter():
+	 * - Potential jumps: https://support.microsoft.com/en-us/help/274323/performance-counter-value-may-unexpectedly-leap-forward
+	 * - Differences between cores (XP): https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx#qpc_support_in_windows_versions
+	 *
+	 * We avoid these by enabling QPC by default only for Vista or later.
+	 */
+
+	if (QueryPerformanceCounter(&count) && QueryPerformanceFrequency(&freq)) {
+		/* XXX: QueryPerformanceFrequency() can be cached */
+		return (duk_double_t) count.QuadPart / (duk_double_t) freq.QuadPart * 1000.0;
+	} else {
+		/* MSDN: "On systems that run Windows XP or later, the function
+		 * will always succeed and will thus never return zero."
+		 * Provide minimal error path just in case user enables this
+		 * feature in pre-XP Windows.
+		 */
+		return 0.0;
+	}
+}
+#endif  /* DUK_USE_GET_MONOTONIC_TIME_WINDOWS_QPC */
 #line 1 "duk_bi_duktape.c"
 /*
  *  Duktape built-ins
@@ -29937,37 +31108,36 @@
 
 #if defined(DUK_USE_DUKTAPE_BUILTIN)
 
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) {
-	duk_inspect_value(ctx, -1);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_hthread *thr) {
+	duk_inspect_value(thr, -1);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_hthread *thr) {
 	duk_int_t level;
 
-	level = duk_to_int(ctx, 0);
-	duk_inspect_callstack_entry(ctx, level);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	level = duk_to_int(thr, 0);
+	duk_inspect_callstack_entry(thr, level);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_hthread *thr) {
 	duk_small_uint_t flags;
 
-	flags = (duk_small_uint_t) duk_get_uint(ctx, 0);
+	flags = (duk_small_uint_t) duk_get_uint(thr, 0);
 	duk_heap_mark_and_sweep(thr->heap, flags);
 
 	/* XXX: Not sure what the best return value would be in the API.
 	 * Return true for now.
 	 */
-	duk_push_true(ctx);
+	duk_push_true(thr);
 	return 1;
 }
 
 #if defined(DUK_USE_FINALIZER_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx) {
-	(void) duk_require_hobject(ctx, 0);
-	if (duk_get_top(ctx) >= 2) {
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_hthread *thr) {
+	(void) duk_require_hobject(thr, 0);
+	if (duk_get_top(thr) >= 2) {
 		/* Set: currently a finalizer is disabled by setting it to
 		 * undefined; this does not remove the property at the moment.
 		 * The value could be type checked to be either a function
@@ -29975,43 +31145,40 @@
 		 * be deleted.  Must use duk_set_finalizer() to keep
 		 * DUK_HOBJECT_FLAG_HAVE_FINALIZER in sync.
 		 */
-		duk_set_top(ctx, 2);
-		duk_set_finalizer(ctx, 0);
+		duk_set_top(thr, 2);
+		duk_set_finalizer(thr, 0);
 		return 0;
 	} else {
 		/* Get. */
-		DUK_ASSERT(duk_get_top(ctx) == 1);
-		duk_get_finalizer(ctx, 0);
+		DUK_ASSERT(duk_get_top(thr) == 1);
+		duk_get_finalizer(thr, 0);
 		return 1;
 	}
 }
 #endif  /* DUK_USE_FINALIZER_SUPPORT */
 
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_hthread *thr) {
 	duk_hstring *h_str;
 
-	DUK_UNREF(thr);
-
 	/* Vararg function: must be careful to check/require arguments.
 	 * The JSON helpers accept invalid indices and treat them like
 	 * non-existent optional parameters.
 	 */
 
-	h_str = duk_require_hstring(ctx, 0);  /* Could reject symbols, but no point: won't match comparisons. */
-	duk_require_valid_index(ctx, 1);
+	h_str = duk_require_hstring(thr, 0);  /* Could reject symbols, but no point: won't match comparisons. */
+	duk_require_valid_index(thr, 1);
 
 	if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
-		duk_set_top(ctx, 2);
-		duk_hex_encode(ctx, 1);
-		DUK_ASSERT_TOP(ctx, 2);
+		duk_set_top(thr, 2);
+		duk_hex_encode(thr, 1);
+		DUK_ASSERT_TOP(thr, 2);
 	} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
-		duk_set_top(ctx, 2);
-		duk_base64_encode(ctx, 1);
-		DUK_ASSERT_TOP(ctx, 2);
+		duk_set_top(thr, 2);
+		duk_base64_encode(thr, 1);
+		DUK_ASSERT_TOP(thr, 2);
 #if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX)
 	} else if (h_str == DUK_HTHREAD_STRING_JX(thr)) {
-		duk_bi_json_stringify_helper(ctx,
+		duk_bi_json_stringify_helper(thr,
 		                             1 /*idx_value*/,
 		                             2 /*idx_replacer*/,
 		                             3 /*idx_space*/,
@@ -30021,7 +31188,7 @@
 #endif
 #if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC)
 	} else if (h_str == DUK_HTHREAD_STRING_JC(thr)) {
-		duk_bi_json_stringify_helper(ctx,
+		duk_bi_json_stringify_helper(thr,
 		                             1 /*idx_value*/,
 		                             2 /*idx_replacer*/,
 		                             3 /*idx_space*/,
@@ -30034,38 +31201,35 @@
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_hthread *thr) {
 	duk_hstring *h_str;
 
-	DUK_UNREF(thr);
-
 	/* Vararg function: must be careful to check/require arguments.
 	 * The JSON helpers accept invalid indices and treat them like
 	 * non-existent optional parameters.
 	 */
 
-	h_str = duk_require_hstring(ctx, 0);  /* Could reject symbols, but no point: won't match comparisons */
-	duk_require_valid_index(ctx, 1);
+	h_str = duk_require_hstring(thr, 0);  /* Could reject symbols, but no point: won't match comparisons */
+	duk_require_valid_index(thr, 1);
 
 	if (h_str == DUK_HTHREAD_STRING_HEX(thr)) {
-		duk_set_top(ctx, 2);
-		duk_hex_decode(ctx, 1);
-		DUK_ASSERT_TOP(ctx, 2);
+		duk_set_top(thr, 2);
+		duk_hex_decode(thr, 1);
+		DUK_ASSERT_TOP(thr, 2);
 	} else if (h_str == DUK_HTHREAD_STRING_BASE64(thr)) {
-		duk_set_top(ctx, 2);
-		duk_base64_decode(ctx, 1);
-		DUK_ASSERT_TOP(ctx, 2);
+		duk_set_top(thr, 2);
+		duk_base64_decode(thr, 1);
+		DUK_ASSERT_TOP(thr, 2);
 #if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JX)
 	} else if (h_str == DUK_HTHREAD_STRING_JX(thr)) {
-		duk_bi_json_parse_helper(ctx,
+		duk_bi_json_parse_helper(thr,
 		                         1 /*idx_value*/,
 		                         2 /*idx_replacer*/,
 		                         DUK_JSON_FLAG_EXT_CUSTOM /*flags*/);
 #endif
 #if defined(DUK_USE_JSON_SUPPORT) && defined(DUK_USE_JC)
 	} else if (h_str == DUK_HTHREAD_STRING_JC(thr)) {
-		duk_bi_json_parse_helper(ctx,
+		duk_bi_json_parse_helper(thr,
 		                         1 /*idx_value*/,
 		                         2 /*idx_replacer*/,
 		                         DUK_JSON_FLAG_EXT_COMPATIBLE /*flags*/);
@@ -30080,9 +31244,9 @@
  *  Compact an object
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_context *ctx) {
-	DUK_ASSERT_TOP(ctx, 1);
-	duk_compact(ctx, 0);
+DUK_INTERNAL duk_ret_t duk_bi_duktape_object_compact(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 1);
+	duk_compact(thr, 0);
 	return 1;  /* return the argument object */
 }
 
@@ -30267,7 +31431,7 @@
 		} else {
 			/* low surrogate */
 			if (enc_ctx->lead != 0x0000L) {
-				codepoint = 0x010000L + ((enc_ctx->lead - 0xd800L) << 10) + (codepoint - 0xdc00L);
+				codepoint = (duk_codepoint_t) (0x010000L + ((enc_ctx->lead - 0xd800L) << 10) + (codepoint - 0xdc00L));
 				enc_ctx->lead = 0x0000L;
 			} else {
 				/* unpaired low surrogate */
@@ -30286,14 +31450,14 @@
 	/* Codepoint may be original input, a decoded surrogate pair, or may
 	 * have been replaced with U+FFFD.
 	 */
-	enc_ctx->out += duk_unicode_encode_xutf8(codepoint, enc_ctx->out);
+	enc_ctx->out += duk_unicode_encode_xutf8((duk_ucodepoint_t) codepoint, enc_ctx->out);
 }
 #endif  /* DUK_USE_ENCODING_BUILTINS */
 
 /* Shared helper for buffer-to-string using a TextDecoder() compatible UTF-8
  * decoder.
  */
-DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *dec_ctx) {
+DUK_LOCAL duk_ret_t duk__decode_helper(duk_hthread *thr, duk__decode_context *dec_ctx) {
 	const duk_uint8_t *input;
 	duk_size_t len = 0;
 	duk_size_t len_tmp;
@@ -30313,24 +31477,24 @@
 	 * required side effect order.
 	 */
 
-	if (duk_is_undefined(ctx, 0)) {
-		duk_push_fixed_buffer_nozero(ctx, 0);
-		duk_replace(ctx, 0);
-	}
-	(void) duk_require_buffer_data(ctx, 0, &len);  /* Need 'len', avoid pointer. */
-
-	if (duk_check_type_mask(ctx, 1, DUK_TYPE_MASK_UNDEFINED |
+	if (duk_is_undefined(thr, 0)) {
+		duk_push_fixed_buffer_nozero(thr, 0);
+		duk_replace(thr, 0);
+	}
+	(void) duk_require_buffer_data(thr, 0, &len);  /* Need 'len', avoid pointer. */
+
+	if (duk_check_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED |
 	                                DUK_TYPE_MASK_NULL |
 	                                DUK_TYPE_MASK_NONE)) {
 		/* Use defaults, treat missing value like undefined. */
 	} else {
-		duk_require_type_mask(ctx, 1, DUK_TYPE_MASK_UNDEFINED |
+		duk_require_type_mask(thr, 1, DUK_TYPE_MASK_UNDEFINED |
 	                                      DUK_TYPE_MASK_NULL |
 	                                      DUK_TYPE_MASK_LIGHTFUNC |
 	                                      DUK_TYPE_MASK_BUFFER |
 		                              DUK_TYPE_MASK_OBJECT);
-		if (duk_get_prop_string(ctx, 1, "stream")) {
-			stream = duk_to_boolean(ctx, -1);
+		if (duk_get_prop_string(thr, 1, "stream")) {
+			stream = duk_to_boolean(thr, -1);
 		}
 	}
 
@@ -30343,11 +31507,11 @@
 	 * XXX: As with TextEncoder, need a better buffer allocation strategy here.
 	 */
 	if (len >= (DUK_HBUFFER_MAX_BYTELEN / 3) - 3) {
-		DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_RESULT_TOO_LONG);
-	}
-	output = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, 3 + (3 * len));  /* used parts will be always manually written over */
-
-	input = (const duk_uint8_t *) duk_get_buffer_data(ctx, 0, &len_tmp);
+		DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG);
+	}
+	output = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, 3 + (3 * len));  /* used parts will be always manually written over */
+
+	input = (const duk_uint8_t *) duk_get_buffer_data(thr, 0, &len_tmp);
 	DUK_ASSERT(input != NULL || len == 0);
 	if (DUK_UNLIKELY(len != len_tmp)) {
 		/* Very unlikely but possible: source buffer was resized by
@@ -30396,7 +31560,7 @@
 			}
 		}
 
-		out += duk_unicode_encode_cesu8(codepoint, out);
+		out += duk_unicode_encode_cesu8((duk_ucodepoint_t) codepoint, out);
 		DUK_ASSERT(out <= output + (3 + (3 * len)));
 	}
 
@@ -30416,11 +31580,11 @@
 	/* Output buffer is fixed and thus stable even if there had been
 	 * side effects (which there shouldn't be).
 	 */
-	duk_push_lstring(ctx, (const char *) output, (duk_size_t) (out - output));
+	duk_push_lstring(thr, (const char *) output, (duk_size_t) (out - output));
 	return 1;
 
  fail_type:
-	DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_DECODE_FAILED);
+	DUK_ERROR_TYPE(thr, DUK_STR_UTF8_DECODE_FAILED);
 	DUK_UNREACHABLE();
 }
 
@@ -30429,38 +31593,38 @@
  */
 
 #if defined(DUK_USE_ENCODING_BUILTINS)
-DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_hthread *thr) {
 	/* TextEncoder currently requires no persistent state, so the constructor
 	 * does nothing on purpose.
 	 */
 
-	duk_require_constructor_call(ctx);
-	return 0;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_context *ctx) {
-	duk_push_string(ctx, "utf-8");
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx) {
+	duk_require_constructor_call(thr);
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_hthread *thr) {
+	duk_push_string(thr, "utf-8");
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_hthread *thr) {
 	duk__encode_context enc_ctx;
 	duk_size_t len;
 	duk_size_t final_len;
 	duk_uint8_t *output;
 
-	DUK_ASSERT_TOP(ctx, 1);
-	if (duk_is_undefined(ctx, 0)) {
+	DUK_ASSERT_TOP(thr, 1);
+	if (duk_is_undefined(thr, 0)) {
 		len = 0;
 	} else {
 		duk_hstring *h_input;
 
-		h_input = duk_to_hstring(ctx, 0);
+		h_input = duk_to_hstring(thr, 0);
 		DUK_ASSERT(h_input != NULL);
 
 		len = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_input);
 		if (len >= DUK_HBUFFER_MAX_BYTELEN / 3) {
-			DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_RESULT_TOO_LONG);
+			DUK_ERROR_TYPE(thr, DUK_STR_RESULT_TOO_LONG);
 		}
 	}
 
@@ -30474,10 +31638,10 @@
 	 * figure out the space needed ahead of time?
 	 */
 	DUK_ASSERT(3 * len >= len);
-	output = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, 3 * len);
+	output = (duk_uint8_t *) duk_push_dynamic_buffer(thr, 3 * len);
 
 	if (len > 0) {
-		DUK_ASSERT(duk_is_string(ctx, 0));  /* True if len > 0. */
+		DUK_ASSERT(duk_is_string(thr, 0));  /* True if len > 0. */
 
 		/* XXX: duk_decode_string() is used to process the input
 		 * string.  For standard Ecmascript strings, represented
@@ -30493,7 +31657,7 @@
 		 */
 		enc_ctx.lead = 0x0000L;
 		enc_ctx.out = output;
-		duk_decode_string(ctx, 0, duk__utf8_encode_char, (void *) &enc_ctx);
+		duk_decode_string(thr, 0, duk__utf8_encode_char, (void *) &enc_ctx);
 		if (enc_ctx.lead != 0x0000L) {
 			/* unpaired high surrogate at end of string */
 			enc_ctx.out = duk__utf8_emit_repl(enc_ctx.out);
@@ -30503,11 +31667,11 @@
 		/* The output buffer is usually very much oversized, so shrink it to
 		 * actually needed size.  Pointer stability assumed up to this point.
 		 */
-		DUK_ASSERT_TOP(ctx, 2);
-		DUK_ASSERT(output == (duk_uint8_t *) duk_get_buffer_data(ctx, -1, NULL));
+		DUK_ASSERT_TOP(thr, 2);
+		DUK_ASSERT(output == (duk_uint8_t *) duk_get_buffer_data(thr, -1, NULL));
 
 		final_len = (duk_size_t) (enc_ctx.out - output);
-		duk_resize_buffer(ctx, -1, final_len);
+		duk_resize_buffer(thr, -1, final_len);
 		/* 'output' and 'enc_ctx.out' are potentially invalidated by the resize. */
 	} else {
 		final_len = 0;
@@ -30520,84 +31684,84 @@
 	 * returns a plain dynamic buffer.
 	 */
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-	duk_push_buffer_object(ctx, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY);
-#endif
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx) {
+	duk_push_buffer_object(thr, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY);
+#endif
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_hthread *thr) {
 	duk__decode_context *dec_ctx;
 	duk_bool_t fatal = 0;
 	duk_bool_t ignore_bom = 0;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_require_constructor_call(ctx);
-	if (!duk_is_undefined(ctx, 0)) {
+	DUK_ASSERT_TOP(thr, 2);
+	duk_require_constructor_call(thr);
+	if (!duk_is_undefined(thr, 0)) {
 		/* XXX: For now ignore 'label' (encoding identifier). */
-		duk_to_string(ctx, 0);
-	}
-	if (!duk_is_null_or_undefined(ctx, 1)) {
-		if (duk_get_prop_string(ctx, 1, "fatal")) {
-			fatal = duk_to_boolean(ctx, -1);
-		}
-		if (duk_get_prop_string(ctx, 1, "ignoreBOM")) {
-			ignore_bom = duk_to_boolean(ctx, -1);
-		}
-	}
-
-	duk_push_this(ctx);
+		duk_to_string(thr, 0);
+	}
+	if (!duk_is_null_or_undefined(thr, 1)) {
+		if (duk_get_prop_string(thr, 1, "fatal")) {
+			fatal = duk_to_boolean(thr, -1);
+		}
+		if (duk_get_prop_string(thr, 1, "ignoreBOM")) {
+			ignore_bom = duk_to_boolean(thr, -1);
+		}
+	}
+
+	duk_push_this(thr);
 
 	/* The decode context is not assumed to be zeroed; all fields are
 	 * initialized explicitly.
 	 */
-	dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(ctx, sizeof(duk__decode_context));
+	dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(thr, sizeof(duk__decode_context));
 	dec_ctx->fatal = (duk_uint8_t) fatal;
 	dec_ctx->ignore_bom = (duk_uint8_t) ignore_bom;
 	duk__utf8_decode_init(dec_ctx);  /* Initializes remaining fields. */
 
-	duk_put_prop_string(ctx, -2, "\xff" "Context");
+	duk_put_prop_string(thr, -2, DUK_INTERNAL_SYMBOL("Context"));
 	return 0;
 }
 
 /* Get TextDecoder context from 'this'; leaves garbage on stack. */
-DUK_LOCAL duk__decode_context *duk__get_textdecoder_context(duk_context *ctx) {
+DUK_LOCAL duk__decode_context *duk__get_textdecoder_context(duk_hthread *thr) {
 	duk__decode_context *dec_ctx;
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, "\xff" "Context");
-	dec_ctx = (duk__decode_context *) duk_require_buffer(ctx, -1, NULL);
+	duk_push_this(thr);
+	duk_get_prop_string(thr, -1, DUK_INTERNAL_SYMBOL("Context"));
+	dec_ctx = (duk__decode_context *) duk_require_buffer(thr, -1, NULL);
 	DUK_ASSERT(dec_ctx != NULL);
 	return dec_ctx;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_shared_getter(duk_hthread *thr) {
 	duk__decode_context *dec_ctx;
 	duk_int_t magic;
 
-	dec_ctx = duk__get_textdecoder_context(ctx);
-	magic = duk_get_current_magic(ctx);
+	dec_ctx = duk__get_textdecoder_context(thr);
+	magic = duk_get_current_magic(thr);
 	switch (magic) {
 	case 0:
 		/* Encoding is now fixed, so _Context lookup is only needed to
 		 * validate the 'this' binding (TypeError if not TextDecoder-like).
 		 */
-		duk_push_string(ctx, "utf-8");
+		duk_push_string(thr, "utf-8");
 		break;
 	case 1:
-		duk_push_boolean(ctx, dec_ctx->fatal);
+		duk_push_boolean(thr, dec_ctx->fatal);
 		break;
 	default:
-		duk_push_boolean(ctx, dec_ctx->ignore_bom);
-		break;
-	}
-
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx) {
+		duk_push_boolean(thr, dec_ctx->ignore_bom);
+		break;
+	}
+
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_hthread *thr) {
 	duk__decode_context *dec_ctx;
 
-	dec_ctx = duk__get_textdecoder_context(ctx);
-	return duk__decode_helper(ctx, dec_ctx);
+	dec_ctx = duk__get_textdecoder_context(thr);
+	return duk__decode_helper(thr, dec_ctx);
 }
 #endif  /* DUK_USE_ENCODING_BUILTINS */
 
@@ -30610,14 +31774,14 @@
  * index 0, and decode options (not present for Buffer) at index 1.  Return value
  * is a Duktape/C function return value.
  */
-DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_hthread *thr) {
 	duk__decode_context dec_ctx;
 
 	dec_ctx.fatal = 0;  /* use replacement chars */
 	dec_ctx.ignore_bom = 1;  /* ignore BOMs (matches Node.js Buffer .toString()) */
 	duk__utf8_decode_init(&dec_ctx);
 
-	return duk__decode_helper(ctx, &dec_ctx);
+	return duk__decode_helper(thr, &dec_ctx);
 }
 
 /* automatic undefs */
@@ -30631,7 +31795,7 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_hthread *thr) {
 	/* Behavior for constructor and non-constructor call is
 	 * the same except for augmenting the created error.  When
 	 * called as a constructor, the caller (duk_new()) will handle
@@ -30639,25 +31803,22 @@
 	 * it here.
 	 */
 
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_small_int_t bidx_prototype = duk_get_current_magic(ctx);
+	duk_small_int_t bidx_prototype = duk_get_current_magic(thr);
 
 	/* same for both error and each subclass like TypeError */
 	duk_uint_t flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                             DUK_HOBJECT_FLAG_FASTREFS |
 	                             DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR);
 
-	DUK_UNREF(thr);
-
-	(void) duk_push_object_helper(ctx, flags_and_class, bidx_prototype);
+	(void) duk_push_object_helper(thr, flags_and_class, bidx_prototype);
 
 	/* If message is undefined, the own property 'message' is not set at
 	 * all to save property space.  An empty message is inherited anyway.
 	 */
-	if (!duk_is_undefined(ctx, 0)) {
-		duk_to_string(ctx, 0);
-		duk_dup_0(ctx);  /* [ message error message ] */
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
+	if (!duk_is_undefined(thr, 0)) {
+		duk_to_string(thr, 0);
+		duk_dup_0(thr);  /* [ message error message ] */
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
 	}
 
 	/* Augment the error if called as a normal function.  __FILE__ and __LINE__
@@ -30665,28 +31826,28 @@
 	 */
 
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-	if (!duk_is_constructor_call(ctx)) {
-		duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/);
-	}
-#endif
-
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) {
+	if (!duk_is_constructor_call(thr)) {
+		duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE);
+	}
+#endif
+
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_hthread *thr) {
 	/* XXX: optimize with more direct internal access */
 
-	duk_push_this(ctx);
-	(void) duk_require_hobject_promote_mask(ctx, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	duk_push_this(thr);
+	(void) duk_require_hobject_promote_mask(thr, -1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
 
 	/* [ ... this ] */
 
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_NAME);
-	if (duk_is_undefined(ctx, -1)) {
-		duk_pop(ctx);
-		duk_push_string(ctx, "Error");
-	} else {
-		duk_to_string(ctx, -1);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME);
+	if (duk_is_undefined(thr, -1)) {
+		duk_pop(thr);
+		duk_push_string(thr, "Error");
+	} else {
+		duk_to_string(thr, -1);
 	}
 
 	/* [ ... this name ] */
@@ -30695,28 +31856,28 @@
 	 * accident or are they actually needed?  The first ToString()
 	 * could conceivably return 'undefined'.
 	 */
-	duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_MESSAGE);
-	if (duk_is_undefined(ctx, -1)) {
-		duk_pop(ctx);
-		duk_push_hstring_empty(ctx);
-	} else {
-		duk_to_string(ctx, -1);
+	duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE);
+	if (duk_is_undefined(thr, -1)) {
+		duk_pop(thr);
+		duk_push_hstring_empty(thr);
+	} else {
+		duk_to_string(thr, -1);
 	}
 
 	/* [ ... this name message ] */
 
-	if (duk_get_length(ctx, -2) == 0) {
+	if (duk_get_length(thr, -2) == 0) {
 		/* name is empty -> return message */
 		return 1;
 	}
-	if (duk_get_length(ctx, -1) == 0) {
+	if (duk_get_length(thr, -1) == 0) {
 		/* message is empty -> return name */
-		duk_pop(ctx);
+		duk_pop(thr);
 		return 1;
 	}
-	duk_push_string(ctx, ": ");
-	duk_insert(ctx, -2);  /* ... name ': ' message */
-	duk_concat(ctx, 3);
+	duk_push_string(thr, ": ");
+	duk_insert(thr, -2);  /* ... name ': ' message */
+	duk_concat(thr, 3);
 
 	return 1;
 }
@@ -30742,8 +31903,7 @@
 #define DUK__OUTPUT_TYPE_FILENAME    0
 #define DUK__OUTPUT_TYPE_LINENUMBER  1
 
-DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t output_type) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_hthread *thr, duk_small_int_t output_type) {
 	duk_idx_t idx_td;
 	duk_small_int_t i;  /* traceback depth fits into 16 bits */
 	duk_small_int_t t;  /* stack type fits into 16 bits */
@@ -30755,39 +31915,38 @@
 	const char *str_directeval = " directeval";
 	const char *str_empty = "";
 
-	DUK_ASSERT_TOP(ctx, 0);  /* fixed arg count */
-	DUK_UNREF(thr);
-
-	duk_push_this(ctx);
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TRACEDATA);
-	idx_td = duk_get_top_index(ctx);
-
-	duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_4SPACE);
-	duk_push_this(ctx);
+	DUK_ASSERT_TOP(thr, 0);  /* fixed arg count */
+
+	duk_push_this(thr);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_TRACEDATA);
+	idx_td = duk_get_top_index(thr);
+
+	duk_push_hstring_stridx(thr, DUK_STRIDX_NEWLINE_4SPACE);
+	duk_push_this(thr);
 
 	/* [ ... this tracedata sep this ] */
 
 	/* XXX: skip null filename? */
 
-	if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) {
+	if (duk_check_type(thr, idx_td, DUK_TYPE_OBJECT)) {
 		/* Current tracedata contains 2 entries per callstack entry. */
 		for (i = 0; ; i += 2) {
 			duk_int_t pc;
-			duk_int_t line;
-			duk_int_t flags;
+			duk_uint_t line;
+			duk_uint_t flags;
 			duk_double_t d;
 			const char *funcname;
 			const char *filename;
 			duk_hobject *h_func;
 			duk_hstring *h_name;
 
-			duk_require_stack(ctx, 5);
-			duk_get_prop_index(ctx, idx_td, i);
-			duk_get_prop_index(ctx, idx_td, i + 1);
-			d = duk_to_number_m1(ctx);
+			duk_require_stack(thr, 5);
+			duk_get_prop_index(thr, idx_td, (duk_uarridx_t) i);
+			duk_get_prop_index(thr, idx_td, (duk_uarridx_t) (i + 1));
+			d = duk_to_number_m1(thr);
 			pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32);
-			flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32);
-			t = (duk_small_int_t) duk_get_type(ctx, -2);
+			flags = (duk_uint_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32);
+			t = (duk_small_int_t) duk_get_type(thr, -2);
 
 			if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) {
 				/*
@@ -30798,17 +31957,15 @@
 
 				/* [ ... v1(func) v2(pc+flags) ] */
 
-				h_func = duk_get_hobject(ctx, -2);  /* NULL for lightfunc */
-
 				/* These may be systematically omitted by Duktape
 				 * with certain config options, but allow user to
 				 * set them on a case-by-case basis.
 				 */
-				duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME);
-				duk_get_prop_stridx_short(ctx, -3, DUK_STRIDX_FILE_NAME);
+				duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
+				duk_get_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME);
 
 #if defined(DUK_USE_PC2LINE)
-				line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc);
+				line = (duk_uint_t) duk_hobject_pc2line_query(thr, -4, (duk_uint_fast32_t) pc);
 #else
 				line = 0;
 #endif
@@ -30818,27 +31975,29 @@
 				/* When looking for .fileName/.lineNumber, blame first
 				 * function which has a .fileName.
 				 */
-				if (duk_is_string_notsymbol(ctx, -1)) {
+				if (duk_is_string_notsymbol(thr, -1)) {
 					if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
 						return 1;
 					} else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
-						duk_push_int(ctx, line);
+						duk_push_uint(thr, line);
 						return 1;
 					}
 				}
 
 				/* XXX: Change 'anon' handling here too, to use empty string for anonymous functions? */
 				/* XXX: Could be improved by coercing to a readable duk_tval (especially string escaping) */
-				h_name = duk_get_hstring_notsymbol(ctx, -2);  /* may be NULL */
+				h_name = duk_get_hstring_notsymbol(thr, -2);  /* may be NULL */
 				funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ?
 				           "[anon]" : (const char *) DUK_HSTRING_GET_DATA(h_name);
-				filename = duk_get_string_notsymbol(ctx, -1);
+				filename = duk_get_string_notsymbol(thr, -1);
 				filename = filename ? filename : "";
 				DUK_ASSERT(funcname != NULL);
 				DUK_ASSERT(filename != NULL);
 
+				h_func = duk_get_hobject(thr, -4);  /* NULL for lightfunc */
+
 				if (h_func == NULL) {
-					duk_push_sprintf(ctx, "at %s light%s%s%s%s%s",
+					duk_push_sprintf(thr, "at %s light%s%s%s%s%s",
 					                 (const char *) funcname,
 					                 (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty),
@@ -30846,7 +32005,7 @@
 					                 (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
 				} else if (DUK_HOBJECT_HAS_NATFUNC(h_func)) {
-					duk_push_sprintf(ctx, "at %s (%s) native%s%s%s%s%s",
+					duk_push_sprintf(thr, "at %s (%s) native%s%s%s%s%s",
 					                 (const char *) funcname,
 					                 (const char *) filename,
 					                 (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
@@ -30855,18 +32014,18 @@
 					                 (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
 				} else {
-					duk_push_sprintf(ctx, "at %s (%s:%ld)%s%s%s%s%s",
+					duk_push_sprintf(thr, "at %s (%s:%lu)%s%s%s%s%s",
 					                 (const char *) funcname,
 					                 (const char *) filename,
-					                 (long) line,
+					                 (unsigned long) line,
 					                 (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcall : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
 					                 (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
 				}
-				duk_replace(ctx, -5);   /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */
-				duk_pop_3(ctx);         /* -> [ ... str ] */
+				duk_replace(thr, -5);   /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */
+				duk_pop_3(thr);         /* -> [ ... str ] */
 			} else if (t == DUK_TYPE_STRING) {
 				const char *str_file;
 
@@ -30883,10 +32042,10 @@
 				 */
 				if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) {
 					if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
-						duk_pop(ctx);
+						duk_pop(thr);
 						return 1;
 					} else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
-						duk_push_int(ctx, pc);
+						duk_push_int(thr, pc);
 						return 1;
 					}
 				}
@@ -30896,14 +32055,14 @@
 				 * don't need to be explicitly rejected as they pose no memory
 				 * safety issues.
 				 */
-				str_file = (const char *) duk_get_string(ctx, -2);
-				duk_push_sprintf(ctx, "at [anon] (%s:%ld) internal",
+				str_file = (const char *) duk_get_string(thr, -2);
+				duk_push_sprintf(thr, "at [anon] (%s:%ld) internal",
 				                 (const char *) (str_file ? str_file : "null"), (long) pc);
-				duk_replace(ctx, -3);  /* [ ... v1 v2 str ] -> [ ... str v2 ] */
-				duk_pop(ctx);          /* -> [ ... str ] */
+				duk_replace(thr, -3);  /* [ ... v1 v2 str ] -> [ ... str v2 ] */
+				duk_pop(thr);          /* -> [ ... str ] */
 			} else {
 				/* unknown, ignore */
-				duk_pop_2(ctx);
+				duk_pop_2(thr);
 				break;
 			}
 		}
@@ -30913,7 +32072,7 @@
 			 * marker so this is the best we can do.
 			 */
 
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_BRACKETED_ELLIPSIS);
 		}
 	}
 
@@ -30926,7 +32085,7 @@
 		 * duk_join() automatically.  We don't want to do that
 		 * coercion when providing .fileName or .lineNumber (GH-254).
 		 */
-		duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/);
+		duk_join(thr, duk_get_top(thr) - (idx_td + 2) /*count, not including sep*/);
 		return 1;
 	}
 }
@@ -30935,16 +32094,16 @@
  * save space.  For setters the stridx could be encoded into 'magic'.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) {
-	return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_TRACEBACK);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) {
-	return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_FILENAME);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) {
-	return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_LINENUMBER);
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) {
+	return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_TRACEBACK);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) {
+	return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_FILENAME);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) {
+	return duk__error_getter_helper(thr, DUK__OUTPUT_TYPE_LINENUMBER);
 }
 
 #else  /* DUK_USE_TRACEBACKS */
@@ -30961,26 +32120,26 @@
  *  of the error so this makes sense.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_hthread *thr) {
 	/* XXX: remove this native function and map 'stack' accessor
 	 * to the toString() implementation directly.
 	 */
-	return duk_bi_error_prototype_to_string(ctx);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) {
-	DUK_UNREF(ctx);
-	return 0;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) {
-	DUK_UNREF(ctx);
+	return duk_bi_error_prototype_to_string(thr);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_hthread *thr) {
+	DUK_UNREF(thr);
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_hthread *thr) {
+	DUK_UNREF(thr);
 	return 0;
 }
 
 #endif  /* DUK_USE_TRACEBACKS */
 
-DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_context *ctx, duk_small_uint_t stridx_key) {
+DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_hthread *thr, duk_small_uint_t stridx_key) {
 	/* Attempt to write 'stack', 'fileName', 'lineNumber' works as if
 	 * user code called Object.defineProperty() to create an overriding
 	 * own property.  This allows user code to overwrite .fileName etc
@@ -30988,34 +32147,34 @@
 	 * See https://github.com/svaarala/duktape/issues/387.
 	 */
 
-	DUK_ASSERT_TOP(ctx, 1);  /* fixed arg count: value */
-
-	duk_push_this(ctx);
-	duk_push_hstring_stridx(ctx, (duk_small_int_t) stridx_key);
-	duk_dup_0(ctx);
+	DUK_ASSERT_TOP(thr, 1);  /* fixed arg count: value */
+
+	duk_push_this(thr);
+	duk_push_hstring_stridx(thr, stridx_key);
+	duk_dup_0(thr);
 
 	/* [ ... obj key value ] */
 
 	DUK_DD(DUK_DDPRINT("error setter: %!T %!T %!T",
-	                   duk_get_tval(ctx, -3), duk_get_tval(ctx, -2), duk_get_tval(ctx, -1)));
-
-	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |
+	                   duk_get_tval(thr, -3), duk_get_tval(thr, -2), duk_get_tval(thr, -1)));
+
+	duk_def_prop(thr, -3, DUK_DEFPROP_HAVE_VALUE |
 	                      DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE |
 	                      DUK_DEFPROP_HAVE_ENUMERABLE | /*not enumerable*/
 	                      DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE);
 	return 0;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_context *ctx) {
-	return duk__error_setter_helper(ctx, DUK_STRIDX_STACK);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_context *ctx) {
-	return duk__error_setter_helper(ctx, DUK_STRIDX_FILE_NAME);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx) {
-	return duk__error_setter_helper(ctx, DUK_STRIDX_LINE_NUMBER);
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_setter(duk_hthread *thr) {
+	return duk__error_setter_helper(thr, DUK_STRIDX_STACK);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_setter(duk_hthread *thr) {
+	return duk__error_setter_helper(thr, DUK_STRIDX_FILE_NAME);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_hthread *thr) {
+	return duk__error_setter_helper(thr, DUK_STRIDX_LINE_NUMBER);
 }
 
 /* automatic undefs */
@@ -31030,15 +32189,14 @@
 /* #include duk_internal.h -> already included */
 
 /* Needed even when Function built-in is disabled. */
-DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_hthread *thr) {
 	/* ignore arguments, return undefined (E5 Section 15.3.4) */
-	DUK_UNREF(ctx);
+	DUK_UNREF(thr);
 	return 0;
 }
 
 #if defined(DUK_USE_FUNCTION_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_hthread *thr) {
 	duk_hstring *h_sourcecode;
 	duk_idx_t nargs;
 	duk_idx_t i;
@@ -31049,57 +32207,57 @@
 
 	/* normal and constructor calls have identical semantics */
 
-	nargs = duk_get_top(ctx);
+	nargs = duk_get_top(thr);
 	for (i = 0; i < nargs; i++) {
-		duk_to_string(ctx, i);  /* Rejects Symbols during coercion. */
+		duk_to_string(thr, i);  /* Rejects Symbols during coercion. */
 	}
 
 	if (nargs == 0) {
-		duk_push_hstring_empty(ctx);
-		duk_push_hstring_empty(ctx);
+		duk_push_hstring_empty(thr);
+		duk_push_hstring_empty(thr);
 	} else if (nargs == 1) {
 		/* XXX: cover this with the generic >1 case? */
-		duk_push_hstring_empty(ctx);
-	} else {
-		duk_insert(ctx, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
-		duk_push_string(ctx, ",");
-		duk_insert(ctx, 1);
-		duk_join(ctx, nargs - 1);
+		duk_push_hstring_empty(thr);
+	} else {
+		duk_insert(thr, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
+		duk_push_string(thr, ",");
+		duk_insert(thr, 1);
+		duk_join(thr, nargs - 1);
 	}
 
 	/* [ body formals ], formals is comma separated list that needs to be parsed */
 
-	DUK_ASSERT_TOP(ctx, 2);
+	DUK_ASSERT_TOP(thr, 2);
 
 	/* XXX: this placeholder is not always correct, but use for now.
 	 * It will fail in corner cases; see test-dev-func-cons-args.js.
 	 */
-	duk_push_string(ctx, "function(");
-	duk_dup_1(ctx);
-	duk_push_string(ctx, "){");
-	duk_dup_0(ctx);
-	duk_push_string(ctx, "}");
-	duk_concat(ctx, 5);
+	duk_push_string(thr, "function(");
+	duk_dup_1(thr);
+	duk_push_string(thr, "){");
+	duk_dup_0(thr);
+	duk_push_string(thr, "\n}");  /* Newline is important to handle trailing // comment. */
+	duk_concat(thr, 5);
 
 	/* [ body formals source ] */
 
-	DUK_ASSERT_TOP(ctx, 3);
+	DUK_ASSERT_TOP(thr, 3);
 
 	/* strictness is not inherited, intentional */
 	comp_flags = DUK_COMPILE_FUNCEXPR;
 
-	duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
-	h_sourcecode = duk_require_hstring(ctx, -2);  /* no symbol check needed; -2 is concat'd code */
+	duk_push_hstring_stridx(thr, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
+	h_sourcecode = duk_require_hstring(thr, -2);  /* no symbol check needed; -2 is concat'd code */
 	duk_js_compile(thr,
 	               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode),
 	               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode),
 	               comp_flags);
 
 	/* Force .name to 'anonymous' (ES2015). */
-	duk_push_string(ctx, "anonymous");
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
-
-	func = (duk_hcompfunc *) duk_known_hobject(ctx, -1);
+	duk_push_string(thr, "anonymous");
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
+
+	func = (duk_hcompfunc *) duk_known_hobject(thr, -1);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func));
 	DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) func));
 
@@ -31121,7 +32279,7 @@
 #endif  /* DUK_USE_FUNCTION_BUILTIN */
 
 #if defined(DUK_USE_FUNCTION_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_hthread *thr) {
 	duk_tval *tv;
 
 	/*
@@ -31144,8 +32302,8 @@
 	 *  [lightfunc code].
 	 */
 
-	duk_push_this(ctx);
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	duk_push_this(thr);
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_OBJECT(tv)) {
@@ -31159,25 +32317,25 @@
 		 * if the name contained a suitable prefix followed by '//' it
 		 * might cause the result to parse without error.
 		 */
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_NAME);
-		if (duk_is_undefined(ctx, -1)) {
+		duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME);
+		if (duk_is_undefined(thr, -1)) {
 			func_name = "";
 		} else {
-			func_name = duk_to_string(ctx, -1);
+			func_name = duk_to_string(thr, -1);
 			DUK_ASSERT(func_name != NULL);
 		}
 
 		if (DUK_HOBJECT_IS_COMPFUNC(obj)) {
-			duk_push_sprintf(ctx, "function %s() { [ecmascript code] }", (const char *) func_name);
+			duk_push_sprintf(thr, "function %s() { [ecmascript code] }", (const char *) func_name);
 		} else if (DUK_HOBJECT_IS_NATFUNC(obj)) {
-			duk_push_sprintf(ctx, "function %s() { [native code] }", (const char *) func_name);
+			duk_push_sprintf(thr, "function %s() { [native code] }", (const char *) func_name);
 		} else if (DUK_HOBJECT_IS_BOUNDFUNC(obj)) {
-			duk_push_sprintf(ctx, "function %s() { [bound code] }", (const char *) func_name);
+			duk_push_sprintf(thr, "function %s() { [bound code] }", (const char *) func_name);
 		} else {
 			goto type_error;
 		}
 	} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
-		duk_push_lightfunc_tostring(ctx, tv);
+		duk_push_lightfunc_tostring(thr, tv);
 	} else {
 		goto type_error;
 	}
@@ -31185,266 +32343,287 @@
 	return 1;
 
  type_error:
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-}
-#endif
-
-#if defined(DUK_USE_FUNCTION_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) {
-	/*
-	 *  magic = 0: Function.prototype.apply()
-	 *  magic = 1: Reflect.apply()
-	 *  magic = 2: Reflect.construct()
-	 */
-
-	duk_idx_t idx_args;
-	duk_idx_t len;
-	duk_idx_t i;
-	duk_int_t magic;
-	duk_idx_t nargs;
-	duk_uint_t mask;
-
-	magic = duk_get_current_magic(ctx);
-	switch (magic) {
-	case 0:  /* Function.prototype.apply() */
-		DUK_ASSERT_TOP(ctx, 2);  /* not a vararg function */
-		duk_push_this(ctx);
-		duk_insert(ctx, 0);
-		/* Fall through intentionally for shared handling. */
-	case 1:  /* Reflect.apply(); Function.prototype.apply() after 'this' fixup. */
-		DUK_ASSERT_TOP(ctx, 3);  /* not a vararg function */
-		idx_args = 2;
-		duk_require_callable(ctx, 0);
-		break;
-	default:  /* Reflect.construct() */
-		DUK_ASSERT(magic == 2);
-		duk_require_constructable(ctx, 0);
-		nargs = duk_get_top(ctx);
-		if (nargs < 2) {
-			DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-		}
-		if (nargs >= 3 && !duk_strict_equals(ctx, 0, 2)) {
-			/* XXX: [[Construct]] newTarget currently unsupported */
-			DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
-		}
-		idx_args = 1;
-		break;
-	}
-
-	if (magic != 2) {
-		DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT",
-							 (duk_tval *) duk_get_tval(ctx, 0),
-							 (duk_tval *) duk_get_tval(ctx, 1),
-							 (duk_tval *) duk_get_tval(ctx, 2)));
-	} else {
-		/* thisArg is not applicable for Reflect.construct(). */
-		DUK_DDD(DUK_DDDPRINT("func=%!iT, argArray=%!iT",
-							 (duk_tval *) duk_get_tval(ctx, 0),
-							 (duk_tval *) duk_get_tval(ctx, 1)));
-	}
-
-	/* [ func thisArg? argArray ] */
-
-	mask = duk_get_type_mask(ctx, idx_args);
-	if (mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) {
-		DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args"));
-		len = 0;
-	} else if (mask & DUK_TYPE_MASK_OBJECT) {
-		DUK_DDD(DUK_DDDPRINT("argArray is an object"));
-
-		/* XXX: make this an internal helper */
-		DUK_ASSERT(idx_args >= 0 && idx_args <= 0x7fffL);  /* short variants would work, but avoid shifting */
-		duk_get_prop_stridx(ctx, idx_args, DUK_STRIDX_LENGTH);
-		len = (duk_idx_t) duk_to_uint32(ctx, -1);  /* ToUint32() coercion required */
-		duk_pop(ctx);
-
-		duk_require_stack(ctx, len);
-
-		DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len));
-		for (i = 0; i < len; i++) {
-			duk_get_prop_index(ctx, idx_args, i);
-		}
-	} else {
-		goto type_error;
-	}
-	duk_remove(ctx, idx_args);
-	DUK_ASSERT_TOP(ctx, idx_args + len);
-
-	/* [ func thisArg? arg1 ... argN ] */
-
-	if (magic != 2) {
-		/* Function.prototype.apply() or Reflect.apply() */
-		DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld",
-							 (duk_tval *) duk_get_tval(ctx, 0),
-							 (duk_tval *) duk_get_tval(ctx, 1),
-							 (long) len));
-		duk_call_method(ctx, len);
-	} else {
-		/* Reflect.construct() */
-		DUK_DDD(DUK_DDDPRINT("construct, func=%!iT, len=%ld",
-							 (duk_tval *) duk_get_tval(ctx, 0),
-							 (long) len));
-		duk_new(ctx, len);
-	}
-	return 1;
-
- type_error:
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-}
-#endif
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+}
+#endif
+
+/* Always present because the native function pointer is needed in call
+ * handling.
+ */
+DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_hthread *thr) {
+	/* .call() is dealt with in call handling by simulating its
+	 * effects so this function is actually never called.
+	 */
+	DUK_UNREF(thr);
+	return DUK_RET_TYPE_ERROR;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_hthread *thr) {
+	/* Like .call(), never actually called. */
+	DUK_UNREF(thr);
+	return DUK_RET_TYPE_ERROR;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_reflect_apply(duk_hthread *thr) {
+	/* Like .call(), never actually called. */
+	DUK_UNREF(thr);
+	return DUK_RET_TYPE_ERROR;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_reflect_construct(duk_hthread *thr) {
+	/* Like .call(), never actually called. */
+	DUK_UNREF(thr);
+	return DUK_RET_TYPE_ERROR;
+}
 
 #if defined(DUK_USE_FUNCTION_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) {
-	duk_idx_t nargs;
-
-	/* Step 1 is not necessary because duk_call_method() will take
-	 * care of it.
-	 */
-
-	/* vararg function, thisArg needs special handling */
-	nargs = duk_get_top(ctx);  /* = 1 + arg count */
-	if (nargs == 0) {
-		duk_push_undefined(ctx);
+/* Create a bound function which points to a target function which may
+ * be bound or non-bound.  If the target is bound, the argument lists
+ * and 'this' binding of the functions are merged and the resulting
+ * function points directly to the non-bound target.
+ */
+DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_hthread *thr) {
+	duk_hboundfunc *h_bound;
+	duk_idx_t nargs;  /* bound args, not counting 'this' binding */
+	duk_idx_t bound_nargs;
+	duk_int_t bound_len;
+	duk_tval *tv_prevbound;
+	duk_idx_t n_prevbound;
+	duk_tval *tv_res;
+	duk_tval *tv_tmp;
+
+	/* XXX: C API call, e.g. duk_push_bound_function(thr, target_idx, nargs); */
+
+	/* Vararg function, careful arg handling, e.g. thisArg may not
+	 * be present.
+	 */
+	nargs = duk_get_top(thr) - 1;  /* actual args, not counting 'this' binding */
+	if (nargs < 0) {
 		nargs++;
-	}
-	DUK_ASSERT(nargs >= 1);
-
-	/* [ thisArg arg1 ... argN ] */
-
-	duk_push_this(ctx);  /* 'func' in the algorithm */
-	duk_insert(ctx, 0);
-
-	/* [ func thisArg arg1 ... argN ] */
-
-	DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1),
-	                     (long) (nargs - 1),
-	                     (long) duk_get_top(ctx)));
-	duk_call_method(ctx, nargs - 1);
-	return 1;
-}
-#endif  /* DUK_USE_FUNCTION_BUILTIN */
-
-#if defined(DUK_USE_FUNCTION_BUILTIN)
-/* XXX: the implementation now assumes "chained" bound functions,
- * whereas "collapsed" bound functions (where there is ever only
- * one bound function which directly points to a non-bound, final
- * function) would require a "collapsing" implementation which
- * merges argument lists etc here.
- */
-DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *h_bound;
-	duk_hobject *h_target;
-	duk_idx_t nargs;
-	duk_idx_t i;
-
-	/* vararg function, careful arg handling (e.g. thisArg may not be present) */
-	nargs = duk_get_top(ctx);  /* = 1 + arg count */
-	if (nargs == 0) {
-		duk_push_undefined(ctx);
-		nargs++;
-	}
-	DUK_ASSERT(nargs >= 1);
-
-	duk_push_this(ctx);
-	duk_require_callable(ctx, -1);
-
-	/* [ thisArg arg1 ... argN func ]  (thisArg+args == nargs total) */
-	DUK_ASSERT_TOP(ctx, nargs + 1);
-
-	/* create bound function object */
-	h_bound = duk_push_object_helper(ctx,
-	                                 DUK_HOBJECT_FLAG_EXTENSIBLE |
-	                                 DUK_HOBJECT_FLAG_FASTREFS |
-	                                 DUK_HOBJECT_FLAG_BOUNDFUNC |
-	                                 DUK_HOBJECT_FLAG_CONSTRUCTABLE |
-	                                 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION),
-	                                 DUK_BIDX_FUNCTION_PROTOTYPE);
-	DUK_ASSERT(h_bound != NULL);
+		duk_push_undefined(thr);
+	}
+	DUK_ASSERT(nargs >= 0);
+
+	/* Limit 'nargs' for bound functions to guarantee arithmetic
+	 * below will never wrap.
+	 */
+	if (nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) {
+		DUK_DCERROR_RANGE_INVALID_COUNT(thr);
+	}
+
+	duk_push_this(thr);
+	duk_require_callable(thr, -1);
+
+	/* [ thisArg arg1 ... argN func ]  (thisArg+args == nargs+1 total) */
+	DUK_ASSERT_TOP(thr, nargs + 2);
+
+	/* Create bound function object. */
+	h_bound = duk_push_hboundfunc(thr);
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->target));
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&h_bound->this_binding));
+	DUK_ASSERT(h_bound->args == NULL);
+	DUK_ASSERT(h_bound->nargs == 0);
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_bound) == NULL);
 
 	/* [ thisArg arg1 ... argN func boundFunc ] */
-	duk_dup_m2(ctx);  /* func */
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
-
-	duk_dup_0(ctx);   /* thisArg */
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE);
-
-	duk_push_array(ctx);
-
-	/* [ thisArg arg1 ... argN func boundFunc argArray ] */
-
-	for (i = 0; i < nargs - 1; i++) {
-		duk_dup(ctx, 1 + i);
-		duk_put_prop_index(ctx, -2, i);
-	}
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE);
+
+	/* If the target is a bound function, argument lists must be
+	 * merged.  The 'this' binding closest to the target function
+	 * wins because in call handling the 'this' gets replaced over
+	 * and over again until we call the non-bound function.
+	 */
+	tv_prevbound = NULL;
+	n_prevbound = 0;
+	tv_tmp = DUK_GET_TVAL_POSIDX(thr, 0);
+	DUK_TVAL_SET_TVAL(&h_bound->this_binding, tv_tmp);
+	tv_tmp = DUK_GET_TVAL_NEGIDX(thr, -2);
+	DUK_TVAL_SET_TVAL(&h_bound->target, tv_tmp);
+
+	if (DUK_TVAL_IS_OBJECT(tv_tmp)) {
+		duk_hobject *h_target;
+		duk_hobject *bound_proto;
+
+		h_target = DUK_TVAL_GET_OBJECT(tv_tmp);
+		DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(h_target));
+
+		/* Internal prototype must be copied from the target.
+		 * For lightfuncs Function.prototype is used and is already
+		 * in place.
+		 */
+		bound_proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_target);
+		DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto);
+
+		/* The 'strict' flag is copied to get the special [[Get]] of E5.1
+		 * Section 15.3.5.4 to apply when a 'caller' value is a strict bound
+		 * function.  Not sure if this is correct, because the specification
+		 * is a bit ambiguous on this point but it would make sense.
+		 */
+		/* Strictness is inherited from target. */
+		if (DUK_HOBJECT_HAS_STRICT(h_target)) {
+			DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound);
+		}
+
+		if (DUK_HOBJECT_HAS_BOUNDFUNC(h_target)) {
+			duk_hboundfunc *h_boundtarget;
+
+			h_boundtarget = (duk_hboundfunc *) h_target;
+
+			/* The final function should always be non-bound, unless
+			 * there's a bug in the internals.  Assert for it.
+			 */
+			DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(&h_boundtarget->target) ||
+			           (DUK_TVAL_IS_OBJECT(&h_boundtarget->target) &&
+			            DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(&h_boundtarget->target)) &&
+			            !DUK_HOBJECT_IS_BOUNDFUNC(DUK_TVAL_GET_OBJECT(&h_boundtarget->target))));
+
+			DUK_TVAL_SET_TVAL(&h_bound->target, &h_boundtarget->target);
+			DUK_TVAL_SET_TVAL(&h_bound->this_binding, &h_boundtarget->this_binding);
+
+			tv_prevbound = h_boundtarget->args;
+			n_prevbound = h_boundtarget->nargs;
+		}
+	} else {
+		/* Lightfuncs are always strict. */
+		duk_hobject *bound_proto;
+
+		DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_tmp));
+		DUK_HOBJECT_SET_STRICT((duk_hobject *) h_bound);
+		bound_proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
+		DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) h_bound, bound_proto);
+	}
+
+	DUK_TVAL_INCREF(thr, &h_bound->target);  /* old values undefined, no decref needed */
+	DUK_TVAL_INCREF(thr, &h_bound->this_binding);
+
+	bound_nargs = n_prevbound + nargs;
+	if (bound_nargs > (duk_idx_t) DUK_HBOUNDFUNC_MAX_ARGS) {
+		DUK_DCERROR_RANGE_INVALID_COUNT(thr);
+	}
+	tv_res = (duk_tval *) DUK_ALLOC_CHECKED(thr, ((duk_size_t) bound_nargs) * sizeof(duk_tval));
+	DUK_ASSERT(tv_res != NULL);
+	DUK_ASSERT(h_bound->args == NULL);
+	DUK_ASSERT(h_bound->nargs == 0);
+	h_bound->args = tv_res;
+	h_bound->nargs = bound_nargs;
+
+	DUK_ASSERT(n_prevbound >= 0);
+	duk_copy_tvals_incref(thr, tv_res, tv_prevbound, (duk_size_t) n_prevbound);
+	DUK_ASSERT(nargs >= 0);
+	duk_copy_tvals_incref(thr, tv_res + n_prevbound, DUK_GET_TVAL_POSIDX(thr, 1), (duk_size_t) nargs);
 
 	/* [ thisArg arg1 ... argN func boundFunc ] */
 
-	h_target = duk_get_hobject(ctx, -2);
-
-	/* internal prototype must be copied from the target */
-	if (h_target != NULL) {
-		/* For lightfuncs Function.prototype is used and is already in place. */
-		DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_bound, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_target));
-	}
-
-	/* bound function 'length' property is interesting */
-	if (h_target == NULL ||  /* lightfunc */
-	    DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) {
-		/* For lightfuncs, simply read the virtual property. */
-		duk_int_t tmp;
-		duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH);
-		tmp = duk_to_int(ctx, -1) - (nargs - 1);  /* step 15.a */
-		duk_pop(ctx);
-		duk_push_int(ctx, (tmp < 0 ? 0 : tmp));
-	} else {
-		duk_push_int(ctx, 0);
-	}
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);  /* attrs in E6 Section 9.2.4 */
-
-	/* caller and arguments must use the same thrower, [[ThrowTypeError]] */
-	duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER);
-	duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS);
-
-	/* XXX: 'copy properties' API call? */
-#if defined(DUK_USE_FUNC_NAME_PROPERTY)
-	duk_push_string(ctx, "bound ");  /* ES2015 19.2.3.2. */
-	duk_get_prop_stridx_short(ctx, -3, DUK_STRIDX_NAME);
-	if (!duk_is_string_notsymbol(ctx, -1)) {
+	/* Bound function 'length' property is interesting.
+	 * For lightfuncs, simply read the virtual property.
+	 */
+	duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH);
+	bound_len = duk_get_int(thr, -1);  /* ES2015: no coercion */
+	if (bound_len < nargs) {
+		bound_len = 0;
+	} else {
+		bound_len -= nargs;
+	}
+	if (sizeof(duk_int_t) > 4 && bound_len > (duk_int_t) DUK_UINT32_MAX) {
+		bound_len = (duk_int_t) DUK_UINT32_MAX;
+	}
+	duk_pop(thr);
+	DUK_ASSERT(bound_len >= 0);
+	tv_tmp = thr->valstack_top++;
+	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv_tmp));
+	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(tv_tmp));
+	DUK_TVAL_SET_U32(tv_tmp, (duk_uint32_t) bound_len);  /* in-place update, fastint */
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);  /* attrs in E6 Section 9.2.4 */
+
+	/* XXX: could these be virtual? */
+	/* Caller and arguments must use the same thrower, [[ThrowTypeError]]. */
+	duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_CALLER);
+	duk_xdef_prop_stridx_thrower(thr, -1, DUK_STRIDX_LC_ARGUMENTS);
+
+	/* Function name and fileName (non-standard). */
+	duk_push_string(thr, "bound ");  /* ES2015 19.2.3.2. */
+	duk_get_prop_stridx(thr, -3, DUK_STRIDX_NAME);
+	if (!duk_is_string_notsymbol(thr, -1)) {
 		/* ES2015 has requirement to check that .name of target is a string
 		 * (also must check for Symbol); if not, targetName should be the
 		 * empty string.  ES2015 19.2.3.2.
 		 */
-		duk_pop(ctx);
-		duk_push_hstring_empty(ctx);
-	}
-	duk_concat(ctx, 2);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
-#endif
+		duk_pop(thr);
+		duk_push_hstring_empty(thr);
+	}
+	duk_concat(thr, 2);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
 #if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
-	duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_FILE_NAME);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
-#endif
-
-	/* The 'strict' flag is copied to get the special [[Get]] of E5.1
-	 * Section 15.3.5.4 to apply when a 'caller' value is a strict bound
-	 * function.  Not sure if this is correct, because the specification
-	 * is a bit ambiguous on this point but it would make sense.
-	 */
-	if (h_target == NULL) {
-		/* Lightfuncs are always strict. */
-		DUK_HOBJECT_SET_STRICT(h_bound);
-	} else if (DUK_HOBJECT_HAS_STRICT(h_target)) {
-		DUK_HOBJECT_SET_STRICT(h_bound);
-	}
-	DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
+#endif
+
+	DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(thr, -1)));
 
 	return 1;
 }
 #endif  /* DUK_USE_FUNCTION_BUILTIN */
+
+/* %NativeFunctionPrototype% .length getter. */
+DUK_INTERNAL duk_ret_t duk_bi_native_function_length(duk_hthread *thr) {
+	duk_tval *tv;
+	duk_hnatfunc *h;
+	duk_int16_t func_nargs;
+
+	tv = duk_get_borrowed_this_tval(thr);
+	DUK_ASSERT(tv != NULL);
+
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) {
+			goto fail_type;
+		}
+		func_nargs = h->nargs;
+		duk_push_int(thr, func_nargs == DUK_HNATFUNC_NARGS_VARARGS ? 0 : func_nargs);
+	} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+		duk_small_uint_t lf_flags;
+		duk_small_uint_t lf_len;
+
+		lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
+		lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
+		duk_push_uint(thr, lf_len);
+	} else {
+		goto fail_type;
+	}
+	return 1;
+
+ fail_type:
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+}
+
+/* %NativeFunctionPrototype% .name getter. */
+DUK_INTERNAL duk_ret_t duk_bi_native_function_name(duk_hthread *thr) {
+	duk_tval *tv;
+	duk_hnatfunc *h;
+
+	tv = duk_get_borrowed_this_tval(thr);
+	DUK_ASSERT(tv != NULL);
+
+	if (DUK_TVAL_IS_OBJECT(tv)) {
+		h = (duk_hnatfunc *) DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(h != NULL);
+		if (!DUK_HOBJECT_IS_NATFUNC((duk_hobject *) h)) {
+			goto fail_type;
+		}
+#if 0
+		duk_push_hnatfunc_name(thr, h);
+#endif
+		duk_push_hstring_empty(thr);
+	} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+		duk_push_lightfunc_name(thr, tv);
+	} else {
+		goto fail_type;
+	}
+	return 1;
+
+ fail_type:
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+}
 #line 1 "duk_bi_global.c"
 /*
  *  Global object built-ins
@@ -31561,15 +32740,14 @@
 	return t;
 }
 
-DUK_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback callback, const void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL int duk__transform_helper(duk_hthread *thr, duk__transform_callback callback, const void *udata) {
 	duk__transform_context tfm_ctx_alloc;
 	duk__transform_context *tfm_ctx = &tfm_ctx_alloc;
 	duk_codepoint_t cp;
 
 	tfm_ctx->thr = thr;
 
-	tfm_ctx->h_str = duk_to_hstring(ctx, 0);
+	tfm_ctx->h_str = duk_to_hstring(thr, 0);
 	DUK_ASSERT(tfm_ctx->h_str != NULL);
 
 	DUK_BW_INIT_PUSHBUF(thr, &tfm_ctx->bw, DUK_HSTRING_GET_BYTELEN(tfm_ctx->h_str));  /* initial size guess */
@@ -31585,7 +32763,7 @@
 
 	DUK_BW_COMPACT(thr, &tfm_ctx->bw);
 
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe if transform is safe. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if transform is safe. */
 	return 1;
 }
 
@@ -31618,7 +32796,7 @@
 			goto uri_error;
 		}
 		cp1 = cp;
-		cp = ((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L;
+		cp = (duk_codepoint_t) (((cp1 - 0xd800L) << 10) + (cp2 - 0xdc00L) + 0x10000L);
 	} else if (cp > 0x10ffffL) {
 		/* Although we can allow non-BMP characters (they'll decode
 		 * back into surrogate pairs), we don't allow extended UTF-8
@@ -31776,7 +32954,7 @@
 			DUK_ASSERT(cp < 0x100000L);
 
 			DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp >> 10) + 0xd800L));
-			DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffUL) + 0xdc00L));
+			DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, ((cp & 0x03ffL) + 0xdc00L));
 		} else {
 			DUK_BW_WRITE_RAW_XUTF8(tfm_ctx->thr, &tfm_ctx->bw, cp);
 		}
@@ -31864,20 +33042,19 @@
  *  calling activation at all which needs careful handling.
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_activation *act_caller;
 	duk_activation *act_eval;
-	duk_activation *act;
 	duk_hcompfunc *func;
 	duk_hobject *outer_lex_env;
 	duk_hobject *outer_var_env;
 	duk_bool_t this_to_global = 1;
 	duk_small_uint_t comp_flags;
 	duk_int_t level = -2;
-
-	DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2);  /* 2 when called by debugger */
+	duk_small_uint_t call_flags;
+
+	DUK_ASSERT(duk_get_top(thr) == 1 || duk_get_top(thr) == 2);  /* 2 when called by debugger */
 	DUK_ASSERT(thr->callstack_top >= 1);  /* at least this function exists */
 	DUK_ASSERT(thr->callstack_curr != NULL);
 	DUK_ASSERT((thr->callstack_curr->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */
@@ -31891,7 +33068,7 @@
 	 *  activation doesn't exist, call must be indirect.
 	 */
 
-	h = duk_get_hstring_notsymbol(ctx, 0);
+	h = duk_get_hstring_notsymbol(thr, 0);
 	if (!h) {
 		/* Symbol must be returned as is, like any non-string values. */
 		return 1;  /* return arg as-is */
@@ -31902,8 +33079,8 @@
 	 * for an Ecmascript eval().
 	 */
 	DUK_ASSERT(level == -2);  /* by default, use caller's environment */
-	if (duk_get_top(ctx) >= 2 && duk_is_number(ctx, 1)) {
-		level = duk_get_int(ctx, 1);
+	if (duk_get_top(thr) >= 2 && duk_is_number(thr, 1)) {
+		level = duk_get_int(thr, 1);
 	}
 	DUK_ASSERT(level <= -2);  /* This is guaranteed by debugger code. */
 #endif
@@ -31913,11 +33090,11 @@
 	comp_flags = DUK_COMPILE_EVAL;
 	act_eval = thr->callstack_curr;  /* this function */
 	DUK_ASSERT(act_eval != NULL);
-	if (thr->callstack_top >= (duk_size_t) -level) {
+	act_caller = duk_hthread_get_activation_for_level(thr, level);
+	if (act_caller != NULL) {
 		/* Have a calling activation, check for direct eval (otherwise
 		 * assume indirect eval.
 		 */
-		act_caller = thr->callstack + thr->callstack_top + level;  /* caller */
 		if ((act_caller->flags & DUK_ACT_FLAG_STRICT) &&
 		    (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL)) {
 			/* Only direct eval inherits strictness from calling code
@@ -31928,35 +33105,31 @@
 	} else {
 		DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0);
 	}
-	act_caller = NULL;  /* avoid dereference after potential callstack realloc */
-	act_eval = NULL;
-
-	duk_push_hstring_stridx(ctx, DUK_STRIDX_INPUT);  /* XXX: copy from caller? */
+
+	duk_push_hstring_stridx(thr, DUK_STRIDX_INPUT);  /* XXX: copy from caller? */
 	duk_js_compile(thr,
 	               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h),
 	               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h),
 	               comp_flags);
-	func = (duk_hcompfunc *) duk_known_hobject(ctx, -1);
+	func = (duk_hcompfunc *) duk_known_hobject(thr, -1);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) func));
 
 	/* [ source template ] */
 
 	/* E5 Section 10.4.2 */
-	DUK_ASSERT(thr->callstack_top >= 1);
-	act = thr->callstack_curr;  /* this function */
-	if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
+
+	if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
 		DUK_ASSERT(thr->callstack_top >= 2);
-		act = thr->callstack + thr->callstack_top + level;  /* caller */
-		if (act->lex_env == NULL) {
-			DUK_ASSERT(act->var_env == NULL);
+		DUK_ASSERT(act_caller != NULL);
+		if (act_caller->lex_env == NULL) {
+			DUK_ASSERT(act_caller->var_env == NULL);
 			DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
 
 			/* this may have side effects, so re-lookup act */
-			duk_js_init_activation_environment_records_delayed(thr, act);
-			act = thr->callstack + thr->callstack_top + level;
-		}
-		DUK_ASSERT(act->lex_env != NULL);
-		DUK_ASSERT(act->var_env != NULL);
+			duk_js_init_activation_environment_records_delayed(thr, act_caller);
+		}
+		DUK_ASSERT(act_caller->lex_env != NULL);
+		DUK_ASSERT(act_caller->var_env != NULL);
 
 		this_to_global = 0;
 
@@ -31968,14 +33141,13 @@
 			                     "var_env and lex_env to a fresh env, "
 			                     "this_binding to caller's this_binding"));
 
-			act_lex_env = act->lex_env;
-			act = NULL;  /* invalidated */
+			act_lex_env = act_caller->lex_env;
 
 			new_env = duk_hdecenv_alloc(thr,
 			                            DUK_HOBJECT_FLAG_EXTENSIBLE |
 			                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
 			DUK_ASSERT(new_env != NULL);
-			duk_push_hobject(ctx, (duk_hobject *) new_env);
+			duk_push_hobject(thr, (duk_hobject *) new_env);
 
 			DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
 			DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act_lex_env);
@@ -31985,7 +33157,7 @@
 			outer_lex_env = (duk_hobject *) new_env;
 			outer_var_env = (duk_hobject *) new_env;
 
-			duk_insert(ctx, 0);  /* stash to bottom of value stack to keep new_env reachable for duration of eval */
+			duk_insert(thr, 0);  /* stash to bottom of value stack to keep new_env reachable for duration of eval */
 
 			/* compiler's responsibility */
 			DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
@@ -31994,8 +33166,8 @@
 			                     "var_env and lex_env to caller's envs, "
 			                     "this_binding to caller's this_binding"));
 
-			outer_lex_env = act->lex_env;
-			outer_var_env = act->var_env;
+			outer_lex_env = act_caller->lex_env;
+			outer_var_env = act_caller->var_env;
 
 			/* compiler's responsibility */
 			DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) func));
@@ -32008,7 +33180,6 @@
 		outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
 		outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
 	}
-	act = NULL;
 
 	/* Eval code doesn't need an automatic .prototype object. */
 	duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 0 /*add_auto_proto*/);
@@ -32017,24 +33188,34 @@
 
 	if (this_to_global) {
 		DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL);
-		duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL);
+		duk_push_hobject_bidx(thr, DUK_BIDX_GLOBAL);
 	} else {
 		duk_tval *tv;
 		DUK_ASSERT(thr->callstack_top >= 2);
-		act = thr->callstack + thr->callstack_top + level;  /* caller */
-		tv = thr->valstack + act->idx_bottom - 1;  /* this is just beneath bottom */
+		DUK_ASSERT(act_caller != NULL);
+		tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act_caller->bottom_byteoff - sizeof(duk_tval));  /* this is just beneath bottom */
 		DUK_ASSERT(tv >= thr->valstack);
-		duk_push_tval(ctx, tv);
+		duk_push_tval(thr, tv);
 	}
 
 	DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T",
 	                     (duk_heaphdr *) outer_lex_env,
 	                     (duk_heaphdr *) outer_var_env,
-	                     duk_get_tval(ctx, -1)));
+	                     duk_get_tval(thr, -1)));
 
 	/* [ env? source template closure this ] */
 
-	duk_call_method(ctx, 0);
+	call_flags = 0;
+	if (act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
+		/* Set DIRECT_EVAL flag for the call; it's not strictly
+		 * needed for the 'inner' eval call (the eval body) but
+		 * current new.target implementation expects to find it
+		 * so it can traverse direct eval chains up to the real
+		 * calling function.
+		 */
+		call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
+	}
+	duk_handle_call_unprotected_nargs(thr, 0, call_flags);
 
 	/* [ env? source template result ] */
 
@@ -32046,14 +33227,14 @@
  */
 
 #if defined(DUK_USE_GLOBAL_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_hthread *thr) {
 	duk_int32_t radix;
 	duk_small_uint_t s2n_flags;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_to_string(ctx, 0);  /* Reject symbols. */
-
-	radix = duk_to_int32(ctx, 1);
+	DUK_ASSERT_TOP(thr, 2);
+	duk_to_string(thr, 0);  /* Reject symbols. */
+
+	radix = duk_to_int32(thr, 1);
 
 	/* While parseInt() recognizes 0xdeadbeef, it doesn't recognize
 	 * ES2015 0o123 or 0b10001.
@@ -32083,25 +33264,22 @@
 		radix = 10;
 	}
 
-	duk_dup_0(ctx);
-	duk_numconv_parse(ctx, radix, s2n_flags);
+	duk_dup_0(thr);
+	duk_numconv_parse(thr, (duk_small_int_t) radix, s2n_flags);
 	return 1;
 
  ret_nan:
-	duk_push_nan(ctx);
+	duk_push_nan(thr);
 	return 1;
 }
 #endif  /* DUK_USE_GLOBAL_BUILTIN */
 
 #if defined(DUK_USE_GLOBAL_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_hthread *thr) {
 	duk_small_uint_t s2n_flags;
-	duk_int32_t radix;
-
-	DUK_ASSERT_TOP(ctx, 1);
-	duk_to_string(ctx, 0);  /* Reject symbols. */
-
-	radix = 10;
+
+	DUK_ASSERT_TOP(thr, 1);
+	duk_to_string(thr, 0);  /* Reject symbols. */
 
 	/* XXX: check flags */
 	s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
@@ -32115,7 +33293,7 @@
 	            DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
 	            DUK_S2N_FLAG_ALLOW_LEADING_ZERO;
 
-	duk_numconv_parse(ctx, radix, s2n_flags);
+	duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
 	return 1;
 }
 #endif  /* DUK_USE_GLOBAL_BUILTIN */
@@ -32125,17 +33303,17 @@
  */
 
 #if defined(DUK_USE_GLOBAL_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_context *ctx) {
-	duk_double_t d = duk_to_number(ctx, 0);
-	duk_push_boolean(ctx, DUK_ISNAN(d));
+DUK_INTERNAL duk_ret_t duk_bi_global_object_is_nan(duk_hthread *thr) {
+	duk_double_t d = duk_to_number(thr, 0);
+	duk_push_boolean(thr, (duk_bool_t) DUK_ISNAN(d));
 	return 1;
 }
 #endif  /* DUK_USE_GLOBAL_BUILTIN */
 
 #if defined(DUK_USE_GLOBAL_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx) {
-	duk_double_t d = duk_to_number(ctx, 0);
-	duk_push_boolean(ctx, DUK_ISFINITE(d));
+DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_hthread *thr) {
+	duk_double_t d = duk_to_number(thr, 0);
+	duk_push_boolean(thr, (duk_bool_t) DUK_ISFINITE(d));
 	return 1;
 }
 #endif  /* DUK_USE_GLOBAL_BUILTIN */
@@ -32145,29 +33323,29 @@
  */
 
 #if defined(DUK_USE_GLOBAL_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table);
+DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_reserved_table);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_global_object_decode_uri_component(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_decode_uri, (const void *) duk__decode_uri_component_reserved_table);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uriunescaped_table);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_global_object_encode_uri_component(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_encode_uri, (const void *) duk__encode_uricomponent_unescaped_table);
 }
 
 #if defined(DUK_USE_SECTION_B)
-DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_escape, (const void *) NULL);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_context *ctx) {
-	return duk__transform_helper(ctx, duk__transform_callback_unescape, (const void *) NULL);
+DUK_INTERNAL duk_ret_t duk_bi_global_object_escape(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_escape, (const void *) NULL);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_global_object_unescape(duk_hthread *thr) {
+	return duk__transform_helper(thr, duk__transform_callback_unescape, (const void *) NULL);
 }
 #endif  /* DUK_USE_SECTION_B */
 #endif  /* DUK_USE_GLOBAL_BUILTIN */
@@ -32260,7 +33438,7 @@
 #if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
 DUK_LOCAL_DECL void duk__enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h);
 #endif
-DUK_LOCAL_DECL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth);
+DUK_LOCAL_DECL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth);
 
 /*
  *  Helper tables
@@ -32459,7 +33637,7 @@
 		DUK_ASSERT(duk_hex_dectab[0] == -1);
 		t = duk_hex_dectab[x & 0xff];
 		if (DUK_LIKELY(t >= 0)) {
-			res = (res * 16) + t;
+			res = (res * 16) + (duk_uint_fast32_t) t;
 		} else {
 			/* catches EOF and invalid digits */
 			goto syntax_error;
@@ -32563,7 +33741,6 @@
 
 DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) {
 	duk_hthread *thr = js_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_bufwriter_ctx bw_alloc;
 	duk_bufwriter_ctx *bw;
 	duk_uint8_t *q;
@@ -32656,7 +33833,7 @@
 #endif  /* DUK_USE_JSON_DECSTRING_FASTPATH */
 
 	DUK_BW_SETPTR_AND_COMPACT(js_ctx->thr, bw, q);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe if input string is safe. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if input string is safe. */
 
 	/* [ ... str ] */
 
@@ -32673,7 +33850,6 @@
  */
 DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) {
 	duk_hthread *thr = js_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	const duk_uint8_t *p;
 	duk_small_int_t x;
 
@@ -32706,7 +33882,7 @@
 		p++;
 	}
 
-	duk_push_lstring(ctx, (const char *) js_ctx->p, (duk_size_t) (p - js_ctx->p));
+	duk_push_lstring(thr, (const char *) js_ctx->p, (duk_size_t) (p - js_ctx->p));
 	js_ctx->p = p;
 
 	/* [ ... str ] */
@@ -32716,7 +33892,6 @@
 #if defined(DUK_USE_JX)
 DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) {
 	duk_hthread *thr = js_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	const duk_uint8_t *p;
 	duk_small_int_t x;
 	void *voidptr;
@@ -32754,7 +33929,7 @@
 
 	voidptr = NULL;
 	(void) DUK_SSCANF((const char *) js_ctx->p, DUK_STR_FMT_PTR, &voidptr);
-	duk_push_pointer(ctx, voidptr);
+	duk_push_pointer(thr, voidptr);
 	js_ctx->p = p + 1;  /* skip ')' */
 
 	/* [ ... ptr ] */
@@ -32770,7 +33945,6 @@
 #if defined(DUK_USE_JX)
 DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) {
 	duk_hthread *thr = js_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	const duk_uint8_t *p;
 	duk_uint8_t *buf;
 	duk_size_t src_len;
@@ -32809,10 +33983,10 @@
 
 	/* XXX: this is not very nice; unnecessary copy is made. */
 	src_len = (duk_size_t) (p - js_ctx->p);
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, src_len);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, src_len);
 	DUK_ASSERT(buf != NULL);
 	DUK_MEMCPY((void *) buf, (const void *) js_ctx->p, src_len);
-	duk_hex_decode(ctx, -1);
+	duk_hex_decode(thr, -1);
 
 	js_ctx->p = p + 1;  /* skip '|' */
 
@@ -32828,7 +34002,7 @@
 
 /* Parse a number, other than NaN or +/- Infinity */
 DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	const duk_uint8_t *p_start;
 	const duk_uint8_t *p;
 	duk_uint8_t x;
@@ -32873,35 +34047,35 @@
 	js_ctx->p = p;
 
 	DUK_ASSERT(js_ctx->p > p_start);
-	duk_push_lstring(ctx, (const char *) p_start, (duk_size_t) (p - p_start));
+	duk_push_lstring(thr, (const char *) p_start, (duk_size_t) (p - p_start));
 
 	s2n_flags = DUK_S2N_FLAG_ALLOW_EXP |
 	            DUK_S2N_FLAG_ALLOW_MINUS |  /* but don't allow leading plus */
 	            DUK_S2N_FLAG_ALLOW_FRAC;
 
 	DUK_DDD(DUK_DDDPRINT("parse_number: string before parsing: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-	duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags);
-	if (duk_is_nan(ctx, -1)) {
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+	duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
+	if (duk_is_nan(thr, -1)) {
 		duk__dec_syntax_error(js_ctx);
 	}
-	DUK_ASSERT(duk_is_number(ctx, -1));
+	DUK_ASSERT(duk_is_number(thr, -1));
 	DUK_DDD(DUK_DDDPRINT("parse_number: final number: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* [ ... num ] */
 }
 
 DUK_LOCAL void duk__dec_objarr_entry(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
-	duk_require_stack(ctx, DUK_JSON_DEC_REQSTACK);
+	duk_hthread *thr = js_ctx->thr;
+	duk_require_stack(thr, DUK_JSON_DEC_REQSTACK);
 
 	/* c recursion check */
 
-	DUK_ASSERT(js_ctx->recursion_depth >= 0);
+	DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
 	DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
 	if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
-		DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_JSONDEC_RECLIMIT);
+		DUK_ERROR_RANGE(thr, DUK_STR_JSONDEC_RECLIMIT);
 	}
 	js_ctx->recursion_depth++;
 }
@@ -32915,7 +34089,7 @@
 }
 
 DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_int_t key_count;  /* XXX: a "first" flag would suffice */
 	duk_uint8_t x;
 
@@ -32923,7 +34097,7 @@
 
 	duk__dec_objarr_entry(js_ctx);
 
-	duk_push_object(ctx);
+	duk_push_object(thr);
 
 	/* Initial '{' has been checked and eaten by caller. */
 
@@ -32932,7 +34106,7 @@
 		x = duk__dec_get_nonwhite(js_ctx);
 
 		DUK_DDD(DUK_DDDPRINT("parse_object: obj=%!T, x=%ld, key_count=%ld",
-		                     (duk_tval *) duk_get_tval(ctx, -1),
+		                     (duk_tval *) duk_get_tval(thr, -1),
 		                     (long) x, (long) key_count));
 
 		/* handle comma and closing brace */
@@ -32977,7 +34151,7 @@
 
 		/* [ ... obj key val ] */
 
-		duk_xdef_prop_wec(ctx, -3);
+		duk_xdef_prop_wec(thr, -3);
 
 		/* [ ... obj ] */
 
@@ -32987,7 +34161,7 @@
 	/* [ ... obj ] */
 
 	DUK_DDD(DUK_DDDPRINT("parse_object: final object is %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	duk__dec_objarr_exit(js_ctx);
 	return;
@@ -32998,7 +34172,7 @@
 }
 
 DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_uarridx_t arr_idx;
 	duk_uint8_t x;
 
@@ -33006,7 +34180,7 @@
 
 	duk__dec_objarr_entry(js_ctx);
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 
 	/* Initial '[' has been checked and eaten by caller. */
 
@@ -33015,7 +34189,7 @@
 		x = duk__dec_get_nonwhite(js_ctx);
 
 		DUK_DDD(DUK_DDDPRINT("parse_array: arr=%!T, x=%ld, arr_idx=%ld",
-		                     (duk_tval *) duk_get_tval(ctx, -1),
+		                     (duk_tval *) duk_get_tval(thr, -1),
 		                     (long) x, (long) arr_idx));
 
 		/* handle comma and closing bracket */
@@ -33042,7 +34216,7 @@
 
 		/* [ ... arr val ] */
 
-		duk_xdef_prop_index_wec(ctx, -2, arr_idx);
+		duk_xdef_prop_index_wec(thr, -2, arr_idx);
 		arr_idx++;
 	}
 
@@ -33050,12 +34224,12 @@
 	 * set the values.
 	 */
 
-	duk_set_length(ctx, -1, arr_idx);
+	duk_set_length(thr, -1, arr_idx);
 
 	/* [ ... arr ] */
 
 	DUK_DDD(DUK_DDDPRINT("parse_array: final array is %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	duk__dec_objarr_exit(js_ctx);
 	return;
@@ -33066,7 +34240,7 @@
 }
 
 DUK_LOCAL void duk__dec_value(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_uint8_t x;
 
 	x = duk__dec_get_nonwhite(js_ctx);
@@ -33081,7 +34255,7 @@
 #if defined(DUK_USE_JX)
 		if (js_ctx->flag_ext_custom && x == DUK_ASC_MINUS && duk__dec_peek(js_ctx) == DUK_ASC_UC_I) {
 			duk__dec_req_stridx(js_ctx, DUK_STRIDX_MINUS_INFINITY);  /* "-Infinity", '-' has been eaten */
-			duk_push_number(ctx, -DUK_DOUBLE_INFINITY);
+			duk_push_number(thr, -DUK_DOUBLE_INFINITY);
 		} else {
 #else
 		{  /* unconditional block */
@@ -33092,23 +34266,23 @@
 		}
 	} else if (x == DUK_ASC_LC_T) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_TRUE);
-		duk_push_true(ctx);
+		duk_push_true(thr);
 	} else if (x == DUK_ASC_LC_F) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_FALSE);
-		duk_push_false(ctx);
+		duk_push_false(thr);
 	} else if (x == DUK_ASC_LC_N) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_LC_NULL);
-		duk_push_null(ctx);
+		duk_push_null(thr);
 #if defined(DUK_USE_JX)
 	} else if (js_ctx->flag_ext_custom && x == DUK_ASC_LC_U) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_LC_UNDEFINED);
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 	} else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_N) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_NAN);
-		duk_push_nan(ctx);
+		duk_push_nan(thr);
 	} else if (js_ctx->flag_ext_custom && x == DUK_ASC_UC_I) {
 		duk__dec_req_stridx(js_ctx, DUK_STRIDX_INFINITY);
-		duk_push_number(ctx, DUK_DOUBLE_INFINITY);
+		duk_push_number(thr, DUK_DOUBLE_INFINITY);
 	} else if (js_ctx->flag_ext_custom && x == DUK_ASC_LPAREN) {
 		duk__dec_pointer(js_ctx);
 	} else if (js_ctx->flag_ext_custom && x == DUK_ASC_PIPE) {
@@ -33138,65 +34312,65 @@
  * there is a reasonable limit on C recursion depth and hence object depth.
  */
 DUK_LOCAL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_hobject *h;
 	duk_uarridx_t i, arr_len;
 
 	DUK_DDD(DUK_DDDPRINT("walk: top=%ld, holder=%!T, name=%!T",
-	                     (long) duk_get_top(ctx),
-	                     (duk_tval *) duk_get_tval(ctx, -2),
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-	duk_dup_top(ctx);
-	duk_get_prop(ctx, -3);  /* -> [ ... holder name val ] */
-
-	h = duk_get_hobject(ctx, -1);
+	                     (long) duk_get_top(thr),
+	                     (duk_tval *) duk_get_tval(thr, -2),
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+
+	duk_dup_top(thr);
+	duk_get_prop(thr, -3);  /* -> [ ... holder name val ] */
+
+	h = duk_get_hobject(thr, -1);
 	if (h != NULL) {
 		if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY) {
-			arr_len = (duk_uarridx_t) duk_get_length(ctx, -1);
+			arr_len = (duk_uarridx_t) duk_get_length(thr, -1);
 			for (i = 0; i < arr_len; i++) {
 				/* [ ... holder name val ] */
 
 				DUK_DDD(DUK_DDDPRINT("walk: array, top=%ld, i=%ld, arr_len=%ld, holder=%!T, name=%!T, val=%!T",
-				                     (long) duk_get_top(ctx), (long) i, (long) arr_len,
-				                     (duk_tval *) duk_get_tval(ctx, -3), (duk_tval *) duk_get_tval(ctx, -2),
-				                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-				duk_dup_top(ctx);
-				(void) duk_push_uint_to_hstring(ctx, (duk_uint_t) i);  /* -> [ ... holder name val val ToString(i) ] */
+				                     (long) duk_get_top(thr), (long) i, (long) arr_len,
+				                     (duk_tval *) duk_get_tval(thr, -3), (duk_tval *) duk_get_tval(thr, -2),
+				                     (duk_tval *) duk_get_tval(thr, -1)));
+
+				duk_dup_top(thr);
+				(void) duk_push_uint_to_hstring(thr, (duk_uint_t) i);  /* -> [ ... holder name val val ToString(i) ] */
 				duk__dec_reviver_walk(js_ctx);  /* -> [ ... holder name val new_elem ] */
 
-				if (duk_is_undefined(ctx, -1)) {
-					duk_pop(ctx);
-					duk_del_prop_index(ctx, -1, i);
+				if (duk_is_undefined(thr, -1)) {
+					duk_pop(thr);
+					duk_del_prop_index(thr, -1, i);
 				} else {
 					/* XXX: duk_xdef_prop_index_wec() would be more appropriate
 					 * here but it currently makes some assumptions that might
 					 * not hold (e.g. that previous property is not an accessor).
 					 */
-					duk_put_prop_index(ctx, -2, i);
+					duk_put_prop_index(thr, -2, i);
 				}
 			}
 		} else {
 			/* [ ... holder name val ] */
-			duk_enum(ctx, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);
-			while (duk_next(ctx, -1 /*enum_index*/, 0 /*get_value*/)) {
+			duk_enum(thr, -1, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);
+			while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) {
 				DUK_DDD(DUK_DDDPRINT("walk: object, top=%ld, holder=%!T, name=%!T, val=%!T, enum=%!iT, obj_key=%!T",
-				                     (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, -5),
-				                     (duk_tval *) duk_get_tval(ctx, -4), (duk_tval *) duk_get_tval(ctx, -3),
-				                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
+				                     (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -5),
+				                     (duk_tval *) duk_get_tval(thr, -4), (duk_tval *) duk_get_tval(thr, -3),
+				                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
 
 				/* [ ... holder name val enum obj_key ] */
-				duk_dup_m3(ctx);
-				duk_dup_m2(ctx);
+				duk_dup_m3(thr);
+				duk_dup_m2(thr);
 
 				/* [ ... holder name val enum obj_key val obj_key ] */
 				duk__dec_reviver_walk(js_ctx);
 
 				/* [ ... holder name val enum obj_key new_elem ] */
-				if (duk_is_undefined(ctx, -1)) {
-					duk_pop(ctx);
-					duk_del_prop(ctx, -3);
+				if (duk_is_undefined(thr, -1)) {
+					duk_pop(thr);
+					duk_del_prop(thr, -3);
 				} else {
 					/* XXX: duk_xdef_prop_index_wec() would be more appropriate
 					 * here but it currently makes some assumptions that might
@@ -33207,21 +34381,21 @@
 					 * does not happen normally, but a clever reviver can trigger
 					 * that, see complex reviver case in: test-bug-json-parse-__proto__.js.
 					 */
-					duk_put_prop(ctx, -4);
-				}
-			}
-			duk_pop(ctx);  /* pop enum */
+					duk_put_prop(thr, -4);
+				}
+			}
+			duk_pop(thr);  /* pop enum */
 		}
 	}
 
 	/* [ ... holder name val ] */
 
-	duk_dup(ctx, js_ctx->idx_reviver);
-	duk_insert(ctx, -4);  /* -> [ ... reviver holder name val ] */
-	duk_call_method(ctx, 2);  /* -> [ ... res ] */
+	duk_dup(thr, js_ctx->idx_reviver);
+	duk_insert(thr, -4);  /* -> [ ... reviver holder name val ] */
+	duk_call_method(thr, 2);  /* -> [ ... res ] */
 
 	DUK_DDD(DUK_DDDPRINT("walk: top=%ld, result=%!T",
-	                     (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, -1)));
 }
 
 /*
@@ -33291,7 +34465,7 @@
 
 #if defined(DUK_USE_JX)
 	if (DUK_LIKELY(cp < 0x100UL)) {
-		if (DUK_UNLIKELY(js_ctx->flag_ext_custom)) {
+		if (DUK_UNLIKELY(js_ctx->flag_ext_custom != 0U)) {
 			tmp = DUK__MKESC(2, DUK_ASC_BACKSLASH, DUK_ASC_LC_X);
 		} else {
 			tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U);
@@ -33302,7 +34476,7 @@
 		tmp = DUK__MKESC(4, DUK_ASC_BACKSLASH, DUK_ASC_LC_U);
 	} else {
 #if defined(DUK_USE_JX)
-		if (DUK_LIKELY(js_ctx->flag_ext_custom)) {
+		if (DUK_LIKELY(js_ctx->flag_ext_custom != 0U)) {
 			tmp = DUK__MKESC(8, DUK_ASC_BACKSLASH, DUK_ASC_UC_U);
 		} else
 #endif
@@ -33519,7 +34693,6 @@
  */
 DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) {
 	duk_hthread *thr;
-	duk_context *ctx;
 	duk_tval *tv;
 	duk_double_t d;
 	duk_small_int_t c;
@@ -33531,10 +34704,9 @@
 	DUK_ASSERT(js_ctx != NULL);
 	thr = js_ctx->thr;
 	DUK_ASSERT(thr != NULL);
-	ctx = (duk_context *) thr;
 
 	/* Caller must ensure 'tv' is indeed a double and not a fastint! */
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
 	DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
 	d = DUK_TVAL_GET_DOUBLE(tv);
 
@@ -33551,15 +34723,15 @@
 		 */
 		if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 &&
 		                 (js_ctx->flag_ext_custom_or_compatible))) {
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_MINUS_ZERO);  /* '-0' */
+			duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_ZERO);  /* '-0' */
 		} else
 #endif  /* DUK_USE_JX || DUK_USE_JC */
 		{
 			n2s_flags = 0;
 			/* [ ... number ] -> [ ... string ] */
-			duk_numconv_stringify(ctx, 10 /*radix*/, 0 /*digits*/, n2s_flags);
-		}
-		h_str = duk_known_hstring(ctx, -1);
+			duk_numconv_stringify(thr, 10 /*radix*/, 0 /*digits*/, n2s_flags);
+		}
+		h_str = duk_known_hstring(thr, -1);
 		DUK__EMIT_HSTR(js_ctx, h_str);
 		return;
 	}
@@ -33761,7 +34933,7 @@
 	DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);
 
 	/* Maximum encoded length with 32-bit index: 1 + 10 + 2 + 3 + 1 + 1 = 18,
-	 * with 64-bit index: 1 + 20 + 2 + 3 + 1 + 1 = 28.  32 has some spare.
+	 * with 64-bit index: 1 + 20 + 2 + 3 + 1 + 1 = 28.  32 has some slack.
 	 *
 	 * Note that because the output buffer is reallocated from time to time,
 	 * side effects (such as finalizers) affecting the buffer 'h' must be
@@ -33851,7 +35023,7 @@
  * directly related to indent depth.
  */
 #if defined(DUK_USE_PREFER_SIZE)
-DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth) {
+DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) {
 	DUK_ASSERT(js_ctx->h_gap != NULL);
 	DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) > 0);  /* caller guarantees */
 
@@ -33861,7 +35033,7 @@
 	}
 }
 #else  /* DUK_USE_PREFER_SIZE */
-DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth) {
+DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_uint_t depth) {
 	const duk_uint8_t *gap_data;
 	duk_size_t gap_len;
 	duk_size_t avail_bytes;   /* bytes of indent available for copying */
@@ -33915,19 +35087,19 @@
 
 /* Shared entry handling for object/array serialization. */
 DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_hobject *h_target;
 	duk_uint_fast32_t i, n;
 
-	*entry_top = duk_get_top(ctx);
-
-	duk_require_stack(ctx, DUK_JSON_ENC_REQSTACK);
+	*entry_top = duk_get_top(thr);
+
+	duk_require_stack(thr, DUK_JSON_ENC_REQSTACK);
 
 	/* Loop check using a hybrid approach: a fixed-size visited[] array
 	 * with overflow in a loop check object.
 	 */
 
-	h_target = duk_known_hobject(ctx, -1);  /* object or array */
+	h_target = duk_known_hobject(thr, -1);  /* object or array */
 
 	n = js_ctx->recursion_depth;
 	if (DUK_UNLIKELY(n > DUK_JSON_ENC_LOOPARRAY)) {
@@ -33936,37 +35108,37 @@
 	for (i = 0; i < n; i++) {
 		if (DUK_UNLIKELY(js_ctx->visiting[i] == h_target)) {
 			DUK_DD(DUK_DDPRINT("slow path loop detect"));
-			DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CYCLIC_INPUT);
+			DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT);
 		}
 	}
 	if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) {
 		js_ctx->visiting[js_ctx->recursion_depth] = h_target;
 	} else {
-		duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
-		duk_dup_top(ctx);  /* -> [ ... voidp voidp ] */
-		if (duk_has_prop(ctx, js_ctx->idx_loop)) {
-			DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CYCLIC_INPUT);
-		}
-		duk_push_true(ctx);  /* -> [ ... voidp true ] */
-		duk_put_prop(ctx, js_ctx->idx_loop);  /* -> [ ... ] */
+		duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target);
+		duk_dup_top(thr);  /* -> [ ... voidp voidp ] */
+		if (duk_has_prop(thr, js_ctx->idx_loop)) {
+			DUK_ERROR_TYPE(thr, DUK_STR_CYCLIC_INPUT);
+		}
+		duk_push_true(thr);  /* -> [ ... voidp true ] */
+		duk_put_prop(thr, js_ctx->idx_loop);  /* -> [ ... ] */
 	}
 
 	/* C recursion check. */
 
-	DUK_ASSERT(js_ctx->recursion_depth >= 0);
+	DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
 	DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
 	if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
-		DUK_ERROR_RANGE((duk_hthread *) ctx, DUK_STR_JSONENC_RECLIMIT);
+		DUK_ERROR_RANGE(thr, DUK_STR_JSONENC_RECLIMIT);
 	}
 	js_ctx->recursion_depth++;
 
 	DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T",
-	                     (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop)));
+	                     (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop)));
 }
 
 /* Shared exit handling for object/array serialization. */
 DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_top) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_hobject *h_target;
 
 	/* C recursion check. */
@@ -33977,20 +35149,20 @@
 
 	/* Loop check. */
 
-	h_target = duk_known_hobject(ctx, *entry_top - 1);  /* original target at entry_top - 1 */
+	h_target = duk_known_hobject(thr, *entry_top - 1);  /* original target at entry_top - 1 */
 
 	if (js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY) {
 		/* Previous entry was inside visited[], nothing to do. */
 	} else {
-		duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
-		duk_del_prop(ctx, js_ctx->idx_loop);  /* -> [ ... ] */
+		duk_push_sprintf(thr, DUK_STR_FMT_PTR, (void *) h_target);
+		duk_del_prop(thr, js_ctx->idx_loop);  /* -> [ ... ] */
 	}
 
 	/* Restore stack top after unbalanced code paths. */
-	duk_set_top(ctx, *entry_top);
+	duk_set_top(thr, *entry_top);
 
 	DUK_DDD(DUK_DDDPRINT("shared entry finished: top=%ld, loop=%!T",
-	                     (long) duk_get_top(ctx), (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop)));
+	                     (long) duk_get_top(thr), (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop)));
 }
 
 /* The JO(value) operation: encode object.
@@ -33998,7 +35170,7 @@
  * Stack policy: [ object ] -> [ object ].
  */
 DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_hstring *h_key;
 	duk_idx_t entry_top;
 	duk_idx_t idx_obj;
@@ -34007,7 +35179,7 @@
 	duk_uarridx_t arr_len, i;
 	duk_size_t prev_size;
 
-	DUK_DDD(DUK_DDDPRINT("duk__enc_object: obj=%!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	DUK_DDD(DUK_DDDPRINT("duk__enc_object: obj=%!T", (duk_tval *) duk_get_tval(thr, -1)));
 
 	duk__enc_objarr_entry(js_ctx, &entry_top);
 
@@ -34017,14 +35189,14 @@
 		idx_keys = js_ctx->idx_proplist;
 	} else {
 		/* XXX: would be nice to enumerate an object at specified index */
-		duk_dup(ctx, idx_obj);
-		(void) duk_hobject_get_enumerated_keys(ctx, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);  /* [ ... target ] -> [ ... target keys ] */
-		idx_keys = duk_require_normalize_index(ctx, -1);
+		duk_dup(thr, idx_obj);
+		(void) duk_hobject_get_enumerated_keys(thr, DUK_ENUM_OWN_PROPERTIES_ONLY /*flags*/);  /* [ ... target ] -> [ ... target keys ] */
+		idx_keys = duk_require_normalize_index(thr, -1);
 		/* leave stack unbalanced on purpose */
 	}
 
 	DUK_DDD(DUK_DDDPRINT("idx_keys=%ld, h_keys=%!T",
-	                     (long) idx_keys, (duk_tval *) duk_get_tval(ctx, idx_keys)));
+	                     (long) idx_keys, (duk_tval *) duk_get_tval(thr, idx_keys)));
 
 	/* Steps 8-10 have been merged to avoid a "partial" variable. */
 
@@ -34036,16 +35208,16 @@
 	 * that it can be reallocated).
 	 */
 
-	arr_len = (duk_uarridx_t) duk_get_length(ctx, idx_keys);
+	arr_len = (duk_uarridx_t) duk_get_length(thr, idx_keys);
 	emitted = 0;
 	for (i = 0; i < arr_len; i++) {
-		duk_get_prop_index(ctx, idx_keys, i);  /* -> [ ... key ] */
+		duk_get_prop_index(thr, idx_keys, i);  /* -> [ ... key ] */
 
 		DUK_DDD(DUK_DDDPRINT("object property loop: holder=%!T, key=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, idx_obj),
-		                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-		h_key = duk_known_hstring(ctx, -1);
+		                     (duk_tval *) duk_get_tval(thr, idx_obj),
+		                     (duk_tval *) duk_get_tval(thr, -1)));
+
+		h_key = duk_known_hstring(thr, -1);
 		DUK_ASSERT(h_key != NULL);
 		DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(h_key));  /* proplist filtering; enum options */
 
@@ -34079,14 +35251,14 @@
 		DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
 		if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
 			DUK_ASSERT(js_ctx->recursion_depth >= 1);
-			duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1);
+			duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
 		}
 	}
 	DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);
 
 	duk__enc_objarr_exit(js_ctx, &entry_top);
 
-	DUK_ASSERT_TOP(ctx, entry_top);
+	DUK_ASSERT_TOP(thr, entry_top);
 }
 
 /* The JA(value) operation: encode array.
@@ -34094,14 +35266,14 @@
  * Stack policy: [ array ] -> [ array ].
  */
 DUK_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
+	duk_hthread *thr = js_ctx->thr;
 	duk_idx_t entry_top;
 	duk_idx_t idx_arr;
 	duk_bool_t emitted;
 	duk_uarridx_t i, arr_len;
 
 	DUK_DDD(DUK_DDDPRINT("duk__enc_array: array=%!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	duk__enc_objarr_entry(js_ctx, &entry_top);
 
@@ -34111,11 +35283,11 @@
 
 	DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET);
 
-	arr_len = (duk_uarridx_t) duk_get_length(ctx, idx_arr);
+	arr_len = (duk_uarridx_t) duk_get_length(thr, idx_arr);
 	emitted = 0;
 	for (i = 0; i < arr_len; i++) {
 		DUK_DDD(DUK_DDDPRINT("array entry loop: array=%!T, index=%ld, arr_len=%ld",
-		                     (duk_tval *) duk_get_tval(ctx, idx_arr),
+		                     (duk_tval *) duk_get_tval(thr, idx_arr),
 		                     (long) i, (long) arr_len));
 
 		if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
@@ -34123,7 +35295,7 @@
 			duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth);
 		}
 
-		(void) duk_push_uint_to_hstring(ctx, (duk_uint_t) i);  /* -> [ ... key ] */
+		(void) duk_push_uint_to_hstring(thr, (duk_uint_t) i);  /* -> [ ... key ] */
 
 		/* [ ... key ] */
 
@@ -34145,14 +35317,14 @@
 		DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
 		if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
 			DUK_ASSERT(js_ctx->recursion_depth >= 1);
-			duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1);
+			duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
 		}
 	}
 	DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET);
 
 	duk__enc_objarr_exit(js_ctx, &entry_top);
 
-	DUK_ASSERT_TOP(ctx, entry_top);
+	DUK_ASSERT_TOP(thr, entry_top);
 }
 
 /* The Str(key, holder) operation.
@@ -34160,71 +35332,68 @@
  * Stack policy: [ ... key ] -> [ ... ]
  */
 DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder) {
-	duk_context *ctx = (duk_context *) js_ctx->thr;
-	duk_hthread *thr = (duk_hthread *) ctx;
+	duk_hthread *thr = js_ctx->thr;
 	duk_tval *tv;
 	duk_tval *tv_holder;
 	duk_tval *tv_key;
 	duk_small_int_t c;
 
 	DUK_DDD(DUK_DDDPRINT("duk__enc_value: idx_holder=%ld, holder=%!T, key=%!T",
-	                     (long) idx_holder, (duk_tval *) duk_get_tval(ctx, idx_holder),
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-	DUK_UNREF(thr);
-
-	tv_holder = DUK_GET_TVAL_POSIDX(ctx, idx_holder);
+	                     (long) idx_holder, (duk_tval *) duk_get_tval(thr, idx_holder),
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+
+	tv_holder = DUK_GET_TVAL_POSIDX(thr, idx_holder);
 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_holder));
-	tv_key = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv_key = DUK_GET_TVAL_NEGIDX(thr, -1);
 	DUK_ASSERT(DUK_TVAL_IS_STRING(tv_key));
 	DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(DUK_TVAL_GET_STRING(tv_key)));  /* Caller responsible. */
 	(void) duk_hobject_getprop(thr, tv_holder, tv_key);
 
 	/* -> [ ... key val ] */
 
-	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* Standard JSON checks for .toJSON() only for actual objects; for
 	 * example, setting Number.prototype.toJSON and then serializing a
 	 * number won't invoke the .toJSON() method.  However, lightfuncs and
 	 * plain buffers mimic objects so we check for their .toJSON() method.
 	 */
-	if (duk_check_type_mask(ctx, -1, DUK_TYPE_MASK_OBJECT |
+	if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT |
 	                                 DUK_TYPE_MASK_LIGHTFUNC |
 	                                 DUK_TYPE_MASK_BUFFER)) {
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_TO_JSON);
-		if (duk_is_callable(ctx, -1)) {  /* toJSON() can also be a lightfunc */
+		duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_TO_JSON);
+		if (duk_is_callable(thr, -1)) {  /* toJSON() can also be a lightfunc */
 			DUK_DDD(DUK_DDDPRINT("value is object, has callable toJSON() -> call it"));
-			/* XXX: duk_dup_unvalidated(ctx, -2) etc. */
-			duk_dup_m2(ctx);          /* -> [ ... key val toJSON val ] */
-			duk_dup_m4(ctx);          /* -> [ ... key val toJSON val key ] */
-			duk_call_method(ctx, 1);  /* -> [ ... key val val' ] */
-			duk_remove_m2(ctx);       /* -> [ ... key val' ] */
-		} else {
-			duk_pop(ctx);             /* -> [ ... key val ] */
+			/* XXX: duk_dup_unvalidated(thr, -2) etc. */
+			duk_dup_m2(thr);          /* -> [ ... key val toJSON val ] */
+			duk_dup_m4(thr);          /* -> [ ... key val toJSON val key ] */
+			duk_call_method(thr, 1);  /* -> [ ... key val val' ] */
+			duk_remove_m2(thr);       /* -> [ ... key val' ] */
+		} else {
+			duk_pop(thr);             /* -> [ ... key val ] */
 		}
 	}
 
 	/* [ ... key val ] */
 
-	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));
 
 	if (js_ctx->h_replacer) {
 		/* XXX: Here a "slice copy" would be useful. */
 		DUK_DDD(DUK_DDDPRINT("replacer is set, call replacer"));
-		duk_push_hobject(ctx, js_ctx->h_replacer);  /* -> [ ... key val replacer ] */
-		duk_dup(ctx, idx_holder);                   /* -> [ ... key val replacer holder ] */
-		duk_dup_m4(ctx);                            /* -> [ ... key val replacer holder key ] */
-		duk_dup_m4(ctx);                            /* -> [ ... key val replacer holder key val ] */
-		duk_call_method(ctx, 2);                    /* -> [ ... key val val' ] */
-		duk_remove_m2(ctx);                         /* -> [ ... key val' ] */
+		duk_push_hobject(thr, js_ctx->h_replacer);  /* -> [ ... key val replacer ] */
+		duk_dup(thr, idx_holder);                   /* -> [ ... key val replacer holder ] */
+		duk_dup_m4(thr);                            /* -> [ ... key val replacer holder key ] */
+		duk_dup_m4(thr);                            /* -> [ ... key val replacer holder key val ] */
+		duk_call_method(thr, 2);                    /* -> [ ... key val val' ] */
+		duk_remove_m2(thr);                         /* -> [ ... key val' ] */
 	}
 
 	/* [ ... key val ] */
 
-	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1)));
-
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));
+
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
 	if (DUK_TVAL_IS_OBJECT(tv)) {
 		duk_hobject *h;
 
@@ -34249,19 +35418,19 @@
 		switch (c) {
 		case DUK_HOBJECT_CLASS_NUMBER: {
 			DUK_DDD(DUK_DDDPRINT("value is a Number object -> coerce with ToNumber()"));
-			duk_to_number_m1(ctx);
+			duk_to_number_m1(thr);
 			/* The coercion potentially invokes user .valueOf() and .toString()
 			 * but can't result in a function value because ToPrimitive() would
 			 * reject such a result: test-dev-json-stringify-coercion-1.js.
 			 */
-			DUK_ASSERT(!duk_is_callable(ctx, -1));
+			DUK_ASSERT(!duk_is_callable(thr, -1));
 			break;
 		}
 		case DUK_HOBJECT_CLASS_STRING: {
 			DUK_DDD(DUK_DDDPRINT("value is a String object -> coerce with ToString()"));
-			duk_to_string(ctx, -1);
+			duk_to_string(thr, -1);
 			/* Same coercion behavior as for Number. */
-			DUK_ASSERT(!duk_is_callable(ctx, -1));
+			DUK_ASSERT(!duk_is_callable(thr, -1));
 			break;
 		}
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
@@ -34269,8 +35438,8 @@
 #endif
 		case DUK_HOBJECT_CLASS_BOOLEAN: {
 			DUK_DDD(DUK_DDDPRINT("value is a Boolean/Buffer/Pointer object -> get internal value"));
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
-			duk_remove_m2(ctx);
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
+			duk_remove_m2(thr);
 			break;
 		}
 		default: {
@@ -34306,14 +35475,14 @@
 
 	/* [ ... key val ] */
 
-	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(ctx, -1)));
-
-	if (duk_check_type_mask(ctx, -1, js_ctx->mask_for_undefined)) {
+	DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1)));
+
+	if (duk_check_type_mask(thr, -1, js_ctx->mask_for_undefined)) {
 		/* will result in undefined */
 		DUK_DDD(DUK_DDDPRINT("-> will result in undefined (type mask check)"));
 		goto pop2_undef;
 	}
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
@@ -34381,7 +35550,7 @@
 		/* Could implement a fastpath, but the fast path would need
 		 * to handle realloc side effects correctly.
 		 */
-		duk_to_object(ctx, -1);
+		duk_to_object(thr, -1);
 		duk__enc_object(js_ctx);
 		break;
 	}
@@ -34419,11 +35588,11 @@
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
  pop2_emitted:
 #endif
-	duk_pop_2(ctx); /* [ ... key val ] -> [ ... ] */
+	duk_pop_2(thr); /* [ ... key val ] -> [ ... ] */
 	return 1;  /* emitted */
 
  pop2_undef:
-	duk_pop_2(ctx);  /* [ ... key val ] -> [ ... ] */
+	duk_pop_2(thr);  /* [ ... key val ] -> [ ... ] */
 	return 0;  /* not emitted */
 }
 
@@ -34552,7 +35721,7 @@
 		 * it (though it's OK to abort the fast path).
 		 */
 
-		DUK_ASSERT(js_ctx->recursion_depth >= 0);
+		DUK_ASSERT_DISABLE(js_ctx->recursion_depth >= 0);  /* unsigned */
 		DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
 		if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
 			DUK_DD(DUK_DDPRINT("fast path recursion limit"));
@@ -34584,7 +35753,7 @@
 		 * but does at the moment, probably not worth fixing.
 		 */
 		if (duk_hobject_hasprop_raw(js_ctx->thr, obj, DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr)) ||
-		    DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) {
+		    DUK_HOBJECT_IS_PROXY(obj)) {
 			DUK_DD(DUK_DDPRINT("object has a .toJSON property or object is a Proxy, abort fast path"));
 			goto abort_fastpath;
 		}
@@ -34632,7 +35801,7 @@
 			c_object = c_all & ~(c_array | c_unbox | c_func | c_bufobj | c_undef | c_abort);
 		}
 
-		c_bit = DUK_HOBJECT_GET_CLASS_MASK(obj);
+		c_bit = (duk_uint32_t) DUK_HOBJECT_GET_CLASS_MASK(obj);
 		if (c_bit & c_object) {
 			/* All other object types. */
 			DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);
@@ -34708,7 +35877,7 @@
 				DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
 				if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
 					DUK_ASSERT(js_ctx->recursion_depth >= 1);
-					duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1);
+					duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
 				}
 			}
 			DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);
@@ -34755,9 +35924,9 @@
 				 * to support gappy arrays for all practical code.
 				 */
 
-				h_tmp = duk_push_uint_to_hstring((duk_context *) js_ctx->thr, (duk_uint_t) i);
+				h_tmp = duk_push_uint_to_hstring(js_ctx->thr, (duk_uint_t) i);
 				has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp);
-				duk_pop((duk_context *) js_ctx->thr);
+				duk_pop(js_ctx->thr);
 				if (has_inherited) {
 					DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path"));
 					goto abort_fastpath;
@@ -34785,7 +35954,7 @@
 				DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
 				if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) {
 					DUK_ASSERT(js_ctx->recursion_depth >= 1);
-					duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1);
+					duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth - 1U);
 				}
 			}
 			DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET);
@@ -34915,9 +36084,9 @@
 		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
 
 		/* XXX: Stack discipline is annoying, could be changed in numconv. */
-		duk_push_tval((duk_context *) js_ctx->thr, tv);
+		duk_push_tval(js_ctx->thr, tv);
 		duk__enc_double(js_ctx);
-		duk_pop((duk_context *) js_ctx->thr);
+		duk_pop(js_ctx->thr);
 
 #if 0
 		/* Could also rely on native sprintf(), but it will handle
@@ -34946,20 +36115,20 @@
 	return 0;  /* unreachable */
 }
 
-DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_context *ctx, void *udata) {
+DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_hthread *thr, void *udata) {
 	duk_json_enc_ctx *js_ctx;
 	duk_tval *tv;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(udata != NULL);
 
 	js_ctx = (duk_json_enc_ctx *) udata;
 	DUK_ASSERT(js_ctx != NULL);
 
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv = DUK_GET_TVAL_NEGIDX(thr, -1);
 	if (duk__json_stringify_fast_value(js_ctx, tv) == 0) {
 		DUK_DD(DUK_DDPRINT("top level value not supported, fail fast path"));
-		DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);  /* Error message is ignored, so doesn't matter. */
+		DUK_DCERROR_TYPE_INVALID_ARGS(thr);  /* Error message is ignored, so doesn't matter. */
 	}
 
 	return 0;
@@ -34971,16 +36140,15 @@
  */
 
 DUK_INTERNAL
-void duk_bi_json_parse_helper(duk_context *ctx,
+void duk_bi_json_parse_helper(duk_hthread *thr,
                               duk_idx_t idx_value,
                               duk_idx_t idx_reviver,
                               duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_json_dec_ctx js_ctx_alloc;
 	duk_json_dec_ctx *js_ctx = &js_ctx_alloc;
 	duk_hstring *h_text;
 #if defined(DUK_USE_ASSERTIONS)
-	duk_idx_t entry_top = duk_get_top(ctx);
+	duk_idx_t entry_top = duk_get_top(thr);
 #endif
 
 	/* negative top-relative indices not allowed now */
@@ -34988,10 +36156,10 @@
 	DUK_ASSERT(idx_reviver == DUK_INVALID_INDEX || idx_reviver >= 0);
 
 	DUK_DDD(DUK_DDDPRINT("JSON parse start: text=%!T, reviver=%!T, flags=0x%08lx, stack_top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, idx_value),
-	                     (duk_tval *) duk_get_tval(ctx, idx_reviver),
+	                     (duk_tval *) duk_get_tval(thr, idx_value),
+	                     (duk_tval *) duk_get_tval(thr, idx_reviver),
 	                     (unsigned long) flags,
-	                     (long) duk_get_top(ctx)));
+	                     (long) duk_get_top(thr)));
 
 	DUK_MEMZERO(&js_ctx_alloc, sizeof(js_ctx_alloc));
 	js_ctx->thr = thr;
@@ -35016,7 +36184,7 @@
 	js_ctx->flag_ext_custom_or_compatible = flags & (DUK_JSON_FLAG_EXT_CUSTOM | DUK_JSON_FLAG_EXT_COMPATIBLE);
 #endif
 
-	h_text = duk_to_hstring(ctx, idx_value);  /* coerce in-place; rejects Symbols */
+	h_text = duk_to_hstring(thr, idx_value);  /* coerce in-place; rejects Symbols */
 	DUK_ASSERT(h_text != NULL);
 
 	/* JSON parsing code is allowed to read [p_start,p_end]: p_end is
@@ -35039,47 +36207,46 @@
 		duk__dec_syntax_error(js_ctx);
 	}
 
-	if (duk_is_callable(ctx, idx_reviver)) {
+	if (duk_is_callable(thr, idx_reviver)) {
 		DUK_DDD(DUK_DDDPRINT("applying reviver: %!T",
-		                     (duk_tval *) duk_get_tval(ctx, idx_reviver)));
+		                     (duk_tval *) duk_get_tval(thr, idx_reviver)));
 
 		js_ctx->idx_reviver = idx_reviver;
 
-		duk_push_object(ctx);
-		duk_dup_m2(ctx);  /* -> [ ... val root val ] */
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_EMPTY_STRING);  /* default attrs ok */
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);  /* -> [ ... val root "" ] */
+		duk_push_object(thr);
+		duk_dup_m2(thr);  /* -> [ ... val root val ] */
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING);  /* default attrs ok */
+		duk_push_hstring_stridx(thr, DUK_STRIDX_EMPTY_STRING);  /* -> [ ... val root "" ] */
 
 		DUK_DDD(DUK_DDDPRINT("start reviver walk, root=%!T, name=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, -2),
-		                     (duk_tval *) duk_get_tval(ctx, -1)));
+		                     (duk_tval *) duk_get_tval(thr, -2),
+		                     (duk_tval *) duk_get_tval(thr, -1)));
 
 		duk__dec_reviver_walk(js_ctx);  /* [ ... val root "" ] -> [ ... val val' ] */
-		duk_remove_m2(ctx);             /* -> [ ... val' ] */
+		duk_remove_m2(thr);             /* -> [ ... val' ] */
 	} else {
 		DUK_DDD(DUK_DDDPRINT("reviver does not exist or is not callable: %!T",
-		                     (duk_tval *) duk_get_tval(ctx, idx_reviver)));
+		                     (duk_tval *) duk_get_tval(thr, idx_reviver)));
 	}
 
 	/* Final result is at stack top. */
 
 	DUK_DDD(DUK_DDDPRINT("JSON parse end: text=%!T, reviver=%!T, flags=0x%08lx, result=%!T, stack_top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, idx_value),
-	                     (duk_tval *) duk_get_tval(ctx, idx_reviver),
+	                     (duk_tval *) duk_get_tval(thr, idx_value),
+	                     (duk_tval *) duk_get_tval(thr, idx_reviver),
 	                     (unsigned long) flags,
-	                     (duk_tval *) duk_get_tval(ctx, -1),
-	                     (long) duk_get_top(ctx)));
-
-	DUK_ASSERT(duk_get_top(ctx) == entry_top + 1);
+	                     (duk_tval *) duk_get_tval(thr, -1),
+	                     (long) duk_get_top(thr)));
+
+	DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
 }
 
 DUK_INTERNAL
-void duk_bi_json_stringify_helper(duk_context *ctx,
+void duk_bi_json_stringify_helper(duk_hthread *thr,
                                   duk_idx_t idx_value,
                                   duk_idx_t idx_replacer,
                                   duk_idx_t idx_space,
                                   duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_json_enc_ctx js_ctx_alloc;
 	duk_json_enc_ctx *js_ctx = &js_ctx_alloc;
 	duk_hobject *h;
@@ -35092,13 +36259,13 @@
 	DUK_ASSERT(idx_space == DUK_INVALID_INDEX || idx_space >= 0);
 
 	DUK_DDD(DUK_DDDPRINT("JSON stringify start: value=%!T, replacer=%!T, space=%!T, flags=0x%08lx, stack_top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, idx_value),
-	                     (duk_tval *) duk_get_tval(ctx, idx_replacer),
-	                     (duk_tval *) duk_get_tval(ctx, idx_space),
+	                     (duk_tval *) duk_get_tval(thr, idx_value),
+	                     (duk_tval *) duk_get_tval(thr, idx_replacer),
+	                     (duk_tval *) duk_get_tval(thr, idx_space),
 	                     (unsigned long) flags,
-	                     (long) duk_get_top(ctx)));
-
-	entry_top = duk_get_top(ctx);
+	                     (long) duk_get_top(thr)));
+
+	entry_top = duk_get_top(thr);
 
 	/*
 	 *  Context init
@@ -35180,7 +36347,7 @@
 
 	DUK_BW_INIT_PUSHBUF(thr, &js_ctx->bw, DUK__JSON_STRINGIFY_BUFSIZE);
 
-	js_ctx->idx_loop = duk_push_bare_object(ctx);
+	js_ctx->idx_loop = duk_push_bare_object(thr);
 	DUK_ASSERT(js_ctx->idx_loop >= 0);
 
 	/* [ ... buf loop ] */
@@ -35189,7 +36356,7 @@
 	 *  Process replacer/proplist (2nd argument to JSON.stringify)
 	 */
 
-	h = duk_get_hobject(ctx, idx_replacer);
+	h = duk_get_hobject(thr, idx_replacer);
 	if (h != NULL) {
 		if (DUK_HOBJECT_IS_CALLABLE(h)) {
 			js_ctx->h_replacer = h;
@@ -35203,30 +36370,30 @@
 			duk_uarridx_t plist_idx = 0;
 			duk_small_uint_t enum_flags;
 
-			js_ctx->idx_proplist = duk_push_array(ctx);  /* XXX: array internal? */
+			js_ctx->idx_proplist = duk_push_array(thr);  /* XXX: array internal? */
 
 			enum_flags = DUK_ENUM_ARRAY_INDICES_ONLY |
 			             DUK_ENUM_SORT_ARRAY_INDICES;  /* expensive flag */
-			duk_enum(ctx, idx_replacer, enum_flags);
-			while (duk_next(ctx, -1 /*enum_index*/, 1 /*get_value*/)) {
+			duk_enum(thr, idx_replacer, enum_flags);
+			while (duk_next(thr, -1 /*enum_index*/, 1 /*get_value*/)) {
 				/* [ ... proplist enum_obj key val ] */
-				if (duk__enc_allow_into_proplist(duk_get_tval(ctx, -1))) {
+				if (duk__enc_allow_into_proplist(duk_get_tval(thr, -1))) {
 					/* XXX: duplicates should be eliminated here */
 					DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> accept",
-					                     (duk_tval *) duk_get_tval(ctx, -2),
-					                     (duk_tval *) duk_get_tval(ctx, -1)));
-					duk_to_string(ctx, -1);  /* extra coercion of strings is OK */
-					duk_put_prop_index(ctx, -4, plist_idx);  /* -> [ ... proplist enum_obj key ] */
+					                     (duk_tval *) duk_get_tval(thr, -2),
+					                     (duk_tval *) duk_get_tval(thr, -1)));
+					duk_to_string(thr, -1);  /* extra coercion of strings is OK */
+					duk_put_prop_index(thr, -4, plist_idx);  /* -> [ ... proplist enum_obj key ] */
 					plist_idx++;
-					duk_pop(ctx);
+					duk_pop(thr);
 				} else {
 					DUK_DDD(DUK_DDDPRINT("proplist enum: key=%!T, val=%!T --> reject",
-					                     (duk_tval *) duk_get_tval(ctx, -2),
-					                     (duk_tval *) duk_get_tval(ctx, -1)));
-					duk_pop_2(ctx);
+					                     (duk_tval *) duk_get_tval(thr, -2),
+					                     (duk_tval *) duk_get_tval(thr, -1)));
+					duk_pop_2(thr);
 				}
                         }
-                        duk_pop(ctx);  /* pop enum */
+                        duk_pop(thr);  /* pop enum */
 
 			/* [ ... proplist ] */
 		}
@@ -35238,17 +36405,17 @@
 	 *  Process space (3rd argument to JSON.stringify)
 	 */
 
-	h = duk_get_hobject(ctx, idx_space);
+	h = duk_get_hobject(thr, idx_space);
 	if (h != NULL) {
-		int c = DUK_HOBJECT_GET_CLASS_NUMBER(h);
+		duk_small_uint_t c = DUK_HOBJECT_GET_CLASS_NUMBER(h);
 		if (c == DUK_HOBJECT_CLASS_NUMBER) {
-			duk_to_number(ctx, idx_space);
+			duk_to_number(thr, idx_space);
 		} else if (c == DUK_HOBJECT_CLASS_STRING) {
-			duk_to_string(ctx, idx_space);
-		}
-	}
-
-	if (duk_is_number(ctx, idx_space)) {
+			duk_to_string(thr, idx_space);
+		}
+	}
+
+	if (duk_is_number(thr, idx_space)) {
 		duk_small_int_t nspace;
 		/* spaces[] must be static to allow initializer with old compilers like BCC */
 		static const char spaces[10] = {
@@ -35258,16 +36425,16 @@
 		};  /* XXX: helper */
 
 		/* ToInteger() coercion; NaN -> 0, infinities are clamped to 0 and 10 */
-		nspace = (duk_small_int_t) duk_to_int_clamped(ctx, idx_space, 0 /*minval*/, 10 /*maxval*/);
+		nspace = (duk_small_int_t) duk_to_int_clamped(thr, idx_space, 0 /*minval*/, 10 /*maxval*/);
 		DUK_ASSERT(nspace >= 0 && nspace <= 10);
 
-		duk_push_lstring(ctx, spaces, (duk_size_t) nspace);
-		js_ctx->h_gap = duk_known_hstring(ctx, -1);
+		duk_push_lstring(thr, spaces, (duk_size_t) nspace);
+		js_ctx->h_gap = duk_known_hstring(thr, -1);
 		DUK_ASSERT(js_ctx->h_gap != NULL);
-	} else if (duk_is_string_notsymbol(ctx, idx_space)) {
-		duk_dup(ctx, idx_space);
-		duk_substring(ctx, -1, 0, 10);  /* clamp to 10 chars */
-		js_ctx->h_gap = duk_known_hstring(ctx, -1);
+	} else if (duk_is_string_notsymbol(thr, idx_space)) {
+		duk_dup(thr, idx_space);
+		duk_substring(thr, -1, 0, 10);  /* clamp to 10 chars */
+		js_ctx->h_gap = duk_known_hstring(thr, -1);
 	} else {
 		/* nop */
 	}
@@ -35312,7 +36479,7 @@
 		 * limited loop detection).
 		 */
 
-		duk_dup(ctx, idx_value);
+		duk_dup(thr, idx_value);
 
 		/* Must prevent finalizers which may have arbitrary side effects. */
 		prev_ms_base_flags = thr->heap->ms_base_flags;
@@ -35321,7 +36488,7 @@
 		thr->heap->pf_prevent_count++;                 /* Prevent finalizers. */
 		DUK_ASSERT(thr->heap->pf_prevent_count != 0);  /* Wrap. */
 
-		pcall_rc = duk_safe_call(ctx, duk__json_stringify_fast, (void *) js_ctx /*udata*/, 1 /*nargs*/, 0 /*nret*/);
+		pcall_rc = duk_safe_call(thr, duk__json_stringify_fast, (void *) js_ctx /*udata*/, 1 /*nargs*/, 0 /*nret*/);
 
 		DUK_ASSERT(thr->heap->pf_prevent_count > 0);
 		thr->heap->pf_prevent_count--;
@@ -35348,22 +36515,22 @@
 	 *  Create wrapper object and serialize
 	 */
 
-	idx_holder = duk_push_object(ctx);
-	duk_dup(ctx, idx_value);
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_EMPTY_STRING);
+	idx_holder = duk_push_object(thr);
+	duk_dup(thr, idx_value);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_EMPTY_STRING);
 
 	DUK_DDD(DUK_DDDPRINT("before: flags=0x%08lx, loop=%!T, replacer=%!O, "
 	                     "proplist=%!T, gap=%!O, holder=%!T",
 	                     (unsigned long) js_ctx->flags,
-	                     (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop),
+	                     (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop),
 	                     (duk_heaphdr *) js_ctx->h_replacer,
-	                     (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(ctx, js_ctx->idx_proplist) : NULL),
+	                     (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL),
 	                     (duk_heaphdr *) js_ctx->h_gap,
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* serialize the wrapper with empty string key */
 
-	duk_push_hstring_empty(ctx);
+	duk_push_hstring_empty(thr);
 
 	/* [ ... buf loop (proplist) (gap) holder "" ] */
 
@@ -35372,7 +36539,7 @@
 
 	if (DUK_UNLIKELY(duk__enc_value(js_ctx, idx_holder) == 0)) {  /* [ ... holder key ] -> [ ... holder ] */
 		/* Result is undefined. */
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 	} else {
 		/* Convert buffer to result string. */
 		DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw);
@@ -35381,11 +36548,11 @@
 	DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, "
 	                     "proplist=%!T, gap=%!O, holder=%!T",
 	                     (unsigned long) js_ctx->flags,
-	                     (duk_tval *) duk_get_tval(ctx, js_ctx->idx_loop),
+	                     (duk_tval *) duk_get_tval(thr, js_ctx->idx_loop),
 	                     (duk_heaphdr *) js_ctx->h_replacer,
-	                     (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(ctx, js_ctx->idx_proplist) : NULL),
+	                     (duk_tval *) (js_ctx->idx_proplist >= 0 ? duk_get_tval(thr, js_ctx->idx_proplist) : NULL),
 	                     (duk_heaphdr *) js_ctx->h_gap,
-	                     (duk_tval *) duk_get_tval(ctx, idx_holder)));
+	                     (duk_tval *) duk_get_tval(thr, idx_holder)));
 
 	/* The stack has a variable shape here, so force it to the
 	 * desired one explicitly.
@@ -35394,19 +36561,19 @@
 #if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
  replace_finished:
 #endif
-	duk_replace(ctx, entry_top);
-	duk_set_top(ctx, entry_top + 1);
+	duk_replace(thr, entry_top);
+	duk_set_top(thr, entry_top + 1);
 
 	DUK_DDD(DUK_DDDPRINT("JSON stringify end: value=%!T, replacer=%!T, space=%!T, "
 	                     "flags=0x%08lx, result=%!T, stack_top=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, idx_value),
-	                     (duk_tval *) duk_get_tval(ctx, idx_replacer),
-	                     (duk_tval *) duk_get_tval(ctx, idx_space),
+	                     (duk_tval *) duk_get_tval(thr, idx_value),
+	                     (duk_tval *) duk_get_tval(thr, idx_replacer),
+	                     (duk_tval *) duk_get_tval(thr, idx_space),
 	                     (unsigned long) flags,
-	                     (duk_tval *) duk_get_tval(ctx, -1),
-	                     (long) duk_get_top(ctx)));
-
-	DUK_ASSERT(duk_get_top(ctx) == entry_top + 1);
+	                     (duk_tval *) duk_get_tval(thr, -1),
+	                     (long) duk_get_top(thr)));
+
+	DUK_ASSERT(duk_get_top(thr) == entry_top + 1);
 }
 
 #if defined(DUK_USE_JSON_BUILTIN)
@@ -35415,16 +36582,16 @@
  *  Entry points
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_context *ctx) {
-	duk_bi_json_parse_helper(ctx,
+DUK_INTERNAL duk_ret_t duk_bi_json_object_parse(duk_hthread *thr) {
+	duk_bi_json_parse_helper(thr,
 	                         0 /*idx_value*/,
 	                         1 /*idx_replacer*/,
 	                         0 /*flags*/);
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx) {
-	duk_bi_json_stringify_helper(ctx,
+DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_hthread *thr) {
+	duk_bi_json_stringify_helper(thr,
 	                             0 /*idx_value*/,
 	                             1 /*idx_replacer*/,
 	                             2 /*idx_space*/,
@@ -35470,8 +36637,8 @@
 typedef double (*duk__one_arg_func)(double);
 typedef double (*duk__two_arg_func)(double, double);
 
-DUK_LOCAL duk_ret_t duk__math_minmax(duk_context *ctx, duk_double_t initial, duk__two_arg_func min_max) {
-	duk_idx_t n = duk_get_top(ctx);
+DUK_LOCAL duk_ret_t duk__math_minmax(duk_hthread *thr, duk_double_t initial, duk__two_arg_func min_max) {
+	duk_idx_t n = duk_get_top(thr);
 	duk_idx_t i;
 	duk_double_t res = initial;
 	duk_double_t t;
@@ -35488,7 +36655,7 @@
 	 */
 
 	for (i = 0; i < n; i++) {
-		t = duk_to_number(ctx, i);
+		t = duk_to_number(thr, i);
 		if (DUK_FPCLASSIFY(t) == DUK_FP_NAN || DUK_FPCLASSIFY(res) == DUK_FP_NAN) {
 			/* Note: not normalized, but duk_push_number() will normalize */
 			res = (duk_double_t) DUK_DOUBLE_NAN;
@@ -35497,7 +36664,7 @@
 		}
 	}
 
-	duk_push_number(ctx, res);
+	duk_push_number(thr, res);
 	return 1;
 }
 
@@ -35765,49 +36932,49 @@
 #endif
 };
 
-DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_context *ctx) {
-	duk_small_int_t fun_idx = duk_get_current_magic(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_math_object_onearg_shared(duk_hthread *thr) {
+	duk_small_int_t fun_idx = duk_get_current_magic(thr);
 	duk__one_arg_func fun;
 	duk_double_t arg1;
 
 	DUK_ASSERT(fun_idx >= 0);
 	DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__one_arg_funcs) / sizeof(duk__one_arg_func)));
-	arg1 = duk_to_number(ctx, 0);
+	arg1 = duk_to_number(thr, 0);
 	fun = duk__one_arg_funcs[fun_idx];
-	duk_push_number(ctx, (duk_double_t) fun((double) arg1));
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_context *ctx) {
-	duk_small_int_t fun_idx = duk_get_current_magic(ctx);
+	duk_push_number(thr, (duk_double_t) fun((double) arg1));
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_math_object_twoarg_shared(duk_hthread *thr) {
+	duk_small_int_t fun_idx = duk_get_current_magic(thr);
 	duk__two_arg_func fun;
 	duk_double_t arg1;
 	duk_double_t arg2;
 
 	DUK_ASSERT(fun_idx >= 0);
 	DUK_ASSERT(fun_idx < (duk_small_int_t) (sizeof(duk__two_arg_funcs) / sizeof(duk__two_arg_func)));
-	arg1 = duk_to_number(ctx, 0);  /* explicit ordered evaluation to match coercion semantics */
-	arg2 = duk_to_number(ctx, 1);
+	arg1 = duk_to_number(thr, 0);  /* explicit ordered evaluation to match coercion semantics */
+	arg2 = duk_to_number(thr, 1);
 	fun = duk__two_arg_funcs[fun_idx];
-	duk_push_number(ctx, (duk_double_t) fun((double) arg1, (double) arg2));
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_context *ctx) {
-	return duk__math_minmax(ctx, -DUK_DOUBLE_INFINITY, duk__fmax_fixed);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_context *ctx) {
-	return duk__math_minmax(ctx, DUK_DOUBLE_INFINITY, duk__fmin_fixed);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_context *ctx) {
-	duk_push_number(ctx, (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE((duk_hthread *) ctx));
+	duk_push_number(thr, (duk_double_t) fun((double) arg1, (double) arg2));
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_math_object_max(duk_hthread *thr) {
+	return duk__math_minmax(thr, -DUK_DOUBLE_INFINITY, duk__fmax_fixed);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_math_object_min(duk_hthread *thr) {
+	return duk__math_minmax(thr, DUK_DOUBLE_INFINITY, duk__fmin_fixed);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_math_object_random(duk_hthread *thr) {
+	duk_push_number(thr, (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE(thr));
 	return 1;
 }
 
 #if defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_hthread *thr) {
 	/*
 	 *  E6 Section 20.2.2.18: Math.hypot
 	 *
@@ -35827,13 +36994,13 @@
 	duk_double_t comp, prelim;
 	duk_double_t t;
 
-	nargs = duk_get_top(ctx);
+	nargs = duk_get_top(thr);
 
 	/* Find the highest value.  Also ToNumber() coerces. */
 	max = 0.0;
 	found_nan = 0;
 	for (i = 0; i < nargs; i++) {
-		t = DUK_FABS(duk_to_number(ctx, i));
+		t = DUK_FABS(duk_to_number(thr, i));
 		if (DUK_FPCLASSIFY(t) == DUK_FP_NAN) {
 			found_nan = 1;
 		} else {
@@ -35843,13 +37010,13 @@
 
 	/* Early return cases. */
 	if (max == DUK_DOUBLE_INFINITY) {
-		duk_push_number(ctx, DUK_DOUBLE_INFINITY);
+		duk_push_number(thr, DUK_DOUBLE_INFINITY);
 		return 1;
 	} else if (found_nan) {
-		duk_push_number(ctx, DUK_DOUBLE_NAN);
+		duk_push_number(thr, DUK_DOUBLE_NAN);
 		return 1;
 	} else if (max == 0.0) {
-		duk_push_number(ctx, 0.0);
+		duk_push_number(thr, 0.0);
 		/* Otherwise we'd divide by zero. */
 		return 1;
 	}
@@ -35862,14 +37029,107 @@
 	sum = 0.0;
 	comp = 0.0;
 	for (i = 0; i < nargs; i++) {
-		t = DUK_FABS(duk_get_number(ctx, i)) / max;
+		t = DUK_FABS(duk_get_number(thr, i)) / max;
 		summand = (t * t) - comp;
 		prelim = sum + summand;
 		comp = (prelim - sum) - summand;
 		sum = prelim;
 	}
 
-	duk_push_number(ctx, (duk_double_t) DUK_SQRT(sum) * max);
+	duk_push_number(thr, (duk_double_t) DUK_SQRT(sum) * max);
+	return 1;
+}
+#endif  /* DUK_USE_ES6 */
+
+#if defined(DUK_USE_ES6)
+DUK_INTERNAL duk_ret_t duk_bi_math_object_sign(duk_hthread *thr) {
+	duk_double_t d;
+
+	d = duk_to_number(thr, 0);
+	if (duk_double_is_nan(d)) {
+		DUK_ASSERT(duk_is_nan(thr, -1));
+		return 1;  /* NaN input -> return NaN */
+	}
+	if (d == 0.0) {
+		/* Zero sign kept, i.e. -0 -> -0, +0 -> +0. */
+		return 1;
+	}
+	duk_push_int(thr, (d > 0.0 ? 1 : -1));
+	return 1;
+}
+#endif  /* DUK_USE_ES6 */
+
+#if defined(DUK_USE_ES6)
+DUK_INTERNAL duk_ret_t duk_bi_math_object_clz32(duk_hthread *thr) {
+	duk_uint32_t x;
+	duk_small_uint_t i;
+
+#if defined(DUK_USE_PREFER_SIZE)
+	duk_uint32_t mask;
+
+	x = duk_to_uint32(thr, 0);
+	for (i = 0, mask = 0x80000000UL; mask != 0; mask >>= 1) {
+		if (x & mask) {
+			break;
+		}
+		i++;
+	}
+	DUK_ASSERT(i <= 32);
+	duk_push_uint(thr, i);
+	return 1;
+#else  /* DUK_USE_PREFER_SIZE */
+	i = 0;
+	x = duk_to_uint32(thr, 0);
+	if (x & 0xffff0000UL) {
+		x >>= 16;
+	} else {
+		i += 16;
+	}
+	if (x & 0x0000ff00UL) {
+		x >>= 8;
+	} else {
+		i += 8;
+	}
+	if (x & 0x000000f0UL) {
+		x >>= 4;
+	} else {
+		i += 4;
+	}
+	if (x & 0x0000000cUL) {
+		x >>= 2;
+	} else {
+		i += 2;
+	}
+	if (x & 0x00000002UL) {
+		x >>= 1;
+	} else {
+		i += 1;
+	}
+	if (x & 0x00000001UL) {
+		;
+	} else {
+		i += 1;
+	}
+	DUK_ASSERT(i <= 32);
+	duk_push_uint(thr, i);
+	return 1;
+#endif  /* DUK_USE_PREFER_SIZE */
+}
+#endif  /* DUK_USE_ES6 */
+
+#if defined(DUK_USE_ES6)
+DUK_INTERNAL duk_ret_t duk_bi_math_object_imul(duk_hthread *thr) {
+	duk_uint32_t x, y, z;
+
+	x = duk_to_uint32(thr, 0);
+	y = duk_to_uint32(thr, 1);
+	z = x * y;
+
+	/* While arguments are ToUint32() coerced and the multiplication
+	 * is unsigned as such, the final result is curiously interpreted
+	 * as a signed 32-bit value.
+	 */
+	duk_push_i32(thr, (duk_int32_t) z);
 	return 1;
 }
 #endif  /* DUK_USE_ES6 */
@@ -35884,41 +37144,38 @@
 
 #if defined(DUK_USE_NUMBER_BUILTIN)
 
-DUK_LOCAL duk_double_t duk__push_this_number_plain(duk_context *ctx) {
+DUK_LOCAL duk_double_t duk__push_this_number_plain(duk_hthread *thr) {
 	duk_hobject *h;
 
 	/* Number built-in accepts a plain number or a Number object (whose
 	 * internal value is operated on).  Other types cause TypeError.
 	 */
 
-	duk_push_this(ctx);
-	if (duk_is_number(ctx, -1)) {
-		DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_push_this(thr);
+	if (duk_is_number(thr, -1)) {
+		DUK_DDD(DUK_DDDPRINT("plain number value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
 		goto done;
 	}
-	h = duk_get_hobject(ctx, -1);
+	h = duk_get_hobject(thr, -1);
 	if (!h ||
 	    (DUK_HOBJECT_GET_CLASS_NUMBER(h) != DUK_HOBJECT_CLASS_NUMBER)) {
-		DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
-		DUK_ERROR_TYPE((duk_hthread *) ctx, "number expected");
-	}
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
-	DUK_ASSERT(duk_is_number(ctx, -1));
+		DUK_DDD(DUK_DDDPRINT("unacceptable this value: %!T", (duk_tval *) duk_get_tval(thr, -1)));
+		DUK_ERROR_TYPE(thr, "number expected");
+	}
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
+	DUK_ASSERT(duk_is_number(thr, -1));
 	DUK_DDD(DUK_DDDPRINT("number object: %!T, internal value: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
-	duk_remove_m2(ctx);
+	                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
+	duk_remove_m2(thr);
 
  done:
-	return duk_get_number(ctx, -1);
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	return duk_get_number(thr, -1);
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_hobject *h_this;
 
-	DUK_UNREF(thr);
-
 	/*
 	 *  The Number constructor uses ToNumber(arg) for number coercion
 	 *  (coercing an undefined argument to NaN).  However, if the
@@ -35926,15 +37183,15 @@
 	 *  this, a vararg function is used.
 	 */
 
-	nargs = duk_get_top(ctx);
+	nargs = duk_get_top(thr);
 	if (nargs == 0) {
-		duk_push_int(ctx, 0);
-	}
-	duk_to_number(ctx, 0);
-	duk_set_top(ctx, 1);
-	DUK_ASSERT_TOP(ctx, 1);
-
-	if (!duk_is_constructor_call(ctx)) {
+		duk_push_int(thr, 0);
+	}
+	duk_to_number(thr, 0);
+	duk_set_top(thr, 1);
+	DUK_ASSERT_TOP(thr, 1);
+
+	if (!duk_is_constructor_call(thr)) {
 		return 1;
 	}
 
@@ -35952,50 +37209,50 @@
 	 */
 
 	/* XXX: helper */
-	duk_push_this(ctx);
-	h_this = duk_known_hobject(ctx, -1);
+	duk_push_this(thr);
+	h_this = duk_known_hobject(thr, -1);
 	DUK_HOBJECT_SET_CLASS_NUMBER(h_this, DUK_HOBJECT_CLASS_NUMBER);
 
 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_this) == thr->builtins[DUK_BIDX_NUMBER_PROTOTYPE]);
 	DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_this) == DUK_HOBJECT_CLASS_NUMBER);
 	DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_this));
 
-	duk_dup_0(ctx);  /* -> [ val obj val ] */
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
+	duk_dup_0(thr);  /* -> [ val obj val ] */
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
 	return 0;  /* no return value -> don't replace created value */
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_value_of(duk_context *ctx) {
-	(void) duk__push_this_number_plain(ctx);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_string(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_value_of(duk_hthread *thr) {
+	(void) duk__push_this_number_plain(thr);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_string(duk_hthread *thr) {
 	duk_small_int_t radix;
 	duk_small_uint_t n2s_flags;
 
-	(void) duk__push_this_number_plain(ctx);
-	if (duk_is_undefined(ctx, 0)) {
+	(void) duk__push_this_number_plain(thr);
+	if (duk_is_undefined(thr, 0)) {
 		radix = 10;
 	} else {
-		radix = (duk_small_int_t) duk_to_int_check_range(ctx, 0, 2, 36);
+		radix = (duk_small_int_t) duk_to_int_check_range(thr, 0, 2, 36);
 	}
 	DUK_DDD(DUK_DDDPRINT("radix=%ld", (long) radix));
 
 	n2s_flags = 0;
 
-	duk_numconv_stringify(ctx,
+	duk_numconv_stringify(thr,
 	                      radix /*radix*/,
 	                      0 /*digits*/,
 	                      n2s_flags /*flags*/);
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_hthread *thr) {
 	/* XXX: just use toString() for now; permitted although not recommended.
 	 * nargs==1, so radix is passed to toString().
 	 */
-	return duk_bi_number_prototype_to_string(ctx);
+	return duk_bi_number_prototype_to_string(thr);
 }
 
 /*
@@ -36004,14 +37261,14 @@
 
 /* XXX: shared helper for toFixed(), toExponential(), toPrecision()? */
 
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_hthread *thr) {
 	duk_small_int_t frac_digits;
 	duk_double_t d;
 	duk_small_int_t c;
 	duk_small_uint_t n2s_flags;
 
-	frac_digits = (duk_small_int_t) duk_to_int_check_range(ctx, 0, 0, 20);
-	d = duk__push_this_number_plain(ctx);
+	frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);
+	d = duk__push_this_number_plain(thr);
 
 	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
 	if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
@@ -36025,53 +37282,53 @@
 	n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
 	            DUK_N2S_FLAG_FRACTION_DIGITS;
 
-	duk_numconv_stringify(ctx,
+	duk_numconv_stringify(thr,
 	                      10 /*radix*/,
 	                      frac_digits /*digits*/,
 	                      n2s_flags /*flags*/);
 	return 1;
 
  use_to_string:
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_to_string(ctx, -1);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_exponential(duk_context *ctx) {
+	DUK_ASSERT_TOP(thr, 2);
+	duk_to_string(thr, -1);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_exponential(duk_hthread *thr) {
 	duk_bool_t frac_undefined;
 	duk_small_int_t frac_digits;
 	duk_double_t d;
 	duk_small_int_t c;
 	duk_small_uint_t n2s_flags;
 
-	d = duk__push_this_number_plain(ctx);
-
-	frac_undefined = duk_is_undefined(ctx, 0);
-	duk_to_int(ctx, 0);  /* for side effects */
+	d = duk__push_this_number_plain(thr);
+
+	frac_undefined = duk_is_undefined(thr, 0);
+	duk_to_int(thr, 0);  /* for side effects */
 
 	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
 	if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
 		goto use_to_string;
 	}
 
-	frac_digits = (duk_small_int_t) duk_to_int_check_range(ctx, 0, 0, 20);
+	frac_digits = (duk_small_int_t) duk_to_int_check_range(thr, 0, 0, 20);
 
 	n2s_flags = DUK_N2S_FLAG_FORCE_EXP |
 	           (frac_undefined ? 0 : DUK_N2S_FLAG_FIXED_FORMAT);
 
-	duk_numconv_stringify(ctx,
+	duk_numconv_stringify(thr,
 	                      10 /*radix*/,
 	                      frac_digits + 1 /*leading digit + fractions*/,
 	                      n2s_flags /*flags*/);
 	return 1;
 
  use_to_string:
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_to_string(ctx, -1);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx) {
+	DUK_ASSERT_TOP(thr, 2);
+	duk_to_string(thr, -1);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_hthread *thr) {
 	/* The specification has quite awkward order of coercion and
 	 * checks for toPrecision().  The operations below are a bit
 	 * reordered, within constraints of observable side effects.
@@ -36082,27 +37339,27 @@
 	duk_small_int_t c;
 	duk_small_uint_t n2s_flags;
 
-	DUK_ASSERT_TOP(ctx, 1);
-
-	d = duk__push_this_number_plain(ctx);
-	if (duk_is_undefined(ctx, 0)) {
+	DUK_ASSERT_TOP(thr, 1);
+
+	d = duk__push_this_number_plain(thr);
+	if (duk_is_undefined(thr, 0)) {
 		goto use_to_string;
 	}
-	DUK_ASSERT_TOP(ctx, 2);
-
-	duk_to_int(ctx, 0);  /* for side effects */
+	DUK_ASSERT_TOP(thr, 2);
+
+	duk_to_int(thr, 0);  /* for side effects */
 
 	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
 	if (c == DUK_FP_NAN || c == DUK_FP_INFINITE) {
 		goto use_to_string;
 	}
 
-	prec = (duk_small_int_t) duk_to_int_check_range(ctx, 0, 1, 21);
+	prec = (duk_small_int_t) duk_to_int_check_range(thr, 0, 1, 21);
 
 	n2s_flags = DUK_N2S_FLAG_FIXED_FORMAT |
 	            DUK_N2S_FLAG_NO_ZERO_PAD;
 
-	duk_numconv_stringify(ctx,
+	duk_numconv_stringify(thr,
 	                      10 /*radix*/,
 	                      prec /*digits*/,
 	                      n2s_flags /*flags*/);
@@ -36113,8 +37370,8 @@
 	 * and +/- infinity (-> "Infinity", "-Infinity").
 	 */
 
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_to_string(ctx, -1);
+	DUK_ASSERT_TOP(thr, 2);
+	duk_to_string(thr, -1);
 	return 1;
 }
 
@@ -36127,26 +37384,26 @@
 /* #include duk_internal.h -> already included */
 
 /* Needed even when Object built-in disabled. */
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx) {
-	duk_tval *tv;
-	tv = DUK_HTHREAD_THIS_PTR((duk_hthread *) ctx);
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) {
+	duk_tval *tv;
+	tv = DUK_HTHREAD_THIS_PTR(thr);
 	/* XXX: This is not entirely correct anymore; in ES2015 the
 	 * default lookup should use @@toStringTag to come up with
 	 * e.g. [object Symbol].
 	 */
-	duk_push_class_string_tval(ctx, tv);
+	duk_push_class_string_tval(thr, tv);
 	return 1;
 }
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_hthread *thr) {
 	duk_uint_t arg_mask;
 
-	arg_mask = duk_get_type_mask(ctx, 0);
-
-	if (!duk_is_constructor_call(ctx) &&  /* not a constructor call */
+	arg_mask = duk_get_type_mask(thr, 0);
+
+	if (!duk_is_constructor_call(thr) &&  /* not a constructor call */
 	    ((arg_mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) == 0)) {  /* and argument not null or undefined */
-		duk_to_object(ctx, 0);
+		duk_to_object(thr, 0);
 		return 1;
 	}
 
@@ -36166,11 +37423,11 @@
 		 * be checked for explicitly, but Object(obj) calls are
 		 * not very common so opt for minimal footprint.
 		 */
-		duk_to_object(ctx, 0);
+		duk_to_object(thr, 0);
 		return 1;
 	}
 
-	(void) duk_push_object_helper(ctx,
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
@@ -36180,27 +37437,27 @@
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_hthread *thr) {
 	duk_idx_t nargs;
 	duk_int_t idx;
 
-	nargs = duk_get_top_require_min(ctx, 1 /*min_top*/);
-
-	duk_to_object(ctx, 0);
+	nargs = duk_get_top_require_min(thr, 1 /*min_top*/);
+
+	duk_to_object(thr, 0);
 	for (idx = 1; idx < nargs; idx++) {
 		/* E7 19.1.2.1 (step 4a) */
-		if (duk_is_null_or_undefined(ctx, idx)) {
+		if (duk_is_null_or_undefined(thr, idx)) {
 			continue;
 		}
 
 		/* duk_enum() respects ES2015+ [[OwnPropertyKeys]] ordering, which is
 		 * convenient here.
 		 */
-		duk_to_object(ctx, idx);
-		duk_enum(ctx, idx, DUK_ENUM_OWN_PROPERTIES_ONLY);
-		while (duk_next(ctx, -1, 1 /*get_value*/)) {
+		duk_to_object(thr, idx);
+		duk_enum(thr, idx, DUK_ENUM_OWN_PROPERTIES_ONLY);
+		while (duk_next(thr, -1, 1 /*get_value*/)) {
 			/* [ target ... enum key value ] */
-			duk_put_prop(ctx, 0);
+			duk_put_prop(thr, 0);
 			/* [ target ... enum ] */
 		}
 		/* Could pop enumerator, but unnecessary because of duk_set_top()
@@ -36208,41 +37465,41 @@
 		 */
 	}
 
-	duk_set_top(ctx, 1);
+	duk_set_top(thr, 1);
 	return 1;
 }
 #endif
 
 #if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_context *ctx) {
-	DUK_ASSERT_TOP(ctx, 2);
-	duk_push_boolean(ctx, duk_samevalue(ctx, 0, 1));
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 2);
+	duk_push_boolean(thr, duk_samevalue(thr, 0, 1));
 	return 1;
 }
 #endif
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_hthread *thr) {
 	duk_hobject *proto;
 
-	DUK_ASSERT_TOP(ctx, 2);
+	DUK_ASSERT_TOP(thr, 2);
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-	duk_hbufobj_promote_plain(ctx, 0);
-#endif
-	proto = duk_require_hobject_accept_mask(ctx, 0, DUK_TYPE_MASK_NULL);
-	DUK_ASSERT(proto != NULL || duk_is_null(ctx, 0));
-
-	(void) duk_push_object_helper_proto(ctx,
+	duk_hbufobj_promote_plain(thr, 0);
+#endif
+	proto = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_NULL);
+	DUK_ASSERT(proto != NULL || duk_is_null(thr, 0));
+
+	(void) duk_push_object_helper_proto(thr,
 	                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                                    DUK_HOBJECT_FLAG_FASTREFS |
 	                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
 	                                    proto);
 
-	if (!duk_is_undefined(ctx, 1)) {
+	if (!duk_is_undefined(thr, 1)) {
 		/* [ O Properties obj ] */
 
-		duk_replace(ctx, 0);
+		duk_replace(thr, 0);
 
 		/* [ obj Properties ] */
 
@@ -36250,7 +37507,7 @@
 		 * finish up.
 		 */
 
-		return duk_bi_object_constructor_define_properties(ctx);
+		return duk_bi_object_constructor_define_properties(thr);
 	}
 
 	/* [ O Properties obj ] */
@@ -36260,7 +37517,7 @@
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_hthread *thr) {
 	duk_small_uint_t pass;
 	duk_uint_t defprop_flags;
 	duk_hobject *obj;
@@ -36269,14 +37526,14 @@
 	duk_hobject *set;
 
 	/* Lightfunc and plain buffer handling by ToObject() coercion. */
-	obj = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
 	DUK_ASSERT(obj != NULL);
 
-	duk_to_object(ctx, 1);        /* properties object */
+	duk_to_object(thr, 1);        /* properties object */
 
 	DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT",
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1)));
+	                     (duk_tval *) duk_get_tval(thr, 0),
+	                     (duk_tval *) duk_get_tval(thr, 1)));
 
 	/*
 	 *  Two pass approach to processing the property descriptors.
@@ -36289,27 +37546,27 @@
 	 */
 
 	for (pass = 0; pass < 2; pass++) {
-		duk_set_top(ctx, 2);  /* -> [ hobject props ] */
-		duk_enum(ctx, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/);
+		duk_set_top(thr, 2);  /* -> [ hobject props ] */
+		duk_enum(thr, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/);
 
 		for (;;) {
 			duk_hstring *key;
 
 			/* [ hobject props enum(props) ] */
 
-			duk_set_top(ctx, 3);
-
-			if (!duk_next(ctx, 2, 1 /*get_value*/)) {
+			duk_set_top(thr, 3);
+
+			if (!duk_next(thr, 2, 1 /*get_value*/)) {
 				break;
 			}
 
 			DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT",
-			                     (duk_tval *) duk_get_tval(ctx, -2),
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -2),
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 
 			/* [ hobject props enum(props) key desc ] */
 
-			duk_hobject_prepare_property_descriptor(ctx,
+			duk_hobject_prepare_property_descriptor(thr,
 			                                        4 /*idx_desc*/,
 			                                        &defprop_flags,
 			                                        &idx_value,
@@ -36323,10 +37580,10 @@
 			}
 
 			/* This allows symbols on purpose. */
-			key = duk_known_hstring(ctx, 3);
+			key = duk_known_hstring(thr, 3);
 			DUK_ASSERT(key != NULL);
 
-			duk_hobject_define_property_helper(ctx,
+			duk_hobject_define_property_helper(thr,
 			                                   defprop_flags,
 			                                   obj,
 			                                   key,
@@ -36341,149 +37598,100 @@
 	 *  Return target object
 	 */
 
-	duk_dup_0(ctx);
+	duk_dup_0(thr);
+	return 1;
+}
+#endif  /* DUK_USE_OBJECT_BUILTIN */
+
+#if defined(DUK_USE_OBJECT_BUILTIN)
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 1);
+
+	duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/);
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_hobject *h;
-	duk_bool_t is_freeze;
-
-	DUK_ASSERT_TOP(ctx, 1);
-
-	is_freeze = (duk_bool_t) duk_get_current_magic(ctx);
-	if (duk_is_buffer(ctx, 0)) {
-		/* Plain buffer: already sealed, but not frozen (and can't be frozen
-		 * because index properties can't be made non-writable.
-		 */
-		if (is_freeze) {
-			goto fail_cannot_freeze;
-		}
-		return 1;
-	} else if (duk_is_lightfunc(ctx, 0)) {
-		/* Lightfunc: already sealed and frozen, success. */
-		return 1;
-	}
-#if 0
-	/* Seal/freeze are quite rare in practice so it'd be nice to get the
-	 * correct behavior simply via automatic promotion (at the cost of some
-	 * memory churn).  However, the promoted objects don't behave the same,
-	 * e.g. promoted lightfuncs are extensible.
-	 */
-	h = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-#endif
-
-	h = duk_get_hobject(ctx, 0);
-	if (h == NULL) {
-		/* ES2015 Sections 19.1.2.5, 19.1.2.17 */
-		return 1;
-	}
-
-	if (is_freeze && DUK_HOBJECT_IS_BUFOBJ(h)) {
-		/* Buffer objects cannot be frozen because there's no internal
-		 * support for making virtual array indices non-writable.
-		 */
-		DUK_DD(DUK_DDPRINT("cannot freeze a buffer object"));
-		goto fail_cannot_freeze;
-	}
-
-	duk_hobject_object_seal_freeze_helper(thr, h, is_freeze);
-
-	/* Sealed and frozen objects cannot gain any more properties,
-	 * so this is a good time to compact them.
-	 */
-	duk_hobject_compact_props(thr, h);
-	return 1;
-
- fail_cannot_freeze:
-	DUK_DCERROR_TYPE_INVALID_ARGS(thr);  /* XXX: proper error message */
-}
-#endif  /* DUK_USE_OBJECT_BUILTIN */
-
-#if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread *thr) {
 	duk_hobject *h;
 	duk_bool_t is_frozen;
 	duk_uint_t mask;
 
-	is_frozen = duk_get_current_magic(ctx);
-	mask = duk_get_type_mask(ctx, 0);
+	is_frozen = (duk_bool_t) duk_get_current_magic(thr);
+	mask = duk_get_type_mask(thr, 0);
 	if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
 		DUK_ASSERT(is_frozen == 0 || is_frozen == 1);
-		duk_push_boolean(ctx, (mask & DUK_TYPE_MASK_LIGHTFUNC) ?
+		duk_push_boolean(thr, (mask & DUK_TYPE_MASK_LIGHTFUNC) ?
 		                          1 :               /* lightfunc always frozen and sealed */
 		                          (is_frozen ^ 1)); /* buffer sealed but not frozen (index props writable) */
 	} else {
 		/* ES2015 Sections 19.1.2.12, 19.1.2.13: anything other than an object
 		 * is considered to be already sealed and frozen.
 		 */
-		h = duk_get_hobject(ctx, 0);
-		duk_push_boolean(ctx, (h == NULL) ||
-		                      duk_hobject_object_is_sealed_frozen_helper((duk_hthread *) ctx, h, is_frozen /*is_frozen*/));
+		h = duk_get_hobject(thr, 0);
+		duk_push_boolean(thr, (h == NULL) ||
+		                      duk_hobject_object_is_sealed_frozen_helper(thr, h, is_frozen /*is_frozen*/));
 	}
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx) {
-	DUK_ASSERT_TOP(ctx, 0);
-	(void) duk_push_this_coercible_to_object(ctx);
-	duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_TO_STRING);
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 0);
+	(void) duk_push_this_coercible_to_object(thr);
+	duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_TO_STRING);
 #if 0  /* This is mentioned explicitly in the E5.1 spec, but duk_call_method() checks for it in practice. */
-	duk_require_callable(ctx, 1);
-#endif
-	duk_dup_0(ctx);  /* -> [ O toString O ] */
-	duk_call_method(ctx, 0);  /* XXX: call method tail call? */
+	duk_require_callable(thr, 1);
+#endif
+	duk_dup_0(thr);  /* -> [ O toString O ] */
+	duk_call_method(thr, 0);  /* XXX: call method tail call? */
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_hthread *thr) {
 	/* For lightfuncs and plain buffers, returns Object() coerced. */
-	(void) duk_push_this_coercible_to_object(ctx);
+	(void) duk_push_this_coercible_to_object(thr);
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_hthread *thr) {
 	duk_hobject *h_v;
 	duk_hobject *h_obj;
 
-	DUK_ASSERT_TOP(ctx, 1);
-
-	h_v = duk_get_hobject(ctx, 0);
+	DUK_ASSERT_TOP(thr, 1);
+
+	h_v = duk_get_hobject(thr, 0);
 	if (!h_v) {
-		duk_push_false(ctx);  /* XXX: tail call: return duk_push_false(ctx) */
+		duk_push_false(thr);  /* XXX: tail call: return duk_push_false(thr) */
 		return 1;
 	}
 
-	h_obj = duk_push_this_coercible_to_object(ctx);
+	h_obj = duk_push_this_coercible_to_object(thr);
 	DUK_ASSERT(h_obj != NULL);
 
 	/* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare.
 	 * Prototype loops should cause an error to be thrown.
 	 */
-	duk_push_boolean(ctx, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/));
+	duk_push_boolean(thr, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/));
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx) {
-	return duk_hobject_object_ownprop_helper(ctx, 0 /*required_desc_flags*/);
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_hthread *thr) {
+	return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, 0 /*required_desc_flags*/);
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx) {
-	return duk_hobject_object_ownprop_helper(ctx, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/);
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_hthread *thr) {
+	return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/);
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN */
 
@@ -36493,31 +37701,30 @@
  *
  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
  */
-DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_hthread *thr) {
 	/*
 	 *  magic = 0: __proto__ getter
 	 *  magic = 1: Object.getPrototypeOf()
 	 *  magic = 2: Reflect.getPrototypeOf()
 	 */
 
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_hobject *h;
 	duk_hobject *proto;
 	duk_tval *tv;
 	duk_int_t magic;
 
-	magic = duk_get_current_magic(ctx);
+	magic = duk_get_current_magic(thr);
 
 	if (magic == 0) {
-		DUK_ASSERT_TOP(ctx, 0);
-		duk_push_this_coercible_to_object(ctx);
-	}
-	DUK_ASSERT(duk_get_top(ctx) >= 1);
+		DUK_ASSERT_TOP(thr, 0);
+		duk_push_this_coercible_to_object(thr);
+	}
+	DUK_ASSERT(duk_get_top(thr) >= 1);
 	if (magic < 2) {
 		/* ES2015 Section 19.1.2.9, step 1 */
-		duk_to_object(ctx, 0);
-	}
-	tv = DUK_GET_TVAL_POSIDX(ctx, 0);
+		duk_to_object(thr, 0);
+	}
+	tv = DUK_GET_TVAL_POSIDX(thr, 0);
 
 	switch (DUK_TVAL_GET_TAG(tv)) {
 	case DUK_TAG_BUFFER:
@@ -36537,9 +37744,9 @@
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	}
 	if (proto != NULL) {
-		duk_push_hobject(ctx, proto);
-	} else {
-		duk_push_null(ctx);
+		duk_push_hobject(thr, proto);
+	} else {
+		duk_push_null(thr);
 	}
 	return 1;
 }
@@ -36552,14 +37759,13 @@
  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof
  */
-DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_hthread *thr) {
 	/*
 	 *  magic = 0: __proto__ setter
 	 *  magic = 1: Object.setPrototypeOf()
 	 *  magic = 2: Reflect.setPrototypeOf()
 	 */
 
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_hobject *h_obj;
 	duk_hobject *h_new_proto;
 	duk_hobject *h_curr;
@@ -36568,11 +37774,11 @@
 	duk_int_t magic;
 
 	/* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4). */
-	magic = duk_get_current_magic(ctx);
+	magic = duk_get_current_magic(thr);
 	if (magic == 0) {
-		duk_push_this_check_object_coercible(ctx);
-		duk_insert(ctx, 0);
-		if (!duk_check_type_mask(ctx, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) {
+		duk_push_this_check_object_coercible(thr);
+		duk_insert(thr, 0);
+		if (!duk_check_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) {
 			return 0;
 		}
 
@@ -36582,19 +37788,19 @@
 		ret_success = 0;
 	} else {
 		if (magic == 1) {
-			duk_require_object_coercible(ctx, 0);
-		} else {
-			duk_require_hobject_accept_mask(ctx, 0,
+			duk_require_object_coercible(thr, 0);
+		} else {
+			duk_require_hobject_accept_mask(thr, 0,
 			                                DUK_TYPE_MASK_LIGHTFUNC |
 			                                DUK_TYPE_MASK_BUFFER);
 		}
-		duk_require_type_mask(ctx, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT);
-	}
-
-	h_new_proto = duk_get_hobject(ctx, 1);
+		duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT);
+	}
+
+	h_new_proto = duk_get_hobject(thr, 1);
 	/* h_new_proto may be NULL */
 
-	mask = duk_get_type_mask(ctx, 0);
+	mask = duk_get_type_mask(thr, 0);
 	if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
 		duk_hobject *curr_proto;
 		curr_proto = thr->builtins[(mask & DUK_TYPE_MASK_LIGHTFUNC) ?
@@ -36605,7 +37811,7 @@
 		}
 		goto fail_nonextensible;
 	}
-	h_obj = duk_get_hobject(ctx, 0);
+	h_obj = duk_get_hobject(thr, 0);
 	if (h_obj == NULL) {
 		goto skip;
 	}
@@ -36630,9 +37836,9 @@
 	/* fall thru */
 
  skip:
-	duk_set_top(ctx, 1);
+	duk_set_top(thr, 1);
 	if (magic == 2) {
-		duk_push_true(ctx);
+		duk_push_true(thr);
 	}
 	return ret_success;
 
@@ -36641,14 +37847,14 @@
 	if (magic != 2) {
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 	} else {
-		duk_push_false(ctx);
+		duk_push_false(thr);
 		return 1;
 	}
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *thr) {
 	/*
 	 *  magic = 0: Object.defineProperty()
 	 *  magic = 1: Reflect.defineProperty()
@@ -36660,34 +37866,34 @@
 	duk_hobject *set;
 	duk_idx_t idx_value;
 	duk_uint_t defprop_flags;
-	duk_int_t magic;
+	duk_small_uint_t magic;
 	duk_bool_t throw_flag;
 	duk_bool_t ret;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT(thr != NULL);
 
 	DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T",
-	                     (void *) ctx,
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1),
-	                     (duk_tval *) duk_get_tval(ctx, 2)));
+	                     (void *) thr,
+	                     (duk_tval *) duk_get_tval(thr, 0),
+	                     (duk_tval *) duk_get_tval(thr, 1),
+	                     (duk_tval *) duk_get_tval(thr, 2)));
 
 	/* [ obj key desc ] */
 
-	magic = duk_get_current_magic(ctx);
+	magic = (duk_small_uint_t) duk_get_current_magic(thr);
 
 	/* Lightfuncs are currently supported by coercing to a temporary
 	 * Function object; changes will be allowed (the coerced value is
 	 * extensible) but will be lost.  Same for plain buffers.
 	 */
-	obj = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
 	DUK_ASSERT(obj != NULL);
-	key = duk_to_property_key_hstring(ctx, 1);
-	(void) duk_require_hobject(ctx, 2);
+	key = duk_to_property_key_hstring(thr, 1);
+	(void) duk_require_hobject(thr, 2);
 
 	DUK_ASSERT(obj != NULL);
 	DUK_ASSERT(key != NULL);
-	DUK_ASSERT(duk_get_hobject(ctx, 2) != NULL);
+	DUK_ASSERT(duk_get_hobject(thr, 2) != NULL);
 
 	/*
 	 *  Validate and convert argument property descriptor (an Ecmascript
@@ -36697,7 +37903,7 @@
 	 *  Lightfunc set/get values are coerced to full Functions.
 	 */
 
-	duk_hobject_prepare_property_descriptor(ctx,
+	duk_hobject_prepare_property_descriptor(thr,
 	                                        2 /*idx_desc*/,
 	                                        &defprop_flags,
 	                                        &idx_value,
@@ -36708,9 +37914,9 @@
 	 *  Use Object.defineProperty() helper for the actual operation.
 	 */
 
-	DUK_ASSERT(magic == 0 || magic == 1);
-	throw_flag = magic ^ 1;
-	ret = duk_hobject_define_property_helper(ctx,
+	DUK_ASSERT(magic == 0U || magic == 1U);
+	throw_flag = magic ^ 1U;
+	ret = duk_hobject_define_property_helper(thr,
 	                                         defprop_flags,
 	                                         obj,
 	                                         key,
@@ -36723,35 +37929,35 @@
 	 * they're popped automatically.
 	 */
 
-	if (magic == 0) {
+	if (magic == 0U) {
 		/* Object.defineProperty(): return target object. */
-		duk_push_hobject(ctx, obj);
+		duk_push_hobject(thr, obj);
 	} else {
 		/* Reflect.defineProperty(): return success/fail. */
-		duk_push_boolean(ctx, ret);
+		duk_push_boolean(thr, ret);
 	}
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_context *ctx) {
-	DUK_ASSERT_TOP(ctx, 2);
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 2);
 
 	/* ES2015 Section 19.1.2.6, step 1 */
-	if (duk_get_current_magic(ctx) == 0) {
-		duk_to_object(ctx, 0);
+	if (duk_get_current_magic(thr) == 0) {
+		duk_to_object(thr, 0);
 	}
 
 	/* [ obj key ] */
 
-	duk_hobject_object_get_own_property_descriptor(ctx, -2);
+	duk_hobject_object_get_own_property_descriptor(thr, -2);
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_hthread *thr) {
 	/*
 	 *  magic = 0: Object.isExtensible()
 	 *  magic = 1: Reflect.isExtensible()
@@ -36759,16 +37965,16 @@
 
 	duk_hobject *h;
 
-	if (duk_get_current_magic(ctx) == 0) {
-		h = duk_get_hobject(ctx, 0);
+	if (duk_get_current_magic(thr) == 0) {
+		h = duk_get_hobject(thr, 0);
 	} else {
 		/* Reflect.isExtensible(): throw if non-object, but we accept lightfuncs
 		 * and plain buffers here because they pretend to be objects.
 		 */
-		h = duk_require_hobject_accept_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-	}
-
-	duk_push_boolean(ctx, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h));
+		h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	}
+
+	duk_push_boolean(thr, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h));
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
@@ -36804,8 +38010,7 @@
 	    DUK_ENUM_NO_PROXY_BEHAVIOR
 };
 
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) {
 	duk_hobject *obj;
 #if defined(DUK_USE_ES6_PROXY)
 	duk_hobject *h_proxy_target;
@@ -36815,18 +38020,17 @@
 	duk_small_uint_t enum_flags;
 	duk_int_t magic;
 
-	DUK_ASSERT_TOP(ctx, 1);
-	DUK_UNREF(thr);
-
-	magic = duk_get_current_magic(ctx);
+	DUK_ASSERT_TOP(thr, 1);
+
+	magic = duk_get_current_magic(thr);
 	if (magic == 3) {
 		/* ES2015 Section 26.1.11 requires a TypeError for non-objects.  Lightfuncs
 		 * and plain buffers pretend to be objects, so accept those too.
 		 */
-		obj = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+		obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
 	} else {
 		/* ES2015: ToObject coerce. */
-		obj = duk_to_hobject(ctx, 0);
+		obj = duk_to_hobject(thr, 0);
 	}
 	DUK_ASSERT(obj != NULL);
 	DUK_UNREF(obj);
@@ -36835,64 +38039,62 @@
 
 #if defined(DUK_USE_ES6_PROXY)
 	/* XXX: better sharing of code between proxy target call sites */
-	if (DUK_LIKELY(!duk_hobject_proxy_check(thr,
-	                                        obj,
+	if (DUK_LIKELY(!duk_hobject_proxy_check(obj,
 	                                        &h_proxy_target,
 	                                        &h_proxy_handler))) {
 		goto skip_proxy;
 	}
 
-	duk_push_hobject(ctx, h_proxy_handler);
-	if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_OWN_KEYS)) {
+	duk_push_hobject(thr, h_proxy_handler);
+	if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
 		/* Careful with reachability here: don't pop 'obj' before pushing
 		 * proxy target.
 		 */
 		DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead"));
-		duk_pop_2(ctx);
-		duk_push_hobject(ctx, h_proxy_target);
-		duk_replace(ctx, 0);
-		DUK_ASSERT_TOP(ctx, 1);
+		duk_pop_2(thr);
+		duk_push_hobject(thr, h_proxy_target);
+		duk_replace(thr, 0);
+		DUK_ASSERT_TOP(thr, 1);
 		goto skip_proxy;
 	}
 
 	/* [ obj handler trap ] */
-	duk_insert(ctx, -2);
-	duk_push_hobject(ctx, h_proxy_target);  /* -> [ obj trap handler target ] */
-	duk_call_method(ctx, 1 /*nargs*/);      /* -> [ obj trap_result ] */
-	h_trap_result = duk_require_hobject(ctx, -1);
+	duk_insert(thr, -2);
+	duk_push_hobject(thr, h_proxy_target);  /* -> [ obj trap handler target ] */
+	duk_call_method(thr, 1 /*nargs*/);      /* -> [ obj trap_result ] */
+	h_trap_result = duk_require_hobject(thr, -1);
 	DUK_UNREF(h_trap_result);
 
-	magic = duk_get_current_magic(ctx);
+	magic = duk_get_current_magic(thr);
 	DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
 	enum_flags = duk__object_keys_enum_flags[magic];
 
-	duk_proxy_ownkeys_postprocess(ctx, h_proxy_target, enum_flags);
+	duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags);
 	return 1;
 
  skip_proxy:
 #endif  /* DUK_USE_ES6_PROXY */
 
-	DUK_ASSERT_TOP(ctx, 1);
-	magic = duk_get_current_magic(ctx);
+	DUK_ASSERT_TOP(thr, 1);
+	magic = duk_get_current_magic(thr);
 	DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
 	enum_flags = duk__object_keys_enum_flags[magic];
-	return duk_hobject_get_enumerated_keys(ctx, enum_flags);
+	return duk_hobject_get_enumerated_keys(thr, enum_flags);
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
 
 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_hthread *thr) {
 	/*
 	 *  magic = 0: Object.preventExtensions()
 	 *  magic = 1: Reflect.preventExtensions()
 	 */
 
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_hobject *h;
 	duk_uint_t mask;
 	duk_int_t magic;
 
-	magic = duk_get_current_magic(ctx);
+	magic = duk_get_current_magic(thr);
 
 	/* Silent success for lightfuncs and plain buffers always. */
 	mask = DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER;
@@ -36907,11 +38109,11 @@
 		        DUK_TYPE_MASK_POINTER;
 	}
 
-	if (duk_check_type_mask(ctx, 0, mask)) {
+	if (duk_check_type_mask(thr, 0, mask)) {
 		/* Not an object, already non-extensible so always success. */
 		goto done;
 	}
-	h = duk_require_hobject(ctx, 0);
+	h = duk_require_hobject(thr, 0);
 	DUK_ASSERT(h != NULL);
 
 	DUK_HOBJECT_CLEAR_EXTENSIBLE(h);
@@ -36923,11 +38125,95 @@
 
  done:
 	if (magic == 1) {
-		duk_push_true(ctx);
+		duk_push_true(thr);
 	}
 	return 1;
 }
 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
+
+/*
+ *  __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
+ */
+
+#if defined(DUK_USE_ES8)
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_hthread *thr) {
+	duk_push_this(thr);
+	duk_insert(thr, 0);
+	duk_to_object(thr, 0);
+	duk_require_callable(thr, 2);
+
+	/* [ ToObject(this) key getter/setter ] */
+
+	/* ToPropertyKey() coercion is not needed, duk_def_prop() does it. */
+	duk_def_prop(thr, 0, DUK_DEFPROP_SET_ENUMERABLE |
+	                     DUK_DEFPROP_SET_CONFIGURABLE |
+	                     (duk_get_current_magic(thr) ? DUK_DEFPROP_HAVE_SETTER : DUK_DEFPROP_HAVE_GETTER));
+	return 0;
+}
+DUK_INTERNAL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_hthread *thr) {
+	duk_uint_t sanity;
+
+	duk_push_this(thr);
+	duk_to_object(thr, -1);
+
+	/* XXX: Prototype walk (with sanity) should be a core property
+	 * operation, could add a flag to e.g. duk_get_prop_desc().
+	 */
+
+	/* ToPropertyKey() coercion is not needed, duk_get_prop_desc() does it. */
+	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
+	while (!duk_is_undefined(thr, -1)) {
+		/* [ key obj ] */
+		duk_dup(thr, 0);
+		duk_get_prop_desc(thr, 1, 0 /*flags*/);
+		if (!duk_is_undefined(thr, -1)) {
+			duk_get_prop_stridx(thr, -1, (duk_get_current_magic(thr) != 0 ? DUK_STRIDX_SET : DUK_STRIDX_GET));
+			return 1;
+		}
+		duk_pop(thr);
+
+		if (DUK_UNLIKELY(sanity-- == 0)) {
+			DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
+		}
+
+		duk_get_prototype(thr, -1);
+		duk_remove(thr, -2);
+	}
+	return 1;
+}
+#endif  /* DUK_USE_ES8 */
+#line 1 "duk_bi_performance.c"
+/*
+ *  High resolution time API (performance.now() et al)
+ *
+ *  API specification: https://encoding.spec.whatwg.org/#ap://www.w3.org/TR/hr-time/
+ */
+
+/* #include duk_internal.h -> already included */
+
+#if defined(DUK_USE_PERFORMANCE_BUILTIN)
+DUK_INTERNAL duk_ret_t duk_bi_performance_now(duk_hthread *thr) {
+	/* From API spec:
+	 * The DOMHighResTimeStamp type is used to store a time value in
+	 * milliseconds, measured relative from the time origin, global
+	 * monotonic clock, or a time value that represents a duration
+	 * between two DOMHighResTimeStamp's.
+	 */
+	duk_push_number(thr, duk_time_get_monotonic_time(thr));
+	return 1;
+}
+
+#if 0  /* Missing until semantics decided. */
+DUK_INTERNAL duk_ret_t duk_bi_performance_timeorigin_getter(duk_hthread *thr) {
+	/* No decision yet how to handle timeOrigins, e.g. should one be
+	 * initialized per heap, or per global object set.  See
+	 * https://www.w3.org/TR/hr-time/#time-origin.
+	 */
+	duk_push_uint(thr, 0);
+	return 1;
+}
+#endif  /* 0 */
+#endif  /* DUK_USE_PERFORMANCE_BUILTIN */
 #line 1 "duk_bi_pointer.c"
 /*
  *  Pointer built-ins
@@ -36939,29 +38225,29 @@
  *  Constructor
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_hthread *thr) {
 	/* XXX: this behavior is quite useless now; it would be nice to be able
 	 * to create pointer values from e.g. numbers or strings.  Numbers are
 	 * problematic on 64-bit platforms though.  Hex encoded strings?
 	 */
-	if (duk_get_top(ctx) == 0) {
-		duk_push_pointer(ctx, NULL);
-	} else {
-		duk_to_pointer(ctx, 0);
-	}
-	DUK_ASSERT(duk_is_pointer(ctx, 0));
-	duk_set_top(ctx, 1);
-
-	if (duk_is_constructor_call(ctx)) {
-		(void) duk_push_object_helper(ctx,
+	if (duk_get_top(thr) == 0) {
+		duk_push_pointer(thr, NULL);
+	} else {
+		duk_to_pointer(thr, 0);
+	}
+	DUK_ASSERT(duk_is_pointer(thr, 0));
+	duk_set_top(thr, 1);
+
+	if (duk_is_constructor_call(thr)) {
+		(void) duk_push_object_helper(thr,
 		                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 		                              DUK_HOBJECT_FLAG_FASTREFS |
 		                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER),
 		                              DUK_BIDX_POINTER_PROTOTYPE);
 
 		/* Pointer object internal value is immutable */
-		duk_dup_0(ctx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
+		duk_dup_0(thr);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
 	}
 	/* Note: unbalanced stack on purpose */
 
@@ -36972,12 +38258,12 @@
  *  toString(), valueOf()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx) {
-	duk_tval *tv;
-	duk_small_int_t to_string = duk_get_current_magic(ctx);
-
-	duk_push_this(ctx);
-	tv = duk_require_tval(ctx, -1);
+DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_hthread *thr) {
+	duk_tval *tv;
+	duk_small_int_t to_string = duk_get_current_magic(thr);
+
+	duk_push_this(thr);
+	tv = duk_require_tval(thr, -1);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_POINTER(tv)) {
@@ -36991,19 +38277,64 @@
 			goto type_error;
 		}
 
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
+		duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
 	} else {
 		goto type_error;
 	}
 
 	if (to_string) {
-		duk_to_string(ctx, -1);
+		duk_to_string(thr, -1);
 	}
 	return 1;
 
  type_error:
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-}
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
+}
+#line 1 "duk_bi_promise.c"
+/*
+ *  Promise built-in
+ */
+
+/* #include duk_internal.h -> already included */
+
+#if defined(DUK_USE_PROMISE_BUILTIN)
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_constructor(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_all(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_race(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_reject(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_resolve(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_catch(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_promise_then(duk_hthread *thr) {
+	DUK_ERROR_TYPE(thr, "unimplemented");
+	return 0;
+}
+
+#endif  /* DUK_USE_PROMISE_BUILTIN */
 #line 1 "duk_bi_proxy.c"
 /*
  *  Proxy built-in (ES2015)
@@ -37016,24 +38347,23 @@
  * array of valid result keys (strings or symbols).  TypeError for invalid
  * values.  Flags are shared with duk_enum().
  */
-DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h_proxy_target, duk_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_hthread *thr, duk_hobject *h_proxy_target, duk_uint_t flags) {
 	duk_uarridx_t i, len, idx;
 	duk_propdesc desc;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(h_proxy_target != NULL);
 
-	len = (duk_uarridx_t) duk_get_length(ctx, -1);
+	len = (duk_uarridx_t) duk_get_length(thr, -1);
 	idx = 0;
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	/* XXX: preallocated dense array, fill in directly */
 	for (i = 0; i < len; i++) {
 		duk_hstring *h;
 
 		/* [ obj trap_result res_arr ] */
-		(void) duk_get_prop_index(ctx, -2, i);
-		h = duk_get_hstring(ctx, -1);
+		(void) duk_get_prop_index(thr, -2, i);
+		h = duk_get_hstring(thr, -1);
 		if (h == NULL) {
 			DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr);
 		}
@@ -37043,38 +38373,38 @@
 			 * so check enumerability always from target object
 			 * descriptor.
 			 */
-			if (duk_hobject_get_own_propdesc(thr, h_proxy_target, duk_known_hstring(ctx, -1), &desc, 0 /*flags*/)) {
+			if (duk_hobject_get_own_propdesc(thr, h_proxy_target, duk_known_hstring(thr, -1), &desc, 0 /*flags*/)) {
 				if ((desc.flags & DUK_PROPDESC_FLAG_ENUMERABLE) == 0) {
-					DUK_DDD(DUK_DDDPRINT("ignore non-enumerable property: %!T", duk_get_tval(ctx, -1)));
+					DUK_DDD(DUK_DDDPRINT("ignore non-enumerable property: %!T", duk_get_tval(thr, -1)));
 					goto skip_key;
 				}
 			} else {
-				DUK_DDD(DUK_DDDPRINT("ignore non-existent property: %!T", duk_get_tval(ctx, -1)));
+				DUK_DDD(DUK_DDDPRINT("ignore non-existent property: %!T", duk_get_tval(thr, -1)));
 				goto skip_key;
 			}
 		}
 		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
 			if (!(flags & DUK_ENUM_INCLUDE_SYMBOLS)) {
-				DUK_DDD(DUK_DDDPRINT("ignore symbol property: %!T", duk_get_tval(ctx, -1)));
+				DUK_DDD(DUK_DDDPRINT("ignore symbol property: %!T", duk_get_tval(thr, -1)));
 				goto skip_key;
 			}
 			if (DUK_HSTRING_HAS_HIDDEN(h) && !(flags & DUK_ENUM_INCLUDE_HIDDEN)) {
-				DUK_DDD(DUK_DDDPRINT("ignore hidden symbol property: %!T", duk_get_tval(ctx, -1)));
+				DUK_DDD(DUK_DDDPRINT("ignore hidden symbol property: %!T", duk_get_tval(thr, -1)));
 				goto skip_key;
 			}
 		} else {
 			if (flags & DUK_ENUM_EXCLUDE_STRINGS) {
-				DUK_DDD(DUK_DDDPRINT("ignore string property: %!T", duk_get_tval(ctx, -1)));
+				DUK_DDD(DUK_DDDPRINT("ignore string property: %!T", duk_get_tval(thr, -1)));
 				goto skip_key;
 			}
 		}
 
 		/* [ obj trap_result res_arr propname ] */
-		duk_put_prop_index(ctx, -2, idx++);
+		duk_put_prop_index(thr, -2, idx++);
 		continue;
 
 	 skip_key:
-		duk_pop(ctx);
+		duk_pop(thr);
 		continue;
 	}
 
@@ -37093,66 +38423,12 @@
 #endif  /* DUK_USE_ES6_PROXY */
 
 #if defined(DUK_USE_ES6_PROXY)
-DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx) {
-	duk_hobject *h_target;
-	duk_hobject *h_handler;
-
-	duk_require_constructor_call(ctx);
-
-	/* Reject a proxy object as the target because it would need
-	 * special handler in property lookups.  (ES2015 has no such restriction)
-	 */
-	h_target = duk_require_hobject_promote_mask(ctx, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-	DUK_ASSERT(h_target != NULL);
-	if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h_target)) {
-		goto fail_args;
-	}
-
-	/* Reject a proxy object as the handler because it would cause
-	 * potentially unbounded recursion.  (ES2015 has no such restriction)
-	 *
-	 * There's little practical reason to use a lightfunc or a plain
-	 * buffer as the handler table: one could only provide traps via
-	 * their prototype objects (Function.prototype and ArrayBuffer.prototype).
-	 * Even so, as lightfuncs and plain buffers mimic their object
-	 * counterparts, they're promoted and accepted here.
-	 */
-	h_handler = duk_require_hobject_promote_mask(ctx, 1, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-	DUK_ASSERT(h_handler != NULL);
-	if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h_handler)) {
-		goto fail_args;
-	}
-
-	/* XXX: the returned value is exotic in ES2015, but we use a
-	 * simple object here with no prototype.  Without a prototype,
-	 * ToPrimitive() coercion fails which is a bit confusing.
-	 * No callable check/handling in the current Proxy subset.
-	 */
-	(void) duk_push_object_helper_proto(ctx,
-	                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
-	                                    DUK_HOBJECT_FLAG_FASTREFS |
-	                                    DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ |
-	                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
-	                                    NULL);
-	DUK_ASSERT_TOP(ctx, 3);
-
-	/* Make _Target and _Handler non-configurable and non-writable.
-	 * They can still be forcibly changed by C code (both user and
-	 * Duktape internal), but not by Ecmascript code.
-	 */
-
-	/* Proxy target */
-	duk_dup_0(ctx);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
-
-	/* Proxy handler */
-	duk_dup_1(ctx);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_HANDLER, DUK_PROPDESC_FLAGS_NONE);
-
-	return 1;  /* replacement handler */
-
- fail_args:
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 2);  /* [ target handler ] */
+
+	duk_require_constructor_call(thr);
+	duk_push_proxy(thr, 0 /*flags*/);  /* [ target handler ] -> [ proxy ] */
+	return 1;  /* replacement */
 }
 #endif  /* DUK_USE_ES6_PROXY */
 #line 1 "duk_bi_reflect.c"
@@ -37167,97 +38443,89 @@
 /* #include duk_internal.h -> already included */
 
 #if defined(DUK_USE_REFLECT_BUILTIN)
-DUK_INTERNAL duk_ret_t duk_bi_reflect_object_delete_property(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_reflect_object_delete_property(duk_hthread *thr) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_bool_t ret;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	(void) duk_require_hobject(ctx, 0);
-	(void) duk_to_string(ctx, 1);
+	DUK_ASSERT_TOP(thr, 2);
+	(void) duk_require_hobject(thr, 0);
+	(void) duk_to_string(thr, 1);
 
 	/* [ target key ] */
 
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
-	tv_obj = DUK_GET_TVAL_POSIDX(ctx, 0);
-	tv_key = DUK_GET_TVAL_POSIDX(ctx, 1);
+	DUK_ASSERT(thr != NULL);
+	tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
+	tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
 	ret = duk_hobject_delprop(thr, tv_obj, tv_key, 0 /*throw_flag*/);
-	duk_push_boolean(ctx, ret);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx) {
-	duk_hthread *thr;
+	duk_push_boolean(thr, ret);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_reflect_object_get(duk_hthread *thr) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_idx_t nargs;
 
-	nargs = duk_get_top_require_min(ctx, 2 /*min_top*/);
-	(void) duk_require_hobject(ctx, 0);
-	(void) duk_to_string(ctx, 1);
-	if (nargs >= 3 && !duk_strict_equals(ctx, 0, 2)) {
+	DUK_ASSERT(thr != NULL);
+	nargs = duk_get_top_require_min(thr, 2 /*min_top*/);
+	(void) duk_require_hobject(thr, 0);
+	(void) duk_to_string(thr, 1);
+	if (nargs >= 3 && !duk_strict_equals(thr, 0, 2)) {
 		/* XXX: [[Get]] receiver currently unsupported */
-		DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+		DUK_ERROR_UNSUPPORTED(thr);
 	}
 
 	/* [ target key receiver? ...? ] */
 
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
-	tv_obj = DUK_GET_TVAL_POSIDX(ctx, 0);
-	tv_key = DUK_GET_TVAL_POSIDX(ctx, 1);
+	tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
+	tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
 	(void) duk_hobject_getprop(thr, tv_obj, tv_key);  /* This could also be a duk_get_prop(). */
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_reflect_object_has(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_reflect_object_has(duk_hthread *thr) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_bool_t ret;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	(void) duk_require_hobject(ctx, 0);
-	(void) duk_to_string(ctx, 1);
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_TOP(thr, 2);
+	(void) duk_require_hobject(thr, 0);
+	(void) duk_to_string(thr, 1);
 
 	/* [ target key ] */
 
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
-	tv_obj = DUK_GET_TVAL_POSIDX(ctx, 0);
-	tv_key = DUK_GET_TVAL_POSIDX(ctx, 1);
+	tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
+	tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
 	ret = duk_hobject_hasprop(thr, tv_obj, tv_key);
-	duk_push_boolean(ctx, ret);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx) {
-	duk_hthread *thr;
+	duk_push_boolean(thr, ret);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_hthread *thr) {
 	duk_tval *tv_obj;
 	duk_tval *tv_key;
 	duk_tval *tv_val;
 	duk_idx_t nargs;
 	duk_bool_t ret;
 
-	nargs = duk_get_top_require_min(ctx, 3 /*min_top*/);
-	(void) duk_require_hobject(ctx, 0);
-	(void) duk_to_string(ctx, 1);
-	if (nargs >= 4 && !duk_strict_equals(ctx, 0, 3)) {
+	DUK_ASSERT(thr != NULL);
+	nargs = duk_get_top_require_min(thr, 3 /*min_top*/);
+	(void) duk_require_hobject(thr, 0);
+	(void) duk_to_string(thr, 1);
+	if (nargs >= 4 && !duk_strict_equals(thr, 0, 3)) {
 		/* XXX: [[Set]] receiver currently unsupported */
-		DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx);
+		DUK_ERROR_UNSUPPORTED(thr);
 	}
 
 	/* [ target key value receiver? ...? ] */
 
-	thr = (duk_hthread *) ctx;
-	DUK_ASSERT(thr != NULL);
-	tv_obj = DUK_GET_TVAL_POSIDX(ctx, 0);
-	tv_key = DUK_GET_TVAL_POSIDX(ctx, 1);
-	tv_val = DUK_GET_TVAL_POSIDX(ctx, 2);
+	tv_obj = DUK_GET_TVAL_POSIDX(thr, 0);
+	tv_key = DUK_GET_TVAL_POSIDX(thr, 1);
+	tv_val = DUK_GET_TVAL_POSIDX(thr, 2);
 	ret = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, 0 /*throw_flag*/);
-	duk_push_boolean(ctx, ret);
+	duk_push_boolean(thr, ret);
 	return 1;
 }
 #endif  /* DUK_USE_REFLECT_BUILTIN */
@@ -37270,35 +38538,34 @@
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
 
-DUK_LOCAL void duk__get_this_regexp(duk_context *ctx) {
+DUK_LOCAL void duk__get_this_regexp(duk_hthread *thr) {
 	duk_hobject *h;
 
-	duk_push_this(ctx);
-	h = duk_require_hobject_with_class(ctx, -1, DUK_HOBJECT_CLASS_REGEXP);
+	duk_push_this(thr);
+	h = duk_require_hobject_with_class(thr, -1, DUK_HOBJECT_CLASS_REGEXP);
 	DUK_ASSERT(h != NULL);
 	DUK_UNREF(h);
-	duk_insert(ctx, 0);  /* prepend regexp to valstack 0 index */
+	duk_insert(thr, 0);  /* prepend regexp to valstack 0 index */
 }
 
 /* XXX: much to improve (code size) */
-DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_hthread *thr) {
 	duk_hobject *h_pattern;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	h_pattern = duk_get_hobject(ctx, 0);
-
-	if (!duk_is_constructor_call(ctx) &&
+	DUK_ASSERT_TOP(thr, 2);
+	h_pattern = duk_get_hobject(thr, 0);
+
+	if (!duk_is_constructor_call(thr) &&
 	    h_pattern != NULL &&
 	    DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP &&
-	    duk_is_undefined(ctx, 1)) {
+	    duk_is_undefined(thr, 1)) {
 		/* Called as a function, pattern has [[Class]] "RegExp" and
 		 * flags is undefined -> return object as is.
 		 */
 		/* XXX: ES2015 has a NewTarget SameValue() check which is not
 		 * yet implemented.
 		 */
-		duk_dup_0(ctx);
+		duk_dup_0(thr);
 		return 1;
 	}
 
@@ -37308,40 +38575,40 @@
 
 	if (h_pattern != NULL &&
 	    DUK_HOBJECT_GET_CLASS_NUMBER(h_pattern) == DUK_HOBJECT_CLASS_REGEXP) {
-		duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_SOURCE);
-		if (duk_is_undefined(ctx, 1)) {
+		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_SOURCE);
+		if (duk_is_undefined(thr, 1)) {
 			/* In ES5 one would need to read the flags individually;
 			 * in ES2015 just read .flags.
 			 */
-			duk_get_prop_stridx(ctx, 0, DUK_STRIDX_FLAGS);
+			duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS);
 		} else {
 			/* In ES2015 allowed; overrides argument RegExp flags. */
-			duk_dup_1(ctx);
-		}
-	} else {
-		if (duk_is_undefined(ctx, 0)) {
-			duk_push_hstring_empty(ctx);
-		} else {
-			duk_dup_0(ctx);
-			duk_to_string(ctx, -1);  /* Rejects Symbols. */
-		}
-		if (duk_is_undefined(ctx, 1)) {
-			duk_push_hstring_empty(ctx);
-		} else {
-			duk_dup_1(ctx);
-			duk_to_string(ctx, -1);  /* Rejects Symbols. */
+			duk_dup_1(thr);
+		}
+	} else {
+		if (duk_is_undefined(thr, 0)) {
+			duk_push_hstring_empty(thr);
+		} else {
+			duk_dup_0(thr);
+			duk_to_string(thr, -1);  /* Rejects Symbols. */
+		}
+		if (duk_is_undefined(thr, 1)) {
+			duk_push_hstring_empty(thr);
+		} else {
+			duk_dup_1(thr);
+			duk_to_string(thr, -1);  /* Rejects Symbols. */
 		}
 
 		/* [ ... pattern flags ] */
 	}
 
 	DUK_DDD(DUK_DDDPRINT("RegExp constructor/function call, pattern=%!T, flags=%!T",
-	                     (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -2), (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* [ ... pattern flags ] (both uncoerced) */
 
-	duk_to_string(ctx, -2);
-	duk_to_string(ctx, -1);
+	duk_to_string(thr, -2);
+	duk_to_string(thr, -1);
 	duk_regexp_compile(thr);
 
 	/* [ ... bytecode escaped_source ] */
@@ -37353,46 +38620,46 @@
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_context *ctx) {
-	duk__get_this_regexp(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_exec(duk_hthread *thr) {
+	duk__get_this_regexp(thr);
 
 	/* [ regexp input ] */
 
-	duk_regexp_match((duk_hthread *) ctx);
+	duk_regexp_match(thr);
 
 	/* [ result ] */
 
 	return 1;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_context *ctx) {
-	duk__get_this_regexp(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_test(duk_hthread *thr) {
+	duk__get_this_regexp(thr);
 
 	/* [ regexp input ] */
 
 	/* result object is created and discarded; wasteful but saves code space */
-	duk_regexp_match((duk_hthread *) ctx);
+	duk_regexp_match(thr);
 
 	/* [ result ] */
 
-	duk_push_boolean(ctx, (duk_is_null(ctx, -1) ? 0 : 1));
-
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_context *ctx) {
+	duk_push_boolean(thr, (duk_is_null(thr, -1) ? 0 : 1));
+
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_hthread *thr) {
 	/* This must be generic in ES2015 and later. */
-	DUK_ASSERT_TOP(ctx, 0);
-	duk_push_this(ctx);
-	duk_push_string(ctx, "/");
-	duk_get_prop_stridx(ctx, 0, DUK_STRIDX_SOURCE);
-	duk_dup_m2(ctx);  /* another "/" */
-	duk_get_prop_stridx(ctx, 0, DUK_STRIDX_FLAGS);
-	duk_concat(ctx, 4);
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx) {
+	DUK_ASSERT_TOP(thr, 0);
+	duk_push_this(thr);
+	duk_push_string(thr, "/");
+	duk_get_prop_stridx(thr, 0, DUK_STRIDX_SOURCE);
+	duk_dup_m2(thr);  /* another "/" */
+	duk_get_prop_stridx(thr, 0, DUK_STRIDX_FLAGS);
+	duk_concat(thr, 4);
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_hthread *thr) {
 	/* .flags is ES2015 but present even when ES2015 bindings are
 	 * disabled because the constructor relies on it.
 	 */
@@ -37400,15 +38667,15 @@
 	duk_uint8_t *p = buf;
 
 	/* .flags is generic and works on any object. */
-	duk_push_this(ctx);
-	(void) duk_require_hobject(ctx, -1);
-	if (duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL)) {
+	duk_push_this(thr);
+	(void) duk_require_hobject(thr, -1);
+	if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL)) {
 		*p++ = DUK_ASC_LC_G;
 	}
-	if (duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_IGNORE_CASE, NULL)) {
+	if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_IGNORE_CASE, NULL)) {
 		*p++ = DUK_ASC_LC_I;
 	}
-	if (duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_MULTILINE, NULL)) {
+	if (duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_MULTILINE, NULL)) {
 		*p++ = DUK_ASC_LC_M;
 	}
 	/* .unicode: to be added */
@@ -37416,30 +38683,29 @@
 	*p++ = DUK_ASC_NUL;
 	DUK_ASSERT((duk_size_t) (p - buf) <= sizeof(buf));
 
-	duk_push_string(ctx, (const char *) buf);
+	duk_push_string(thr, (const char *) buf);
 	return 1;
 }
 
 /* Shared helper for providing .source, .global, .multiline, etc getters. */
-DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_hthread *thr) {
 	duk_hstring *h_bc;
-	duk_small_int_t re_flags;
+	duk_small_uint_t re_flags;
 	duk_hobject *h;
 	duk_int_t magic;
 
-	DUK_ASSERT_TOP(ctx, 0);
-
-	duk_push_this(ctx);
-	h = duk_require_hobject(ctx, -1);
-	magic = duk_get_current_magic(ctx);
+	DUK_ASSERT_TOP(thr, 0);
+
+	duk_push_this(thr);
+	h = duk_require_hobject(thr, -1);
+	magic = duk_get_current_magic(thr);
 
 	if (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_REGEXP) {
-		duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_INT_SOURCE);
-		duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_INT_BYTECODE);
-		h_bc = duk_require_hstring(ctx, -1);
-		re_flags = (duk_small_int_t) DUK_HSTRING_GET_DATA(h_bc)[0];  /* Safe even if h_bc length is 0 (= NUL) */
-		duk_pop(ctx);
+		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_INT_SOURCE);
+		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_INT_BYTECODE);
+		h_bc = duk_require_hstring(thr, -1);
+		re_flags = (duk_small_uint_t) DUK_HSTRING_GET_DATA(h_bc)[0];  /* Safe even if h_bc length is 0 (= NUL) */
+		duk_pop(thr);
 	} else if (h == thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]) {
 		/* In ES2015 and ES2016 a TypeError would be thrown here.
 		 * However, this had real world issues so ES2017 draft
@@ -37449,7 +38715,7 @@
 		if (magic != 16 /* .source */) {
 			return 0;
 		}
-		duk_push_string(ctx, "(?:)");  /* .source handled by switch-case */
+		duk_push_string(thr, "(?:)");  /* .source handled by switch-case */
 		re_flags = 0;
 	} else {
 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
@@ -37459,15 +38725,15 @@
 
 	switch (magic) {
 	case 0: {  /* global */
-		duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_GLOBAL));
+		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_GLOBAL));
 		break;
 	}
 	case 1: {  /* ignoreCase */
-		duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_IGNORE_CASE));
+		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_IGNORE_CASE));
 		break;
 	}
 	case 2: {  /* multiline */
-		duk_push_boolean(ctx, (re_flags & DUK_RE_FLAG_MULTILINE));
+		duk_push_boolean(thr, (re_flags & DUK_RE_FLAG_MULTILINE));
 		break;
 	}
 #if 0
@@ -37476,7 +38742,7 @@
 	 */
 	case 3:    /* sticky */
 	case 4: {  /* unicode */
-		duk_push_false(ctx);
+		duk_push_false(thr);
 		break;
 	}
 #endif
@@ -37516,19 +38782,19 @@
  *  Helpers
  */
 
-DUK_LOCAL duk_hstring *duk__str_tostring_notregexp(duk_context *ctx, duk_idx_t idx) {
+DUK_LOCAL duk_hstring *duk__str_tostring_notregexp(duk_hthread *thr, duk_idx_t idx) {
 	duk_hstring *h;
 
-	if (duk_get_class_number(ctx, idx) == DUK_HOBJECT_CLASS_REGEXP) {
-		DUK_ERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
-	}
-	h = duk_to_hstring(ctx, idx);
+	if (duk_get_class_number(thr, idx) == DUK_HOBJECT_CLASS_REGEXP) {
+		DUK_ERROR_TYPE_INVALID_ARGS(thr);
+	}
+	h = duk_to_hstring(thr, idx);
 	DUK_ASSERT(h != NULL);
 
 	return h;
 }
 
-DUK_LOCAL duk_int_t duk__str_search_shared(duk_context *ctx, duk_hstring *h_this, duk_hstring *h_search, duk_int_t start_cpos, duk_bool_t backwards) {
+DUK_LOCAL duk_int_t duk__str_search_shared(duk_hthread *thr, duk_hstring *h_this, duk_hstring *h_search, duk_int_t start_cpos, duk_bool_t backwards) {
 	duk_int_t cpos;
 	duk_int_t bpos;
 	const duk_uint8_t *p_start, *p_end, *p;
@@ -37550,7 +38816,7 @@
 	}
 	DUK_ASSERT(q_blen > 0);
 
-	bpos = (duk_int_t) duk_heap_strcache_offset_char2byte((duk_hthread *) ctx, h_this, (duk_uint32_t) cpos);
+	bpos = (duk_int_t) duk_heap_strcache_offset_char2byte(thr, h_this, (duk_uint32_t) cpos);
 
 	p_start = DUK_HSTRING_GET_DATA(h_this);
 	p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_this);
@@ -37606,7 +38872,7 @@
  *  Constructor
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_uint_t flags;
 
@@ -37620,36 +38886,35 @@
 	 * current magic call sites.
 	 */
 
-	if (duk_get_top(ctx) == 0) {
-		duk_push_hstring_empty(ctx);
-	} else {
-		h = duk_to_hstring_acceptsymbol(ctx, 0);
-		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(ctx))) {
-			duk_push_symbol_descriptive_string(ctx, h);
-			duk_replace(ctx, 0);
-		}
-	}
-	duk_to_string(ctx, 0);  /* catches symbol argument for constructor call */
-	DUK_ASSERT(duk_is_string(ctx, 0));
-	duk_set_top(ctx, 1);  /* Top may be 1 or larger. */
-
-	if (duk_is_constructor_call(ctx)) {
+	if (duk_get_top(thr) == 0) {
+		duk_push_hstring_empty(thr);
+	} else {
+		h = duk_to_hstring_acceptsymbol(thr, 0);
+		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(thr))) {
+			duk_push_symbol_descriptive_string(thr, h);
+			duk_replace(thr, 0);
+		}
+	}
+	duk_to_string(thr, 0);  /* catches symbol argument for constructor call */
+	DUK_ASSERT(duk_is_string(thr, 0));
+	duk_set_top(thr, 1);  /* Top may be 1 or larger. */
+
+	if (duk_is_constructor_call(thr)) {
 		/* String object internal value is immutable */
 		flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
 		        DUK_HOBJECT_FLAG_FASTREFS |
 		        DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
 		        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
-		duk_push_object_helper(ctx, flags, DUK_BIDX_STRING_PROTOTYPE);
-		duk_dup_0(ctx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_object_helper(thr, flags, DUK_BIDX_STRING_PROTOTYPE);
+		duk_dup_0(thr);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
 	}
 	/* Note: unbalanced stack on purpose */
 
 	return 1;
 }
 
-DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_context *ctx, duk_bool_t nonbmp) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_hthread *thr, duk_bool_t nonbmp) {
 	duk_bufwriter_ctx bw_alloc;
 	duk_bufwriter_ctx *bw;
 	duk_idx_t i, n;
@@ -37661,10 +38926,10 @@
 	 * build a string from a duk_tval number sequence in one go?).
 	 */
 
-	n = duk_get_top(ctx);
+	n = duk_get_top(thr);
 
 	bw = &bw_alloc;
-	DUK_BW_INIT_PUSHBUF(thr, bw, n);  /* initial estimate for ASCII only codepoints */
+	DUK_BW_INIT_PUSHBUF(thr, bw, (duk_size_t) n);  /* initial estimate for ASCII only codepoints */
 
 	for (i = 0; i < n; i++) {
 		/* XXX: could improve bufwriter handling to write multiple codepoints
@@ -37678,9 +38943,9 @@
 			 * the same.
 			 */
 			duk_int32_t i32 = 0;
-			if (!duk_is_whole_get_int32(duk_to_number(ctx, i), &i32) ||
+			if (!duk_is_whole_get_int32(duk_to_number(thr, i), &i32) ||
 			    i32 < 0 || i32 > 0x10ffffL) {
-				DUK_DCERROR_RANGE_INVALID_ARGS((duk_hthread *) ctx);
+				DUK_DCERROR_RANGE_INVALID_ARGS(thr);
 			}
 			DUK_ASSERT(i32 >= 0 && i32 <= 0x10ffffL);
 			cp = (duk_ucodepoint_t) i32;
@@ -37692,10 +38957,10 @@
 			 * non-BMP codepoints.  Don't use CESU-8 because that'd create
 			 * surrogate pairs.
 			 */
-			cp = (duk_ucodepoint_t) duk_to_uint32(ctx, i);
+			cp = (duk_ucodepoint_t) duk_to_uint32(thr, i);
 			DUK_BW_WRITE_ENSURE_XUTF8(thr, bw, cp);
 #else
-			cp = (duk_ucodepoint_t) duk_to_uint16(ctx, i);
+			cp = (duk_ucodepoint_t) duk_to_uint16(thr, i);
 			DUK_ASSERT(cp >= 0 && cp <= 0x10ffffL);
 			DUK_BW_WRITE_ENSURE_CESU8(thr, bw, cp);
 #endif
@@ -37703,17 +38968,17 @@
 	}
 
 	DUK_BW_COMPACT(thr, bw);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe, extended UTF-8 or CESU-8 encoded. */
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_char_code(duk_context *ctx) {
-	return duk__construct_from_codepoints(ctx, 0 /*nonbmp*/);
+	(void) duk_buffer_to_string(thr, -1);  /* Safe, extended UTF-8 or CESU-8 encoded. */
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_char_code(duk_hthread *thr) {
+	return duk__construct_from_codepoints(thr, 0 /*nonbmp*/);
 }
 
 #if defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_code_point(duk_context *ctx) {
-	return duk__construct_from_codepoints(ctx, 1 /*nonbmp*/);
+DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_code_point(duk_hthread *thr) {
+	return duk__construct_from_codepoints(thr, 1 /*nonbmp*/);
 }
 #endif
 
@@ -37721,11 +38986,11 @@
  *  toString(), valueOf()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx) {
-	duk_tval *tv;
-
-	duk_push_this(ctx);
-	tv = duk_require_tval(ctx, -1);
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_hthread *thr) {
+	duk_tval *tv;
+
+	duk_push_this(thr);
+	tv = duk_require_tval(thr, -1);
 	DUK_ASSERT(tv != NULL);
 
 	if (DUK_TVAL_IS_STRING(tv)) {
@@ -37739,37 +39004,36 @@
 			goto type_error;
 		}
 
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VALUE);
-		DUK_ASSERT(duk_is_string(ctx, -1));
+		duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VALUE);
+		DUK_ASSERT(duk_is_string(thr, -1));
 	} else {
 		goto type_error;
 	}
 
-	(void) duk_require_hstring_notsymbol(ctx, -1);  /* Reject symbols (and wrapped symbols). */
+	(void) duk_require_hstring_notsymbol(thr, -1);  /* Reject symbols (and wrapped symbols). */
 	return 1;
 
  type_error:
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 }
 
 /*
  *  Character and charcode access
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_at(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_at(duk_hthread *thr) {
 	duk_int_t pos;
 
 	/* XXX: faster implementation */
 
-	(void) duk_push_this_coercible_to_string(ctx);
-	pos = duk_to_int(ctx, 0);
-	duk_substring(ctx, -1, pos, pos + 1);
+	(void) duk_push_this_coercible_to_string(thr);
+	pos = duk_to_int(thr, 0);
+	duk_substring(thr, -1, (duk_size_t) pos, (duk_size_t) (pos + 1));
 	return 1;
 }
 
 /* Magic: 0=charCodeAt, 1=codePointAt */
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_hthread *thr) {
 	duk_int_t pos;
 	duk_hstring *h;
 	duk_bool_t clamped;
@@ -37778,20 +39042,20 @@
 
 	/* XXX: faster implementation */
 
-	DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(ctx, 0)));
-
-	h = duk_push_this_coercible_to_string(ctx);
+	DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(thr, 0)));
+
+	h = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h != NULL);
 
-	pos = duk_to_int_clamped_raw(ctx,
+	pos = duk_to_int_clamped_raw(thr,
 	                             0 /*index*/,
 	                             0 /*min(incl)*/,
 	                             (duk_int_t) DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(incl)*/,
 	                             &clamped /*out_clamped*/);
 #if defined(DUK_USE_ES6)
-	magic = duk_get_current_magic(ctx);
-#else
-	DUK_ASSERT(duk_get_current_magic(ctx) == 0);
+	magic = duk_get_current_magic(thr);
+#else
+	DUK_ASSERT(duk_get_current_magic(thr) == 0);
 	magic = 0;
 #endif
 	if (clamped) {
@@ -37801,10 +39065,11 @@
 		if (magic != 0) {
 			return 0;
 		}
-		duk_push_nan(ctx);
-	} else {
-		cp = (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, pos, (duk_bool_t) magic /*surrogate_aware*/);
-		duk_push_u32(ctx, cp);
+		duk_push_nan(thr);
+	} else {
+		DUK_ASSERT(pos >= 0);
+		cp = (duk_uint32_t) duk_hstring_char_code_at_raw(thr, h, (duk_uint_t) pos, (duk_bool_t) magic /*surrogate_aware*/);
+		duk_push_u32(thr, cp);
 	}
 	return 1;
 }
@@ -37817,22 +39082,22 @@
  * different algorithms so that footprint would be reduced?
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_int_t start_pos, end_pos;
 	duk_int_t len;
 
-	h = duk_push_this_coercible_to_string(ctx);
+	h = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h != NULL);
 	len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
 
 	/* [ start end str ] */
 
-	start_pos = duk_to_int_clamped(ctx, 0, 0, len);
-	if (duk_is_undefined(ctx, 1)) {
+	start_pos = duk_to_int_clamped(thr, 0, 0, len);
+	if (duk_is_undefined(thr, 1)) {
 		end_pos = len;
 	} else {
-		end_pos = duk_to_int_clamped(ctx, 1, 0, len);
+		end_pos = duk_to_int_clamped(thr, 1, 0, len);
 	}
 	DUK_ASSERT(start_pos >= 0 && start_pos <= len);
 	DUK_ASSERT(end_pos >= 0 && end_pos <= len);
@@ -37845,12 +39110,12 @@
 
 	DUK_ASSERT(end_pos >= start_pos);
 
-	duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
+	duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
 	return 1;
 }
 
 #if defined(DUK_USE_SECTION_B)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_int_t start_pos, end_pos;
 	duk_int_t len;
@@ -37859,8 +39124,8 @@
 	 * specification will happily coerce undefined and null to strings
 	 * ("undefined" and "null").
 	 */
-	duk_push_this(ctx);
-	h = duk_to_hstring_m1(ctx);  /* Reject Symbols. */
+	duk_push_this(thr);
+	h = duk_to_hstring_m1(thr);  /* Reject Symbols. */
 	DUK_ASSERT(h != NULL);
 	len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
 
@@ -37872,47 +39137,47 @@
 	 */
 
 	/* combines steps 2 and 5; -len ensures max() not needed for step 5 */
-	start_pos = duk_to_int_clamped(ctx, 0, -len, len);
+	start_pos = duk_to_int_clamped(thr, 0, -len, len);
 	if (start_pos < 0) {
 		start_pos = len + start_pos;
 	}
 	DUK_ASSERT(start_pos >= 0 && start_pos <= len);
 
 	/* combines steps 3, 6; step 7 is not needed */
-	if (duk_is_undefined(ctx, 1)) {
+	if (duk_is_undefined(thr, 1)) {
 		end_pos = len;
 	} else {
 		DUK_ASSERT(start_pos <= len);
-		end_pos = start_pos + duk_to_int_clamped(ctx, 1, 0, len - start_pos);
+		end_pos = start_pos + duk_to_int_clamped(thr, 1, 0, len - start_pos);
 	}
 	DUK_ASSERT(start_pos >= 0 && start_pos <= len);
 	DUK_ASSERT(end_pos >= 0 && end_pos <= len);
 	DUK_ASSERT(end_pos >= start_pos);
 
-	duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
+	duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
 	return 1;
 }
 #endif  /* DUK_USE_SECTION_B */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_int_t start_pos, end_pos;
 	duk_int_t len;
 
-	h = duk_push_this_coercible_to_string(ctx);
+	h = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h != NULL);
 	len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
 
 	/* [ start end str ] */
 
-	start_pos = duk_to_int_clamped(ctx, 0, -len, len);
+	start_pos = duk_to_int_clamped(thr, 0, -len, len);
 	if (start_pos < 0) {
 		start_pos = len + start_pos;
 	}
-	if (duk_is_undefined(ctx, 1)) {
+	if (duk_is_undefined(thr, 1)) {
 		end_pos = len;
 	} else {
-		end_pos = duk_to_int_clamped(ctx, 1, -len, len);
+		end_pos = duk_to_int_clamped(thr, 1, -len, len);
 		if (end_pos < 0) {
 			end_pos = len + end_pos;
 		}
@@ -37926,7 +39191,7 @@
 
 	DUK_ASSERT(end_pos >= start_pos);
 
-	duk_substring(ctx, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
+	duk_substring(thr, -1, (duk_size_t) start_pos, (duk_size_t) end_pos);
 	return 1;
 }
 
@@ -37934,11 +39199,10 @@
  *  Case conversion
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-	duk_small_int_t uppercase = duk_get_current_magic(ctx);
-
-	(void) duk_push_this_coercible_to_string(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_hthread *thr) {
+	duk_small_int_t uppercase = duk_get_current_magic(thr);
+
+	(void) duk_push_this_coercible_to_string(thr);
 	duk_unicode_case_convert_string(thr, (duk_bool_t) uppercase);
 	return 1;
 }
@@ -37947,33 +39211,33 @@
  *  indexOf() and lastIndexOf()
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_hthread *thr) {
 	duk_hstring *h_this;
 	duk_hstring *h_search;
 	duk_int_t clen_this;
 	duk_int_t cpos;
-	duk_small_int_t is_lastindexof = duk_get_current_magic(ctx);  /* 0=indexOf, 1=lastIndexOf */
-
-	h_this = duk_push_this_coercible_to_string(ctx);
+	duk_small_uint_t is_lastindexof = (duk_small_uint_t) duk_get_current_magic(thr);  /* 0=indexOf, 1=lastIndexOf */
+
+	h_this = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h_this != NULL);
 	clen_this = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h_this);
 
-	h_search = duk_to_hstring(ctx, 0);
+	h_search = duk_to_hstring(thr, 0);
 	DUK_ASSERT(h_search != NULL);
 
-	duk_to_number(ctx, 1);
-	if (duk_is_nan(ctx, 1) && is_lastindexof) {
+	duk_to_number(thr, 1);
+	if (duk_is_nan(thr, 1) && is_lastindexof) {
 		/* indexOf: NaN should cause pos to be zero.
 		 * lastIndexOf: NaN should cause pos to be +Infinity
 		 * (and later be clamped to len).
 		 */
 		cpos = clen_this;
 	} else {
-		cpos = duk_to_int_clamped(ctx, 1, 0, clen_this);
-	}
-
-	cpos = duk__str_search_shared(ctx, h_this, h_search, cpos, is_lastindexof /*backwards*/);
-	duk_push_int(ctx, cpos);
+		cpos = duk_to_int_clamped(thr, 1, 0, clen_this);
+	}
+
+	cpos = duk__str_search_shared(thr, h_this, h_search, cpos, is_lastindexof /*backwards*/);
+	duk_push_int(thr, cpos);
 	return 1;
 }
 
@@ -37992,8 +39256,7 @@
  * - API call to get_prop and to_boolean
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_hthread *thr) {
 	duk_hstring *h_input;
 	duk_hstring *h_match;
 	duk_hstring *h_search;
@@ -38013,14 +39276,14 @@
 	const duk_uint8_t *r_start, *r_end, *r;   /* repl string scan */
 	duk_size_t tmp_sz;
 
-	DUK_ASSERT_TOP(ctx, 2);
-	h_input = duk_push_this_coercible_to_string(ctx);
+	DUK_ASSERT_TOP(thr, 2);
+	h_input = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h_input != NULL);
 
 	bw = &bw_alloc;
 	DUK_BW_INIT_PUSHBUF(thr, bw, DUK_HSTRING_GET_BYTELEN(h_input));  /* input size is good output starting point */
 
-	DUK_ASSERT_TOP(ctx, 4);
+	DUK_ASSERT_TOP(thr, 4);
 
 	/* stack[0] = search value
 	 * stack[1] = replace value
@@ -38028,29 +39291,29 @@
 	 * stack[3] = result buffer
 	 */
 
-	h_re = duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP);
+	h_re = duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP);
 	if (h_re) {
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		is_regexp = 1;
-		is_global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL);
+		is_global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL);
 
 		if (is_global) {
 			/* start match from beginning */
-			duk_push_int(ctx, 0);
-			duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
+			duk_push_int(thr, 0);
+			duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
 		}
 #else  /* DUK_USE_REGEXP_SUPPORT */
 		DUK_DCERROR_UNSUPPORTED(thr);
 #endif  /* DUK_USE_REGEXP_SUPPORT */
 	} else {
-		duk_to_string(ctx, 0);  /* rejects symbols */
+		duk_to_string(thr, 0);  /* rejects symbols */
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		is_regexp = 0;
 		is_global = 0;
 #endif
 	}
 
-	if (duk_is_function(ctx, 1)) {
+	if (duk_is_function(thr, 1)) {
 		is_repl_func = 1;
 		r_start = NULL;
 		r_end = NULL;
@@ -38058,7 +39321,7 @@
 		duk_hstring *h_repl;
 
 		is_repl_func = 0;
-		h_repl = duk_to_hstring(ctx, 1);  /* reject symbols */
+		h_repl = duk_to_hstring(thr, 1);  /* reject symbols */
 		DUK_ASSERT(h_repl != NULL);
 		r_start = DUK_HSTRING_GET_DATA(h_repl);
 		r_end = r_start + DUK_HSTRING_GET_BYTELEN(h_repl);
@@ -38090,27 +39353,27 @@
 		 *  are made?  See: test-bi-string-proto-replace.js for discussion.
 		 */
 
-		DUK_ASSERT_TOP(ctx, 4);
+		DUK_ASSERT_TOP(thr, 4);
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		if (is_regexp) {
-			duk_dup_0(ctx);
-			duk_dup_2(ctx);
+			duk_dup_0(thr);
+			duk_dup_2(thr);
 			duk_regexp_match(thr);  /* [ ... regexp input ] -> [ res_obj ] */
-			if (!duk_is_object(ctx, -1)) {
-				duk_pop(ctx);
+			if (!duk_is_object(thr, -1)) {
+				duk_pop(thr);
 				break;
 			}
 
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INDEX);
-			DUK_ASSERT(duk_is_number(ctx, -1));
-			match_start_coff = duk_get_int(ctx, -1);
-			duk_pop(ctx);
-
-			duk_get_prop_index(ctx, -1, 0);
-			DUK_ASSERT(duk_is_string(ctx, -1));
-			h_match = duk_known_hstring(ctx, -1);
-			duk_pop(ctx);  /* h_match is borrowed, remains reachable through match_obj */
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
+			DUK_ASSERT(duk_is_number(thr, -1));
+			match_start_coff = duk_get_uint(thr, -1);
+			duk_pop(thr);
+
+			duk_get_prop_index(thr, -1, 0);
+			DUK_ASSERT(duk_is_string(thr, -1));
+			h_match = duk_known_hstring(thr, -1);
+			duk_pop(thr);  /* h_match is borrowed, remains reachable through match_obj */
 
 			if (DUK_HSTRING_GET_BYTELEN(h_match) == 0) {
 				/* This should be equivalent to match() algorithm step 8.f.iii.2:
@@ -38118,17 +39381,17 @@
 				 */
 				duk_uint32_t last_index;
 
-				duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-				last_index = (duk_uint32_t) duk_get_uint(ctx, -1);
+				duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+				last_index = (duk_uint32_t) duk_get_uint(thr, -1);
 				DUK_DDD(DUK_DDDPRINT("empty match, bump lastIndex: %ld -> %ld",
 				                     (long) last_index, (long) (last_index + 1)));
-				duk_pop(ctx);
-				duk_push_int(ctx, last_index + 1);
-				duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-			}
-
-			DUK_ASSERT(duk_get_length(ctx, -1) <= DUK_INT_MAX);  /* string limits */
-			match_caps = (duk_int_t) duk_get_length(ctx, -1);
+				duk_pop(thr);
+				duk_push_uint(thr, (duk_uint_t) (last_index + 1));
+				duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+			}
+
+			DUK_ASSERT(duk_get_length(thr, -1) <= DUK_INT_MAX);  /* string limits */
+			match_caps = (duk_int_t) duk_get_length(thr, -1);
 		} else {
 #else  /* DUK_USE_REGEXP_SUPPORT */
 		{  /* unconditionally */
@@ -38145,7 +39408,7 @@
 			p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
 			p = p_start;
 
-			h_search = duk_known_hstring(ctx, 0);
+			h_search = duk_known_hstring(thr, 0);
 			q_start = DUK_HSTRING_GET_DATA(h_search);
 			q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_search);
 
@@ -38156,8 +39419,8 @@
 			while (p <= p_end) {
 				DUK_ASSERT(p + q_blen <= DUK_HSTRING_GET_DATA(h_input) + DUK_HSTRING_GET_BYTELEN(h_input));
 				if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) {
-					duk_dup_0(ctx);
-					h_match = duk_known_hstring(ctx, -1);
+					duk_dup_0(thr);
+					h_match = duk_known_hstring(thr, -1);
 #if defined(DUK_USE_REGEXP_SUPPORT)
 					match_caps = 0;
 #endif
@@ -38183,7 +39446,7 @@
 		 * stack[4] = regexp match OR match string
 		 */
 
-		match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);
+		match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);
 
 		tmp_sz = (duk_size_t) (match_start_boff - prev_match_end_boff);
 		DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz);
@@ -38196,36 +39459,36 @@
 
 			/* regexp res_obj is at index 4 */
 
-			duk_dup_1(ctx);
-			idx_args = duk_get_top(ctx);
+			duk_dup_1(thr);
+			idx_args = duk_get_top(thr);
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
 			if (is_regexp) {
 				duk_int_t idx;
-				duk_require_stack(ctx, match_caps + 2);
+				duk_require_stack(thr, match_caps + 2);
 				for (idx = 0; idx < match_caps; idx++) {
 					/* match followed by capture(s) */
-					duk_get_prop_index(ctx, 4, idx);
+					duk_get_prop_index(thr, 4, (duk_uarridx_t) idx);
 				}
 			} else {
 #else  /* DUK_USE_REGEXP_SUPPORT */
 			{  /* unconditionally */
 #endif  /* DUK_USE_REGEXP_SUPPORT */
 				/* match == search string, by definition */
-				duk_dup_0(ctx);
-			}
-			duk_push_int(ctx, match_start_coff);
-			duk_dup_2(ctx);
+				duk_dup_0(thr);
+			}
+			duk_push_uint(thr, (duk_uint_t) match_start_coff);
+			duk_dup_2(thr);
 
 			/* [ ... replacer match [captures] match_char_offset input ] */
 
-			duk_call(ctx, duk_get_top(ctx) - idx_args);
-			h_repl = duk_to_hstring_m1(ctx);  /* -> [ ... repl_value ] */
+			duk_call(thr, duk_get_top(thr) - idx_args);
+			h_repl = duk_to_hstring_m1(thr);  /* -> [ ... repl_value ] */
 			DUK_ASSERT(h_repl != NULL);
 
 			DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_repl);
 
-			duk_pop(ctx);  /* repl_value */
+			duk_pop(thr);  /* repl_value */
 		} else {
 			r = r_start;
 
@@ -38241,7 +39504,8 @@
 				if (ch1 != DUK_ASC_DOLLAR) {
 					goto repl_write;
 				}
-				left = r_end - r;
+				DUK_ASSERT(r <= r_end);
+				left = (duk_size_t) (r_end - r);
 
 				if (left <= 0) {
 					goto repl_write;
@@ -38271,9 +39535,9 @@
 					 * match codepoint encodings would have different lengths.
 					 */
 					/* XXX: charlen computed here, and also in char2byte helper. */
-					match_end_boff = duk_heap_strcache_offset_char2byte(thr,
-					                                                    h_input,
-					                                                    match_start_coff + (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_match));
+					match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr,
+					                                                                   h_input,
+					                                                                   match_start_coff + (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_match));
 
 					tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - match_end_boff);
 					DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + match_end_boff, tmp_sz);
@@ -38312,17 +39576,17 @@
 						DUK_ASSERT(is_regexp != 0);  /* match_caps == 0 without regexps */
 
 						/* regexp res_obj is at offset 4 */
-						duk_get_prop_index(ctx, 4, (duk_uarridx_t) capnum);
-						if (duk_is_string(ctx, -1)) {
+						duk_get_prop_index(thr, 4, (duk_uarridx_t) capnum);
+						if (duk_is_string(thr, -1)) {
 							duk_hstring *h_tmp_str;
 
-							h_tmp_str = duk_known_hstring(ctx, -1);
+							h_tmp_str = duk_known_hstring(thr, -1);
 
 							DUK_BW_WRITE_ENSURE_HSTRING(thr, bw, h_tmp_str);
 						} else {
 							/* undefined -> skip (replaced with empty) */
 						}
-						duk_pop(ctx);
+						duk_pop(thr);
 						r += capadv;
 						continue;
 					} else {
@@ -38342,7 +39606,7 @@
 			}  /* while repl */
 		}  /* if (is_repl_func) */
 
-		duk_pop(ctx);  /* pop regexp res_obj or match string */
+		duk_pop(thr);  /* pop regexp res_obj or match string */
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		if (!is_global) {
@@ -38357,9 +39621,9 @@
 	tmp_sz = (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff);
 	DUK_BW_WRITE_ENSURE_BYTES(thr, bw, DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff, tmp_sz);
 
-	DUK_ASSERT_TOP(ctx, 4);
+	DUK_ASSERT_TOP(thr, 4);
 	DUK_BW_COMPACT(thr, bw);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe if inputs are safe. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if inputs are safe. */
 	return 1;
 }
 
@@ -38371,8 +39635,7 @@
  * used so compiler doesn't complain).
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_hthread *thr) {
 	duk_hstring *h_input;
 	duk_hstring *h_sep;
 	duk_uint32_t limit;
@@ -38385,17 +39648,15 @@
 	duk_uint32_t match_start_boff, match_start_coff;
 	duk_uint32_t match_end_boff, match_end_coff;
 
-	DUK_UNREF(thr);
-
-	h_input = duk_push_this_coercible_to_string(ctx);
+	h_input = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h_input != NULL);
 
-	duk_push_array(ctx);
-
-	if (duk_is_undefined(ctx, 1)) {
+	duk_push_array(thr);
+
+	if (duk_is_undefined(thr, 1)) {
 		limit = 0xffffffffUL;
 	} else {
-		limit = duk_to_uint32(ctx, 1);
+		limit = duk_to_uint32(thr, 1);
 	}
 
 	if (limit == 0) {
@@ -38408,27 +39669,27 @@
 	 * which will use global-style matching even when the RegExp itself is non-global.
 	 */
 
-	if (duk_is_undefined(ctx, 0)) {
+	if (duk_is_undefined(thr, 0)) {
 		/* The spec algorithm first does "R = ToString(separator)" before checking
 		 * whether separator is undefined.  Since this is side effect free, we can
 		 * skip the ToString() here.
 		 */
-		duk_dup_2(ctx);
-		duk_put_prop_index(ctx, 3, 0);
+		duk_dup_2(thr);
+		duk_put_prop_index(thr, 3, 0);
 		return 1;
-	} else if (duk_get_hobject_with_class(ctx, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) {
+	} else if (duk_get_hobject_with_class(thr, 0, DUK_HOBJECT_CLASS_REGEXP) != NULL) {
 #if defined(DUK_USE_REGEXP_SUPPORT)
-		duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR);
-		duk_dup_0(ctx);
-		duk_new(ctx, 1);  /* [ ... RegExp val ] -> [ ... res ] */
-		duk_replace(ctx, 0);
+		duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR);
+		duk_dup_0(thr);
+		duk_new(thr, 1);  /* [ ... RegExp val ] -> [ ... res ] */
+		duk_replace(thr, 0);
 		/* lastIndex is initialized to zero by new RegExp() */
 		is_regexp = 1;
 #else
 		DUK_DCERROR_UNSUPPORTED(thr);
 #endif
 	} else {
-		duk_to_string(ctx, 0);
+		duk_to_string(thr, 0);
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		is_regexp = 0;
 #endif
@@ -38453,42 +39714,42 @@
 		 *  special variant which forces global-like behavior for matching.
 		 */
 
-		DUK_ASSERT_TOP(ctx, 4);
+		DUK_ASSERT_TOP(thr, 4);
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
 		if (is_regexp) {
-			duk_dup_0(ctx);
-			duk_dup_2(ctx);
+			duk_dup_0(thr);
+			duk_dup_2(thr);
 			duk_regexp_match_force_global(thr);  /* [ ... regexp input ] -> [ res_obj ] */
-			if (!duk_is_object(ctx, -1)) {
-				duk_pop(ctx);
+			if (!duk_is_object(thr, -1)) {
+				duk_pop(thr);
 				break;
 			}
 			matched = 1;
 
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INDEX);
-			DUK_ASSERT(duk_is_number(ctx, -1));
-			match_start_coff = duk_get_int(ctx, -1);
-			match_start_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);
-			duk_pop(ctx);
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
+			DUK_ASSERT(duk_is_number(thr, -1));
+			match_start_coff = duk_get_uint(thr, -1);
+			match_start_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_start_coff);
+			duk_pop(thr);
 
 			if (match_start_coff == DUK_HSTRING_GET_CHARLEN(h_input)) {
 				/* don't allow an empty match at the end of the string */
-				duk_pop(ctx);
+				duk_pop(thr);
 				break;
 			}
 
-			duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-			DUK_ASSERT(duk_is_number(ctx, -1));
-			match_end_coff = duk_get_int(ctx, -1);
-			match_end_boff = duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff);
-			duk_pop(ctx);
+			duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+			DUK_ASSERT(duk_is_number(thr, -1));
+			match_end_coff = duk_get_uint(thr, -1);
+			match_end_boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h_input, match_end_coff);
+			duk_pop(thr);
 
 			/* empty match -> bump and continue */
 			if (prev_match_end_boff == match_end_boff) {
-				duk_push_int(ctx, match_end_coff + 1);
-				duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-				duk_pop(ctx);
+				duk_push_uint(thr, (duk_uint_t) (match_end_coff + 1));
+				duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+				duk_pop(thr);
 				continue;
 			}
 		} else {
@@ -38503,7 +39764,7 @@
 			p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
 			p = p_start + prev_match_end_boff;
 
-			h_sep = duk_known_hstring(ctx, 0);  /* symbol already rejected above */
+			h_sep = duk_known_hstring(thr, 0);  /* symbol already rejected above */
 			q_start = DUK_HSTRING_GET_DATA(h_sep);
 			q_blen = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sep);
 			q_clen = (duk_size_t) DUK_HSTRING_GET_CHARLEN(h_sep);
@@ -38580,10 +39841,10 @@
 		                     (long) match_end_boff, (long) match_end_coff,
 		                     (long) prev_match_end_boff, (long) prev_match_end_coff));
 
-		duk_push_lstring(ctx,
+		duk_push_lstring(thr,
 		                 (const char *) (DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff),
 		                 (duk_size_t) (match_start_boff - prev_match_end_boff));
-		duk_put_prop_index(ctx, 3, arr_idx);
+		duk_put_prop_index(thr, 3, arr_idx);
 		arr_idx++;
 		if (arr_idx >= limit) {
 			goto hit_limit;
@@ -38593,18 +39854,18 @@
 		if (is_regexp) {
 			duk_size_t i, len;
 
-			len = duk_get_length(ctx, 4);
+			len = duk_get_length(thr, 4);
 			for (i = 1; i < len; i++) {
 				DUK_ASSERT(i <= DUK_UARRIDX_MAX);  /* cannot have >4G captures */
-				duk_get_prop_index(ctx, 4, (duk_uarridx_t) i);
-				duk_put_prop_index(ctx, 3, arr_idx);
+				duk_get_prop_index(thr, 4, (duk_uarridx_t) i);
+				duk_put_prop_index(thr, 3, arr_idx);
 				arr_idx++;
 				if (arr_idx >= limit) {
 					goto hit_limit;
 				}
 			}
 
-			duk_pop(ctx);
+			duk_pop(thr);
 			/* lastIndex already set up for next match */
 		} else {
 #else  /* DUK_USE_REGEXP_SUPPORT */
@@ -38629,10 +39890,10 @@
 		 *   b) empty input and no (zero size) match found (step 11)
 		 */
 
-		duk_push_lstring(ctx,
+		duk_push_lstring(thr,
 		                 (const char *) DUK_HSTRING_GET_DATA(h_input) + prev_match_end_boff,
 		                 (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h_input) - prev_match_end_boff));
-		duk_put_prop_index(ctx, 3, arr_idx);
+		duk_put_prop_index(thr, 3, arr_idx);
 		/* No arr_idx update or limit check */
 	}
 
@@ -38641,7 +39902,7 @@
  hit_limit:
 #if defined(DUK_USE_REGEXP_SUPPORT)
 	if (is_regexp) {
-		duk_pop(ctx);
+		duk_pop(thr);
 	}
 #endif
 
@@ -38653,7 +39914,7 @@
  */
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
-DUK_LOCAL void duk__to_regexp_helper(duk_context *ctx, duk_idx_t idx, duk_bool_t force_new) {
+DUK_LOCAL void duk__to_regexp_helper(duk_hthread *thr, duk_idx_t idx, duk_bool_t force_new) {
 	duk_hobject *h;
 
 	/* Shared helper for match() steps 3-4, search() steps 3-4. */
@@ -38664,24 +39925,22 @@
 		goto do_new;
 	}
 
-	h = duk_get_hobject_with_class(ctx, idx, DUK_HOBJECT_CLASS_REGEXP);
+	h = duk_get_hobject_with_class(thr, idx, DUK_HOBJECT_CLASS_REGEXP);
 	if (!h) {
 		goto do_new;
 	}
 	return;
 
  do_new:
-	duk_push_hobject_bidx(ctx, DUK_BIDX_REGEXP_CONSTRUCTOR);
-	duk_dup(ctx, idx);
-	duk_new(ctx, 1);  /* [ ... RegExp val ] -> [ ... res ] */
-	duk_replace(ctx, idx);
+	duk_push_hobject_bidx(thr, DUK_BIDX_REGEXP_CONSTRUCTOR);
+	duk_dup(thr, idx);
+	duk_new(thr, 1);  /* [ ... RegExp val ] -> [ ... res ] */
+	duk_replace(thr, idx);
 }
 #endif  /* DUK_USE_REGEXP_SUPPORT */
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
-
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_hthread *thr) {
 	/* Easiest way to implement the search required by the specification
 	 * is to do a RegExp test() with lastIndex forced to zero.  To avoid
 	 * side effects on the argument, "clone" the RegExp if a RegExp was
@@ -38692,9 +39951,9 @@
 	 * equivalent effect.
 	 */
 
-	DUK_ASSERT_TOP(ctx, 1);
-	(void) duk_push_this_coercible_to_string(ctx);  /* at index 1 */
-	duk__to_regexp_helper(ctx, 0 /*index*/, 1 /*force_new*/);
+	DUK_ASSERT_TOP(thr, 1);
+	(void) duk_push_this_coercible_to_string(thr);  /* at index 1 */
+	duk__to_regexp_helper(thr, 0 /*index*/, 1 /*force_new*/);
 
 	/* stack[0] = regexp
 	 * stack[1] = string
@@ -38704,34 +39963,33 @@
 	 * configurable and may have been changed.
 	 */
 
-	duk_dup_0(ctx);
-	duk_dup_1(ctx);  /* [ ... re_obj input ] */
+	duk_dup_0(thr);
+	duk_dup_1(thr);  /* [ ... re_obj input ] */
 	duk_regexp_match(thr);  /* -> [ ... res_obj ] */
 
-	if (!duk_is_object(ctx, -1)) {
-		duk_push_int(ctx, -1);
+	if (!duk_is_object(thr, -1)) {
+		duk_push_int(thr, -1);
 		return 1;
 	}
 
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INDEX);
-	DUK_ASSERT(duk_is_number(ctx, -1));
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INDEX);
+	DUK_ASSERT(duk_is_number(thr, -1));
 	return 1;
 }
 #endif  /* DUK_USE_REGEXP_SUPPORT */
 
 #if defined(DUK_USE_REGEXP_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_hthread *thr) {
 	duk_bool_t global;
 	duk_int_t prev_last_index;
 	duk_int_t this_index;
 	duk_int_t arr_idx;
 
-	DUK_ASSERT_TOP(ctx, 1);
-	(void) duk_push_this_coercible_to_string(ctx);
-	duk__to_regexp_helper(ctx, 0 /*index*/, 0 /*force_new*/);
-	global = duk_get_prop_stridx_boolean(ctx, 0, DUK_STRIDX_GLOBAL, NULL);
-	DUK_ASSERT_TOP(ctx, 2);
+	DUK_ASSERT_TOP(thr, 1);
+	(void) duk_push_this_coercible_to_string(thr);
+	duk__to_regexp_helper(thr, 0 /*index*/, 0 /*force_new*/);
+	global = duk_get_prop_stridx_boolean(thr, 0, DUK_STRIDX_GLOBAL, NULL);
+	DUK_ASSERT_TOP(thr, 2);
 
 	/* stack[0] = regexp
 	 * stack[1] = string
@@ -38746,9 +40004,9 @@
 
 	/* [ regexp string ] */
 
-	duk_push_int(ctx, 0);
-	duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-	duk_push_array(ctx);
+	duk_push_int(thr, 0);
+	duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+	duk_push_array(thr);
 
 	/* [ regexp string res_arr ] */
 
@@ -38756,61 +40014,61 @@
 	arr_idx = 0;
 
 	for (;;) {
-		DUK_ASSERT_TOP(ctx, 3);
-
-		duk_dup_0(ctx);
-		duk_dup_1(ctx);
+		DUK_ASSERT_TOP(thr, 3);
+
+		duk_dup_0(thr);
+		duk_dup_1(thr);
 		duk_regexp_match(thr);  /* -> [ ... regexp string ] -> [ ... res_obj ] */
 
-		if (!duk_is_object(ctx, -1)) {
-			duk_pop(ctx);
-			break;
-		}
-
-		duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
-		DUK_ASSERT(duk_is_number(ctx, -1));
-		this_index = duk_get_int(ctx, -1);
-		duk_pop(ctx);
+		if (!duk_is_object(thr, -1)) {
+			duk_pop(thr);
+			break;
+		}
+
+		duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
+		DUK_ASSERT(duk_is_number(thr, -1));
+		this_index = duk_get_int(thr, -1);
+		duk_pop(thr);
 
 		if (this_index == prev_last_index) {
 			this_index++;
-			duk_push_int(ctx, this_index);
-			duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_LAST_INDEX);
+			duk_push_int(thr, this_index);
+			duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LAST_INDEX);
 		}
 		prev_last_index = this_index;
 
-		duk_get_prop_index(ctx, -1, 0);  /* match string */
-		duk_put_prop_index(ctx, 2, arr_idx);
+		duk_get_prop_index(thr, -1, 0);  /* match string */
+		duk_put_prop_index(thr, 2, (duk_uarridx_t) arr_idx);
 		arr_idx++;
-		duk_pop(ctx);  /* res_obj */
+		duk_pop(thr);  /* res_obj */
 	}
 
 	if (arr_idx == 0) {
-		duk_push_null(ctx);
+		duk_push_null(thr);
 	}
 
 	return 1;  /* return 'res_arr' or 'null' */
 }
 #endif  /* DUK_USE_REGEXP_SUPPORT */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_concat(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_concat(duk_hthread *thr) {
 	/* duk_concat() coerces arguments with ToString() in correct order */
-	(void) duk_push_this_coercible_to_string(ctx);
-	duk_insert(ctx, 0);  /* this is relatively expensive */
-	duk_concat(ctx, duk_get_top(ctx));
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx) {
-	DUK_ASSERT_TOP(ctx, 0);
-	(void) duk_push_this_coercible_to_string(ctx);
-	duk_trim(ctx, 0);
-	DUK_ASSERT_TOP(ctx, 1);
+	(void) duk_push_this_coercible_to_string(thr);
+	duk_insert(thr, 0);  /* this is relatively expensive */
+	duk_concat(thr, duk_get_top(thr));
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_trim(duk_hthread *thr) {
+	DUK_ASSERT_TOP(thr, 0);
+	(void) duk_push_this_coercible_to_string(thr);
+	duk_trim(thr, 0);
+	DUK_ASSERT_TOP(thr, 1);
 	return 1;
 }
 
 #if defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_hthread *thr) {
 	duk_hstring *h_input;
 	duk_size_t input_blen;
 	duk_size_t result_len;
@@ -38825,8 +40083,8 @@
 	duk_uint8_t *p_end;
 #endif
 
-	DUK_ASSERT_TOP(ctx, 1);
-	h_input = duk_push_this_coercible_to_string(ctx);
+	DUK_ASSERT_TOP(thr, 1);
+	h_input = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h_input != NULL);
 	input_blen = DUK_HSTRING_GET_BYTELEN(h_input);
 
@@ -38836,11 +40094,11 @@
 	 * because duk_get_int() clamps it to DUK_INT_MIN which gets rejected
 	 * as a negative value (regardless of input string length).
 	 */
-	d = duk_to_number(ctx, 0);
+	d = duk_to_number(thr, 0);
 	if (duk_double_is_posinf(d)) {
 		goto fail_range;
 	}
-	count_signed = duk_get_int(ctx, 0);
+	count_signed = duk_get_int(thr, 0);
 	if (count_signed < 0) {
 		goto fail_range;
 	}
@@ -38853,7 +40111,7 @@
 	}
 
 	/* Temporary fixed buffer, later converted to string. */
-	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(ctx, result_len);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, result_len);
 	src = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
 
 #if defined(DUK_USE_PREFER_SIZE)
@@ -38899,15 +40157,15 @@
 	 * intern table (they are not in heap_allocated).
 	 */
 
-	duk_buffer_to_string(ctx, -1);  /* Safe if input is safe. */
+	duk_buffer_to_string(thr, -1);  /* Safe if input is safe. */
 	return 1;
 
  fail_range:
-	DUK_DCERROR_RANGE_INVALID_ARGS((duk_hthread *) ctx);
+	DUK_DCERROR_RANGE_INVALID_ARGS(thr);
 }
 #endif  /* DUK_USE_ES6 */
 
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_hthread *thr) {
 	duk_hstring *h1;
 	duk_hstring *h2;
 	duk_size_t h1_len, h2_len, prefix_len;
@@ -38926,10 +40184,10 @@
 
 	/* XXX: could share code with duk_js_ops.c, duk_js_compare_helper */
 
-	h1 = duk_push_this_coercible_to_string(ctx);
+	h1 = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h1 != NULL);
 
-	h2 = duk_to_hstring(ctx, 0);
+	h2 = duk_to_hstring(thr, 0);
 	DUK_ASSERT(h2 != NULL);
 
 	h1_len = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1);
@@ -38961,12 +40219,12 @@
 	goto done;
 
  done:
-	duk_push_int(ctx, (duk_int_t) ret);
+	duk_push_int(thr, (duk_int_t) ret);
 	return 1;
 }
 
 #if defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_hthread *thr) {
 	duk_int_t magic;
 	duk_hstring *h;
 	duk_hstring *h_search;
@@ -38974,18 +40232,18 @@
 	const duk_uint8_t *p_cmp_start;
 	duk_bool_t result;
 
-	h = duk_push_this_coercible_to_string(ctx);
+	h = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h != NULL);
 
-	h_search = duk__str_tostring_notregexp(ctx, 0);
+	h_search = duk__str_tostring_notregexp(thr, 0);
 	DUK_ASSERT(h_search != NULL);
 
-	magic = duk_get_current_magic(ctx);
+	magic = duk_get_current_magic(thr);
 
 	p_cmp_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
 	blen_search = DUK_HSTRING_GET_BYTELEN(h_search);
 
-	if (duk_is_undefined(ctx, 1)) {
+	if (duk_is_undefined(thr, 1)) {
 		if (magic) {
 			p_cmp_start += DUK_HSTRING_GET_BYTELEN(h) - blen_search;
 		} else {
@@ -38997,7 +40255,7 @@
 
 		DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= DUK_INT_MAX);
 		len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
-		pos = duk_to_int_clamped(ctx, 1, 0, len);
+		pos = duk_to_int_clamped(thr, 1, 0, len);
 		DUK_ASSERT(pos >= 0 && pos <= len);
 
 		if (magic) {
@@ -39005,7 +40263,7 @@
 		}
 		DUK_ASSERT(pos >= 0 && pos <= len);
 
-		p_cmp_start += duk_heap_strcache_offset_char2byte((duk_hthread *) ctx, h, pos);
+		p_cmp_start += duk_heap_strcache_offset_char2byte(thr, h, (duk_uint_fast32_t) pos);
 	}
 
 	/* The main comparison can be done using a memcmp() rather than
@@ -39017,7 +40275,7 @@
 
 	result = 0;
 	if (p_cmp_start >= DUK_HSTRING_GET_DATA(h) &&
-	    p_cmp_start - (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h) + blen_search <= DUK_HSTRING_GET_BYTELEN(h)) {
+	    (duk_size_t) (p_cmp_start - (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h)) + blen_search <= DUK_HSTRING_GET_BYTELEN(h)) {
 		if (DUK_MEMCMP((const void *) p_cmp_start,
 		               (const void *) DUK_HSTRING_GET_DATA(h_search),
 		               (size_t) blen_search) == 0) {
@@ -39025,30 +40283,30 @@
 		}
 	}
 
-	duk_push_boolean(ctx, result);
+	duk_push_boolean(thr, result);
 	return 1;
 }
 #endif  /* DUK_USE_ES6 */
 
 #if defined(DUK_USE_ES6)
-DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes(duk_hthread *thr) {
 	duk_hstring *h;
 	duk_hstring *h_search;
 	duk_int_t len;
 	duk_int_t pos;
 
-	h = duk_push_this_coercible_to_string(ctx);
+	h = duk_push_this_coercible_to_string(thr);
 	DUK_ASSERT(h != NULL);
 
-	h_search = duk__str_tostring_notregexp(ctx, 0);
+	h_search = duk__str_tostring_notregexp(thr, 0);
 	DUK_ASSERT(h_search != NULL);
 
 	len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h);
-	pos = duk_to_int_clamped(ctx, 1, 0, len);
+	pos = duk_to_int_clamped(thr, 1, 0, len);
 	DUK_ASSERT(pos >= 0 && pos <= len);
 
-	pos = duk__str_search_shared(ctx, h, h_search, pos, 0 /*backwards*/);
-	duk_push_boolean(ctx, pos >= 0);
+	pos = duk__str_search_shared(thr, h, h_search, pos, 0 /*backwards*/);
+	duk_push_boolean(thr, pos >= 0);
 	return 1;
 }
 #endif  /* DUK_USE_ES6 */
@@ -39066,18 +40324,15 @@
  *  Constructor
  */
 
-DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx) {
-	duk_hthread *thr;
+DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_hthread *thr) {
 	const duk_uint8_t *desc;
 	duk_size_t len;
 	duk_uint8_t *buf;
 	duk_uint8_t *p;
 	duk_int_t magic;
 
-	thr = (duk_hthread *) ctx;
-
-	magic = duk_get_current_magic(ctx);
-	if (duk_is_undefined(ctx, 0) && (magic == 0)) {
+	magic = duk_get_current_magic(thr);
+	if (duk_is_undefined(thr, 0) && (magic == 0)) {
 		/* Symbol() accepts undefined and empty string, but they are
 		 * treated differently.
 		 */
@@ -39085,7 +40340,7 @@
 		len = 0;
 	} else {
 		/* Symbol.for() coerces undefined to 'undefined' */
-		desc = (const duk_uint8_t *) duk_to_lstring(ctx, 0, &len);
+		desc = (const duk_uint8_t *) duk_to_lstring(thr, 0, &len);
 	}
 
 	/* Maximum symbol data length:
@@ -39095,7 +40350,7 @@
 	 *   +17   autogenerated unique suffix: 'ffffffff-ffffffff' is longest
 	 *   +1    0xff after unique suffix for symbols with undefined description
 	 */
-	buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, 1 + len + 1 + 17 + 1);
+	buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, 1 + len + 1 + 17 + 1);
 	p = buf + 1;
 	DUK_ASSERT(desc != NULL || len == 0);  /* may be NULL if len is 0 */
 	DUK_MEMCPY((void *) p, (const void *) desc, len);
@@ -39124,12 +40379,12 @@
 		buf[0] = 0x80;
 	}
 
-	duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (p - buf));
-	DUK_DDD(DUK_DDDPRINT("created symbol: %!T", duk_get_tval(ctx, -1)));
-	return 1;
-}
-
-DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_context *ctx, duk_tval *tv_arg) {
+	duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
+	DUK_DDD(DUK_DDDPRINT("created symbol: %!T", duk_get_tval(thr, -1)));
+	return 1;
+}
+
+DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_hthread *thr, duk_tval *tv_arg) {
 	duk_tval *tv;
 	duk_tval tv_val;
 	duk_hobject *h_obj;
@@ -39137,15 +40392,15 @@
 
 	DUK_ASSERT(tv_arg != NULL);
 
-	/* XXX: add internal helper: duk_auto_unbox_tval(ctx, tv, mask); */
-	/* XXX: add internal helper: duk_auto_unbox(ctx, tv, idx); */
+	/* XXX: add internal helper: duk_auto_unbox_tval(thr, tv, mask); */
+	/* XXX: add internal helper: duk_auto_unbox(thr, tv, idx); */
 
 	tv = tv_arg;
 	if (DUK_TVAL_IS_OBJECT(tv)) {
 		h_obj = DUK_TVAL_GET_OBJECT(tv);
 		DUK_ASSERT(h_obj != NULL);
 		if (DUK_HOBJECT_GET_CLASS_NUMBER(h_obj) == DUK_HOBJECT_CLASS_SYMBOL) {
-			if (!duk_hobject_get_internal_value(((duk_hthread *) ctx)->heap, h_obj, &tv_val)) {
+			if (!duk_hobject_get_internal_value(thr->heap, h_obj, &tv_val)) {
 				return NULL;
 			}
 			tv = &tv_val;
@@ -39168,32 +40423,32 @@
 	return h_str;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_hthread *thr) {
 	duk_hstring *h_str;
 
-	h_str = duk__auto_unbox_symbol(ctx, DUK_HTHREAD_THIS_PTR((duk_hthread *) ctx));
+	h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
 	if (h_str == NULL) {
 		return DUK_RET_TYPE_ERROR;
 	}
 
-	if (duk_get_current_magic(ctx) == 0) {
+	if (duk_get_current_magic(thr) == 0) {
 		/* .toString() */
-		duk_push_symbol_descriptive_string(ctx, h_str);
+		duk_push_symbol_descriptive_string(thr, h_str);
 	} else {
 		/* .valueOf() */
-		duk_push_hstring(ctx, h_str);
-	}
-	return 1;
-}
-
-DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx) {
+		duk_push_hstring(thr, h_str);
+	}
+	return 1;
+}
+
+DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_hthread *thr) {
 	duk_hstring *h;
 	const duk_uint8_t *p;
 
 	/* Argument must be a symbol but not checked here.  The initial byte
 	 * check will catch non-symbol strings.
 	 */
-	h = duk_require_hstring(ctx, 0);
+	h = duk_require_hstring(thr, 0);
 	DUK_ASSERT(h != NULL);
 
 	p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
@@ -39204,9 +40459,9 @@
 	 */
 	if (p[0] == 0x80) {
 		/* Global symbol, return its key (bytes just after the initial byte). */
-		duk_push_lstring(ctx, (const char *) (p + 1), DUK_HSTRING_GET_BYTELEN(h) - 1);
+		duk_push_lstring(thr, (const char *) (p + 1), (duk_size_t) (DUK_HSTRING_GET_BYTELEN(h) - 1));
 		return 1;
-	} else if (p[0] == 0x81 || p[0] == 0xff) {
+	} else if (p[0] == 0x81 || p[0] == 0x82 || p[0] == 0xff) {
 		/* Local symbol or hidden symbol, return undefined. */
 		return 0;
 	}
@@ -39215,14 +40470,14 @@
 	return DUK_RET_TYPE_ERROR;
 }
 
-DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_hthread *thr) {
 	duk_hstring *h_str;
 
-	h_str = duk__auto_unbox_symbol(ctx, DUK_HTHREAD_THIS_PTR((duk_hthread *) ctx));
+	h_str = duk__auto_unbox_symbol(thr, DUK_HTHREAD_THIS_PTR(thr));
 	if (h_str == NULL) {
 		return DUK_RET_TYPE_ERROR;
 	}
-	duk_push_hstring(ctx, h_str);
+	duk_push_hstring(thr, h_str);
 	return 1;
 }
 
@@ -39239,7 +40494,7 @@
  */
 
 #if defined(DUK_USE_COROUTINE_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_hthread *thr) {
 	duk_hthread *new_thr;
 	duk_hobject *func;
 
@@ -39248,18 +40503,18 @@
 	 * Resume will reject such functions in any case.
 	 */
 	/* XXX: need a duk_require_func_promote_lfunc() */
-	func = duk_require_hobject_promote_lfunc(ctx, 0);
+	func = duk_require_hobject_promote_lfunc(thr, 0);
 	DUK_ASSERT(func != NULL);
-	duk_require_callable(ctx, 0);
-
-	duk_push_thread(ctx);
-	new_thr = (duk_hthread *) duk_known_hobject(ctx, -1);
+	duk_require_callable(thr, 0);
+
+	duk_push_thread(thr);
+	new_thr = (duk_hthread *) duk_known_hobject(thr, -1);
 	new_thr->state = DUK_HTHREAD_STATE_INACTIVE;
 
 	/* push initial function call to new thread stack; this is
 	 * picked up by resume().
 	 */
-	duk_push_hobject((duk_context *) new_thr, func);
+	duk_push_hobject(new_thr, func);
 
 	return 1;  /* return thread */
 }
@@ -39281,23 +40536,23 @@
  */
 
 #if defined(DUK_USE_COROUTINE_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) {
+DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_hthread *ctx) {
 	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_hthread *thr_resume;
 	duk_hobject *caller_func;
-	duk_small_int_t is_error;
+	duk_small_uint_t is_error;
 
 	DUK_DDD(DUK_DDDPRINT("Duktape.Thread.resume(): thread=%!T, value=%!T, is_error=%!T",
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1),
-	                     (duk_tval *) duk_get_tval(ctx, 2)));
+	                     (duk_tval *) duk_get_tval(thr, 0),
+	                     (duk_tval *) duk_get_tval(thr, 1),
+	                     (duk_tval *) duk_get_tval(thr, 2)));
 
 	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
 	DUK_ASSERT(thr->heap->curr_thread == thr);
 
-	thr_resume = duk_require_hthread(ctx, 0);
-	is_error = (duk_small_int_t) duk_to_boolean(ctx, 2);
-	duk_set_top(ctx, 2);
+	thr_resume = duk_require_hthread(thr, 0);
+	is_error = (duk_small_uint_t) duk_to_boolean(thr, 2);
+	duk_set_top(thr, 2);
 
 	/* [ thread value ] */
 
@@ -39310,11 +40565,12 @@
 		goto state_error;
 	}
 	DUK_ASSERT(thr->callstack_curr != NULL);
+	DUK_ASSERT(thr->callstack_curr->parent != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);  /* us */
 	DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
-	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL);  /* caller */
-
-	caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1);
+	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL);  /* caller */
+
+	caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent);
 	if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) {
 		DUK_DD(DUK_DDPRINT("resume state invalid: caller must be Ecmascript code"));
 		goto state_error;
@@ -39354,13 +40610,13 @@
 			goto state_error;
 		}
 
-		duk_push_tval(ctx, DUK_GET_TVAL_NEGIDX((duk_context *) thr_resume, -1));
-		duk_resolve_nonbound_function(ctx);
-		h_fun = duk_require_hobject(ctx, -1);  /* reject lightfuncs on purpose */
+		duk_push_tval(thr, DUK_GET_TVAL_NEGIDX(thr_resume, -1));
+		duk_resolve_nonbound_function(thr);
+		h_fun = duk_require_hobject(thr, -1);  /* reject lightfuncs on purpose */
 		if (!DUK_HOBJECT_IS_CALLABLE(h_fun) || !DUK_HOBJECT_IS_COMPFUNC(h_fun)) {
 			goto state_error;
 		}
-		duk_pop(ctx);
+		duk_pop(thr);
 	}
 
 	/*
@@ -39373,7 +40629,7 @@
 
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
 	if (is_error) {
-		DUK_ASSERT_TOP(ctx, 2);  /* value (error) is at stack top */
+		DUK_ASSERT_TOP(thr, 2);  /* value (error) is at stack top */
 		duk_err_augment_error_throw(thr);  /* in resumer's context */
 	}
 #endif
@@ -39381,16 +40637,16 @@
 #if defined(DUK_USE_DEBUG)
 	if (is_error) {
 		DUK_DDD(DUK_DDDPRINT("RESUME ERROR: thread=%!T, value=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, 0),
-		                     (duk_tval *) duk_get_tval(ctx, 1)));
+		                     (duk_tval *) duk_get_tval(thr, 0),
+		                     (duk_tval *) duk_get_tval(thr, 1)));
 	} else if (thr_resume->state == DUK_HTHREAD_STATE_YIELDED) {
 		DUK_DDD(DUK_DDDPRINT("RESUME NORMAL: thread=%!T, value=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, 0),
-		                     (duk_tval *) duk_get_tval(ctx, 1)));
+		                     (duk_tval *) duk_get_tval(thr, 0),
+		                     (duk_tval *) duk_get_tval(thr, 1)));
 	} else {
 		DUK_DDD(DUK_DDDPRINT("RESUME INITIAL: thread=%!T, value=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, 0),
-		                     (duk_tval *) duk_get_tval(ctx, 1)));
+		                     (duk_tval *) duk_get_tval(thr, 0),
+		                     (duk_tval *) duk_get_tval(thr, 1)));
 	}
 #endif
 
@@ -39433,20 +40689,19 @@
  */
 
 #if defined(DUK_USE_COROUTINE_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_hthread *thr) {
 	duk_hobject *caller_func;
-	duk_small_int_t is_error;
+	duk_small_uint_t is_error;
 
 	DUK_DDD(DUK_DDDPRINT("Duktape.Thread.yield(): value=%!T, is_error=%!T",
-	                     (duk_tval *) duk_get_tval(ctx, 0),
-	                     (duk_tval *) duk_get_tval(ctx, 1)));
+	                     (duk_tval *) duk_get_tval(thr, 0),
+	                     (duk_tval *) duk_get_tval(thr, 1)));
 
 	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
 	DUK_ASSERT(thr->heap->curr_thread == thr);
 
-	is_error = (duk_small_int_t) duk_to_boolean(ctx, 1);
-	duk_set_top(ctx, 1);
+	is_error = (duk_small_uint_t) duk_to_boolean(thr, 1);
+	duk_set_top(thr, 1);
 
 	/* [ value ] */
 
@@ -39465,11 +40720,12 @@
 		goto state_error;
 	}
 	DUK_ASSERT(thr->callstack_curr != NULL);
+	DUK_ASSERT(thr->callstack_curr->parent != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);  /* us */
 	DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
-	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL);  /* caller */
-
-	caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1);
+	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL);  /* caller */
+
+	caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr->parent);
 	if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) {
 		DUK_DD(DUK_DDPRINT("yield state invalid: caller must be Ecmascript code"));
 		goto state_error;
@@ -39492,7 +40748,7 @@
 
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
 	if (is_error) {
-		DUK_ASSERT_TOP(ctx, 1);  /* value (error) is at stack top */
+		DUK_ASSERT_TOP(thr, 1);  /* value (error) is at stack top */
 		duk_err_augment_error_throw(thr);  /* in yielder's context */
 	}
 #endif
@@ -39500,10 +40756,10 @@
 #if defined(DUK_USE_DEBUG)
 	if (is_error) {
 		DUK_DDD(DUK_DDDPRINT("YIELD ERROR: value=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, 0)));
+		                     (duk_tval *) duk_get_tval(thr, 0)));
 	} else {
 		DUK_DDD(DUK_DDDPRINT("YIELD NORMAL: value=%!T",
-		                     (duk_tval *) duk_get_tval(ctx, 0)));
+		                     (duk_tval *) duk_get_tval(thr, 0)));
 	}
 #endif
 
@@ -39534,8 +40790,8 @@
 #endif
 
 #if defined(DUK_USE_COROUTINE_SUPPORT)
-DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_context *ctx) {
-	duk_push_current_thread(ctx);
+DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_hthread *thr) {
+	duk_push_current_thread(thr);
 	return 1;
 }
 #endif
@@ -39546,8 +40802,8 @@
 
 /* #include duk_internal.h -> already included */
 
-DUK_INTERNAL duk_ret_t duk_bi_type_error_thrower(duk_context *ctx) {
-	DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx);
+DUK_INTERNAL duk_ret_t duk_bi_type_error_thrower(duk_hthread *thr) {
+	DUK_DCERROR_TYPE_INVALID_ARGS(thr);
 }
 #line 1 "duk_debug_fixedbuffer.c"
 /*
@@ -39601,7 +40857,7 @@
 			}
 		} else {
 			/* normal */
-			fb->offset += res;
+			fb->offset += (duk_size_t) res;
 		}
 	}
 	va_end(ap);
@@ -39695,9 +40951,9 @@
 #define DUK__LOOP_STACK_DEPTH  256
 
 /* must match bytecode defines now; build autogenerate? */
-DUK_LOCAL const char *duk__bc_optab[256] = {
-	"LDREG", "STREG", "LDCONST", "LDINT", "LDINTX", "LDTHIS", "LDUNDEF", "LDNULL",
-	"LDTRUE", "LDFALSE", "BNOT", "LNOT", "UNM", "UNP", "TYPEOF", "TYPEOFID",
+DUK_LOCAL const char * const duk__bc_optab[256] = {
+	"LDREG", "STREG", "JUMP", "LDCONST", "LDINT", "LDINTX", "LDTHIS", "LDUNDEF",
+	"LDNULL", "LDTRUE", "LDFALSE", "GETVAR", "BNOT", "LNOT", "UNM", "UNP",
 	"EQ_RR", "EQ_CR", "EQ_RC", "EQ_CC", "NEQ_RR", "NEQ_CR", "NEQ_RC", "NEQ_CC",
 	"SEQ_RR", "SEQ_CR", "SEQ_RC", "SEQ_CC", "SNEQ_RR", "SNEQ_CR", "SNEQ_RC", "SNEQ_CC",
 
@@ -39707,28 +40963,28 @@
 	"SUB_RR", "SUB_CR", "SUB_RC", "SUB_CC", "MUL_RR", "MUL_CR", "MUL_RC", "MUL_CC",
 
 	"DIV_RR", "DIV_CR", "DIV_RC", "DIV_CC", "MOD_RR", "MOD_CR", "MOD_RC", "MOD_CC",
-	"BAND_RR", "BAND_CR", "BAND_RC", "BAND_CC", "BOR_RR", "BOR_CR", "BOR_RC", "BOR_CC",
-	"BXOR_RR", "BXOR_CR", "BXOR_RC", "BXOR_CC", "BASL_RR", "BASL_CR", "BASL_RC", "BASL_CC",
-	"BLSR_RR", "BLSR_CR", "BLSR_RC", "BLSR_CC", "BASR_RR", "BASR_CR", "BASR_RC", "BASR_CC",
-
-	"INSTOF_RR", "INSTOF_CR", "INSTOF_RC", "INSTOF_CC", "IN_RR", "IN_CR", "IN_RC", "IN_CC",
+	"EXP_RR", "EXP_CR", "EXP_RC", "EXP_CC", "BAND_RR", "BAND_CR", "BAND_RC", "BAND_CC",
+	"BOR_RR", "BOR_CR", "BOR_RC", "BOR_CC", "BXOR_RR", "BXOR_CR", "BXOR_RC", "BXOR_CC",
+	"BASL_RR", "BASL_CR", "BASL_RC", "BASL_CC", "BLSR_RR", "BLSR_CR", "BLSR_RC", "BLSR_CC",
+
+	"BASR_RR", "BASR_CR", "BASR_RC", "BASR_CC", "INSTOF_RR", "INSTOF_CR", "INSTOF_RC", "INSTOF_CC",
+	"IN_RR", "IN_CR", "IN_RC", "IN_CC", "GETPROP_RR", "GETPROP_CR", "GETPROP_RC", "GETPROP_CC",
+	"PUTPROP_RR", "PUTPROP_CR", "PUTPROP_RC", "PUTPROP_CC", "DELPROP_RR", "DELPROP_CR", "DELPROP_RC", "DELPROP_CC",
 	"PREINCR", "PREDECR", "POSTINCR", "POSTDECR", "PREINCV", "PREDECV", "POSTINCV", "POSTDECV",
+
 	"PREINCP_RR", "PREINCP_CR", "PREINCP_RC", "PREINCP_CC", "PREDECP_RR", "PREDECP_CR", "PREDECP_RC", "PREDECP_CC",
 	"POSTINCP_RR", "POSTINCP_CR", "POSTINCP_RC", "POSTINCP_CC", "POSTDECP_RR", "POSTDECP_CR", "POSTDECP_RC", "POSTDECP_CC",
-
-	"GETPROP_RR", "GETPROP_CR", "GETPROP_RC", "GETPROP_CC", "PUTPROP_RR", "PUTPROP_CR", "PUTPROP_RC", "PUTPROP_CC",
-	"DELPROP_RR", "DELPROP_CR", "DELPROP_RC", "DELPROP_CC", "DECLVAR_RR", "DECLVAR_CR", "DECLVAR_RC", "DECLVAR_CC",
-	"REGEXP_RR", "REGEXP_RC", "REGEXP_CR", "REGEXP_CC", "CSVAR_RR", "CSVAR_CR", "CSVAR_RC", "CSVAR_CC",
-	"CLOSURE", "GETVAR", "PUTVAR", "DELVAR", "JUMP", "RETREG", "RETUNDEF", "RETCONST",
+	"DECLVAR_RR", "DECLVAR_CR", "DECLVAR_RC", "DECLVAR_CC", "REGEXP_RR", "REGEXP_RC", "REGEXP_CR", "REGEXP_CC",
+	"CLOSURE", "TYPEOF", "TYPEOFID", "PUTVAR", "DELVAR", "RETREG", "RETUNDEF", "RETCONST",
 
 	"RETCONSTN", "LABEL", "ENDLABEL", "BREAK", "CONTINUE", "TRYCATCH", "ENDTRY", "ENDCATCH",
-	"ENDFIN", "THROW", "CSREG", "EVALCALL", "CALL", "TAILCALL", "NEW", "NEWOBJ",
-	"NEWARR", "MPUTOBJ", "MPUTOBJI", "INITSET", "INITGET", "MPUTARR", "MPUTARRI", "SETALEN",
-	"INITENUM", "NEXTENUM", "INVLHS", "DEBUGGER", "NOP", "INVALID", "UNUSED190", "UNUSED191",
-
-	"UNUSED192", "UNUSED193", "UNUSED194", "UNUSED195", "UNUSED196", "UNUSED197", "UNUSED198", "UNUSED199",
-	"UNUSED200", "UNUSED201", "UNUSED202", "UNUSED203", "UNUSED204", "UNUSED205", "UNUSED206", "UNUSED207",
-	"UNUSED208", "UNUSED209", "UNUSED210", "UNUSED211", "UNUSED212", "UNUSED213", "UNUSED214", "UNUSED215",
+	"ENDFIN", "THROW", "INVLHS", "CSREG", "CSVAR_RR", "CSVAR_CR", "CSVAR_RC", "CSVAR_CC",
+	"CALL0", "CALL1", "CALL2", "CALL3", "CALL4", "CALL5", "CALL6", "CALL7",
+	"CALL8", "CALL9", "CALL10", "CALL11", "CALL12", "CALL13", "CALL14", "CALL15",
+
+	"NEWOBJ", "NEWARR", "MPUTOBJ", "MPUTOBJI", "INITSET", "INITGET", "MPUTARR", "MPUTARRI",
+	"SETALEN", "INITENUM", "NEXTENUM", "NEWTARGET", "DEBUGGER", "NOP", "INVALID", "UNUSED207",
+	"GETPROPC_RR", "GETPROPC_CR", "GETPROPC_RC", "GETPROPC_CC", "UNUSED212", "UNUSED213", "UNUSED214", "UNUSED215",
 	"UNUSED216", "UNUSED217", "UNUSED218", "UNUSED219", "UNUSED220", "UNUSED221", "UNUSED222", "UNUSED223",
 
 	"UNUSED224", "UNUSED225", "UNUSED226", "UNUSED227", "UNUSED228", "UNUSED229", "UNUSED230", "UNUSED231",
@@ -39883,9 +41139,9 @@
 	p_end = p + DUK_HSTRING_GET_BYTELEN(h);
 
 	if (p_end > p && p[0] == DUK_ASC_UNDERSCORE) {
-		/* if property key begins with underscore, encode it with
+		/* If property key begins with underscore, encode it with
 		 * forced quotes (e.g. "_Foo") to distinguish it from encoded
-		 * internal properties (e.g. \xffBar -> _Bar).
+		 * internal properties (e.g. \x82Bar -> _Bar).
 		 */
 		quotes = 1;
 	}
@@ -39903,9 +41159,9 @@
 			duk_fb_sprintf(fb, "\\\"");
 		} else if (ch >= 0x20 && ch <= 0x7e) {
 			duk_fb_put_byte(fb, ch);
-		} else if (ch == 0xff && !quotes) {
-			/* encode \xffBar as _Bar if no quotes are applied, this is for
-			 * readable internal keys.
+		} else if (ch == 0x82 && !quotes) {
+			/* encode \x82Bar as _Bar if no quotes are
+			 * applied, this is for readable internal keys.
 			 */
 			duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_UNDERSCORE);
 		} else {
@@ -40098,9 +41354,6 @@
 		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");
 		}
@@ -40137,7 +41390,7 @@
 		duk_hdecenv *e = (duk_hdecenv *) h;
 		DUK__COMMA(); duk_fb_sprintf(fb, "__thread:"); duk__print_hobject(st, (duk_hobject *) e->thread);
 		DUK__COMMA(); duk_fb_sprintf(fb, "__varmap:"); duk__print_hobject(st, (duk_hobject *) e->varmap);
-		DUK__COMMA(); duk_fb_sprintf(fb, "__regbase:%ld", (long) e->regbase);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__regbase_byteoff:%ld", (long) e->regbase_byteoff);
 	} else if (st->internal && DUK_HOBJECT_IS_OBJENV(h)) {
 		duk_hobjenv *e = (duk_hobjenv *) h;
 		DUK__COMMA(); duk_fb_sprintf(fb, "__target:"); duk__print_hobject(st, (duk_hobject *) e->target);
@@ -40154,23 +41407,35 @@
 		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_PROXY(h)) {
+		duk_hproxy *p = (duk_hproxy *) h;
+		DUK__COMMA(); duk_fb_sprintf(fb, "__target:");
+		duk__print_hobject(st, p->target);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__handler:");
+		duk__print_hobject(st, p->handler);
 	} else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) {
 		duk_hthread *t = (duk_hthread *) h;
+		DUK__COMMA(); duk_fb_sprintf(fb, "__ptr_curr_pc:%p", (void *) t->ptr_curr_pc);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__heap:%p", (void *) t->heap);
 		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_alloc_end:%p/%ld", (void *) t->valstack_alloc_end, (long) (t->valstack_alloc_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, "__callstack_curr:%p", (void *) t->callstack_curr);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_top:%ld", (long) t->callstack_top);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_preventcount:%ld", (long) t->callstack_preventcount);
 		DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__compile_ctx:%p", (void *) t->compile_ctx);
+#if defined(DUK_USE_INTERRUPT_COUNTER)
+		DUK__COMMA(); duk_fb_sprintf(fb, "__interrupt_counter:%ld", (long) t->interrupt_counter);
+		DUK__COMMA(); duk_fb_sprintf(fb, "__interrupt_init:%ld", (long) t->interrupt_init);
+#endif
+
 		/* XXX: print built-ins array? */
 
 	}
@@ -40377,7 +41642,7 @@
 	case DUK_TAG_FASTINT:
 		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
 		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-		duk_fb_sprintf(fb, "%.18gF", (double) DUK_TVAL_GET_NUMBER(tv));
+		duk_fb_sprintf(fb, "%.18g_F", (double) DUK_TVAL_GET_NUMBER(tv));
 		break;
 #endif
 	default: {
@@ -40404,8 +41669,8 @@
 	/* XXX: option to fix opcode length so it lines up nicely */
 
 	if (op == DUK_OP_JUMP) {
-		duk_int_t diff1 = DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS;  /* from next pc */
-		duk_int_t diff2 = diff1 + 1;                            /* from curr pc */
+		duk_int_t diff1 = (duk_int_t) (DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS);  /* from next pc */
+		duk_int_t diff2 = diff1 + 1;                                          /* from curr pc */
 
 		duk_fb_sprintf(fb, "%s %ld (to pc%c%ld)",
 		               (const char *) op_name, (long) diff1,
@@ -40647,7 +41912,7 @@
 #else
 		ch = fptr[fptr_size - 1 - i];
 #endif
-		p += DUK_SNPRINTF((char *) p, left, "%02lx", (unsigned long) ch);
+		p += DUK_SNPRINTF((char *) p, (duk_size_t) left, "%02lx", (unsigned long) ch);
 	}
 }
 
@@ -40669,6 +41934,24 @@
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 
 /*
+ *  Assert helpers
+ */
+
+#if defined(DUK_USE_ASSERTIONS)
+#define DUK__DBG_TPORT_ENTER() do { \
+		DUK_ASSERT(heap->dbg_calling_transport == 0); \
+		heap->dbg_calling_transport = 1; \
+	} while (0)
+#define DUK__DBG_TPORT_EXIT() do { \
+		DUK_ASSERT(heap->dbg_calling_transport == 1); \
+		heap->dbg_calling_transport = 0; \
+	} while (0)
+#else
+#define DUK__DBG_TPORT_ENTER() do {} while (0)
+#define DUK__DBG_TPORT_EXIT() do {} while (0)
+#endif
+
+/*
  *  Helper structs
  */
 
@@ -40730,10 +42013,9 @@
 	/* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */
 	heap->dbg_state_dirty = 0;
 	heap->dbg_force_restart = 0;
-	heap->dbg_step_type = 0;
-	heap->dbg_step_thread = NULL;
-	heap->dbg_step_csindex = 0;
-	heap->dbg_step_startline = 0;
+	heap->dbg_pause_flags = 0;
+	heap->dbg_pause_act = NULL;
+	heap->dbg_pause_startline = 0;
 	heap->dbg_have_next_byte = 0;
 	duk_debug_clear_paused(heap);  /* XXX: some overlap with field inits above */
 	heap->dbg_state_dirty = 0;     /* XXX: clear_paused sets dirty; rework? */
@@ -40752,14 +42034,12 @@
 	duk_debug_detached_function detached_cb;
 	void *detached_udata;
 	duk_hthread *thr;
-	duk_context *ctx;
 
 	thr = heap->heap_thread;
 	if (thr == NULL) {
 		DUK_ASSERT(heap->dbg_detached_cb == NULL);
 		return;
 	}
-	ctx = (duk_context *) thr;
 
 	/* Safe to call multiple times. */
 
@@ -40774,7 +42054,7 @@
 		 * inside the callback.
 		 */
 		DUK_D(DUK_DPRINT("detached during message loop, delayed call to detached_cb"));
-		detached_cb(ctx, detached_udata);
+		detached_cb(thr, detached_udata);
 	}
 
 	heap->dbg_detaching = 0;
@@ -40807,11 +42087,40 @@
 }
 
 /*
+ *  Pause handling
+ */
+
+DUK_LOCAL void duk__debug_set_pause_state(duk_hthread *thr, duk_heap *heap, duk_small_uint_t pause_flags) {
+	duk_uint_fast32_t line;
+
+	line = duk_debug_curr_line(thr);
+	if (line == 0) {
+		/* No line info for current function. */
+		duk_small_uint_t updated_flags;
+
+		updated_flags = pause_flags & ~(DUK_PAUSE_FLAG_LINE_CHANGE);
+		DUK_D(DUK_DPRINT("no line info for current activation, disable line-based pause flags: 0x%08lx -> 0x%08lx",
+		                 (long) pause_flags, (long) updated_flags));
+		pause_flags = updated_flags;
+	}
+
+	heap->dbg_pause_flags = pause_flags;
+	heap->dbg_pause_act = thr->callstack_curr;
+	heap->dbg_pause_startline = (duk_uint32_t) line;
+	heap->dbg_state_dirty = 1;
+
+	DUK_D(DUK_DPRINT("set state for automatic pause triggers, flags=0x%08lx, act=%p, startline=%ld",
+	                 (long) heap->dbg_pause_flags, (void *) heap->dbg_pause_act,
+	                 (long) heap->dbg_pause_startline));
+}
+
+/*
  *  Debug connection peek and flush primitives
  */
 
 DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) {
 	duk_heap *heap;
+	duk_bool_t ret;
 
 	DUK_ASSERT(thr != NULL);
 	heap = thr->heap;
@@ -40826,7 +42135,10 @@
 		return 0;
 	}
 
-	return (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0);
+	DUK__DBG_TPORT_ENTER();
+	ret = (duk_bool_t) (heap->dbg_peek_cb(heap->dbg_udata) > 0);
+	DUK__DBG_TPORT_EXIT();
+	return ret;
 }
 
 DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) {
@@ -40845,7 +42157,9 @@
 		return;
 	}
 
+	DUK__DBG_TPORT_ENTER();
 	heap->dbg_read_flush_cb(heap->dbg_udata);
+	DUK__DBG_TPORT_EXIT();
 }
 
 DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) {
@@ -40864,7 +42178,9 @@
 		return;
 	}
 
+	DUK__DBG_TPORT_ENTER();
 	heap->dbg_write_flush_cb(heap->dbg_udata);
+	DUK__DBG_TPORT_EXIT();
 }
 
 /*
@@ -40942,7 +42258,10 @@
 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
 		left = 1;
 #endif
+		DUK__DBG_TPORT_ENTER();
 		got = heap->dbg_read_cb(heap->dbg_udata, (char *) p, left);
+		DUK__DBG_TPORT_EXIT();
+
 		if (got == 0 || got > left) {
 			DUK_D(DUK_DPRINT("connection error during read, return zero data"));
 			duk__debug_null_most_callbacks(thr);  /* avoid calling write callback in detach1() */
@@ -40977,7 +42296,7 @@
 	       (duk_uint32_t) buf[3];
 }
 
-DUK_LOCAL duk_uint32_t duk__debug_read_int32_raw(duk_hthread *thr) {
+DUK_LOCAL duk_int32_t duk__debug_read_int32_raw(duk_hthread *thr) {
 	return (duk_int32_t) duk__debug_read_uint32_raw(thr);
 }
 
@@ -41013,25 +42332,23 @@
 }
 
 DUK_LOCAL duk_hstring *duk__debug_read_hstring_raw(duk_hthread *thr, duk_uint32_t len) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_uint8_t buf[31];
 	duk_uint8_t *p;
 
 	if (len <= sizeof(buf)) {
 		duk_debug_read_bytes(thr, buf, (duk_size_t) len);
-		duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len);
-	} else {
-		p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len);  /* zero for paranoia */
+		duk_push_lstring(thr, (const char *) buf, (duk_size_t) len);
+	} else {
+		p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len);  /* zero for paranoia */
 		DUK_ASSERT(p != NULL);
 		duk_debug_read_bytes(thr, p, (duk_size_t) len);
-		(void) duk_buffer_to_string(ctx, -1);  /* Safety relies on debug client, which is OK. */
-	}
-
-	return duk_require_hstring(ctx, -1);
+		(void) duk_buffer_to_string(thr, -1);  /* Safety relies on debug client, which is OK. */
+	}
+
+	return duk_require_hstring(thr, -1);
 }
 
 DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_small_uint_t x;
 	duk_uint32_t len;
 
@@ -41054,19 +42371,18 @@
  fail:
 	DUK_D(DUK_DPRINT("debug connection error: failed to decode int"));
 	DUK__SET_CONN_BROKEN(thr, 1);
-	duk_push_hstring_empty(ctx);  /* always push some string */
-	return duk_require_hstring(ctx, -1);
+	duk_push_hstring_empty(thr);  /* always push some string */
+	return duk_require_hstring(thr, -1);
 }
 
 DUK_LOCAL duk_hbuffer *duk__debug_read_hbuffer_raw(duk_hthread *thr, duk_uint32_t len) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_uint8_t *p;
 
-	p = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (duk_size_t) len);  /* zero for paranoia */
+	p = (duk_uint8_t *) duk_push_fixed_buffer(thr, (duk_size_t) len);  /* zero for paranoia */
 	DUK_ASSERT(p != NULL);
 	duk_debug_read_bytes(thr, p, (duk_size_t) len);
 
-	return duk_require_hbuffer(ctx, -1);
+	return duk_require_hbuffer(thr, -1);
 }
 
 DUK_LOCAL void *duk__debug_read_pointer_raw(duk_hthread *thr) {
@@ -41150,7 +42466,6 @@
 }
 
 DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_uint8_t x;
 	duk_uint_t t;
 	duk_uint32_t len;
@@ -41162,11 +42477,11 @@
 	if (x >= 0xc0) {
 		t = (duk_uint_t) (x - 0xc0);
 		t = (t << 8) + duk_debug_read_byte(thr);
-		duk_push_uint(ctx, (duk_uint_t) t);
+		duk_push_uint(thr, (duk_uint_t) t);
 		goto return_ptr;
 	}
 	if (x >= 0x80) {
-		duk_push_uint(ctx, (duk_uint_t) (x - 0x80));
+		duk_push_uint(thr, (duk_uint_t) (x - 0x80));
 		goto return_ptr;
 	}
 	if (x >= 0x60) {
@@ -41178,7 +42493,7 @@
 	switch (x) {
 	case DUK_DBG_IB_INT4: {
 		duk_int32_t i = duk__debug_read_int32_raw(thr);
-		duk_push_i32(ctx, i);
+		duk_push_i32(thr, i);
 		break;
 	}
 	case DUK_DBG_IB_STR4: {
@@ -41202,25 +42517,25 @@
 		break;
 	}
 	case DUK_DBG_IB_UNDEFINED: {
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 		break;
 	}
 	case DUK_DBG_IB_NULL: {
-		duk_push_null(ctx);
+		duk_push_null(thr);
 		break;
 	}
 	case DUK_DBG_IB_TRUE: {
-		duk_push_true(ctx);
+		duk_push_true(thr);
 		break;
 	}
 	case DUK_DBG_IB_FALSE: {
-		duk_push_false(ctx);
+		duk_push_false(thr);
 		break;
 	}
 	case DUK_DBG_IB_NUMBER: {
 		duk_double_t d;
 		d = duk__debug_read_double_raw(thr);
-		duk_push_number(ctx, d);
+		duk_push_number(thr, d);
 		break;
 	}
 	case DUK_DBG_IB_OBJECT: {
@@ -41303,7 +42618,10 @@
 #if defined(DUK_USE_DEBUGGER_TRANSPORT_TORTURE)
 		left = 1;
 #endif
+		DUK__DBG_TPORT_ENTER();
 		got = heap->dbg_write_cb(heap->dbg_udata, (const char *) p, left);
+		DUK__DBG_TPORT_EXIT();
+
 		if (got == 0 || got > left) {
 			duk__debug_null_most_callbacks(thr);  /* avoid calling write callback in detach1() */
 			DUK_D(DUK_DPRINT("connection error during write"));
@@ -41429,8 +42747,7 @@
 }
 
 DUK_LOCAL void duk__debug_write_hstring_safe_top(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_debug_write_hstring(thr, duk_safe_to_hstring(ctx, -1));
+	duk_debug_write_hstring(thr, duk_safe_to_hstring(thr, -1));
 }
 
 DUK_INTERNAL void duk_debug_write_buffer(duk_hthread *thr, const char *data, duk_size_t length) {
@@ -41618,7 +42935,7 @@
 
 DUK_INTERNAL void duk_debug_write_notify(duk_hthread *thr, duk_small_uint_t command) {
 	duk_debug_write_byte(thr, DUK_DBG_IB_NOTIFY);
-	duk_debug_write_int(thr, command);
+	duk_debug_write_int(thr, (duk_int32_t) command);
 }
 
 DUK_INTERNAL void duk_debug_write_eom(duk_hthread *thr) {
@@ -41637,7 +42954,6 @@
  */
 
 DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_activation *act;
 	duk_uint_fast32_t line;
 	duk_uint_fast32_t pc;
@@ -41659,20 +42975,18 @@
 	/* XXX: this should be optimized to be a raw query and avoid valstack
 	 * operations if possible.
 	 */
-	duk_push_tval(ctx, &act->tv_func);
-	line = duk_hobject_pc2line_query(ctx, -1, pc);
-	duk_pop(ctx);
+	duk_push_tval(thr, &act->tv_func);
+	line = duk_hobject_pc2line_query(thr, -1, pc);
+	duk_pop(thr);
 	return line;
 }
 
 DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_activation *act;
 
 	duk_debug_write_notify(thr, DUK_DBG_CMD_STATUS);
 	duk_debug_write_int(thr, (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ? 1 : 0));
 
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);  /* unsigned */
 	act = thr->callstack_curr;
 	if (act == NULL) {
 		duk_debug_write_undefined(thr);
@@ -41680,15 +42994,14 @@
 		duk_debug_write_int(thr, 0);
 		duk_debug_write_int(thr, 0);
 	} else {
-		duk_push_tval(ctx, &act->tv_func);
-		duk_get_prop_string(ctx, -1, "fileName");
+		duk_push_tval(thr, &act->tv_func);
+		duk_get_prop_string(thr, -1, "fileName");
 		duk__debug_write_hstring_safe_top(thr);
-		duk_get_prop_string(ctx, -2, "name");
+		duk_get_prop_string(thr, -2, "name");
 		duk__debug_write_hstring_safe_top(thr);
-		duk_pop_3(ctx);
+		duk_pop_3(thr);
 		/* Report next pc/line to be executed. */
 		duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr));
-		act = thr->callstack_curr;
 		duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act));
 	}
 
@@ -41701,27 +43014,26 @@
 	 *  NFY <int: 5> <int: fatal> <str: msg> <str: filename> <int: linenumber> EOM
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_activation *act;
 	duk_uint32_t pc;
 
 	DUK_ASSERT(thr->valstack_top > thr->valstack);  /* At least: ... [err] */
 
 	duk_debug_write_notify(thr, DUK_DBG_CMD_THROW);
-	duk_debug_write_int(thr, fatal);
+	duk_debug_write_int(thr, (duk_int32_t) fatal);
 
 	/* Report thrown value to client coerced to string */
-	duk_dup_top(ctx);
+	duk_dup_top(thr);
 	duk__debug_write_hstring_safe_top(thr);
-	duk_pop(ctx);
-
-	if (duk_is_error(ctx, -1)) {
+	duk_pop(thr);
+
+	if (duk_is_error(thr, -1)) {
 		/* Error instance, use augmented error data directly */
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_FILE_NAME);
+		duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
 		duk__debug_write_hstring_safe_top(thr);
-		duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_LINE_NUMBER);
-		duk_debug_write_uint(thr, duk_get_uint(ctx, -1));
-		duk_pop_2(ctx);
+		duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER);
+		duk_debug_write_uint(thr, duk_get_uint(thr, -1));
+		duk_pop_2(thr);
 	} else {
 		/* For anything other than an Error instance, we calculate the
 		 * error location directly from the current activation if one
@@ -41729,13 +43041,12 @@
 		 */
 		act = thr->callstack_curr;
 		if (act != NULL) {
-			duk_push_tval(ctx, &act->tv_func);
-			duk_get_prop_string(ctx, -1, "fileName");
+			duk_push_tval(thr, &act->tv_func);
+			duk_get_prop_string(thr, -1, "fileName");
 			duk__debug_write_hstring_safe_top(thr);
-			act = thr->callstack_curr;
-			pc = duk_hthread_get_act_prev_pc(thr, act);
-			duk_debug_write_uint(thr, (duk_uint32_t) duk_hobject_pc2line_query(ctx, -2, pc));
-			duk_pop_2(ctx);
+			pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr, act);
+			duk_debug_write_uint(thr, (duk_uint32_t) duk_hobject_pc2line_query(thr, -2, pc));
+			duk_pop_2(thr);
 		} else {
 			/* Can happen if duk_throw() is called on an empty
 			 * callstack.
@@ -41768,7 +43079,7 @@
 		return 0;
 	}
 	if (x >= 0x60) {
-		duk_debug_skip_bytes(thr, x - 0x60);
+		duk_debug_skip_bytes(thr, (duk_size_t) (x - 0x60));
 		return 0;
 	}
 	switch(x) {
@@ -41836,6 +43147,9 @@
 	}
 }
 
+/* Read and validate a call stack index.  If index is invalid, write out an
+ * error message and return zero.
+ */
 DUK_LOCAL duk_int32_t duk__debug_read_validate_csindex(duk_hthread *thr) {
 	duk_int32_t level;
 	level = duk_debug_read_int(thr);
@@ -41846,6 +43160,21 @@
 	return level;
 }
 
+/* Read a call stack index and lookup the corresponding duk_activation.
+ * If index is invalid, write out an error message and return NULL.
+ */
+DUK_LOCAL duk_activation *duk__debug_read_level_get_activation(duk_hthread *thr) {
+	duk_activation *act;
+	duk_int32_t level;
+
+	level = duk_debug_read_int(thr);
+	act = duk_hthread_get_activation_for_level(thr, level);
+	if (act == NULL) {
+		duk_debug_write_error_eom(thr, DUK_DBG_ERR_NOTFOUND, "invalid callstack index");
+	}
+	return act;
+}
+
 /*
  *  Simple commands
  */
@@ -41882,50 +43211,61 @@
 
 DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) {
 	DUK_D(DUK_DPRINT("debug command Pause"));
-
-	if (duk_debug_is_paused(heap)) {
-		DUK_D(DUK_DPRINT("Pause requested when already paused, ignore"));
-	} else {
-		duk_debug_set_paused(heap);
-	}
+	duk_debug_set_paused(heap);
 	duk_debug_write_reply(thr);
 	duk_debug_write_eom(thr);
 }
 
 DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) {
+	duk_small_uint_t pause_flags;
+
 	DUK_D(DUK_DPRINT("debug command Resume"));
 
 	duk_debug_clear_paused(heap);
+
+	pause_flags = 0;
+#if 0  /* manual testing */
+	pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE;
+	pause_flags |= DUK_PAUSE_FLAG_CAUGHT_ERROR;
+	pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
+#endif
+#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
+	pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
+#endif
+
+	duk__debug_set_pause_state(thr, heap, pause_flags);
+
 	duk_debug_write_reply(thr);
 	duk_debug_write_eom(thr);
 }
 
 DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int32_t cmd) {
-	duk_small_uint_t step_type;
-	duk_uint_fast32_t line;
+	duk_small_uint_t pause_flags;
 
 	DUK_D(DUK_DPRINT("debug command StepInto/StepOver/StepOut: %d", (int) cmd));
 
 	if (cmd == DUK_DBG_CMD_STEPINTO) {
-		step_type = DUK_STEP_TYPE_INTO;
+		pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
+		              DUK_PAUSE_FLAG_FUNC_ENTRY |
+		              DUK_PAUSE_FLAG_FUNC_EXIT;
 	} else if (cmd == DUK_DBG_CMD_STEPOVER) {
-		step_type = DUK_STEP_TYPE_OVER;
+		pause_flags = DUK_PAUSE_FLAG_LINE_CHANGE |
+		              DUK_PAUSE_FLAG_FUNC_EXIT;
 	} else {
 		DUK_ASSERT(cmd == DUK_DBG_CMD_STEPOUT);
-		step_type = DUK_STEP_TYPE_OUT;
-	}
-
-	line = duk_debug_curr_line(thr);
-	if (line > 0) {
-		duk_debug_clear_paused(heap);  /* XXX: overlap with fields below; separate macro/helper? */
-		heap->dbg_step_type = step_type;
-		heap->dbg_step_thread = thr;
-		heap->dbg_step_csindex = thr->callstack_top - 1;
-		heap->dbg_step_startline = line;
-		heap->dbg_state_dirty = 1;
-	} else {
-		DUK_D(DUK_DPRINT("cannot determine current line, stepinto/stepover/stepout ignored"));
-	}
+		pause_flags = DUK_PAUSE_FLAG_FUNC_EXIT;
+	}
+#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
+	pause_flags |= DUK_PAUSE_FLAG_UNCAUGHT_ERROR;
+#endif
+
+	/* If current activation doesn't have line information, line-based
+	 * pause flags are automatically disabled.  As a result, e.g.
+	 * StepInto will then pause on (native) function entry or exit.
+	 */
+	duk_debug_clear_paused(heap);
+	duk__debug_set_pause_state(thr, heap, pause_flags);
+
 	duk_debug_write_reply(thr);
 	duk_debug_write_eom(thr);
 }
@@ -41978,40 +43318,27 @@
 }
 
 DUK_LOCAL void duk__debug_handle_get_var(duk_hthread *thr, duk_heap *heap) {
-	duk_context *ctx = (duk_context *) thr;
+	duk_activation *act;
 	duk_hstring *str;
 	duk_bool_t rc;
-	duk_int32_t level;
 
 	DUK_UNREF(heap);
 	DUK_D(DUK_DPRINT("debug command GetVar"));
 
-	level = duk__debug_read_validate_csindex(thr);
-	if (level == 0) {
+	act = duk__debug_read_level_get_activation(thr);
+	if (act == NULL) {
 		return;
 	}
 	str = duk_debug_read_hstring(thr);  /* push to stack */
 	DUK_ASSERT(str != NULL);
 
-	if (thr->callstack_top > 0) {
-		rc = duk_js_getvar_activation(thr,
-		                              thr->callstack + thr->callstack_top + level,
-		                              str,
-		                              0);
-	} else {
-		/* No activation, no variable access.  Could also pretend
-		 * we're in the global program context and read stuff off
-		 * the global object.
-		 */
-		DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore getvar"));
-		rc = 0;
-	}
+	rc = duk_js_getvar_activation(thr, act, str, 0);
 
 	duk_debug_write_reply(thr);
 	if (rc) {
 		duk_debug_write_int(thr, 1);
-		DUK_ASSERT(duk_get_tval(ctx, -2) != NULL);
-		duk_debug_write_tval(thr, duk_get_tval(ctx, -2));
+		DUK_ASSERT(duk_get_tval(thr, -2) != NULL);
+		duk_debug_write_tval(thr, duk_get_tval(thr, -2));
 	} else {
 		duk_debug_write_int(thr, 0);
 		duk_debug_write_unused(thr);
@@ -42020,15 +43347,15 @@
 }
 
 DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) {
+	duk_activation *act;
 	duk_hstring *str;
 	duk_tval *tv;
-	duk_int32_t level;
 
 	DUK_UNREF(heap);
 	DUK_D(DUK_DPRINT("debug command PutVar"));
 
-	level = duk__debug_read_validate_csindex(thr);
-	if (level == 0) {
+	act = duk__debug_read_level_get_activation(thr);
+	if (act == NULL) {
 		return;
 	}
 	str = duk_debug_read_hstring(thr);  /* push to stack */
@@ -42039,15 +43366,7 @@
 		return;
 	}
 
-	if (thr->callstack_top > 0) {
-		duk_js_putvar_activation(thr,
-		                         thr->callstack + thr->callstack_top + level,
-		                         str,
-		                         tv,
-		                         0);
-	} else {
-		DUK_D(DUK_DPRINT("callstack empty, no activation -> ignore putvar"));
-	}
+	duk_js_putvar_activation(thr, act, str, tv, 0);
 
 	/* XXX: Current putvar implementation doesn't have a success flag,
 	 * add one and send to debug client?
@@ -42057,23 +43376,17 @@
 }
 
 DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hthread *curr_thr = thr;
 	duk_activation *curr_act;
 	duk_uint_fast32_t pc;
 	duk_uint_fast32_t line;
-	duk_size_t i;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_UNREF(heap);
 
 	duk_debug_write_reply(thr);
 	while (curr_thr != NULL) {
-		i = curr_thr->callstack_top;
-		while (i > 0) {
-			i--;
-			curr_act = curr_thr->callstack + i;
-
+		for (curr_act = curr_thr->callstack_curr; curr_act != NULL; curr_act = curr_act->parent) {
 			/* PC/line semantics here are:
 			 *   - For callstack top we're conceptually between two
 			 *     opcodes and current PC indicates next line to
@@ -42087,19 +43400,19 @@
 			/* XXX: optimize to use direct reads, i.e. avoid
 			 * value stack operations.
 			 */
-			duk_push_tval(ctx, &curr_act->tv_func);
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_FILE_NAME);
+			duk_push_tval(thr, &curr_act->tv_func);
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
 			duk__debug_write_hstring_safe_top(thr);
-			duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME);
+			duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
 			duk__debug_write_hstring_safe_top(thr);
 			pc = duk_hthread_get_act_curr_pc(thr, curr_act);
-			if (i != curr_thr->callstack_top - 1 && pc > 0) {
+			if (curr_act != curr_thr->callstack_curr && pc > 0) {
 				pc--;
 			}
-			line = duk_hobject_pc2line_query(ctx, -3, pc);
+			line = duk_hobject_pc2line_query(thr, -3, pc);
 			duk_debug_write_uint(thr, (duk_uint32_t) line);
 			duk_debug_write_uint(thr, (duk_uint32_t) pc);
-			duk_pop_3(ctx);
+			duk_pop_3(thr);
 		}
 		curr_thr = curr_thr->resumer;
 	}
@@ -42110,39 +43423,36 @@
 }
 
 DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_activation *curr_act;
-	duk_int32_t level;
+	duk_activation *act;
 	duk_hstring *varname;
 
 	DUK_UNREF(heap);
 
-	level = duk__debug_read_validate_csindex(thr);
-	if (level == 0) {
-		return;
-	}
+	act = duk__debug_read_level_get_activation(thr);
+	if (act == NULL) {
+		return;
+	}
+
 	duk_debug_write_reply(thr);
 
-	curr_act = thr->callstack + thr->callstack_top + level;
-
 	/* XXX: several nice-to-have improvements here:
 	 *   - Use direct reads avoiding value stack operations
 	 *   - Avoid triggering getters, indicate getter values to debug client
 	 *   - If side effects are possible, add error catching
 	 */
 
-	duk_push_tval(ctx, &curr_act->tv_func);
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VARMAP);
-	if (duk_is_object(ctx, -1)) {
-		duk_enum(ctx, -1, 0 /*enum_flags*/);
-		while (duk_next(ctx, -1 /*enum_index*/, 0 /*get_value*/)) {
-			varname = duk_known_hstring(ctx, -1);
-
-			duk_js_getvar_activation(thr, curr_act, varname, 0 /*throw_flag*/);
+	duk_push_tval(thr, &act->tv_func);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_VARMAP);
+	if (duk_is_object(thr, -1)) {
+		duk_enum(thr, -1, 0 /*enum_flags*/);
+		while (duk_next(thr, -1 /*enum_index*/, 0 /*get_value*/)) {
+			varname = duk_known_hstring(thr, -1);
+
+			duk_js_getvar_activation(thr, act, varname, 0 /*throw_flag*/);
 			/* [ ... func varmap enum key value this ] */
-			duk_debug_write_hstring(thr, duk_get_hstring(ctx, -3));
-			duk_debug_write_tval(thr, duk_get_tval(ctx, -2));
-			duk_pop_3(ctx);  /* -> [ ... func varmap enum ] */
+			duk_debug_write_hstring(thr, duk_get_hstring(thr, -3));
+			duk_debug_write_tval(thr, duk_get_tval(thr, -2));
+			duk_pop_3(thr);  /* -> [ ... func varmap enum ] */
 		}
 	} else {
 		DUK_D(DUK_DPRINT("varmap is not an object in GetLocals, ignore"));
@@ -42152,13 +43462,12 @@
 }
 
 DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_small_uint_t call_flags;
 	duk_int_t call_ret;
 	duk_small_int_t eval_err;
-	duk_int_t num_eval_args;
 	duk_bool_t direct_eval;
 	duk_int32_t level;
+	duk_idx_t idx_func;
 
 	DUK_UNREF(heap);
 
@@ -42173,8 +43482,9 @@
 	 */
 
 	/* nargs == 2 so we can pass a callstack index to eval(). */
-	duk_push_c_function(ctx, duk_bi_global_object_eval, 2 /*nargs*/);
-	duk_push_undefined(ctx);  /* 'this' binding shouldn't matter here */
+	idx_func = duk_get_top(thr);
+	duk_push_c_function(thr, duk_bi_global_object_eval, 2 /*nargs*/);
+	duk_push_undefined(thr);  /* 'this' binding shouldn't matter here */
 
 	/* Read callstack index, if non-null. */
 	if (duk_debug_peek_byte(thr) == DUK_DBG_IB_NULL) {
@@ -42194,32 +43504,31 @@
 
 	(void) duk_debug_read_hstring(thr);
 	if (direct_eval) {
-		num_eval_args = 2;
-		duk_push_int(ctx, level - 1);  /* compensate for eval() call */
-	} else {
-		num_eval_args = 1;
-	}
-
-	/* [ ... eval "eval" eval_input level ] */
+		duk_push_int(thr, level - 1);  /* compensate for eval() call */
+	}
+
+	/* [ ... eval "eval" eval_input level? ] */
 
 	call_flags = 0;
-	if (direct_eval && thr->callstack_top >= (duk_size_t) -level) {
+	if (direct_eval) {
 		duk_activation *act;
 		duk_hobject *fun;
 
-		act = thr->callstack + thr->callstack_top + level;
-		fun = DUK_ACT_GET_FUNC(act);
-		if (fun != NULL && DUK_HOBJECT_IS_COMPFUNC(fun)) {
-			/* Direct eval requires that there's a current
-			 * activation and it is an Ecmascript function.
-			 * When Eval is executed from e.g. cooperate API
-			 * call we'll need to do an indirect eval instead.
-			 */
-			call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
-		}
-	}
-
-	call_ret = duk_handle_call_protected(thr, num_eval_args, call_flags);
+		act = duk_hthread_get_activation_for_level(thr, level);
+		if (act != NULL) {
+			fun = DUK_ACT_GET_FUNC(act);
+			if (fun != NULL && DUK_HOBJECT_IS_COMPFUNC(fun)) {
+				/* Direct eval requires that there's a current
+				 * activation and it is an Ecmascript function.
+				 * When Eval is executed from e.g. cooperate API
+				 * call we'll need to do an indirect eval instead.
+				 */
+				call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
+			}
+		}
+	}
+
+	call_ret = duk_pcall_method_flags(thr, duk_get_top(thr) - (idx_func + 2), call_flags);
 
 	if (call_ret == DUK_EXEC_SUCCESS) {
 		eval_err = 0;
@@ -42230,15 +43539,15 @@
 		 * to traverse the error object.
 		 */
 		eval_err = 1;
-		duk_safe_to_string(ctx, -1);
+		duk_safe_to_string(thr, -1);
 	}
 
 	/* [ ... result ] */
 
 	duk_debug_write_reply(thr);
 	duk_debug_write_int(thr, (duk_int32_t) eval_err);
-	DUK_ASSERT(duk_get_tval(ctx, -1) != NULL);
-	duk_debug_write_tval(thr, duk_get_tval(ctx, -1));
+	DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
+	duk_debug_write_tval(thr, duk_get_tval(thr, -1));
 	duk_debug_write_eom(thr);
 }
 
@@ -42254,12 +43563,11 @@
 }
 
 DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_idx_t old_top;
 
 	DUK_D(DUK_DPRINT("debug command AppRequest"));
 
-	old_top = duk_get_top(ctx);  /* save stack top */
+	old_top = duk_get_top(thr);  /* save stack top */
 
 	if (heap->dbg_request_cb != NULL) {
 		duk_idx_t nrets;
@@ -42271,7 +43579,7 @@
 		 */
 		while (duk_debug_peek_byte(thr) != DUK_DBG_IB_EOM) {
 			duk_tval *tv;
-			if (!duk_check_stack(ctx, 1)) {
+			if (!duk_check_stack(thr, 1)) {
 				DUK_D(DUK_DPRINT("failed to allocate space for request dvalue(s)"));
 				goto fail;
 			}
@@ -42282,41 +43590,41 @@
 			}
 			nvalues++;
 		}
-		DUK_ASSERT(duk_get_top(ctx) == old_top + nvalues);
+		DUK_ASSERT(duk_get_top(thr) == old_top + nvalues);
 
 		/* Request callback should push values for reply to client onto valstack */
 		DUK_D(DUK_DPRINT("calling into AppRequest request_cb with nvalues=%ld, old_top=%ld, top=%ld",
-		                 (long) nvalues, (long) old_top, (long) duk_get_top(ctx)));
-		nrets = heap->dbg_request_cb(ctx, heap->dbg_udata, nvalues);
+		                 (long) nvalues, (long) old_top, (long) duk_get_top(thr)));
+		nrets = heap->dbg_request_cb(thr, heap->dbg_udata, nvalues);
 		DUK_D(DUK_DPRINT("returned from AppRequest request_cb; nvalues=%ld -> nrets=%ld, old_top=%ld, top=%ld",
-		                 (long) nvalues, (long) nrets, (long) old_top, (long) duk_get_top(ctx)));
+		                 (long) nvalues, (long) nrets, (long) old_top, (long) duk_get_top(thr)));
 		if (nrets >= 0) {
-			DUK_ASSERT(duk_get_top(ctx) >= old_top + nrets);
-			if (duk_get_top(ctx) < old_top + nrets) {
+			DUK_ASSERT(duk_get_top(thr) >= old_top + nrets);
+			if (duk_get_top(thr) < old_top + nrets) {
 				DUK_D(DUK_DPRINT("AppRequest callback doesn't match value stack configuration, "
 				                 "top=%ld < old_top=%ld + nrets=%ld; "
 				                 "this might mean it's unsafe to continue!",
-				                 (long) duk_get_top(ctx), (long) old_top, (long) nrets));
+				                 (long) duk_get_top(thr), (long) old_top, (long) nrets));
 				goto fail;
 			}
 
 			/* Reply with tvals pushed by request callback */
 			duk_debug_write_byte(thr, DUK_DBG_IB_REPLY);
-			top = duk_get_top(ctx);
+			top = duk_get_top(thr);
 			for (idx = top - nrets; idx < top; idx++) {
-				duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(ctx, idx));
+				duk_debug_write_tval(thr, DUK_GET_TVAL_POSIDX(thr, idx));
 			}
 			duk_debug_write_eom(thr);
 		} else {
-			DUK_ASSERT(duk_get_top(ctx) >= old_top + 1);
-			if (duk_get_top(ctx) < old_top + 1) {
+			DUK_ASSERT(duk_get_top(thr) >= old_top + 1);
+			if (duk_get_top(thr) < old_top + 1) {
 				DUK_D(DUK_DPRINT("request callback return value doesn't match value stack configuration"));
 				goto fail;
 			}
-			duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(ctx, -1));
-		}
-
-		duk_set_top(ctx, old_top);  /* restore stack top */
+			duk_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(thr, -1));
+		}
+
+		duk_set_top(thr, old_top);  /* restore stack top */
 	} else {
 		DUK_D(DUK_DPRINT("no request callback, treat AppRequest as unsupported"));
 		duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNSUPPORTED, "AppRequest unsupported by target");
@@ -42325,7 +43633,7 @@
 	return;
 
  fail:
-	duk_set_top(ctx, old_top);  /* restore stack top */
+	duk_set_top(thr, old_top);  /* restore stack top */
 	DUK__SET_CONN_BROKEN(thr, 1);
 }
 
@@ -42353,9 +43661,9 @@
 	case DUK_HTYPE_STRING: {
 		duk_hstring *h = (duk_hstring *) hdr;
 
-		duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_BYTELEN(h));
-		duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_CHARLEN(h));
-		duk_debug_write_uint(thr, (duk_int32_t) DUK_HSTRING_GET_HASH(h));
+		duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_BYTELEN(h));
+		duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_CHARLEN(h));
+		duk_debug_write_uint(thr, (duk_uint32_t) DUK_HSTRING_GET_HASH(h));
 		duk_debug_write_hstring(thr, h);
 		break;
 	}
@@ -42482,11 +43790,10 @@
 	}
 
 	if (fun == NULL) {
-		if (level >= 0 || -level > (duk_int32_t) thr->callstack_top) {
-			DUK_D(DUK_DPRINT("invalid callstack index for GetBytecode"));
+		act = duk_hthread_get_activation_for_level(thr, level);
+		if (act == NULL) {
 			goto fail_index;
 		}
-		act = thr->callstack + thr->callstack_top + level;
 		fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
 	}
 
@@ -42578,6 +43885,7 @@
 DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = {
 	"extensible",
 	"constructable",
+	"callable",
 	"boundfunc",
 	"compfunc",
 	"natfunc",
@@ -42589,17 +43897,18 @@
 	"newenv",
 	"namebinding",
 	"createargs",
-	"have_finalizer"
+	"have_finalizer",
 	"exotic_array",
 	"exotic_stringobj",
 	"exotic_arguments",
-	"exotic_dukfunc",
-	"exotic_proxyobj"
+	"exotic_proxyobj",
+	"special_call"
 	/* NULL not needed here */
 };
 DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = {
 	DUK_HOBJECT_FLAG_EXTENSIBLE,
 	DUK_HOBJECT_FLAG_CONSTRUCTABLE,
+	DUK_HOBJECT_FLAG_CALLABLE,
 	DUK_HOBJECT_FLAG_BOUNDFUNC,
 	DUK_HOBJECT_FLAG_COMPFUNC,
 	DUK_HOBJECT_FLAG_NATFUNC,
@@ -42615,8 +43924,8 @@
 	DUK_HOBJECT_FLAG_EXOTIC_ARRAY,
 	DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ,
 	DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS,
-	DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC,
 	DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ,
+	DUK_HOBJECT_FLAG_SPECIAL_CALL,
 	0  /* terminator */
 };
 DUK_LOCAL const char * const duk__debug_getinfo_hbuffer_keys[] = {
@@ -42659,7 +43968,7 @@
 
 	for (;;) {
 		mask = *masks++;
-		if (!mask) {
+		if (mask == 0) {
 			break;
 		}
 		key = *keys++;
@@ -42741,6 +44050,10 @@
 	DUK_D(DUK_DPRINT("debug command GetHeapObjInfo"));
 	DUK_UNREF(heap);
 
+	DUK_ASSERT(sizeof(duk__debug_getinfo_hstring_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hstring_masks) / sizeof(duk_uint_t) - 1);
+	DUK_ASSERT(sizeof(duk__debug_getinfo_hobject_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hobject_masks) / sizeof(duk_uint_t) - 1);
+	DUK_ASSERT(sizeof(duk__debug_getinfo_hbuffer_keys) / sizeof(const char *) == sizeof(duk__debug_getinfo_hbuffer_masks) / sizeof(duk_uint_t) - 1);
+
 	h = duk_debug_read_any_ptr(thr);
 	if (!h) {
 		duk_debug_write_error_eom(thr, DUK_DBG_ERR_UNKNOWN, "invalid target");
@@ -42818,7 +44131,7 @@
 			duk_harray *h_arr;
 			h_arr = (duk_harray *) h_obj;
 
-			duk__debug_getinfo_prop_int(thr, "length", h_arr->length);
+			duk__debug_getinfo_prop_uint(thr, "length", (duk_uint_t) h_arr->length);
 			duk__debug_getinfo_prop_bool(thr, "length_nonwritable", h_arr->length_nonwritable);
 		}
 
@@ -42870,6 +44183,19 @@
 			}
 		}
 
+		if (DUK_HOBJECT_IS_BOUNDFUNC(h_obj)) {
+			duk_hboundfunc *h_bfun;
+			h_bfun = (duk_hboundfunc *) h_obj;
+
+			duk__debug_getinfo_flags_key(thr, "target");
+			duk_debug_write_tval(thr, &h_bfun->target);
+			duk__debug_getinfo_flags_key(thr, "this_binding");
+			duk_debug_write_tval(thr, &h_bfun->this_binding);
+			duk__debug_getinfo_flags_key(thr, "nargs");
+			duk_debug_write_int(thr, h_bfun->nargs);
+			/* h_bfun->args not exposed now */
+		}
+
 		if (DUK_HOBJECT_IS_THREAD(h_obj)) {
 			/* XXX: Currently no inspection of threads, e.g. value stack, call
 			 * stack, catch stack, etc.
@@ -42887,7 +44213,7 @@
 			duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->thread));
 			duk__debug_getinfo_flags_key(thr, "varmap");
 			duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->varmap));
-			duk__debug_getinfo_prop_uint(thr, "regbase", (duk_uint_t) h_env->regbase);
+			duk__debug_getinfo_prop_uint(thr, "regbase", (duk_uint_t) h_env->regbase_byteoff);
 		}
 
 		if (DUK_HOBJECT_IS_OBJENV(h_obj)) {
@@ -42993,8 +44319,8 @@
 	DUK_UNREF(heap);
 
 	h = duk_debug_read_any_ptr(thr);
-	idx_start = duk_debug_read_int(thr);
-	idx_end = duk_debug_read_int(thr);
+	idx_start = (duk_uint_t) duk_debug_read_int(thr);
+	idx_end = (duk_uint_t) duk_debug_read_int(thr);
 	if (h == NULL || DUK_HEAPHDR_GET_TYPE(h) != DUK_HTYPE_OBJECT) {
 		goto fail_args;
 	}
@@ -43035,7 +44361,6 @@
  * stack handling which is convenient.
  */
 DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_heap *heap;
 	duk_uint8_t x;
 	duk_int32_t cmd;
@@ -43044,9 +44369,8 @@
 	DUK_ASSERT(thr != NULL);
 	heap = thr->heap;
 	DUK_ASSERT(heap != NULL);
-	DUK_UNREF(ctx);
-
-	entry_top = duk_get_top(ctx);
+
+	entry_top = duk_get_top(thr);
 
 	x = duk_debug_read_byte(thr);
 	switch (x) {
@@ -43168,14 +44492,14 @@
 	}
 	}  /* switch initial byte */
 
-	DUK_ASSERT(duk_get_top(ctx) >= entry_top);
-	duk_set_top(ctx, entry_top);
+	DUK_ASSERT(duk_get_top(thr) >= entry_top);
+	duk_set_top(thr, entry_top);
 	duk__debug_skip_to_eom(thr);
 	return;
 
  fail:
-	DUK_ASSERT(duk_get_top(ctx) >= entry_top);
-	duk_set_top(ctx, entry_top);
+	DUK_ASSERT(duk_get_top(thr) >= entry_top);
+	duk_set_top(thr, entry_top);
 	DUK__SET_CONN_BROKEN(thr, 1);
 	return;
 }
@@ -43188,23 +44512,21 @@
 }
 
 DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t no_block) {
-	duk_context *ctx = (duk_context *) thr;
 #if defined(DUK_USE_ASSERTIONS)
 	duk_idx_t entry_top;
 #endif
 	duk_bool_t retval = 0;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_UNREF(ctx);
 	DUK_ASSERT(thr->heap != NULL);
 #if defined(DUK_USE_ASSERTIONS)
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 #endif
 
 	DUK_D(DUK_DPRINT("process debug messages: read_cb=%s, no_block=%ld, detaching=%ld, processing=%ld",
 	                 thr->heap->dbg_read_cb ? "not NULL" : "NULL", (long) no_block,
 	                 (long) thr->heap->dbg_detaching, (long) thr->heap->dbg_processing));
-	DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(ctx)));
+	DUK_DD(DUK_DDPRINT("top at entry: %ld", (long) duk_get_top(thr)));
 
 	/* thr->heap->dbg_detaching may be != 0 if a debugger write outside
 	 * the message loop caused a transport error and detach1() to run.
@@ -43223,7 +44545,7 @@
 		/* Process messages until we're no longer paused or we peek
 		 * and see there's nothing to read right now.
 		 */
-		DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(ctx)));
+		DUK_DD(DUK_DDPRINT("top at loop top: %ld", (long) duk_get_top(thr)));
 		DUK_ASSERT(thr->heap->dbg_processing == 1);
 
 		while (thr->heap->dbg_read_cb == NULL && thr->heap->dbg_detaching) {
@@ -43294,11 +44616,11 @@
 	duk_debug_read_flush(thr);  /* this cannot initiate a detach */
 	DUK_ASSERT(thr->heap->dbg_detaching == 0);
 
-	DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(ctx)));
+	DUK_DD(DUK_DDPRINT("top at exit: %ld", (long) duk_get_top(thr)));
 
 #if defined(DUK_USE_ASSERTIONS)
 	/* Easy to get wrong, so assert for it. */
-	DUK_ASSERT(entry_top == duk_get_top(ctx));
+	DUK_ASSERT(entry_top == duk_get_top(thr));
 #endif
 
 	return retval;
@@ -43406,7 +44728,7 @@
 	b->line = line;
 	DUK_HSTRING_INCREF(thr, filename);
 
-	return heap->dbg_breakpoint_count - 1;  /* index */
+	return (duk_small_int_t) (heap->dbg_breakpoint_count - 1);  /* index */
 }
 
 DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index) {
@@ -43470,7 +44792,7 @@
 	} else {
 		DUK_HEAP_SET_DEBUGGER_PAUSED(heap);
 		heap->dbg_state_dirty = 1;
-		duk_debug_clear_step_state(heap);
+		duk_debug_clear_pause_state(heap);
 		DUK_ASSERT(heap->ms_running == 0);  /* debugger can't be triggered within mark-and-sweep */
 		heap->ms_running = 1;  /* prevent mark-and-sweep, prevent refzero queueing */
 		heap->ms_prevent_count++;
@@ -43483,7 +44805,7 @@
 	if (duk_debug_is_paused(heap)) {
 		DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap);
 		heap->dbg_state_dirty = 1;
-		duk_debug_clear_step_state(heap);
+		duk_debug_clear_pause_state(heap);
 		DUK_ASSERT(heap->ms_running == 1);
 		DUK_ASSERT(heap->ms_prevent_count > 0);
 		heap->ms_prevent_count--;
@@ -43494,11 +44816,10 @@
 	}
 }
 
-DUK_INTERNAL void duk_debug_clear_step_state(duk_heap *heap) {
-	heap->dbg_step_type = DUK_STEP_TYPE_NONE;
-	heap->dbg_step_thread = NULL;
-	heap->dbg_step_csindex = 0;
-	heap->dbg_step_startline = 0;
+DUK_INTERNAL void duk_debug_clear_pause_state(duk_heap *heap) {
+	heap->dbg_pause_flags = 0;
+	heap->dbg_pause_act = NULL;
+	heap->dbg_pause_startline = 0;
 }
 
 #else  /* DUK_USE_DEBUGGER_SUPPORT */
@@ -43508,6 +44829,8 @@
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
 /* automatic undefs */
+#undef DUK__DBG_TPORT_ENTER
+#undef DUK__DBG_TPORT_EXIT
 #undef DUK__SET_CONN_BROKEN
 #line 1 "duk_error_augment.c"
 /*
@@ -43572,17 +44895,15 @@
 
 #if defined(DUK_USE_ERRTHROW) || defined(DUK_USE_ERRCREATE)
 DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_cb) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval *tv_hnd;
-	duk_small_uint_t call_flags;
 	duk_int_t rc;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
 	DUK_ASSERT_STRIDX_VALID(stridx_cb);
 
-	if (DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap)) {
-		DUK_DD(DUK_DDPRINT("recursive call to error handler, ignore"));
+	if (thr->heap->augmenting_error) {
+		DUK_D(DUK_DPRINT("recursive call to error augmentation, ignore"));
 		return;
 	}
 
@@ -43617,45 +44938,33 @@
 	}
 	DUK_DDD(DUK_DDDPRINT("error handler dump (callability not checked): %!T",
 	                     (duk_tval *) tv_hnd));
-	duk_push_tval(ctx, tv_hnd);
+	duk_push_tval(thr, tv_hnd);
 
 	/* [ ... errval errhandler ] */
 
-	duk_insert(ctx, -2);  /* -> [ ... errhandler errval ] */
-	duk_push_undefined(ctx);
-	duk_insert(ctx, -2);  /* -> [ ... errhandler undefined(= this) errval ] */
+	duk_insert(thr, -2);  /* -> [ ... errhandler errval ] */
+	duk_push_undefined(thr);
+	duk_insert(thr, -2);  /* -> [ ... errhandler undefined(= this) errval ] */
 
 	/* [ ... errhandler undefined errval ] */
 
 	/*
-	 *  DUK_CALL_FLAG_IGNORE_RECLIMIT causes duk_handle_call() to ignore C
-	 *  recursion depth limit (and won't increase it either).  This is
-	 *  dangerous, but useful because it allows the error handler to run
-	 *  even if the original error is caused by C recursion depth limit.
-	 *
-	 *  The heap level DUK_HEAP_FLAG_ERRHANDLER_RUNNING is set for the
-	 *  duration of the error handler and cleared afterwards.  This flag
-	 *  prevents the error handler from running recursively.  The flag is
-	 *  heap level so that the flag properly controls even coroutines
-	 *  launched by an error handler.  Since the flag is heap level, it is
-	 *  critical to restore it correctly.
+	 *  heap->augmenting_error prevents recursive re-entry and also causes
+	 *  call handling to use a larger (but not unbounded) call stack limit
+	 *  for the duration of error augmentation.
 	 *
 	 *  We ignore errors now: a success return and an error value both
 	 *  replace the original error value.  (This would be easy to change.)
 	 */
 
-	DUK_ASSERT(!DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));  /* since no recursive error handler calls */
-	DUK_HEAP_SET_ERRHANDLER_RUNNING(thr->heap);
-
-	call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT;  /* ignore reclimit, not constructor */
-
-	rc = duk_handle_call_protected(thr,
-	                               1,            /* num args */
-	                               call_flags);  /* call_flags */
+	DUK_ASSERT(thr->heap->augmenting_error == 0);
+	thr->heap->augmenting_error = 1;
+
+	rc = duk_pcall_method(thr, 1);
 	DUK_UNREF(rc);  /* no need to check now: both success and error are OK */
 
-	DUK_ASSERT(DUK_HEAP_HAS_ERRHANDLER_RUNNING(thr->heap));
-	DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(thr->heap);
+	DUK_ASSERT(thr->heap->augmenting_error == 1);
+	thr->heap->augmenting_error = 0;
 
 	/* [ ... errval ] */
 }
@@ -43666,12 +44975,10 @@
  */
 
 #if defined(DUK_USE_TRACEBACKS)
-DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_small_uint_t depth;
-	duk_int_t i, i_min;
+DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
+	duk_activation *act;
+	duk_int_t depth;
 	duk_int_t arr_size;
-	duk_harray *a;
 	duk_tval *tv;
 	duk_hstring *s;
 	duk_uint32_t u32;
@@ -43679,7 +44986,6 @@
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr_callstack != NULL);
-	DUK_ASSERT(ctx != NULL);
 
 	/* [ ... error ] */
 
@@ -43692,13 +44998,25 @@
 	 */
 
 	DUK_DDD(DUK_DDDPRINT("adding traceback to object: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* Preallocate array to correct size, so that we can just write out
 	 * the _Tracedata values into the array part.
 	 */
+	act = thr->callstack_curr;
 	depth = DUK_USE_TRACEBACK_DEPTH;
-	arr_size = (duk_int_t) (thr_callstack->callstack_top <= depth ? thr_callstack->callstack_top : depth) * 2;
+	DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
+	if (depth > (duk_int_t) thr_callstack->callstack_top) {
+		depth = (duk_int_t) thr_callstack->callstack_top;
+	}
+	if (depth > 0) {
+		if (flags & DUK_AUGMENT_FLAG_SKIP_ONE) {
+			DUK_ASSERT(act != NULL);
+			act = act->parent;
+			depth--;
+		}
+	}
+	arr_size = depth * 2;
 	if (thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL) {
 		arr_size += 2;
 	}
@@ -43707,15 +45025,14 @@
 		 * array part pointer to avoid any GC interference while the
 		 * array part is populated.
 		 */
-		duk_push_string(ctx, c_filename);
+		duk_push_string(thr, c_filename);
 		arr_size += 2;
 	}
 
+	/* XXX: uninitialized would be OK */
 	DUK_D(DUK_DPRINT("preallocated _Tracedata to %ld items", (long) arr_size));
-	a = duk_push_harray_with_size(ctx, (duk_uint32_t) arr_size);  /* XXX: call which returns array part pointer directly */
-	DUK_ASSERT(a != NULL);
-	tv = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
-	DUK_ASSERT(tv != NULL || arr_size == 0);
+	tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) arr_size);
+	DUK_ASSERT(arr_size == 0 || tv != NULL);
 
 	/* Compiler SyntaxErrors (and other errors) come first, and are
 	 * blamed by default (not flagged "noblame").
@@ -43746,40 +45063,26 @@
 		DUK_HSTRING_INCREF(thr, s);
 		tv++;
 
-		d = (noblame_fileline ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
+		d = ((flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) ? ((duk_double_t) DUK_TB_FLAG_NOBLAME_FILELINE) * DUK_DOUBLE_2TO32 : 0.0) +
 		    (duk_double_t) c_line;
 		DUK_TVAL_SET_DOUBLE(tv, d);
 		tv++;
 	}
 
-	/* traceback depth doesn't take into account the filename/line
-	 * special handling above (intentional)
-	 */
-	depth = DUK_USE_TRACEBACK_DEPTH;
-	i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
-	DUK_ASSERT(i_min >= 0);
-
-	/* [ ... error c_filename? arr ] */
-
-	DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
-	for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
+	/* Traceback depth doesn't take into account the filename/line
+	 * special handling above (intentional).
+	 */
+	for (; depth-- > 0; act = act->parent) {
 		duk_uint32_t pc;
 		duk_tval *tv_src;
 
-		/*
-		 *  Note: each API operation potentially resizes the callstack,
-		 *  so be careful to re-lookup after every operation.  Currently
-		 *  these is no issue because we don't store a temporary 'act'
-		 *  pointer at all.  (This would be a non-issue if we operated
-		 *  directly on the array part.)
-		 */
-
 		/* [... arr] */
 
-		DUK_ASSERT_DISABLE(thr_callstack->callstack[i].pc >= 0);  /* unsigned */
+		DUK_ASSERT(act != NULL);  /* depth check above, assumes book-keeping is correct */
+		DUK_ASSERT_DISABLE(act->pc >= 0);  /* unsigned */
 
 		/* Add function object. */
-		tv_src = &(thr_callstack->callstack + i)->tv_func;  /* object (function) or lightfunc */
+		tv_src = &act->tv_func;  /* object (function) or lightfunc */
 		DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_src) || DUK_TVAL_IS_LIGHTFUNC(tv_src));
 		DUK_TVAL_SET_TVAL(tv, tv_src);
 		DUK_TVAL_INCREF(thr, tv);
@@ -43790,26 +45093,33 @@
 		 * PC points to next instruction, find offending PC.  Note that
 		 * PC == 0 for native code.
 		 */
-		pc = duk_hthread_get_act_prev_pc(thr_callstack, thr_callstack->callstack + i);
+		pc = (duk_uint32_t) duk_hthread_get_act_prev_pc(thr_callstack, act);
 		DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
 		DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
-		d = ((duk_double_t) thr_callstack->callstack[i].flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
+		d = ((duk_double_t) act->flags) * DUK_DOUBLE_2TO32 + (duk_double_t) pc;
 		DUK_TVAL_SET_DOUBLE(tv, d);
 		tv++;
 	}
 
-	DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length);
-	DUK_ASSERT(a->length == (duk_uint32_t) arr_size);
+#if defined(DUK_USE_ASSERTIONS)
+	{
+		duk_harray *a;
+		a = (duk_harray *) duk_known_hobject(thr, -1);
+		DUK_ASSERT(a != NULL);
+		DUK_ASSERT((duk_uint32_t) (tv - DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a)) == a->length);
+		DUK_ASSERT(a->length == (duk_uint32_t) arr_size);
+	}
+#endif
 
 	/* [ ... error c_filename? arr ] */
 
 	if (c_filename) {
-		duk_remove_m2(ctx);
+		duk_remove_m2(thr);
 	}
 
 	/* [ ... error arr ] */
 
-	duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_TRACEDATA);  /* -> [ ... error ] */
+	duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INT_TRACEDATA);  /* -> [ ... error ] */
 }
 #endif  /* DUK_USE_TRACEBACKS */
 
@@ -43818,15 +45128,13 @@
  */
 
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE) && !defined(DUK_USE_TRACEBACKS)
-DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
-	duk_context *ctx;
+DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
 #if defined(DUK_USE_ASSERTIONS)
 	duk_int_t entry_top;
 #endif
 
-	ctx = (duk_context *) thr;
-#if defined(DUK_USE_ASSERTIONS)
-	entry_top = duk_get_top(ctx);
+#if defined(DUK_USE_ASSERTIONS)
+	entry_top = duk_get_top(thr);
 #endif
 
 	/*
@@ -43841,37 +45149,33 @@
 		/* Compiler SyntaxError (or other error) gets the primary blame.
 		 * Currently no flag to prevent blaming.
 		 */
-		duk_push_uint(ctx, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
-		duk_push_hstring(ctx, thr->compile_ctx->h_filename);
-	} else if (c_filename && !noblame_fileline) {
+		duk_push_uint(thr, (duk_uint_t) thr->compile_ctx->curr_token.start_line);
+		duk_push_hstring(thr, thr->compile_ctx->h_filename);
+	} else if (c_filename && (flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE) == 0) {
 		/* C call site gets blamed next, unless flagged not to do so.
 		 * XXX: file/line is disabled in minimal builds, so disable this
 		 * too when appropriate.
 		 */
-		duk_push_int(ctx, c_line);
-		duk_push_string(ctx, c_filename);
+		duk_push_int(thr, c_line);
+		duk_push_string(thr, c_filename);
 	} else {
 		/* Finally, blame the innermost callstack entry which has a
 		 * .fileName property.
 		 */
 		duk_small_uint_t depth;
-		duk_int_t i, i_min;
 		duk_uint32_t ecma_line;
-
-		depth = DUK_USE_TRACEBACK_DEPTH;
-		i_min = (thr_callstack->callstack_top > (duk_size_t) depth ? (duk_int_t) (thr_callstack->callstack_top - depth) : 0);
-		DUK_ASSERT(i_min >= 0);
+		duk_activation *act;
 
 		DUK_ASSERT(thr_callstack->callstack_top <= DUK_INT_MAX);  /* callstack limits */
-		for (i = (duk_int_t) (thr_callstack->callstack_top - 1); i >= i_min; i--) {
-			duk_activation *act;
+		depth = DUK_USE_TRACEBACK_DEPTH;
+		if (depth > thr_callstack->callstack_top) {
+			depth = thr_callstack->callstack_top;
+		}
+		for (act = thr_callstack->callstack_curr; depth-- > 0; act = act->parent) {
 			duk_hobject *func;
 			duk_uint32_t pc;
 
-			DUK_UNREF(pc);
-			act = thr_callstack->callstack + i;
-			DUK_ASSERT(act >= thr_callstack->callstack && act < thr_callstack->callstack + thr_callstack->callstack_size);
-
+			DUK_ASSERT(act != NULL);
 			func = DUK_ACT_GET_FUNC(act);
 			if (func == NULL) {
 				/* Lightfunc, not blamed now. */
@@ -43882,17 +45186,18 @@
 			 * PC == 0 for native code.
 			 */
 			pc = duk_hthread_get_act_prev_pc(thr, act);  /* thr argument only used for thr->heap, so specific thread doesn't matter */
+			DUK_UNREF(pc);
 			DUK_ASSERT_DISABLE(pc >= 0);  /* unsigned */
 			DUK_ASSERT((duk_double_t) pc < DUK_DOUBLE_2TO32);  /* assume PC is at most 32 bits and non-negative */
 			act = NULL;  /* invalidated by pushes, so get out of the way */
 
-			duk_push_hobject(ctx, func);
+			duk_push_hobject(thr, func);
 
 			/* [ ... error func ] */
 
-			duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_FILE_NAME);
-			if (!duk_is_string_notsymbol(ctx, -1)) {
-				duk_pop_2(ctx);
+			duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_FILE_NAME);
+			if (!duk_is_string_notsymbol(thr, -1)) {
+				duk_pop_2(thr);
 				continue;
 			}
 
@@ -43901,16 +45206,16 @@
 			ecma_line = 0;
 #if defined(DUK_USE_PC2LINE)
 			if (DUK_HOBJECT_IS_COMPFUNC(func)) {
-				ecma_line = duk_hobject_pc2line_query(ctx, -2, (duk_uint_fast32_t) pc);
+				ecma_line = duk_hobject_pc2line_query(thr, -2, (duk_uint_fast32_t) pc);
 			} else {
 				/* Native function, no relevant lineNumber. */
 			}
 #endif  /* DUK_USE_PC2LINE */
-			duk_push_u32(ctx, ecma_line);
+			duk_push_u32(thr, ecma_line);
 
 			/* [ ... error func fileName lineNumber ] */
 
-			duk_replace(ctx, -3);
+			duk_replace(thr, -3);
 
 			/* [ ... error lineNumber fileName ] */
 			goto define_props;
@@ -43920,17 +45225,17 @@
 		 * .lineNumber (matches what we do with a _Tracedata based
 		 * no-match lookup.
 		 */
-		duk_push_undefined(ctx);
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
+		duk_push_undefined(thr);
 	}
 
  define_props:
 	/* [ ... error lineNumber fileName ] */
 #if defined(DUK_USE_ASSERTIONS)
-	DUK_ASSERT(duk_get_top(ctx) == entry_top + 2);
-#endif
-	duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
+	DUK_ASSERT(duk_get_top(thr) == entry_top + 2);
+#endif
+	duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LINE_NUMBER, DUK_PROPDESC_FLAGS_C | DUK_PROPDESC_FLAG_NO_OVERWRITE);
 }
 #endif  /* DUK_USE_AUGMENT_ERROR_CREATE && !DUK_USE_TRACEBACKS */
 
@@ -43940,7 +45245,6 @@
 
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
 DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) {
-	duk_context *ctx;
 
 	/* Append a "(line NNN)" to the "message" property of any error
 	 * thrown during compilation.  Usually compilation errors are
@@ -43950,26 +45254,25 @@
 
 	/* [ ... error ] */
 
-	ctx = (duk_context *) thr;
-	DUK_ASSERT(duk_is_object(ctx, -1));
+	DUK_ASSERT(duk_is_object(thr, -1));
 
 	if (!(thr->compile_ctx != NULL && thr->compile_ctx->h_filename != NULL)) {
 		return;
 	}
 
 	DUK_DDD(DUK_DDDPRINT("compile error, before adding line info: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-	if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_MESSAGE)) {
-		duk_push_sprintf(ctx, " (line %ld)", (long) thr->compile_ctx->curr_token.start_line);
-		duk_concat(ctx, 2);
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_MESSAGE);
-	} else {
-		duk_pop(ctx);
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+
+	if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_MESSAGE)) {
+		duk_push_sprintf(thr, " (line %ld)", (long) thr->compile_ctx->curr_token.start_line);
+		duk_concat(thr, 2);
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_MESSAGE);
+	} else {
+		duk_pop(thr);
 	}
 
 	DUK_DDD(DUK_DDDPRINT("compile error, after adding line info: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 }
 #endif  /* DUK_USE_AUGMENT_ERROR_CREATE */
 
@@ -43979,19 +45282,17 @@
  */
 
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_int_t noblame_fileline, duk_hobject *obj) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_hobject *obj, duk_small_uint_t flags) {
 #if defined(DUK_USE_ASSERTIONS)
 	duk_int_t entry_top;
 #endif
 
 #if defined(DUK_USE_ASSERTIONS)
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 #endif
 	DUK_ASSERT(obj != NULL);
 
 	DUK_UNREF(obj);  /* unreferenced w/o tracebacks */
-	DUK_UNREF(ctx);  /* unreferenced w/o asserts */
 
 	duk__add_compiler_error_line(thr);
 
@@ -44003,17 +45304,17 @@
 	if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_TRACEDATA(thr))) {
 		DUK_DDD(DUK_DDDPRINT("error value already has a '_Tracedata' property, not modifying it"));
 	} else {
-		duk__add_traceback(thr, thr_callstack, c_filename, c_line, noblame_fileline);
+		duk__add_traceback(thr, thr_callstack, c_filename, c_line, flags);
 	}
 #else
 	/* Without tracebacks the concrete .fileName and .lineNumber need
 	 * to be added directly.
 	 */
-	duk__add_fileline(thr, thr_callstack, c_filename, c_line, noblame_fileline);
-#endif
-
-#if defined(DUK_USE_ASSERTIONS)
-	DUK_ASSERT(duk_get_top(ctx) == entry_top);
+	duk__add_fileline(thr, thr_callstack, c_filename, c_line, flags);
+#endif
+
+#if defined(DUK_USE_ASSERTIONS)
+	DUK_ASSERT(duk_get_top(thr) == entry_top);
 #endif
 }
 #endif  /* DUK_USE_AUGMENT_ERROR_CREATE */
@@ -44027,22 +45328,18 @@
  *  thr_callstack: thread which should be used for generating callstack etc.
  *  c_filename: C __FILE__ related to the error
  *  c_line: C __LINE__ related to the error
- *  noblame_fileline: if true, don't fileName/line as error source, otherwise use traceback
- *                    (needed because user code filename/line are reported but internal ones
- *                    are not)
- *
- *  XXX: rename noblame_fileline to flags field; combine it to some existing
- *  field (there are only a few call sites so this may not be worth it).
+ *  flags & DUK_AUGMENT_FLAG_NOBLAME_FILELINE:
+ *      if true, don't fileName/line as error source, otherwise use traceback
+ *      (needed because user code filename/line are reported but internal ones
+ *      are not)
  */
 
 #if defined(DUK_USE_AUGMENT_ERROR_CREATE)
-DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_bool_t noblame_fileline) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *thr_callstack, const char *c_filename, duk_int_t c_line, duk_small_uint_t flags) {
 	duk_hobject *obj;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr_callstack != NULL);
-	DUK_ASSERT(ctx != NULL);
 
 	/* [ ... error ] */
 
@@ -44058,7 +45355,7 @@
 	 *   - error value is an extensible object
 	 */
 
-	obj = duk_get_hobject(ctx, -1);
+	obj = duk_get_hobject(thr, -1);
 	if (!obj) {
 		DUK_DDD(DUK_DDDPRINT("value is not an object, skip both built-in and user augment"));
 		return;
@@ -44073,7 +45370,7 @@
 	}
 	if (DUK_HOBJECT_HAS_EXTENSIBLE(obj)) {
 		DUK_DDD(DUK_DDDPRINT("error meets criteria, built-in augment"));
-		duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, noblame_fileline, obj);
+		duk__err_augment_builtin_create(thr, thr_callstack, c_filename, c_line, obj, flags);
 	} else {
 		DUK_DDD(DUK_DDDPRINT("error does not meet criteria, no built-in augment"));
 	}
@@ -44109,32 +45406,32 @@
 
 #if defined(DUK_USE_PREFER_SIZE)
 DUK_LOCAL void duk__uncaught_minimal(duk_hthread *thr) {
-	(void) duk_fatal((duk_context *) thr, "uncaught error");
+	(void) duk_fatal(thr, "uncaught error");
 }
 #endif
 
 #if 0
 DUK_LOCAL void duk__uncaught_readable(duk_hthread *thr) {
 	const char *summary;
-	char buf[64];
-
-	summary = duk_push_string_tval_readable((duk_context *) thr, &thr->heap->lj.value1);
+	char buf[DUK_USE_FATAL_MAXLEN];
+
+	summary = duk_push_string_tval_readable(thr, &thr->heap->lj.value1);
 	DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary);
 	buf[sizeof(buf) - 1] = (char) 0;
-	(void) duk_fatal((duk_context *) thr, (const char *) buf);
+	(void) duk_fatal(thr, (const char *) buf);
 }
 #endif
 
 #if !defined(DUK_USE_PREFER_SIZE)
 DUK_LOCAL void duk__uncaught_error_aware(duk_hthread *thr) {
 	const char *summary;
-	char buf[64];
-
-	summary = duk_push_string_tval_readable_error((duk_context *) thr, &thr->heap->lj.value1);
+	char buf[DUK_USE_FATAL_MAXLEN];
+
+	summary = duk_push_string_tval_readable_error(thr, &thr->heap->lj.value1);
 	DUK_ASSERT(summary != NULL);
 	DUK_SNPRINTF(buf, sizeof(buf), "uncaught: %s", summary);
 	buf[sizeof(buf) - 1] = (char) 0;
-	(void) duk_fatal((duk_context *) thr, (const char *) buf);
+	(void) duk_fatal(thr, (const char *) buf);
 }
 #endif
 
@@ -44218,32 +45515,31 @@
  *  catcher.  Protected calls or finally blocks aren't considered catching.
  */
 
-#if defined(DUK_USE_DEBUGGER_SUPPORT) && \
-    (defined(DUK_USE_DEBUGGER_THROW_NOTIFY) || defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT))
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
 DUK_LOCAL duk_bool_t duk__have_active_catcher(duk_hthread *thr) {
-	/*
-	 * XXX: As noted above, a protected API call won't be counted as a
-	 * catcher. This is usually convenient, e.g. in the case of a top-
-	 * level duk_pcall(), but may not always be desirable. Perhaps add an
-	 * argument to treat them as catchers?
-	 */
-
-	duk_size_t i;
-
-	DUK_ASSERT(thr != NULL);
-
-	while (thr != NULL) {
-		for (i = 0; i < thr->catchstack_top; i++) {
-			duk_catcher *cat = thr->catchstack + i;
-			if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
-				return 1;  /* all we need to know */
-			}
-		}
-		thr = thr->resumer;
-	}
-	return 0;
-}
-#endif  /* DUK_USE_DEBUGGER_SUPPORT && (DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) */
+	/* As noted above, a protected API call won't be counted as a
+	 * catcher.  This is usually convenient, e.g. in the case of a top-
+	 * level duk_pcall(), but may not always be desirable.  Perhaps add
+	 * an argument to treat them as catchers?
+	 */
+
+	duk_activation *act;
+	duk_catcher *cat;
+
+	DUK_ASSERT(thr != NULL);
+
+	for (; thr != NULL; thr = thr->resumer) {
+		for (act = thr->callstack_curr; act != NULL; act = act->parent) {
+			for (cat = act->cat; cat != NULL; cat = cat->parent) {
+				if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
+					return 1;  /* all we need to know */
+				}
+			}
+		}
+	}
+	return 0;
+}
+#endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
 /*
  *  Get prototype object for an integer error code.
@@ -44274,10 +45570,8 @@
  */
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
-#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) || defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
 DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_bool_t fatal;
+	duk_bool_t uncaught;
 	duk_tval *tv_obj;
 
 	/* If something is thrown with the debugger attached and nobody will
@@ -44315,14 +45609,14 @@
 		return;
 	}
 
-	fatal = !duk__have_active_catcher(thr);
+	uncaught = !duk__have_active_catcher(thr);
 
 	/* Debugger code expects the value at stack top.  This also serves
 	 * as a backup: we need to store/restore the longjmp state because
 	 * when the debugger is paused Eval commands may be executed and
 	 * they can arbitrarily clobber the longjmp state.
 	 */
-	duk_push_tval(ctx, tv_obj);
+	duk_push_tval(thr, tv_obj);
 
 	/* Store and reset longjmp state. */
 	DUK_ASSERT_LJSTATE_SET(thr->heap);
@@ -44335,33 +45629,33 @@
 #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY)
 	/* Report it to the debug client */
 	DUK_D(DUK_DPRINT("throw with debugger attached, report to client"));
-	duk_debug_send_throw(thr, fatal);
-#endif
-
-#if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT)
-	if (fatal) {
-		DUK_D(DUK_DPRINT("throw will be fatal, halt before longjmp"));
-		duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
-	}
-#endif
+	duk_debug_send_throw(thr, uncaught);
+#endif
+
+	if (uncaught) {
+		if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_UNCAUGHT_ERROR) {
+			DUK_D(DUK_DPRINT("PAUSE TRIGGERED by uncaught error"));
+			duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
+		}
+	} else {
+		if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_CAUGHT_ERROR) {
+			DUK_D(DUK_DPRINT("PAUSE TRIGGERED by caught error"));
+			duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
+		}
+	}
 
 	/* Restore longjmp state. */
 	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
 	thr->heap->lj.type = DUK_LJ_TYPE_THROW;
-	tv_obj = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv_obj = DUK_GET_TVAL_NEGIDX(thr, -1);
 	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
 	DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
 	DUK_TVAL_SET_TVAL(&thr->heap->lj.value1, tv_obj);
 	DUK_TVAL_INCREF(thr, tv_obj);
 	DUK_ASSERT_LJSTATE_SET(thr->heap);
 
-	duk_pop(ctx);
-}
-#else  /* DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT */
-DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) {
-	DUK_UNREF(thr);
-}
-#endif  /* DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT */
+	duk_pop(thr);
+}
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
 /*
@@ -44412,8 +45706,6 @@
 #else
 DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) {
 #endif
-	duk_context *ctx = (duk_context *) thr;
-
 #if defined(DUK_USE_VERBOSE_ERRORS)
 	DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld",
 	                   (long) code, (const char *) msg,
@@ -44423,7 +45715,6 @@
 #endif
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 
 	/* Even though nested call is possible because we throw an error when
 	 * trying to create an error, the potential errors must happen before
@@ -44449,10 +45740,6 @@
 		duk_tval tv_val;
 		duk_hobject *h_err;
 
-#if 0  /* XXX: not always true because the second throw may come from a different coroutine */
-		DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11);
-#endif
-		thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX;
 		thr->heap->creating_error = 0;
 
 		h_err = thr->builtins[DUK_BIDX_DOUBLE_ERROR];
@@ -44469,30 +45756,27 @@
 
 		/* No augmentation to avoid any allocations or side effects. */
 	} else {
-		/* Allow headroom for calls during error handling (see GH-191).
-		 * We allow space for 10 additional recursions, with one extra
-		 * for, e.g. a print() call at the deepest level.
-		 */
-#if 0  /* XXX: not always true, second throw may come from a different coroutine */
-		DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX);
-#endif
-		thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11;
+		/* Prevent infinite recursion.  Extra call stack and C
+		 * recursion headroom (see GH-191) is added for augmentation.
+		 * That is now signalled by heap->augmenting error and taken
+		 * into account in call handling without an explicit limit bump.
+		 */
 		thr->heap->creating_error = 1;
 
-		duk_require_stack(ctx, 1);
+		duk_require_stack(thr, 1);
 
 		/* XXX: usually unnecessary '%s' formatting here, but cannot
 		 * use 'msg' as a format string directly.
 		 */
 #if defined(DUK_USE_VERBOSE_ERRORS)
-		duk_push_error_object_raw(ctx,
+		duk_push_error_object_raw(thr,
 		                          code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
 		                          filename,
 		                          line,
 		                          "%s",
 		                          (const char *) msg);
 #else
-		duk_push_error_object_raw(ctx,
+		duk_push_error_object_raw(thr,
 		                          code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
 		                          NULL,
 		                          0,
@@ -44507,12 +45791,11 @@
 		 */
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
 		DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)",
-		                     (duk_tval *) duk_get_tval(ctx, -1)));
+		                     (duk_tval *) duk_get_tval(thr, -1)));
 		duk_err_augment_error_throw(thr);
 #endif
 
-		duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1));
-		thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX;
+		duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1));
 		thr->heap->creating_error = 0;
 
 		/* Error is now created and we assume no errors can occur any
@@ -44542,8 +45825,6 @@
  */
 
 DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc) {
-	duk_context *ctx = (duk_context *) thr;
-
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(rc < 0);
 
@@ -44557,7 +45838,7 @@
 	 *  minimal: they're only really useful for low memory targets.
 	 */
 
-	duk_error_raw(ctx, -rc, NULL, 0, "error (rc %ld)", (long) rc);
+	duk_error_raw(thr, -rc, NULL, 0, "error (rc %ld)", (long) rc);
 	DUK_UNREACHABLE();
 }
 #line 1 "duk_hbuffer_alloc.c"
@@ -44697,12 +45978,6 @@
 /*
  *  duk_hbuffer operations such as resizing and inserting/appending data to
  *  a dynamic buffer.
- *
- *  Append operations append to the end of the buffer and they are relatively
- *  efficient: the buffer is grown with a "spare" part relative to the buffer
- *  size to minimize reallocations.  Insert operations need to move existing
- *  data forward in the buffer with memmove() and are not very efficient.
- *  They are used e.g. by the regexp compiler to "backpatch" regexp bytecode.
  */
 
 /* #include duk_internal.h -> already included */
@@ -44834,19 +46109,43 @@
 		/* Currently nothing to free */
 	} else if (DUK_HOBJECT_IS_THREAD(h)) {
 		duk_hthread *t = (duk_hthread *) h;
+		duk_activation *act;
+
 		DUK_FREE(heap, t->valstack);
-		DUK_FREE(heap, t->callstack);
-		DUK_FREE(heap, t->catchstack);
+
 		/* Don't free h->resumer because it exists in the heap.
 		 * Callstack entries also contain function pointers which
-		 * are not freed for the same reason.
-		 */
+		 * are not freed for the same reason.  They are decref
+		 * finalized and the targets are freed if necessary based
+		 * on their refcount (or reachability).
+		 */
+		for (act = t->callstack_curr; act != NULL;) {
+			duk_activation *act_next;
+			duk_catcher *cat;
+
+			for (cat = act->cat; cat != NULL;) {
+				duk_catcher *cat_next;
+
+				cat_next = cat->parent;
+				DUK_FREE(heap, (void *) cat);
+				cat = cat_next;
+			}
+
+			act_next = act->parent;
+			DUK_FREE(heap, (void *) act);
+			act = act_next;
+		}
 
 		/* XXX: with 'caller' property the callstack would need
 		 * to be unwound to update the 'caller' properties of
 		 * functions in the callstack.
 		 */
-	}
+	} else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
+		duk_hboundfunc *f = (duk_hboundfunc *) h;
+
+		DUK_FREE(heap, f->args);
+	}
+
 	DUK_FREE(heap, (void *) h);
 }
 
@@ -44912,6 +46211,63 @@
  *  after this call.
  */
 
+#if defined(DUK_USE_CACHE_ACTIVATION)
+DUK_LOCAL duk_size_t duk__heap_free_activation_freelist(duk_heap *heap) {
+	duk_activation *act;
+	duk_activation *act_next;
+	duk_size_t count_act = 0;
+
+	for (act = heap->activation_free; act != NULL;) {
+		act_next = act->parent;
+		DUK_FREE(heap, (void *) act);
+		act = act_next;
+#if defined(DUK_USE_DEBUG)
+		count_act++;
+#endif
+	}
+	heap->activation_free = NULL;  /* needed when called from mark-and-sweep */
+	return count_act;
+}
+#endif  /* DUK_USE_CACHE_ACTIVATION */
+
+#if defined(DUK_USE_CACHE_CATCHER)
+DUK_LOCAL duk_size_t duk__heap_free_catcher_freelist(duk_heap *heap) {
+	duk_catcher *cat;
+	duk_catcher *cat_next;
+	duk_size_t count_cat = 0;
+
+	for (cat = heap->catcher_free; cat != NULL;) {
+		cat_next = cat->parent;
+		DUK_FREE(heap, (void *) cat);
+		cat = cat_next;
+#if defined(DUK_USE_DEBUG)
+		count_cat++;
+#endif
+	}
+	heap->catcher_free = NULL;  /* needed when called from mark-and-sweep */
+
+	return count_cat;
+}
+#endif  /* DUK_USE_CACHE_CATCHER */
+
+DUK_INTERNAL void duk_heap_free_freelists(duk_heap *heap) {
+	duk_size_t count_act = 0;
+	duk_size_t count_cat = 0;
+
+#if defined(DUK_USE_CACHE_ACTIVATION)
+	count_act = duk__heap_free_activation_freelist(heap);
+#endif
+#if defined(DUK_USE_CACHE_CATCHER)
+	count_cat = duk__heap_free_catcher_freelist(heap);
+#endif
+	DUK_UNREF(heap);
+	DUK_UNREF(count_act);
+	DUK_UNREF(count_cat);
+
+	DUK_D(DUK_DPRINT("freed %ld activation freelist entries, %ld catcher freelist entries",
+	                 (long) count_act, (long) count_cat));
+}
+
 DUK_LOCAL void duk__free_allocated(duk_heap *heap) {
 	duk_heaphdr *curr;
 	duk_heaphdr *next;
@@ -45117,6 +46473,9 @@
 	 * are on the heap allocated list.
 	 */
 
+	DUK_D(DUK_DPRINT("freeing temporary freelists"));
+	duk_heap_free_freelists(heap);
+
 	DUK_D(DUK_DPRINT("freeing heap_allocated of heap: %p", (void *) heap));
 	duk__free_allocated(heap);
 
@@ -45273,8 +46632,8 @@
 	/* XXX: this may now fail, and is not handled correctly */
 	duk_hthread_create_builtin_objects(thr);
 
-	/* default prototype (Note: 'thr' must be reachable) */
-	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
+	/* default prototype */
+	DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) thr, thr->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
 
 	return 1;
 }
@@ -45390,6 +46749,7 @@
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 	DUK__DUMPSZ(duk_hbufobj);
 #endif
+	DUK__DUMPSZ(duk_hproxy);
 	DUK__DUMPSZ(duk_hbuffer);
 	DUK__DUMPSZ(duk_hbuffer_fixed);
 	DUK__DUMPSZ(duk_hbuffer_dynamic);
@@ -45627,6 +46987,12 @@
 	res->currently_finalizing = NULL;
 #endif
 #endif
+#if defined(DUK_USE_CACHE_ACTIVATION)
+	res->activation_free = NULL;
+#endif
+#if defined(DUK_USE_CACHE_CATCHER)
+	res->catcher_free = NULL;
+#endif
 	res->heap_thread = NULL;
 	res->curr_thread = NULL;
 	res->heap_object = NULL;
@@ -45657,7 +47023,7 @@
 	res->dbg_write_flush_cb = NULL;
 	res->dbg_request_cb = NULL;
 	res->dbg_udata = NULL;
-	res->dbg_step_thread = NULL;
+	res->dbg_pause_act = NULL;
 #endif
 #endif  /* DUK_USE_EXPLICIT_NULL_INIT */
 
@@ -45819,14 +47185,14 @@
 
 #if !defined(DUK_USE_GET_RANDOM_DOUBLE)
 #if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS)
-	res->rnd_state = (duk_uint32_t) DUK_USE_DATE_GET_NOW((duk_context *) res->heap_thread);
+	res->rnd_state = (duk_uint32_t) duk_time_get_ecmascript_time(res->heap_thread);
 	duk_util_tinyrandom_prepare_seed(res->heap_thread);
 #else
-	res->rnd_state[0] = (duk_uint64_t) DUK_USE_DATE_GET_NOW((duk_context *) res->heap_thread);
+	res->rnd_state[0] = (duk_uint64_t) duk_time_get_ecmascript_time(res->heap_thread);
 	DUK_ASSERT(res->rnd_state[1] == 0);  /* Not filled here, filled in by seed preparation. */
 #if 0  /* Manual test values matching misc/xoroshiro128plus_test.c. */
-	res->rnd_state[0] = 0xdeadbeef12345678ULL;
-	res->rnd_state[1] = 0xcafed00d12345678ULL;
+	res->rnd_state[0] = DUK_U64_CONSTANT(0xdeadbeef12345678);
+	res->rnd_state[1] = DUK_U64_CONSTANT(0xcafed00d12345678);
 #endif
 	duk_util_tinyrandom_prepare_seed(res->heap_thread);
 	/* Mix in heap pointer: this ensures that if two Duktape heaps are
@@ -45924,21 +47290,19 @@
  */
 
 #if defined(DUK_USE_FINALIZER_TORTURE)
-DUK_LOCAL duk_ret_t duk__fake_global_finalizer(duk_context *ctx) {
+DUK_LOCAL duk_ret_t duk__fake_global_finalizer(duk_hthread *thr) {
 	DUK_DD(DUK_DDPRINT("fake global torture finalizer executed"));
 
 	/* Require a lot of stack to force a value stack grow/shrink. */
-	duk_require_stack(ctx, 100000);
-
-	/* Force a reallocation with pointer change for value, call, and
-	 * catch stacks to maximize side effects.
-	 */
-	duk_hthread_valstack_torture_realloc((duk_hthread *) ctx);
-	duk_hthread_callstack_torture_realloc((duk_hthread *) ctx);
-	duk_hthread_catchstack_torture_realloc((duk_hthread *) ctx);
+	duk_require_stack(thr, 100000);
+
+	/* Force a reallocation with pointer change for value stack
+	 * to maximize side effects.
+	 */
+	duk_hthread_valstack_torture_realloc(thr);
 
 	/* Inner function call, error throw. */
-	duk_eval_string_noresult(ctx,
+	duk_eval_string_noresult(thr,
 		"(function dummy() {\n"
 		"    dummy.prototype = null;  /* break reference loop */\n"
 		"    try {\n"
@@ -45963,19 +47327,20 @@
 DUK_LOCAL void duk__run_global_torture_finalizer(duk_hthread *thr) {
 	DUK_ASSERT(thr != NULL);
 
-	/* Avoid fake finalization when callstack limit has been reached.
-	 * Otherwise a callstack limit error will be created, then refzero'ed.
-	 */
-	if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit ||
-	    thr->callstack_size + 2 * DUK_CALLSTACK_GROW_STEP >= thr->callstack_max /*approximate*/) {
-		DUK_D(DUK_DPRINT("skip global torture finalizer because of call recursion or call stack size limit"));
+	/* Avoid fake finalization when callstack limit is near.  Otherwise
+	 * a callstack limit error will be created, then refzero'ed.  The
+	 * +5 headroom is conservative.
+	 */
+	if (thr->heap->call_recursion_depth + 5 >= thr->heap->call_recursion_limit ||
+	    thr->callstack_top + 5 >= DUK_USE_CALLSTACK_LIMIT) {
+		DUK_D(DUK_DPRINT("skip global torture finalizer, too little headroom for call recursion or call stack size"));
 		return;
 	}
 
 	/* Run fake finalizer.  Avoid creating unnecessary garbage. */
-	duk_push_c_function((duk_context *) thr, duk__fake_global_finalizer, 0 /*nargs*/);
-	(void) duk_pcall((duk_context *) thr, 0 /*nargs*/);
-	duk_pop((duk_context *) thr);
+	duk_push_c_function(thr, duk__fake_global_finalizer, 0 /*nargs*/);
+	(void) duk_pcall(thr, 0 /*nargs*/);
+	duk_pop(thr);
 }
 #endif  /* DUK_USE_FINALIZER_TORTURE */
 
@@ -46057,8 +47422,6 @@
 	DUK_ASSERT(heap != NULL);
 	DUK_ASSERT(heap->heap_thread != NULL);
 	DUK_ASSERT(heap->heap_thread->valstack != NULL);
-	DUK_ASSERT(heap->heap_thread->callstack != NULL);
-	DUK_ASSERT(heap->heap_thread->catchstack != NULL);
 #if defined(DUK_USE_REFERENCE_COUNTING)
 	DUK_ASSERT(heap->refzero_list == NULL);
 #endif
@@ -46253,11 +47616,8 @@
  *      left on the finalizer stack).
  */
 
-DUK_LOCAL duk_ret_t duk__finalize_helper(duk_context *ctx, void *udata) {
-	duk_hthread *thr;
-
-	DUK_ASSERT(ctx != NULL);
-	thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_ret_t duk__finalize_helper(duk_hthread *thr, void *udata) {
+	DUK_ASSERT(thr != NULL);
 	DUK_UNREF(udata);
 
 	DUK_DDD(DUK_DDDPRINT("protected finalization helper running"));
@@ -46274,11 +47634,11 @@
 	 * caller must ensure that this function is not called if the target is
 	 * a Proxy.
 	 */
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_FINALIZER);  /* -> [... obj finalizer] */
-	duk_dup_m2(ctx);
-	duk_push_boolean(ctx, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap));
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_FINALIZER);  /* -> [... obj finalizer] */
+	duk_dup_m2(thr);
+	duk_push_boolean(thr, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap));
 	DUK_DDD(DUK_DDDPRINT("calling finalizer"));
-	duk_call(ctx, 2);  /* [ ... obj finalizer obj heapDestruct ]  -> [ ... obj retval ] */
+	duk_call(thr, 2);  /* [ ... obj finalizer obj heapDestruct ]  -> [ ... obj retval ] */
 	DUK_DDD(DUK_DDDPRINT("finalizer returned successfully"));
 	return 0;
 
@@ -46289,7 +47649,7 @@
 }
 
 DUK_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) {
-	duk_context *ctx;
+	duk_hthread *thr;
 	duk_ret_t rc;
 #if defined(DUK_USE_ASSERTIONS)
 	duk_idx_t entry_top;
@@ -46299,12 +47659,12 @@
 
 	DUK_ASSERT(heap != NULL);
 	DUK_ASSERT(heap->heap_thread != NULL);
-	ctx = (duk_context *) heap->heap_thread;
+	thr = heap->heap_thread;
 	DUK_ASSERT(obj != NULL);
 	DUK_ASSERT_VALSTACK_SPACE(heap->heap_thread, 1);
 
 #if defined(DUK_USE_ASSERTIONS)
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 #endif
 	/*
 	 *  Get and call the finalizer.  All of this must be wrapped
@@ -46329,30 +47689,32 @@
 #endif
 	DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj);  /* ensure never re-entered until rescue cycle complete */
 
-	if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) {
+#if defined(DUK_USE_ES6_PROXY)
+	if (DUK_HOBJECT_IS_PROXY(obj)) {
 		/* This may happen if duk_set_finalizer() or Duktape.fin() is
 		 * called for a Proxy object.  In such cases the fast finalizer
 		 * flag will be set on the Proxy, not the target, and neither
 		 * will be finalized.
 		 */
-		DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call"));
-		return;
-	}
-
-	duk_push_hobject(ctx, obj);  /* this also increases refcount by one */
-	rc = duk_safe_call(ctx, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/);  /* -> [... obj retval/error] */
-	DUK_ASSERT_TOP(ctx, entry_top + 2);  /* duk_safe_call discipline */
+		DUK_D(DUK_DPRINT("object is a Proxy, skip finalizer call"));
+		return;
+	}
+#endif  /* DUK_USE_ES6_PROXY */
+
+	duk_push_hobject(thr, obj);  /* this also increases refcount by one */
+	rc = duk_safe_call(thr, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/);  /* -> [... obj retval/error] */
+	DUK_ASSERT_TOP(thr, entry_top + 2);  /* duk_safe_call discipline */
 
 	if (rc != DUK_EXEC_SUCCESS) {
 		/* Note: we ask for one return value from duk_safe_call to get this
 		 * error debugging here.
 		 */
 		DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T",
-		                 (void *) obj, (duk_tval *) duk_get_tval(ctx, -1)));
-	}
-	duk_pop_2(ctx);  /* -> [...] */
-
-	DUK_ASSERT_TOP(ctx, entry_top);
+		                 (void *) obj, (duk_tval *) duk_get_tval(thr, -1)));
+	}
+	duk_pop_2(thr);  /* -> [...] */
+
+	DUK_ASSERT_TOP(thr, entry_top);
 }
 
 #else  /* DUK_USE_FINALIZER_SUPPORT */
@@ -46490,7 +47852,9 @@
 /* #include duk_internal.h -> already included */
 
 DUK_LOCAL_DECL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h);
+DUK_LOCAL_DECL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h);
 DUK_LOCAL_DECL void duk__mark_tval(duk_heap *heap, duk_tval *tv);
+DUK_LOCAL_DECL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count);
 
 /*
  *  Marking functions for heap types: mark children recursively.
@@ -46520,7 +47884,7 @@
 		if (key == NULL) {
 			continue;
 		}
-		duk__mark_heaphdr(heap, (duk_heaphdr *) key);
+		duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) key);
 		if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, h, i)) {
 			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.get);
 			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_PTR(heap, h, i)->a.set);
@@ -46546,11 +47910,14 @@
 	}
 	DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h));
 
+	/* XXX: reorg, more common first */
 	if (DUK_HOBJECT_IS_COMPFUNC(h)) {
 		duk_hcompfunc *f = (duk_hcompfunc *) h;
 		duk_tval *tv, *tv_end;
 		duk_hobject **fn, **fn_end;
 
+		DUK_ASSERT_HCOMPFUNC_VALID(f);
+
 		/* 'data' is reachable through every compiled function which
 		 * contains a reference.
 		 */
@@ -46570,19 +47937,13 @@
 			fn = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f);
 			fn_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f);
 			while (fn < fn_end) {
-				duk__mark_heaphdr(heap, (duk_heaphdr *) *fn);
+				duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) *fn);
 				fn++;
 			}
 		} else {
 			/* May happen in some out-of-memory corner cases. */
 			DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping marking"));
 		}
-#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
-	} else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
-		duk_hbufobj *b = (duk_hbufobj *) h;
-		duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf);
-		duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf_prop);
-#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
 	} else if (DUK_HOBJECT_IS_DECENV(h)) {
 		duk_hdecenv *e = (duk_hdecenv *) h;
 		DUK_ASSERT_HDECENV_VALID(e);
@@ -46591,32 +47952,52 @@
 	} else if (DUK_HOBJECT_IS_OBJENV(h)) {
 		duk_hobjenv *e = (duk_hobjenv *) h;
 		DUK_ASSERT_HOBJENV_VALID(e);
-		duk__mark_heaphdr(heap, (duk_heaphdr *) e->target);
+		duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) e->target);
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+	} else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
+		duk_hbufobj *b = (duk_hbufobj *) h;
+		DUK_ASSERT_HBUFOBJ_VALID(b);
+		duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf);
+		duk__mark_heaphdr(heap, (duk_heaphdr *) b->buf_prop);
+#endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
+	} else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
+		duk_hboundfunc *f = (duk_hboundfunc *) h;
+		DUK_ASSERT_HBOUNDFUNC_VALID(f);
+		duk__mark_tval(heap, &f->target);
+		duk__mark_tval(heap, &f->this_binding);
+		duk__mark_tvals(heap, f->args, f->nargs);
+#if defined(DUK_USE_ES6_PROXY)
+	} else if (DUK_HOBJECT_IS_PROXY(h)) {
+		duk_hproxy *p = (duk_hproxy *) h;
+		DUK_ASSERT_HPROXY_VALID(p);
+		duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->target);
+		duk__mark_heaphdr_nonnull(heap, (duk_heaphdr *) p->handler);
+#endif  /* DUK_USE_ES6_PROXY */
 	} else if (DUK_HOBJECT_IS_THREAD(h)) {
 		duk_hthread *t = (duk_hthread *) h;
+		duk_activation *act;
 		duk_tval *tv;
 
+		DUK_ASSERT_HTHREAD_VALID(t);
+
 		tv = t->valstack;
 		while (tv < t->valstack_top) {
 			duk__mark_tval(heap, tv);
 			tv++;
 		}
 
-		for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
-			duk_activation *act = t->callstack + i;
+		for (act = t->callstack_curr; act != NULL; act = act->parent) {
 			duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_ACT_GET_FUNC(act));
 			duk__mark_heaphdr(heap, (duk_heaphdr *) act->var_env);
 			duk__mark_heaphdr(heap, (duk_heaphdr *) act->lex_env);
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
 			duk__mark_heaphdr(heap, (duk_heaphdr *) act->prev_caller);
 #endif
-		}
-
 #if 0  /* nothing now */
-		for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) {
-			duk_catcher *cat = t->catchstack + i;
-		}
-#endif
+			for (cat = act->cat; cat != NULL; cat = cat->parent) {
+			}
+#endif
+		}
 
 		duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer);
 
@@ -46638,22 +48019,30 @@
 	DUK_DDD(DUK_DDDPRINT("duk__mark_heaphdr %p, type %ld",
 	                     (void *) h,
 	                     (h != NULL ? (long) DUK_HEAPHDR_GET_TYPE(h) : (long) -1)));
+
+	/* XXX: add non-null variant? */
 	if (h == NULL) {
 		return;
 	}
+
+	DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h) || DUK_HEAPHDR_HAS_REACHABLE(h));
+
+#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
+	if (!DUK_HEAPHDR_HAS_READONLY(h)) {
+		h->h_assert_refcount++;  /* Comparison refcount: bump even if already reachable. */
+	}
+#endif
+	if (DUK_HEAPHDR_HAS_REACHABLE(h)) {
+		DUK_DDD(DUK_DDDPRINT("already marked reachable, skip"));
+		return;
+	}
 #if defined(DUK_USE_ROM_OBJECTS)
-	if (DUK_HEAPHDR_HAS_READONLY(h)) {
-		DUK_DDD(DUK_DDDPRINT("readonly object %p, skip", (void *) h));
-		return;
-	}
-#endif
-#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
-	h->h_assert_refcount++;  /* Comparison refcount: bump even if already reachable. */
-#endif
-	if (DUK_HEAPHDR_HAS_REACHABLE(h)) {
-		DUK_DDD(DUK_DDDPRINT("already marked reachable, skip"));
-		return;
-	}
+	/* READONLY objects always have REACHABLE set, so the check above
+	 * will prevent READONLY objects from being marked here.
+	 */
+	DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h));
+#endif
+
 	DUK_HEAPHDR_SET_REACHABLE(h);
 
 	if (heap->ms_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) {
@@ -46691,8 +48080,33 @@
 		return;
 	}
 	if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
-		duk__mark_heaphdr(heap, DUK_TVAL_GET_HEAPHDR(tv));
-	}
+		duk_heaphdr *h;
+		h = DUK_TVAL_GET_HEAPHDR(tv);
+		DUK_ASSERT(h != NULL);
+		duk__mark_heaphdr_nonnull(heap, h);
+	}
+}
+
+DUK_LOCAL void duk__mark_tvals(duk_heap *heap, duk_tval *tv, duk_idx_t count) {
+	DUK_ASSERT(count == 0 || tv != NULL);
+
+	while (count-- > 0) {
+		if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
+			duk_heaphdr *h;
+			h = DUK_TVAL_GET_HEAPHDR(tv);
+			DUK_ASSERT(h != NULL);
+			duk__mark_heaphdr_nonnull(heap, h);
+		}
+		tv++;
+	}
+}
+
+/* Mark any duk_heaphdr type, caller guarantees a non-NULL pointer. */
+DUK_LOCAL void duk__mark_heaphdr_nonnull(duk_heap *heap, duk_heaphdr *h) {
+	/* For now, just call the generic handler.  Change when call sites
+	 * are changed too.
+	 */
+	duk__mark_heaphdr(heap, h);
 }
 
 /*
@@ -46785,7 +48199,7 @@
 	hdr = heap->heap_allocated;
 	while (hdr != NULL) {
 		if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) {
-			duk__mark_heaphdr(heap, hdr);
+			duk__mark_heaphdr_nonnull(heap, hdr);
 		}
 
 		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
@@ -46810,7 +48224,7 @@
 
 	hdr = heap->finalize_list;
 	while (hdr != NULL) {
-		duk__mark_heaphdr(heap, hdr);
+		duk__mark_heaphdr_nonnull(heap, hdr);
 		hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr);
 #if defined(DUK_USE_DEBUG)
 		count_finalize_list++;
@@ -46848,6 +48262,8 @@
 #else
 DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) {
 #endif
+	DUK_ASSERT(hdr != NULL);
+
 	if (!DUK_HEAPHDR_HAS_TEMPROOT(hdr)) {
 		DUK_DDD(DUK_DDDPRINT("not a temp root: %p", (void *) hdr));
 		return;
@@ -46859,7 +48275,7 @@
 #if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING)
 	hdr->h_assert_refcount--;  /* Same node visited twice. */
 #endif
-	duk__mark_heaphdr(heap, hdr);
+	duk__mark_heaphdr_nonnull(heap, hdr);
 
 #if defined(DUK_USE_DEBUG)
 	(*count)++;
@@ -47067,7 +48483,7 @@
  *  Sweep heap.
  */
 
-DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_count_keep) {
+DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_small_uint_t flags, duk_size_t *out_count_keep) {
 	duk_heaphdr *prev;  /* last element that was left in the heap */
 	duk_heaphdr *curr;
 	duk_heaphdr *next;
@@ -47078,7 +48494,6 @@
 #endif
 	duk_size_t count_keep = 0;
 
-	DUK_UNREF(flags);
 	DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap));
 
 	prev = NULL;
@@ -47158,6 +48573,18 @@
 				prev = curr;
 			}
 
+			/*
+			 *  Shrink check for value stacks here.  We're inside
+			 *  ms_prevent_count protection which prevents recursive
+			 *  mark-and-sweep and refzero finalizers, so there are
+			 *  no side effects that would affect the heap lists.
+			 */
+			if (DUK_HEAPHDR_IS_OBJECT(curr) && DUK_HOBJECT_IS_THREAD((duk_hobject *) curr)) {
+				duk_hthread *thr_curr = (duk_hthread *) curr;
+				DUK_DD(DUK_DDPRINT("value stack shrink check for thread: %!O", curr));
+				duk_valstack_shrink_check_nothrow(thr_curr, flags & DUK_MS_FLAG_EMERGENCY /*snug*/);
+			}
+
 			DUK_HEAPHDR_CLEAR_REACHABLE(curr);
 			/* Keep FINALIZED if set, used if rescue decisions are postponed. */
 			/* Keep FINALIZABLE for objects on finalize_list. */
@@ -47232,13 +48659,13 @@
  *  Compaction is assumed to never throw an error.
  */
 
-DUK_LOCAL int duk__protected_compact_object(duk_context *ctx, void *udata) {
+DUK_LOCAL int duk__protected_compact_object(duk_hthread *thr, void *udata) {
 	duk_hobject *obj;
-	/* XXX: for threads, compact value stack, call stack, catch stack? */
+	/* XXX: for threads, compact stacks? */
 
 	DUK_UNREF(udata);
-	obj = duk_known_hobject(ctx, -1);
-	duk_hobject_compact_props((duk_hthread *) ctx, obj);
+	obj = duk_known_hobject(thr, -1);
+	duk_hobject_compact_props(thr, obj);
 	return 0;
 }
 
@@ -47271,9 +48698,9 @@
 #endif
 
 		DUK_DD(DUK_DDPRINT("compact object: %p", (void *) obj));
-		duk_push_hobject((duk_context *) thr, obj);
+		duk_push_hobject(thr, obj);
 		/* XXX: disable error handlers for duration of compaction? */
-		duk_safe_call((duk_context *) thr, duk__protected_compact_object, NULL, 1, 0);
+		duk_safe_call(thr, duk__protected_compact_object, NULL, 1, 0);
 
 #if defined(DUK_USE_DEBUG)
 		new_size = DUK_HOBJECT_P_COMPUTE_SIZE(DUK_HOBJECT_GET_ESIZE(obj),
@@ -47466,6 +48893,56 @@
 #endif  /* DUK_USE_ASSERTIONS */
 
 /*
+ *  Stats dump.
+ */
+
+#if defined(DUK_USE_DEBUG)
+DUK_LOCAL void duk__dump_stats(duk_heap *heap) {
+	DUK_D(DUK_DPRINT("stats executor: opcodes=%ld, interrupt=%ld, throw=%ld",
+	                 (long) heap->stats_exec_opcodes, (long) heap->stats_exec_interrupt,
+	                 (long) heap->stats_exec_throw));
+	DUK_D(DUK_DPRINT("stats call: all=%ld, tailcall=%ld, ecmatoecma=%ld",
+	                 (long) heap->stats_call_all, (long) heap->stats_call_tailcall,
+	                 (long) heap->stats_call_ecmatoecma));
+	DUK_D(DUK_DPRINT("stats safecall: all=%ld, nothrow=%ld, throw=%ld",
+	                 (long) heap->stats_safecall_all, (long) heap->stats_safecall_nothrow,
+	                 (long) heap->stats_safecall_throw));
+	DUK_D(DUK_DPRINT("stats mark-and-sweep: try_count=%ld, skip_count=%ld, emergency_count=%ld",
+	                 (long) heap->stats_ms_try_count, (long) heap->stats_ms_skip_count,
+	                 (long) heap->stats_ms_emergency_count));
+	DUK_D(DUK_DPRINT("stats stringtable: intern_hit=%ld, intern_miss=%ld, resize_check=%ld, resize_grow=%ld, resize_shrink=%ld",
+	                 (long) heap->stats_strtab_intern_hit, (long) heap->stats_strtab_intern_miss,
+	                 (long) heap->stats_strtab_resize_check, (long) heap->stats_strtab_resize_grow,
+	                 (long) heap->stats_strtab_resize_shrink));
+	DUK_D(DUK_DPRINT("stats object: realloc_props=%ld, abandon_array=%ld",
+	                 (long) heap->stats_object_realloc_props, (long) heap->stats_object_abandon_array));
+	DUK_D(DUK_DPRINT("stats getownpropdesc: count=%ld, hit=%ld, miss=%ld",
+	                 (long) heap->stats_getownpropdesc_count, (long) heap->stats_getownpropdesc_hit,
+	                 (long) heap->stats_getownpropdesc_miss));
+	DUK_D(DUK_DPRINT("stats getpropdesc: count=%ld, hit=%ld, miss=%ld",
+	                 (long) heap->stats_getpropdesc_count, (long) heap->stats_getpropdesc_hit,
+	                 (long) heap->stats_getpropdesc_miss));
+	DUK_D(DUK_DPRINT("stats getprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, "
+	                 "bufferidx=%ld, bufferlen=%ld, stringidx=%ld, stringlen=%ld, "
+	                 "proxy=%ld, arguments=%ld",
+	                 (long) heap->stats_getprop_all, (long) heap->stats_getprop_arrayidx,
+	                 (long) heap->stats_getprop_bufobjidx, (long) heap->stats_getprop_bufferidx,
+	                 (long) heap->stats_getprop_bufferlen, (long) heap->stats_getprop_stringidx,
+	                 (long) heap->stats_getprop_stringlen, (long) heap->stats_getprop_proxy,
+	                 (long) heap->stats_getprop_arguments));
+	DUK_D(DUK_DPRINT("stats putprop: all=%ld, arrayidx=%ld, bufobjidx=%ld, "
+	                 "bufferidx=%ld, proxy=%ld",
+	                 (long) heap->stats_putprop_all, (long) heap->stats_putprop_arrayidx,
+	                 (long) heap->stats_putprop_bufobjidx, (long) heap->stats_putprop_bufferidx,
+	                 (long) heap->stats_putprop_proxy));
+	DUK_D(DUK_DPRINT("stats getvar: all=%ld",
+	                 (long) heap->stats_getvar_all));
+	DUK_D(DUK_DPRINT("stats putvar: all=%ld",
+	                 (long) heap->stats_putvar_all));
+}
+#endif  /* DUK_USE_DEBUG */
+
+/*
  *  Main mark-and-sweep function.
  *
  *  'flags' represents the features requested by the caller.  The current
@@ -47480,6 +48957,13 @@
 	duk_size_t tmp;
 #endif
 
+	DUK_STATS_INC(heap, stats_ms_try_count);
+#if defined(DUK_USE_DEBUG)
+	if (flags & DUK_MS_FLAG_EMERGENCY) {
+		DUK_STATS_INC(heap, stats_ms_emergency_count);
+	}
+#endif
+
 	/* If debugger is paused, garbage collection is disabled by default.
 	 * This is achieved by bumping ms_prevent_count when becoming paused.
 	 */
@@ -47491,6 +48975,7 @@
 	 */
 	if (heap->ms_prevent_count != 0) {
 		DUK_DD(DUK_DDPRINT("reject recursive mark-and-sweep"));
+		DUK_STATS_INC(heap, stats_ms_skip_count);
 		return;
 	}
 	DUK_ASSERT(heap->ms_running == 0);  /* ms_prevent_count is bumped when ms_running is set */
@@ -47502,8 +48987,6 @@
 	 */
 	DUK_ASSERT(heap->heap_thread != NULL);
 	DUK_ASSERT(heap->heap_thread->valstack != NULL);
-	DUK_ASSERT(heap->heap_thread->callstack != NULL);
-	DUK_ASSERT(heap->heap_thread->catchstack != NULL);
 
 	DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) starting, requested flags: 0x%08lx, effective flags: 0x%08lx",
 	                 (unsigned long) flags, (unsigned long) (flags | heap->ms_base_flags)));
@@ -47544,6 +49027,15 @@
 	heap->ms_running = 1;
 
 	/*
+	 *  Free activation/catcher freelists on every mark-and-sweep for now.
+	 *  This is an initial rough draft; ideally we'd keep count of the
+	 *  freelist size and free only excess entries.
+	 */
+
+	DUK_D(DUK_DPRINT("freeing temporary freelists"));
+	duk_heap_free_freelists(heap);
+
+	/*
 	 *  Mark roots, hoping that recursion limit is not normally hit.
 	 *  If recursion limit is hit, run additional reachability rounds
 	 *  starting from "temproots" until marking is complete.
@@ -47686,6 +49178,14 @@
 #endif
 
 	/*
+	 *  Stats dump
+	 */
+
+#if defined(DUK_USE_DEBUG)
+	duk__dump_stats(heap);
+#endif
+
+	/*
 	 *  Finalize objects in the finalization work list.  Finalized
 	 *  objects are queued back to heap_allocated with FINALIZED set.
 	 *
@@ -47775,7 +49275,9 @@
 	 */
 
 #if defined(DUK_USE_GC_TORTURE)
-	/* simulate alloc failure on every alloc (except when mark-and-sweep is running) */
+	/* Simulate alloc failure on every alloc, except when mark-and-sweep
+	 * is running.
+	 */
 	if (heap->ms_prevent_count == 0) {
 		DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first alloc attempt fails"));
 		res = NULL;
@@ -47785,7 +49287,7 @@
 #endif
 	res = heap->alloc_func(heap->heap_udata, size);
 	if (DUK_LIKELY(res || size == 0)) {
-		/* for zero size allocations NULL is allowed */
+		/* For zero size allocations NULL is allowed. */
 		return res;
 	}
 #if defined(DUK_USE_GC_TORTURE)
@@ -47900,7 +49402,9 @@
 	 */
 
 #if defined(DUK_USE_GC_TORTURE)
-	/* simulate alloc failure on every realloc (except when mark-and-sweep is running) */
+	/* Simulate alloc failure on every realloc, except when mark-and-sweep
+	 * is running.
+	 */
 	if (heap->ms_prevent_count == 0) {
 		DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first realloc attempt fails"));
 		res = NULL;
@@ -47910,7 +49414,7 @@
 #endif
 	res = heap->realloc_func(heap->heap_udata, ptr, newsize);
 	if (DUK_LIKELY(res || newsize == 0)) {
-		/* for zero size allocations NULL is allowed */
+		/* For zero size allocations NULL is allowed. */
 		return res;
 	}
 #if defined(DUK_USE_GC_TORTURE)
@@ -47982,7 +49486,9 @@
 	 */
 
 #if defined(DUK_USE_GC_TORTURE)
-	/* simulate alloc failure on every realloc (except when mark-and-sweep is running) */
+	/* Simulate alloc failure on every realloc, except when mark-and-sweep
+	 * is running.
+	 */
 	if (heap->ms_prevent_count == 0) {
 		DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first indirect realloc attempt fails"));
 		res = NULL;
@@ -47992,7 +49498,7 @@
 #endif
 	res = heap->realloc_func(heap->heap_udata, cb(heap, ud), newsize);
 	if (DUK_LIKELY(res || newsize == 0)) {
-		/* for zero size allocations NULL is allowed */
+		/* For zero size allocations NULL is allowed. */
 		return res;
 	}
 #if defined(DUK_USE_GC_TORTURE)
@@ -48021,12 +49527,12 @@
 	for (i = 0; i < DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT; i++) {
 		duk_small_uint_t flags;
 
-#if defined(DUK_USE_ASSERTIONS)
-		void *ptr_pre;  /* ptr before mark-and-sweep */
+#if defined(DUK_USE_DEBUG)
+		void *ptr_pre;
 		void *ptr_post;
 #endif
 
-#if defined(DUK_USE_ASSERTIONS)
+#if defined(DUK_USE_DEBUG)
 		ptr_pre = cb(heap, ud);
 #endif
 		flags = 0;
@@ -48035,11 +49541,10 @@
 		}
 
 		duk_heap_mark_and_sweep(heap, flags);
-#if defined(DUK_USE_ASSERTIONS)
+#if defined(DUK_USE_DEBUG)
 		ptr_post = cb(heap, ud);
 		if (ptr_pre != ptr_post) {
-			/* useful for debugging */
-			DUK_DD(DUK_DDPRINT("note: base pointer changed by mark-and-sweep: %p -> %p",
+			DUK_DD(DUK_DDPRINT("realloc base pointer changed by mark-and-sweep: %p -> %p",
 			                   (void *) ptr_pre, (void *) ptr_post));
 		}
 #endif
@@ -48298,6 +49803,15 @@
  *  actual references.
  */
 
+DUK_LOCAL void duk__decref_tvals_norz(duk_hthread *thr, duk_tval *tv, duk_idx_t count) {
+	DUK_ASSERT(count == 0 || tv != NULL);
+
+	while (count-- > 0) {
+		DUK_TVAL_DECREF_NORZ(thr, tv);
+		tv++;
+	}
+}
+
 DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h) {
 	duk_hthread *thr;
 	duk_uint_fast32_t i;
@@ -48375,11 +49889,14 @@
 
 	/* Slow path: special object, start bit checks from most likely. */
 
+	/* XXX: reorg, more common first */
 	if (DUK_HOBJECT_IS_COMPFUNC(h)) {
 		duk_hcompfunc *f = (duk_hcompfunc *) h;
 		duk_tval *tv, *tv_end;
 		duk_hobject **funcs, **funcs_end;
 
+		DUK_ASSERT_HCOMPFUNC_VALID(f);
+
 		if (DUK_LIKELY(DUK_HCOMPFUNC_GET_DATA(heap, f) != NULL)) {
 			tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(heap, f);
 			tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(heap, f);
@@ -48419,34 +49936,49 @@
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 	} else if (DUK_HOBJECT_IS_BUFOBJ(h)) {
 		duk_hbufobj *b = (duk_hbufobj *) h;
+		DUK_ASSERT_HBUFOBJ_VALID(b);
 		DUK_HBUFFER_DECREF_NORZ_ALLOWNULL(thr, (duk_hbuffer *) b->buf);
 		DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) b->buf_prop);
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
+	} else if (DUK_HOBJECT_IS_BOUNDFUNC(h)) {
+		duk_hboundfunc *f = (duk_hboundfunc *) h;
+		DUK_ASSERT_HBOUNDFUNC_VALID(f);
+		DUK_TVAL_DECREF_NORZ(thr, &f->target);
+		DUK_TVAL_DECREF_NORZ(thr, &f->this_binding);
+		duk__decref_tvals_norz(thr, f->args, f->nargs);
+#if defined(DUK_USE_ES6_PROXY)
+	} else if (DUK_HOBJECT_IS_PROXY(h)) {
+		duk_hproxy *p = (duk_hproxy *) h;
+		DUK_ASSERT_HPROXY_VALID(p);
+		DUK_HOBJECT_DECREF_NORZ(thr, p->target);
+		DUK_HOBJECT_DECREF_NORZ(thr, p->handler);
+#endif  /* DUK_USE_ES6_PROXY */
 	} else if (DUK_HOBJECT_IS_THREAD(h)) {
 		duk_hthread *t = (duk_hthread *) h;
+		duk_activation *act;
 		duk_tval *tv;
 
+		DUK_ASSERT_HTHREAD_VALID(t);
+
 		tv = t->valstack;
 		while (tv < t->valstack_top) {
 			DUK_TVAL_DECREF_NORZ(thr, tv);
 			tv++;
 		}
 
-		for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) {
-			duk_activation *act = t->callstack + i;
+		for (act = t->callstack_curr; act != NULL; act = act->parent) {
 			DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) DUK_ACT_GET_FUNC(act));
 			DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->var_env);
 			DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->lex_env);
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
 			DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) act->prev_caller);
 #endif
-		}
-
 #if 0  /* nothing now */
-		for (i = 0; i < (duk_uint_fast32_t) t->catchstack_top; i++) {
-			duk_catcher *cat = t->catchstack + i;
-		}
-#endif
+			for (cat = act->cat; cat != NULL; cat = cat->parent) {
+			}
+#endif
+		}
+
 
 		for (i = 0; i < DUK_NUM_BUILTINS; i++) {
 			DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, (duk_hobject *) t->builtins[i]);
@@ -48594,7 +50126,7 @@
 	/* This finalizer check MUST BE side effect free.  It should also be
 	 * as fast as possible because it's applied to every object freed.
 	 */
-	if (DUK_UNLIKELY(DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr))) {
+	if (DUK_UNLIKELY(DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr) != 0U)) {
 		/* Special case: FINALIZED may be set if mark-and-sweep queued
 		 * object for finalization, the finalizer was executed (and
 		 * FINALIZED set), mark-and-sweep hasn't yet processed the
@@ -49613,11 +51145,10 @@
 		 * checks will be false.
 		 */
 		if (DUK_UNLIKELY(data[0] >= 0x80U)) {
-			if (data[0] == 0xffU) {
+			if (data[0] <= 0x81) {
 				DUK_HSTRING_SET_SYMBOL(res);
+			} else if (data[0] == 0x82U || data[0] == 0xffU) {
 				DUK_HSTRING_SET_HIDDEN(res);
-			} else if (data[0] <= 0xbf) {
-				/* Check equivalent to: (data[0] & 0xc0U) == 0x80U. */
 				DUK_HSTRING_SET_SYMBOL(res);
 			}
 		}
@@ -49629,6 +51160,12 @@
 		 * The flag is set lazily for RAM strings.
 		 */
 		DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res));
+
+#if defined(DUK_USE_HSTRING_LAZY_CLEN)
+		/* Charlen initialized to 0, updated on-the-fly. */
+#else
+		duk_hstring_init_charlen(res);  /* Also sets ASCII flag. */
+#endif
 	}
 
 	DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, has_arridx=%ld, has_extdata=%ld",
@@ -49672,6 +51209,8 @@
 	DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0);  /* 2^N */
 	DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL);
 
+	DUK_STATS_INC(heap, stats_strtab_resize_grow);
+
 	new_st_size = heap->st_size << 1U;
 	DUK_ASSERT(new_st_size > heap->st_size);  /* No overflow. */
 
@@ -49788,6 +51327,8 @@
 	DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0);  /* 2^N */
 	DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL);
 
+	DUK_STATS_INC(heap, stats_strtab_resize_shrink);
+
 	new_st_size = heap->st_size >> 1U;
 
 	/* Combine two buckets into a single one.  When we shrink, one hash
@@ -49858,8 +51399,10 @@
 	DUK_ASSERT(heap->strtable != NULL);
 #endif
 
+	DUK_STATS_INC(heap, stats_strtab_resize_check);
+
 	/* Prevent recursive resizing. */
-	if (DUK_UNLIKELY(heap->st_resizing)) {
+	if (DUK_UNLIKELY(heap->st_resizing != 0U)) {
 		DUK_D(DUK_DPRINT("prevent recursive strtable resize"));
 		return;
 	}
@@ -50111,6 +51654,7 @@
 		    DUK_HSTRING_GET_BYTELEN(h) == blen &&
 		    DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) {
 			/* Found existing entry. */
+			DUK_STATS_INC(heap, stats_strtab_intern_hit);
 			return h;
 		}
 		h = h->hdr.h_next;
@@ -50124,12 +51668,14 @@
 #if defined(DUK_USE_ROM_STRINGS)
 	h = duk__strtab_romstring_lookup(heap, str, blen, strhash);
 	if (h != NULL) {
+		DUK_STATS_INC(heap, stats_strtab_intern_hit);
 		return h;
 	}
 #endif
 
 	/* Not found in string table; insert. */
 
+	DUK_STATS_INC(heap, stats_strtab_intern_miss);
 	h = duk__strtable_do_intern(heap, str, blen, strhash);
 	return h;  /* may be NULL */
 }
@@ -50143,8 +51689,8 @@
  */
 
 DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val) {
-	char buf[DUK__STRTAB_U32_MAX_STRLEN];
-	char *p;
+	duk_uint8_t buf[DUK__STRTAB_U32_MAX_STRLEN];
+	duk_uint8_t *p;
 
 	DUK_ASSERT(heap != NULL);
 
@@ -50440,6 +51986,7 @@
 	/* different memory layout, alloc size, and init */
 	DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPFUNC) == 0);
 	DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATFUNC) == 0);
+	DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_BOUNDFUNC) == 0);
 
 	res = (duk_hobject *) DUK_ALLOC_ZEROED(heap, sizeof(duk_hobject));
 	if (DUK_UNLIKELY(res == NULL)) {
@@ -50490,6 +52037,27 @@
 	return res;
 }
 
+DUK_INTERNAL duk_hboundfunc *duk_hboundfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) {
+	duk_hboundfunc *res;
+
+	res = (duk_hboundfunc *) DUK_ALLOC(heap, sizeof(duk_hboundfunc));
+	if (!res) {
+		return NULL;
+	}
+	DUK_MEMZERO(res, sizeof(duk_hboundfunc));
+
+	duk__init_object_parts(heap, hobject_flags, &res->obj);
+
+	DUK_TVAL_SET_UNDEFINED(&res->target);
+	DUK_TVAL_SET_UNDEFINED(&res->this_binding);
+
+#if defined(DUK_USE_EXPLICIT_NULL_INIT)
+	res->args = NULL;
+#endif
+
+	return res;
+}
+
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
 	duk_hbufobj *res;
@@ -50527,11 +52095,10 @@
 	res->heap = NULL;
 	res->valstack = NULL;
 	res->valstack_end = NULL;
+	res->valstack_alloc_end = NULL;
 	res->valstack_bottom = NULL;
 	res->valstack_top = NULL;
-	res->callstack = NULL;
 	res->callstack_curr = NULL;
-	res->catchstack = NULL;
 	res->resumer = NULL;
 	res->compile_ctx = NULL,
 #if defined(DUK_USE_HEAPPTR16)
@@ -50546,14 +52113,12 @@
 		}
 	}
 #endif
-	/* when nothing is running, API calls are in non-strict mode */
+	/* When nothing is running, API calls are in non-strict mode. */
 	DUK_ASSERT(res->strict == 0);
 
 	res->heap = heap;
-	res->valstack_max = DUK_VALSTACK_DEFAULT_MAX;
-	res->callstack_max = DUK_CALLSTACK_DEFAULT_MAX;
-	res->catchstack_max = DUK_CATCHSTACK_DEFAULT_MAX;
-
+
+	/* XXX: Any reason not to merge duk_hthread_alloc.c here? */
 	return res;
 }
 
@@ -50588,7 +52153,7 @@
 
 	DUK_ASSERT(res->thread == NULL);
 	DUK_ASSERT(res->varmap == NULL);
-	DUK_ASSERT(res->regbase == 0);
+	DUK_ASSERT(res->regbase_byteoff == 0);
 
 	return res;
 }
@@ -50605,6 +52170,18 @@
 
 	return res;
 }
+
+DUK_INTERNAL duk_hproxy *duk_hproxy_alloc(duk_hthread *thr, duk_uint_t hobject_flags) {
+	duk_hproxy *res;
+
+	res = (duk_hproxy *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hproxy));
+
+	/* Leave ->target and ->handler uninitialized, as caller will always
+	 * explicitly initialize them before any side effects are possible.
+	 */
+
+	return res;
+}
 #line 1 "duk_hobject_enum.c"
 /*
  *  Object enumeration support.
@@ -50634,7 +52211,6 @@
  */
 #define DUK__ENUM_START_INDEX  2
 
-#if 0
 /* Current implementation suffices for ES2015 for now because there's no symbol
  * sorting, so commented out for now.
  */
@@ -50643,55 +52219,72 @@
  *  Helper to sort enumeration keys using a callback for pairwise duk_hstring
  *  comparisons.  The keys are in the enumeration object entry part, starting
  *  from DUK__ENUM_START_INDEX, and the entry part is dense.  Entry part values
- *  are all "true", e.g. "1" -> true, "3" -> true, "foo" -> true "2" -> true,
+ *  are all "true", e.g. "1" -> true, "3" -> true, "foo" -> true, "2" -> true,
  *  so it suffices to just switch keys without switching values.
  *
+ *  ES2015 [[OwnPropertyKeys]] enumeration order for ordinary objects:
+ *  (1) array indices in ascending order,
+ *  (2) non-array-index keys in insertion order, and
+ *  (3) symbols in insertion order.
+ *  http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys.
+ *
+ *  This rule is applied to "own properties" at each inheritance level;
+ *  non-duplicate parent keys always follow child keys.  For example,
+ *  an inherited array index will enumerate -after- a symbol in the
+ *  child.
+ *
  *  Insertion sort is used because (1) it's simple and compact, (2) works
  *  in-place, (3) minimizes operations if data is already nearly sorted,
  *  (4) doesn't reorder elements considered equal.
  *  http://en.wikipedia.org/wiki/Insertion_sort
  */
 
-typedef duk_bool_t (*duk__sort_compare_fn)(duk_hstring *a, duk_hstring *b, duk_uarridx_t val_b);
-
-DUK_LOCAL duk_bool_t duk__sort_compare_es6(duk_hstring *a, duk_hstring *b, duk_uarridx_t val_b) {
-	duk_uarridx_t val_a;
+/* Sort key, must hold array indices, "not array index" marker, and one more
+ * higher value for symbols.
+ */
+#if !defined(DUK_USE_SYMBOL_BUILTIN)
+typedef duk_uint32_t duk__sort_key_t;
+#elif defined(DUK_USE_64BIT_OPS)
+typedef duk_uint64_t duk__sort_key_t;
+#else
+typedef duk_double_t duk__sort_key_t;
+#endif
+
+/* Get sort key for a duk_hstring. */
+DUK_LOCAL duk__sort_key_t duk__hstring_sort_key(duk_hstring *x) {
+	duk__sort_key_t val;
+
+	/* For array indices [0,0xfffffffe] use the array index as is.
+	 * For strings, use 0xffffffff, the marker 'arridx' already in
+	 * duk_hstring.  For symbols, any value above 0xffffffff works,
+	 * as long as it is the same for all symbols; currently just add
+	 * the masked flag field into the arridx temporary.
+	 */
+	DUK_ASSERT(x != NULL);
+	DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(x) || DUK_HSTRING_GET_ARRIDX_FAST(x) == DUK_HSTRING_NO_ARRAY_INDEX);
+
+	val = (duk__sort_key_t) DUK_HSTRING_GET_ARRIDX_FAST(x);
+
+#if defined(DUK_USE_SYMBOL_BUILTIN)
+	val = val + (duk__sort_key_t) (DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) x) & DUK_HSTRING_FLAG_SYMBOL);
+#endif
+
+	return (duk__sort_key_t) val;
+}
+
+/* Insert element 'b' after element 'a'? */
+DUK_LOCAL duk_bool_t duk__sort_compare_es6(duk_hstring *a, duk_hstring *b, duk__sort_key_t val_b) {
+	duk__sort_key_t val_a;
 
 	DUK_ASSERT(a != NULL);
 	DUK_ASSERT(b != NULL);
 	DUK_UNREF(b);  /* Not actually needed now, val_b suffices. */
 
-	/* ES2015 [[OwnPropertyKeys]] enumeration order for ordinary objects:
-	 * (1) array indices in ascending order, (2) non-array-index keys in
-	 * insertion order, symbols in insertion order:
-	 * http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys.
-	 *
-	 * This rule is applied to "own properties" at each inheritance level;
-	 * non-duplicate parent keys always follow child keys.  For example,
-	 * an inherited array index will enumerate -after- a symbol in the
-	 * child.
-	 */
-
-	val_a = DUK_HSTRING_GET_ARRIDX_FAST(a);
-
-	if (val_b < val_a) {
-		/* Covers:
-		 *   - Both keys are array indices and a > b: don't insert here.
-		 *   - 'b' is array index, 'a' is not: don't insert here.
-		 */
-		return 0;
-	} else {
-		/* Covers:
-		 *   val_a < val_b where:
-		 *   - Both keys are array indices and a < b: insert here.
-		 *   - 'a' is array index, 'b' is not: insert here.
-		 *   val_a == val_b where:
-		 *   - Both keys are array indices and a == b: insert here
-		 *     (shouldn't actually happen, can't have non-duplicate
-		 *     identical array index keys).
-		 *   - Neither key is an array index: insert here, keeps key
-		 *     order regardless of the keys themselves.
-		 */
+	val_a = duk__hstring_sort_key(a);
+
+	if (val_a > val_b) {
+		return 0;
+	} else {
 		return 1;
 	}
 }
@@ -50714,7 +52307,7 @@
 	for (idx = idx_start + 1; idx < idx_end; idx++) {
 		duk_hstring *h_curr;
 		duk_int_fast32_t idx_insert;
-		duk_uarridx_t val_curr;
+		duk__sort_key_t val_curr;
 
 		h_curr = keys[idx];
 		DUK_ASSERT(h_curr != NULL);
@@ -50724,16 +52317,12 @@
 		 * (and optimized for) case.
 		 */
 
-		val_curr = DUK_HSTRING_GET_ARRIDX_FAST(h_curr);  /* Remains same during scanning. */
+		val_curr = duk__hstring_sort_key(h_curr);  /* Remains same during scanning. */
 		for (idx_insert = idx - 1; idx_insert >= idx_start; idx_insert--) {
 			duk_hstring *h_insert;
 			h_insert = keys[idx_insert];
 			DUK_ASSERT(h_insert != NULL);
 
-			/* XXX: fixed callback rather than a callback argument; only
-			 * one argument used and using a callback argument doesn't
-			 * cause e.g. gcc to inline the callback.
-			 */
 			if (duk__sort_compare_es6(h_insert, h_curr, val_curr)) {
 				break;
 			}
@@ -50757,88 +52346,11 @@
 		if (idx != idx_insert) {
 			DUK_MEMMOVE((void *) (keys + idx_insert + 1),
 			            (const void *) (keys + idx_insert),
-			            (size_t) ((idx - idx_insert) * sizeof(duk_hstring *)));
+			            ((size_t) (idx - idx_insert) * sizeof(duk_hstring *)));
 			keys[idx_insert] = h_curr;
 		}
 	}
 }
-#endif  /* disabled */
-
-/*
- *  Helper to sort keys into ES2015 [[OwnPropertyKeys]] enumeration order:
- *  array keys in ascending order first, followed by keys in insertion
- *  order, followed by symbols in insertion order (not handled here).
- *  Insertion sort based.
- *
- *  This algorithm nominally sorts array indices, but because the "no array
- *  index" marker is higher than any array index, non-array-index keys are
- *  sorted after array indices.  Non-array-index keys are also considered
- *  equal for sorting which means that their order is kept as is, so the end
- *  result matches ES2015 [[OwnPropertyKeys]].
- *
- *  Insertion sort is used because (1) it's simple and compact, (2) works
- *  in-place, (3) minimizes operations if data is already nearly sorted,
- *  (4) doesn't reorder elements considered equal.
- *  http://en.wikipedia.org/wiki/Insertion_sort
- */
-
-DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk_int_fast32_t idx_start, duk_int_fast32_t idx_end) {
-	duk_hstring **keys;
-	duk_hstring **p_curr, **p_insert, **p_end;
-	duk_hstring *h_curr;
-	duk_uarridx_t val_highest, val_curr, val_insert;
-
-	DUK_ASSERT(h_obj != NULL);
-	DUK_ASSERT(idx_start >= DUK__ENUM_START_INDEX);
-	DUK_ASSERT(idx_end >= idx_start);
-	DUK_UNREF(thr);
-
-	if (idx_end <= idx_start + 1) {
-		return;  /* Zero or one element(s). */
-	}
-
-	keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, h_obj);
-	p_curr = keys + idx_start;
-	val_highest = DUK_HSTRING_GET_ARRIDX_SLOW(*p_curr);
-	for (p_curr++, p_end = keys + idx_end; p_curr < p_end; p_curr++) {
-		DUK_ASSERT(*p_curr != NULL);
-		val_curr = DUK_HSTRING_GET_ARRIDX_SLOW(*p_curr);
-
-		if (val_curr >= val_highest) {
-			val_highest = val_curr;
-			continue;
-		}
-
-		/* Needs to be inserted; scan backwards, since we optimize
-		 * for the case where elements are nearly in order.
-		 */
-
-		p_insert = p_curr;
-		for (;;) {
-			p_insert--;  /* Start from p_curr - 1. */
-			val_insert = DUK_HSTRING_GET_ARRIDX_SLOW(*p_insert);
-			if (val_insert < val_curr) {
-				p_insert++;
-				break;
-			}
-			if (p_insert == keys + idx_start) {
-				break;
-			}
-		}
-
-		/*        .-- p_insert   .-- p_curr
-		 *        v              v
-		 *  | ... | insert | ... | curr
-		 */
-
-		h_curr = *p_curr;
-		DUK_MEMMOVE((void *) (p_insert + 1),
-		            (const void *) p_insert,
-		            (size_t) ((p_curr - p_insert) * sizeof(duk_hstring *)));
-		*p_insert = h_curr;
-		/* keep val_highest */
-	}
-}
 
 /*
  *  Create an internal enumerator object E, which has its keys ordered
@@ -50849,21 +52361,20 @@
  *  scan would be needed to eliminate duplicates found in the prototype chain.
  */
 
-DUK_LOCAL void duk__add_enum_key(duk_context *ctx, duk_hstring *k) {
+DUK_LOCAL void duk__add_enum_key(duk_hthread *thr, duk_hstring *k) {
 	/* 'k' may be unreachable on entry so must push without any
 	 * potential for GC.
 	 */
-	duk_push_hstring(ctx, k);
-	duk_push_true(ctx);
-	duk_put_prop(ctx, -3);
-}
-
-DUK_LOCAL void duk__add_enum_key_stridx(duk_context *ctx, duk_small_uint_t stridx) {
-	duk__add_enum_key(ctx, DUK_HTHREAD_GET_STRING((duk_hthread *) ctx, stridx));
-}
-
-DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint_t enum_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+	duk_push_hstring(thr, k);
+	duk_push_true(thr);
+	duk_put_prop(thr, -3);
+}
+
+DUK_LOCAL void duk__add_enum_key_stridx(duk_hthread *thr, duk_small_uint_t stridx) {
+	duk__add_enum_key(thr, DUK_HTHREAD_GET_STRING(thr, stridx));
+}
+
+DUK_INTERNAL void duk_hobject_enumerator_create(duk_hthread *thr, duk_small_uint_t enum_flags) {
 	duk_hobject *enum_target;
 	duk_hobject *curr;
 	duk_hobject *res;
@@ -50875,13 +52386,13 @@
 	duk_uint_fast32_t i, len;  /* used for array, stack, and entry indices */
 	duk_uint_fast32_t sort_start_index;
 
-	DUK_ASSERT(ctx != NULL);
-
-	enum_target = duk_require_hobject(ctx, -1);
+	DUK_ASSERT(thr != NULL);
+
+	enum_target = duk_require_hobject(thr, -1);
 	DUK_ASSERT(enum_target != NULL);
 
-	duk_push_bare_object(ctx);
-	res = duk_known_hobject(ctx, -1);
+	duk_push_bare_object(thr);
+	res = duk_known_hobject(thr, -1);
 
 	/* [enum_target res] */
 
@@ -50890,12 +52401,12 @@
 	 * enumeration result comes from a proxy trap as there is no
 	 * real object to check against.
 	 */
-	duk_push_hobject(ctx, enum_target);
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_TARGET);
+	duk_push_hobject(thr, enum_target);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_TARGET);
 
 	/* Initialize index so that we skip internal control keys. */
-	duk_push_int(ctx, DUK__ENUM_START_INDEX);
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_NEXT);
+	duk_push_int(thr, DUK__ENUM_START_INDEX);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT);
 
 	/*
 	 *  Proxy object handling
@@ -50905,8 +52416,7 @@
 	if (DUK_LIKELY((enum_flags & DUK_ENUM_NO_PROXY_BEHAVIOR) != 0)) {
 		goto skip_proxy;
 	}
-	if (DUK_LIKELY(!duk_hobject_proxy_check(thr,
-	                                        enum_target,
+	if (DUK_LIKELY(!duk_hobject_proxy_check(enum_target,
 	                                        &h_proxy_target,
 	                                        &h_proxy_handler))) {
 		goto skip_proxy;
@@ -50918,8 +52428,8 @@
 	 * has been obsoleted and "ownKeys" is used instead.
 	 */
 	DUK_DDD(DUK_DDDPRINT("proxy enumeration"));
-	duk_push_hobject(ctx, h_proxy_handler);
-	if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_OWN_KEYS)) {
+	duk_push_hobject(thr, h_proxy_handler);
+	if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
 		/* No need to replace the 'enum_target' value in stack, only the
 		 * enum_target reference.  This also ensures that the original
 		 * enum target is reachable, which keeps the proxy and the proxy
@@ -50929,38 +52439,38 @@
 		DUK_DDD(DUK_DDDPRINT("h_proxy_target=%!O", (duk_heaphdr *) h_proxy_target));
 		enum_target = h_proxy_target;
 
-		duk_push_hobject(ctx, enum_target);  /* -> [ ... enum_target res handler undefined target ] */
-		duk_put_prop_stridx_short(ctx, -4, DUK_STRIDX_INT_TARGET);
-
-		duk_pop_2(ctx);  /* -> [ ... enum_target res ] */
+		duk_push_hobject(thr, enum_target);  /* -> [ ... enum_target res handler undefined target ] */
+		duk_put_prop_stridx_short(thr, -4, DUK_STRIDX_INT_TARGET);
+
+		duk_pop_2(thr);  /* -> [ ... enum_target res ] */
 		goto skip_proxy;
 	}
 
 	/* [ ... enum_target res handler trap ] */
-	duk_insert(ctx, -2);
-	duk_push_hobject(ctx, h_proxy_target);    /* -> [ ... enum_target res trap handler target ] */
-	duk_call_method(ctx, 1 /*nargs*/);        /* -> [ ... enum_target res trap_result ] */
-	h_trap_result = duk_require_hobject(ctx, -1);
+	duk_insert(thr, -2);
+	duk_push_hobject(thr, h_proxy_target);    /* -> [ ... enum_target res trap handler target ] */
+	duk_call_method(thr, 1 /*nargs*/);        /* -> [ ... enum_target res trap_result ] */
+	h_trap_result = duk_require_hobject(thr, -1);
 	DUK_UNREF(h_trap_result);
 
-	duk_proxy_ownkeys_postprocess(ctx, h_proxy_target, enum_flags);
+	duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags);
 	/* -> [ ... enum_target res trap_result keys_array ] */
 
 	/* Copy cleaned up trap result keys into the enumerator object. */
 	/* XXX: result is a dense array; could make use of that. */
-	DUK_ASSERT(duk_is_array(ctx, -1));
-	len = (duk_uint_fast32_t) duk_get_length(ctx, -1);
+	DUK_ASSERT(duk_is_array(thr, -1));
+	len = (duk_uint_fast32_t) duk_get_length(thr, -1);
 	for (i = 0; i < len; i++) {
-		(void) duk_get_prop_index(ctx, -1, i);
-		DUK_ASSERT(duk_is_string(ctx, -1));  /* postprocess cleaned up */
+		(void) duk_get_prop_index(thr, -1, (duk_uarridx_t) i);
+		DUK_ASSERT(duk_is_string(thr, -1));  /* postprocess cleaned up */
 		/* [ ... enum_target res trap_result keys_array val ] */
-		duk_push_true(ctx);
+		duk_push_true(thr);
 		/* [ ... enum_target res trap_result keys_array val true ] */
-		duk_put_prop(ctx, -5);
+		duk_put_prop(thr, -5);
 	}
 	/* [ ... enum_target res trap_result keys_array ] */
-	duk_pop_2(ctx);
-	duk_remove_m2(ctx);
+	duk_pop_2(thr);
+	duk_remove_m2(thr);
 
 	/* [ ... res ] */
 
@@ -51053,10 +52563,10 @@
 				/* This is a bit fragile: the string is not
 				 * reachable until it is pushed by the helper.
 				 */
-				k = duk_heap_strtable_intern_u32_checked(thr, i);
+				k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i);
 				DUK_ASSERT(k);
 
-				duk__add_enum_key(ctx, k);
+				duk__add_enum_key(thr, k);
 
 				/* [enum_target res] */
 			}
@@ -51067,11 +52577,7 @@
 			 */
 
 			if (have_length && (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) {
-				duk__add_enum_key_stridx(ctx, DUK_STRIDX_LENGTH);
-			}
-		} else if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(curr)) {
-			if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
-				duk__add_enum_key_stridx(ctx, DUK_STRIDX_LENGTH);
+				duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH);
 			}
 		}
 
@@ -51087,10 +52593,10 @@
 			if (DUK_TVAL_IS_UNUSED(tv)) {
 				continue;
 			}
-			k = duk_heap_strtable_intern_u32_checked(thr, i);  /* Fragile reachability. */
+			k = duk_heap_strtable_intern_u32_checked(thr, (duk_uint32_t) i);  /* Fragile reachability. */
 			DUK_ASSERT(k);
 
-			duk__add_enum_key(ctx, k);
+			duk__add_enum_key(thr, k);
 
 			/* [enum_target res] */
 		}
@@ -51098,7 +52604,7 @@
 		if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(curr)) {
 			/* Array .length comes after numeric indices. */
 			if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
-				duk__add_enum_key_stridx(ctx, DUK_STRIDX_LENGTH);
+				duk__add_enum_key_stridx(thr, DUK_STRIDX_LENGTH);
 			}
 		}
 
@@ -51125,6 +52631,9 @@
 				if (!(enum_flags & DUK_ENUM_INCLUDE_SYMBOLS)) {
 					continue;
 				}
+#if !defined(DUK_USE_PREFER_SIZE)
+				need_sort = 1;
+#endif
 			} else {
 				DUK_ASSERT(!DUK_HSTRING_HAS_HIDDEN(k));  /* would also have symbol flag */
 				if (enum_flags & DUK_ENUM_EXCLUDE_STRINGS) {
@@ -51148,7 +52657,7 @@
 			DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, curr, i) ||
 			           !DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE_PTR(thr->heap, curr, i)->v));
 
-			duk__add_enum_key(ctx, k);
+			duk__add_enum_key(thr, k);
 
 			/* [enum_target res] */
 		}
@@ -51171,11 +52680,11 @@
 
 		if (!(enum_flags & DUK_ENUM_SORT_ARRAY_INDICES)) {
 #if defined(DUK_USE_PREFER_SIZE)
-			duk__sort_enum_keys_es6(thr, res, sort_start_index, sort_end_index);
+			duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) sort_start_index, (duk_int_fast32_t) sort_end_index);
 #else
 			if (need_sort) {
 				DUK_DDD(DUK_DDDPRINT("need to sort"));
-				duk__sort_enum_keys_es6(thr, res, sort_start_index, sort_end_index);
+				duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) sort_start_index, (duk_int_fast32_t) sort_end_index);
 			} else {
 				DUK_DDD(DUK_DDDPRINT("no need to sort"));
 			}
@@ -51193,7 +52702,7 @@
 
 	/* [enum_target res] */
 
-	duk_remove_m2(ctx);
+	duk_remove_m2(thr);
 
 	/* [res] */
 
@@ -51208,7 +52717,7 @@
 		/* Sort to ES2015 order which works for pure array incides but
 		 * also for mixed keys.
 		 */
-		duk__sort_enum_keys_es6(thr, res, DUK__ENUM_START_INDEX, DUK_HOBJECT_GET_ENEXT(res));
+		duk__sort_enum_keys_es6(thr, res, (duk_int_fast32_t) DUK__ENUM_START_INDEX, (duk_int_fast32_t) DUK_HOBJECT_GET_ENEXT(res));
 	}
 
 #if defined(DUK_USE_ES6_PROXY)
@@ -51217,7 +52726,7 @@
 	/* compact; no need to seal because object is internal */
 	duk_hobject_compact_props(thr, res);
 
-	DUK_DDD(DUK_DDDPRINT("created enumerator object: %!iT", (duk_tval *) duk_get_tval(ctx, -1)));
+	DUK_DDD(DUK_DDDPRINT("created enumerator object: %!iT", (duk_tval *) duk_get_tval(thr, -1)));
 }
 
 /*
@@ -51228,24 +52737,23 @@
  *
  *  Returns zero without pushing anything on the stack otherwise.
  */
-DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_hthread *thr, duk_bool_t get_value) {
 	duk_hobject *e;
 	duk_hobject *enum_target;
 	duk_hstring *res = NULL;
 	duk_uint_fast32_t idx;
 	duk_bool_t check_existence;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT(thr != NULL);
 
 	/* [... enum] */
 
-	e = duk_require_hobject(ctx, -1);
+	e = duk_require_hobject(thr, -1);
 
 	/* XXX use get tval ptr, more efficient */
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_NEXT);
-	idx = (duk_uint_fast32_t) duk_require_uint(ctx, -1);
-	duk_pop(ctx);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_NEXT);
+	idx = (duk_uint_fast32_t) duk_require_uint(thr, -1);
+	duk_pop(thr);
 	DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx));
 
 	/* Enumeration keys are checked against the enumeration target (to see
@@ -51253,18 +52761,18 @@
 	 * be the proxy, and checking key existence against the proxy is not
 	 * required (or sensible, as the keys may be fully virtual).
 	 */
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TARGET);
-	enum_target = duk_require_hobject(ctx, -1);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_TARGET);
+	enum_target = duk_require_hobject(thr, -1);
 	DUK_ASSERT(enum_target != NULL);
 #if defined(DUK_USE_ES6_PROXY)
-	check_existence = (!DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(enum_target));
+	check_existence = (!DUK_HOBJECT_IS_PROXY(enum_target));
 #else
 	check_existence = 1;
 #endif
-	duk_pop(ctx);  /* still reachable */
+	duk_pop(thr);  /* still reachable */
 
 	DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT",
-	                     (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(thr, -1)));
 
 	/* no array part */
 	for (;;) {
@@ -51296,25 +52804,25 @@
 
 	DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx));
 
-	duk_push_u32(ctx, (duk_uint32_t) idx);
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_NEXT);
+	duk_push_u32(thr, (duk_uint32_t) idx);
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_INT_NEXT);
 
 	/* [... enum] */
 
 	if (res) {
-		duk_push_hstring(ctx, res);
+		duk_push_hstring(thr, res);
 		if (get_value) {
-			duk_push_hobject(ctx, enum_target);
-			duk_dup_m2(ctx);       /* -> [... enum key enum_target key] */
-			duk_get_prop(ctx, -2); /* -> [... enum key enum_target val] */
-			duk_remove_m2(ctx);    /* -> [... enum key val] */
-			duk_remove(ctx, -3);   /* -> [... key val] */
-		} else {
-			duk_remove_m2(ctx);    /* -> [... key] */
+			duk_push_hobject(thr, enum_target);
+			duk_dup_m2(thr);       /* -> [... enum key enum_target key] */
+			duk_get_prop(thr, -2); /* -> [... enum key enum_target val] */
+			duk_remove_m2(thr);    /* -> [... enum key val] */
+			duk_remove(thr, -3);   /* -> [... key val] */
+		} else {
+			duk_remove_m2(thr);    /* -> [... key] */
 		}
 		return 1;
 	} else {
-		duk_pop(ctx);  /* -> [...] */
+		duk_pop(thr);  /* -> [...] */
 		return 0;
 	}
 }
@@ -51324,25 +52832,22 @@
  *  described in E5 Section 15.2.3.14.
  */
 
-DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_small_uint_t enum_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_hthread *thr, duk_small_uint_t enum_flags) {
 	duk_hobject *e;
-	duk_harray *a;
 	duk_hstring **keys;
 	duk_tval *tv;
 	duk_uint_fast32_t count;
 
-	DUK_ASSERT(ctx != NULL);
-	DUK_ASSERT(duk_get_hobject(ctx, -1) != NULL);
-	DUK_UNREF(thr);
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(duk_get_hobject(thr, -1) != NULL);
 
 	/* Create a temporary enumerator to get the (non-duplicated) key list;
 	 * the enumerator state is initialized without being needed, but that
 	 * has little impact.
 	 */
 
-	duk_hobject_enumerator_create(ctx, enum_flags);
-	e = duk_known_hobject(ctx, -1);
+	duk_hobject_enumerator_create(thr, enum_flags);
+	e = duk_known_hobject(thr, -1);
 
 	/* [enum_target enum res] */
 
@@ -51350,11 +52855,9 @@
 	DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(e) >= DUK__ENUM_START_INDEX);
 	count = (duk_uint32_t) (DUK_HOBJECT_GET_ENEXT(e) - DUK__ENUM_START_INDEX);
 
-	a = duk_push_harray_with_size(ctx, count);
-	DUK_ASSERT(a != NULL);
-	DUK_ASSERT(DUK_HOBJECT_GET_ASIZE((duk_hobject *) a) == count);
-	DUK_ASSERT(a->length == count);
-	tv = DUK_HOBJECT_A_GET_BASE(thr->heap, (duk_hobject *) a);
+	/* XXX: uninit would be OK */
+	tv = duk_push_harray_with_size_outptr(thr, (duk_uint32_t) count);
+	DUK_ASSERT(count == 0 || tv != NULL);
 
 	/* Fill result array, no side effects. */
 
@@ -51373,7 +52876,7 @@
 	}
 
 	/* [enum_target enum res] */
-	duk_remove_m2(ctx);
+	duk_remove_m2(thr);
 
 	/* [enum_target res] */
 
@@ -51451,7 +52954,6 @@
 
 /* Generate pc2line data for an instruction sequence, leaving a buffer on stack top. */
 DUK_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr *instrs, duk_uint_fast32_t length) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hbuffer_dynamic *h_buf;
 	duk_bitencoder_ctx be_ctx_alloc;
 	duk_bitencoder_ctx *be_ctx = &be_ctx_alloc;
@@ -51465,15 +52967,11 @@
 
 	DUK_ASSERT(length <= DUK_COMPILER_MAX_BYTECODE_LENGTH);
 
-	/* XXX: add proper spare handling to dynamic buffer, to minimize
-	 * reallocs; currently there is no spare at all.
-	 */
-
 	num_header_entries = (length + DUK_PC2LINE_SKIP - 1) / DUK_PC2LINE_SKIP;
 	curr_offset = (duk_uint_fast32_t) (sizeof(duk_uint32_t) + num_header_entries * sizeof(duk_uint32_t) * 2);
 
-	duk_push_dynamic_buffer(ctx, (duk_size_t) curr_offset);
-	h_buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(ctx, -1);
+	duk_push_dynamic_buffer(thr, (duk_size_t) curr_offset);
+	h_buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1);
 	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(h_buf) && !DUK_HBUFFER_HAS_EXTERNAL(h_buf));
 
 	hdr = (duk_uint32_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, h_buf);
@@ -51525,17 +53023,17 @@
 				duk_be_encode(be_ctx, 0, 1);
 			} else if (diff_line >= 1 && diff_line <= 4) {
 				/* 1 0 <2 bits> */
-				duk_be_encode(be_ctx, (0x02 << 2) + (diff_line - 1), 4);
+				duk_be_encode(be_ctx, (duk_uint32_t) ((0x02 << 2) + (diff_line - 1)), 4);
 			} else if (diff_line >= -0x80 && diff_line <= 0x7f) {
 				/* 1 1 0 <8 bits> */
 				DUK_ASSERT(diff_line + 0x80 >= 0 && diff_line + 0x80 <= 0xff);
-				duk_be_encode(be_ctx, (0x06 << 8) + (diff_line + 0x80), 11);
+				duk_be_encode(be_ctx, (duk_uint32_t) ((0x06 << 8) + (diff_line + 0x80)), 11);
 			} else {
 				/* 1 1 1 <32 bits>
 				 * Encode in two parts to avoid bitencode 24-bit limitation
 				 */
-				duk_be_encode(be_ctx, (0x07 << 16) + ((next_line >> 16) & 0xffffU), 19);
-				duk_be_encode(be_ctx, next_line & 0xffffU, 16);
+				duk_be_encode(be_ctx, (duk_uint32_t) ((0x07 << 16) + ((next_line >> 16) & 0xffff)), 19);
+				duk_be_encode(be_ctx, (duk_uint32_t) (next_line & 0xffff), 16);
 			}
 
 			curr_line = next_line;
@@ -51552,11 +53050,11 @@
 	new_size = (duk_size_t) curr_offset;
 	duk_hbuffer_resize(thr, h_buf, new_size);
 
-	(void) duk_to_fixed_buffer(ctx, -1, NULL);
+	(void) duk_to_fixed_buffer(thr, -1, NULL);
 
 	DUK_DDD(DUK_DDDPRINT("final pc2line data: pc_limit=%ld, length=%ld, %lf bits/opcode --> %!ixT",
 	                     (long) length, (long) new_size, (double) new_size * 8.0 / (double) length,
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 }
 
 /* PC is unsigned.  If caller does PC arithmetic and gets a negative result,
@@ -51661,7 +53159,7 @@
 	return 0;
 }
 
-DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_idx_t idx_func, duk_uint_fast32_t pc) {
+DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_hthread *thr, duk_idx_t idx_func, duk_uint_fast32_t pc) {
 	duk_hbuffer_fixed *pc2line;
 	duk_uint_fast32_t line;
 
@@ -51671,15 +53169,15 @@
 	 * future work in debugger.rst).
 	 */
 
-	duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_PC2LINE);
-	pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1);
+	duk_get_prop_stridx(thr, idx_func, DUK_STRIDX_INT_PC2LINE);
+	pc2line = (duk_hbuffer_fixed *) duk_get_hbuffer(thr, -1);
 	if (pc2line != NULL) {
 		DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC((duk_hbuffer *) pc2line) && !DUK_HBUFFER_HAS_EXTERNAL((duk_hbuffer *) pc2line));
-		line = duk__hobject_pc2line_query_raw((duk_hthread *) ctx, pc2line, (duk_uint_fast32_t) pc);
+		line = duk__hobject_pc2line_query_raw(thr, pc2line, (duk_uint_fast32_t) pc);
 	} else {
 		line = 0;
 	}
-	duk_pop(ctx);
+	duk_pop(thr);
 
 	return line;
 }
@@ -51736,17 +53234,17 @@
 
 #define DUK__NO_ARRAY_INDEX             DUK_HSTRING_NO_ARRAY_INDEX
 
-/* marker values for hash part */
+/* 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)
+/* Valstack space that suffices for all local calls, excluding any recursion
+ * into Ecmascript or Duktape/C calls (Proxy, getters, etc).
  */
 #define DUK__VALSTACK_SPACE             10
 
-/* valstack space allocated especially for proxy lookup which does a
- * recursive property lookup
+/* Valstack space allocated especially for proxy lookup which does a
+ * recursive property lookup.
  */
 #define DUK__VALSTACK_PROXY_LOOKUP      20
 
@@ -51805,7 +53303,7 @@
 	DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
 
 	t = DUK_TVAL_GET_FASTINT(tv);
-	if ((t & ~0xffffffffULL) != 0) {
+	if (((duk_uint64_t) t & ~DUK_U64_CONSTANT(0xffffffff)) != 0) {
 		/* Catches >0x100000000 and negative values. */
 		return DUK__NO_ARRAY_INDEX;
 	}
@@ -51824,14 +53322,14 @@
  * Also check if it's a valid array index and return that (or DUK__NO_ARRAY_INDEX
  * if not).
  */
-DUK_LOCAL duk_uint32_t duk__to_property_key(duk_context *ctx, duk_idx_t idx, duk_hstring **out_h) {
+DUK_LOCAL duk_uint32_t duk__to_property_key(duk_hthread *thr, duk_idx_t idx, duk_hstring **out_h) {
 	duk_uint32_t arr_idx;
 	duk_hstring *h;
 	duk_tval *tv_dst;
 
-	DUK_ASSERT(ctx != NULL);
+	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(out_h != NULL);
-	DUK_ASSERT(duk_is_valid_index(ctx, idx));
+	DUK_ASSERT(duk_is_valid_index(thr, idx));
 	DUK_ASSERT(idx < 0);
 
 	/* XXX: The revised ES2015 ToPropertyKey() handling (ES5.1 was just
@@ -51840,7 +53338,7 @@
 	 * but still be compliant and share code.
 	 */
 
-	tv_dst = DUK_GET_TVAL_NEGIDX((duk_hthread *) ctx, idx);  /* intentionally unvalidated */
+	tv_dst = DUK_GET_TVAL_NEGIDX(thr, idx);  /* intentionally unvalidated */
 	if (DUK_TVAL_IS_STRING(tv_dst)) {
 		/* Most important path: strings and plain symbols are used as
 		 * is.  For symbols the array index check below is unnecessary
@@ -51850,7 +53348,7 @@
 		 */
 		h = DUK_TVAL_GET_STRING(tv_dst);
 	} else {
-		h = duk_to_property_key_hstring(ctx, idx);
+		h = duk_to_property_key_hstring(thr, idx);
 	}
 	DUK_ASSERT(h != NULL);
 	*out_h = h;
@@ -51859,16 +53357,9 @@
 	return arr_idx;
 }
 
-DUK_LOCAL duk_uint32_t duk__push_tval_to_property_key(duk_context *ctx, duk_tval *tv_key, duk_hstring **out_h) {
-	duk_push_tval(ctx, tv_key);  /* XXX: could use an unsafe push here */
-	return duk__to_property_key(ctx, -1, out_h);
-}
-
-/* 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));
+DUK_LOCAL duk_uint32_t duk__push_tval_to_property_key(duk_hthread *thr, duk_tval *tv_key, duk_hstring **out_h) {
+	duk_push_tval(thr, tv_key);  /* XXX: could use an unsafe push here */
+	return duk__to_property_key(thr, -1, out_h);
 }
 
 /* String is an own (virtual) property of a plain buffer. */
@@ -51995,8 +53486,8 @@
 	 * for out_min_size as intended.
 	 */
 
-	*out_used = used;
-	*out_min_size = highest_idx + 1;  /* 0 if no used entries */
+	*out_used = (duk_uint32_t) used;
+	*out_min_size = (duk_uint32_t) (highest_idx + 1);  /* 0 if no used entries */
 }
 
 /* Check array density and indicate whether or not the array part should be abandoned. */
@@ -52051,13 +53542,9 @@
  */
 
 #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_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hobject *obj, duk_hobject **out_target, duk_hobject **out_handler) {
+	duk_hproxy *h_proxy;
+
 	DUK_ASSERT(obj != NULL);
 	DUK_ASSERT(out_target != NULL);
 	DUK_ASSERT(out_handler != NULL);
@@ -52065,31 +53552,16 @@
 	/* 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 */
+	if (DUK_LIKELY(!DUK_HOBJECT_IS_PROXY(obj))) {
+		return 0;
+	}
+	h_proxy = (duk_hproxy *) obj;
+	DUK_ASSERT_HPROXY_VALID(h_proxy);
+
+	DUK_ASSERT(h_proxy->handler != NULL);
+	DUK_ASSERT(h_proxy->target != NULL);
+	*out_handler = h_proxy->handler;
+	*out_target = h_proxy->target;
 
 	return 1;
 }
@@ -52099,25 +53571,21 @@
  * 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_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hobject *obj) {
 	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;
-		}
+	 * a Proxy loop: user code cannot create such a loop (it would only be
+	 * possible by editing duk_hproxy references directly).
+	 */
+
+	while (DUK_HOBJECT_IS_PROXY(obj)) {
+		duk_hproxy *h_proxy;
+
+		h_proxy = (duk_hproxy *) obj;
+		DUK_ASSERT_HPROXY_VALID(h_proxy);
+		obj = h_proxy->target;
+		DUK_ASSERT(obj != NULL);
 	}
 
 	DUK_ASSERT(obj != NULL);
@@ -52127,7 +53595,6 @@
 
 #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);
@@ -52135,7 +53602,7 @@
 	DUK_ASSERT(tv_key != NULL);
 	DUK_ASSERT(out_target != NULL);
 
-	if (!duk_hobject_proxy_check(thr, obj, out_target, &h_handler)) {
+	if (!duk_hobject_proxy_check(obj, out_target, &h_handler)) {
 		return 0;
 	}
 	DUK_ASSERT(*out_target != NULL);
@@ -52177,16 +53644,16 @@
 
 	/* 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_short(ctx, -1, stridx_trap)) {
+	duk_require_stack(thr, DUK__VALSTACK_PROXY_LOOKUP);
+	duk_push_hobject(thr, h_handler);
+	if (duk_get_prop_stridx_short(thr, -1, stridx_trap)) {
 		/* -> [ ... handler trap ] */
-		duk_insert(ctx, -2);  /* -> [ ... trap handler ] */
+		duk_insert(thr, -2);  /* -> [ ... trap handler ] */
 
 		/* stack prepped for func call: [ ... trap handler ] */
 		return 1;
 	} else {
-		duk_pop_2(ctx);
+		duk_pop_2_unsafe(thr);
 		return 0;
 	}
 }
@@ -52227,7 +53694,6 @@
                                             duk_uint32_t new_a_size,
                                             duk_uint32_t new_h_size,
                                             duk_bool_t abandon_array) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_small_uint_t prev_ms_base_flags;
 	duk_uint32_t new_alloc_size;
 	duk_uint32_t new_e_size_adjusted;
@@ -52245,7 +53711,6 @@
 #endif
 
 	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));
@@ -52255,6 +53720,8 @@
 	DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
+	DUK_STATS_INC(thr->heap, stats_object_realloc_props);
+
 	/*
 	 *  Pre resize assertions.
 	 */
@@ -52279,7 +53746,8 @@
 	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));
+	new_e_size_adjusted = (new_e_size + (duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U) &
+	                      (~((duk_uint32_t) DUK_HOBJECT_ALIGN_TARGET - 1U));
 	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);
@@ -52356,6 +53824,7 @@
 		 */
 #if 0  /* XXX: inject test */
 		if (1) {
+			new_p = NULL;
 			goto alloc_failed;
 		}
 #endif
@@ -52410,6 +53879,8 @@
 		 */
 		DUK_ASSERT(new_a_size == 0);
 
+		DUK_STATS_INC(thr->heap, stats_object_abandon_array);
+
 		for (i = 0; i < DUK_HOBJECT_GET_ASIZE(obj); i++) {
 			duk_tval *tv1;
 			duk_tval *tv2;
@@ -52443,15 +53914,15 @@
 			/* Never shrinks; auto-adds DUK_VALSTACK_INTERNAL_EXTRA, which
 			 * is generous.
 			 */
-			if (!duk_check_stack(ctx, 1)) {
+			if (!duk_check_stack(thr, 1)) {
 				goto abandon_error;
 			}
 			DUK_ASSERT_VALSTACK_SPACE(thr, 1);
-			key = duk_heap_strtable_intern_u32(thr->heap, i);
+			key = duk_heap_strtable_intern_u32(thr->heap, (duk_uint32_t) i);
 			if (key == NULL) {
 				goto abandon_error;
 			}
-			duk_push_hstring(ctx, key);  /* keep key reachable for GC etc; guaranteed not to fail */
+			duk_push_hstring(thr, key);  /* keep key reachable for GC etc; guaranteed not to fail */
 
 			/* Key is now reachable in the valstack, don't INCREF
 			 * the new allocation yet (we'll steal the refcounts
@@ -52473,7 +53944,7 @@
 
 		/* Steal refcounts from value stack. */
 		DUK_DDD(DUK_DDDPRINT("abandon array: pop %ld key temps from valstack", (long) new_e_next));
-		duk_pop_n_nodecref_unsafe(ctx, new_e_next);
+		duk_pop_n_nodecref_unsafe(thr, (duk_idx_t) new_e_next);
 	}
 
 	/*
@@ -52556,7 +54027,6 @@
 		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);
 
@@ -52575,7 +54045,7 @@
 				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;
+					new_h[j] = (duk_uint32_t) i;
 					break;
 				}
 				DUK_DDD(DUK_DDDPRINT("rebuild miss %ld, step %ld", (long) j, (long) step));
@@ -52615,7 +54085,7 @@
 	 *  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_FREE_CHECKED(thr, 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);
@@ -52658,7 +54128,7 @@
  alloc_failed:
 	DUK_D(DUK_DPRINT("object property table resize failed"));
 
-	DUK_FREE(thr->heap, new_p);  /* OK for NULL. */
+	DUK_FREE_CHECKED(thr, new_p);  /* OK for NULL. */
 
 	thr->heap->pf_prevent_count--;
 	thr->heap->ms_base_flags = prev_ms_base_flags;
@@ -52674,6 +54144,55 @@
  *  Helpers to resize properties allocation on specific needs.
  */
 
+DUK_INTERNAL void duk_hobject_resize_entrypart(duk_hthread *thr,
+                                               duk_hobject *obj,
+                                               duk_uint32_t new_e_size) {
+	duk_uint32_t old_e_size;
+	duk_uint32_t new_a_size;
+	duk_uint32_t new_h_size;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(obj != NULL);
+
+	old_e_size = DUK_HOBJECT_GET_ESIZE(obj);
+	if (old_e_size > new_e_size) {
+		new_e_size = old_e_size;
+	}
+#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_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
+}
+
+#if 0  /*unused */
+DUK_INTERNAL void duk_hobject_resize_arraypart(duk_hthread *thr,
+                                               duk_hobject *obj,
+                                               duk_uint32_t new_a_size) {
+	duk_uint32_t old_a_size;
+	duk_uint32_t new_e_size;
+	duk_uint32_t new_h_size;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(obj != NULL);
+
+	if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
+		return;
+	}
+	old_a_size = DUK_HOBJECT_GET_ASIZE(obj);
+	if (old_a_size > new_a_size) {
+		new_a_size = old_a_size;
+	}
+	new_e_size = DUK_HOBJECT_GET_ESIZE(obj);
+	new_h_size = DUK_HOBJECT_GET_HSIZE(obj);
+
+	duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
+}
+#endif
+
 /* 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 */
@@ -52842,7 +54361,7 @@
  *  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_INTERNAL duk_bool_t 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);
@@ -52865,9 +54384,9 @@
 		n = DUK_HOBJECT_GET_ENEXT(obj);
 		for (i = 0; i < n; i++) {
 			if (h_keys_base[i] == key) {
-				*e_idx = i;
+				*e_idx = (duk_int_t) i;
 				*h_idx = -1;
-				return;
+				return 1;
 			}
 		}
 	}
@@ -52907,9 +54426,9 @@
 				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;
+					*e_idx = (duk_int_t) t;
+					*h_idx = (duk_int_t) i;
+					return 1;
 				}
 				DUK_DDD(DUK_DDDPRINT("lookup miss i=%ld, t=%ld",
 				                     (long) i, (long) t));
@@ -52921,9 +54440,8 @@
 	}
 #endif  /* DUK_USE_HOBJECT_HASH_PART */
 
-	/* not found */
-	*e_idx = -1;
-	*h_idx = -1;
+	/* Not found, leave e_idx and h_idx unset. */
+	return 0;
 }
 
 /* For internal use: get non-accessor entry value */
@@ -52935,16 +54453,17 @@
 	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;
-	}
+	if (duk_hobject_find_existing_entry(heap, obj, key, &e_idx, &h_idx)) {
+		DUK_ASSERT(e_idx >= 0);
+		if (!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(heap, obj, e_idx)) {
+			return DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(heap, obj, e_idx);
+		}
+	}
+	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_INTERNAL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_uint_t *out_attrs) {
 	duk_int_t e_idx;
 	duk_int_t h_idx;
 
@@ -52953,14 +54472,15 @@
 	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;
-	}
+	if (duk_hobject_find_existing_entry(heap, obj, key, &e_idx, &h_idx)) {
+		DUK_ASSERT(e_idx >= 0);
+		if (!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);
+		}
+	}
+	/* If not found, out_attrs is left unset. */
+	return NULL;
 }
 
 /* For internal use: get array part value */
@@ -52989,7 +54509,7 @@
  *  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_LOCAL duk_int_t duk__hobject_alloc_entry_checked(duk_hthread *thr, duk_hobject *obj, duk_hstring *key) {
 	duk_uint32_t idx;
 
 	DUK_ASSERT(thr != NULL);
@@ -53033,7 +54553,7 @@
 		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",
+				DUK_DDD(DUK_DDDPRINT("duk__hobject_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));
@@ -53042,7 +54562,7 @@
 				h_base[i] = idx;
 				break;
 			}
-			DUK_DDD(DUK_DDDPRINT("duk__alloc_entry_checked() miss %ld", (long) i));
+			DUK_DDD(DUK_DDDPRINT("duk__hobject_alloc_entry_checked() miss %ld", (long) i));
 			i = (i + step) & mask;
 
 			/* Guaranteed to finish (hash is larger than #props). */
@@ -53057,7 +54577,7 @@
 	DUK_ASSERT_DISABLE(idx >= 0);
 	DUK_ASSERT(idx < DUK_HOBJECT_GET_ESIZE(obj));
 	DUK_ASSERT(idx < DUK_HOBJECT_GET_ENEXT(obj));
-	return idx;
+	return (duk_int_t) idx;
 }
 
 /*
@@ -53075,9 +54595,9 @@
 	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) {
+	/* Always in entry part, no need to look up parents etc. */
+	if (duk_hobject_find_existing_entry(heap, obj, DUK_HEAP_STRING_INT_VALUE(heap), &e_idx, &h_idx)) {
+		DUK_ASSERT(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;
@@ -53128,7 +54648,6 @@
                                      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;
@@ -53145,9 +54664,9 @@
 		return 0;
 	}
 
-	map = duk_require_hobject(ctx, -1);
+	map = duk_require_hobject(thr, -1);
 	DUK_ASSERT(map != NULL);
-	duk_pop(ctx);  /* map is reachable through obj */
+	duk_pop_unsafe(thr);  /* 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"));
@@ -53156,16 +54675,16 @@
 
 	/* [... 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 */
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+	DUK_ASSERT(duk_is_string(thr, -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);
+	varenv = duk_require_hobject(thr, -1);
 	DUK_ASSERT(varenv != NULL);
-	duk_pop(ctx);  /* varenv remains reachable through 'obj' */
+	duk_pop_unsafe(thr);  /* varenv remains reachable through 'obj' */
 
 	DUK_DDD(DUK_DDDPRINT("arguments varenv is: %!dO", (duk_heaphdr *) varenv));
 
@@ -53180,7 +54699,6 @@
  * 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;
@@ -53194,9 +54712,9 @@
 
 	/* [... varname] */
 
-	varname = duk_require_hstring(ctx, -1);
+	varname = duk_require_hstring(thr, -1);
 	DUK_ASSERT(varname != NULL);
-	duk_pop(ctx);  /* varname is still reachable */
+	duk_pop_unsafe(thr);  /* varname is still reachable */
 
 	DUK_DDD(DUK_DDDPRINT("arguments object automatic getvar for a bound variable; "
 	                     "key=%!O, varname=%!O",
@@ -53207,7 +54725,7 @@
 
 	/* [... value this_binding] */
 
-	duk_pop(ctx);
+	duk_pop_unsafe(thr);
 
 	/* leave result on stack top */
 	return 1;
@@ -53218,7 +54736,6 @@
  * 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;
@@ -53232,15 +54749,15 @@
 
 	/* [... put_value varname] */
 
-	varname = duk_require_hstring(ctx, -1);
+	varname = duk_require_hstring(thr, -1);
 	DUK_ASSERT(varname != NULL);
-	duk_pop(ctx);  /* varname is still reachable */
+	duk_pop_unsafe(thr);  /* 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)));
+	                     (duk_tval *) duk_require_tval(thr, -1)));
 
 	/* [... put_value] */
 
@@ -53252,7 +54769,7 @@
 	 *  the property write call.
 	 */
 
-	duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(ctx, -1), throw_flag);
+	duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, -1), throw_flag);
 
 	/* [... put_value] */
 }
@@ -53262,7 +54779,6 @@
  * 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);
@@ -53272,9 +54788,9 @@
 		return;
 	}
 
-	map = duk_require_hobject(ctx, -1);
+	map = duk_require_hobject(thr, -1);
 	DUK_ASSERT(map != NULL);
-	duk_pop(ctx);  /* map is reachable through obj */
+	duk_pop_unsafe(thr);  /* map is reachable through obj */
 
 	DUK_DDD(DUK_DDDPRINT("-> have 'map', delete key %!O from map (if exists)); ignore result",
 	                     (duk_heaphdr *) key));
@@ -53326,7 +54842,6 @@
  */
 
 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, "
@@ -53335,7 +54850,6 @@
 	                     (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);
@@ -53343,16 +54857,60 @@
 	DUK_ASSERT(out_desc != NULL);
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
-	/* XXX: optimize this filling behavior later */
+	DUK_STATS_INC(thr->heap, stats_getownpropdesc_count);
+
+	/* Each code path returning 1 (= found) must fill in all the output
+	 * descriptor fields.  We don't do it beforehand because it'd be
+	 * unnecessary work if the property isn't found and would happen
+	 * multiple times for an inheritance chain.
+	 */
+	DUK_ASSERT_SET_GARBAGE(out_desc, sizeof(*out_desc));
+#if 0
 	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
+#endif
+
+	/*
+	 *  Try entries part first because it's the common case.
+	 *
+	 *  Array part lookups are usually handled by the array fast path, and
+	 *  are not usually inherited.  Array and entry parts never contain the
+	 *  same keys so the entry part vs. array part order doesn't matter.
+	 */
+
+	if (duk_hobject_find_existing_entry(thr->heap, obj, key, &out_desc->e_idx, &out_desc->h_idx)) {
+		duk_int_t e_idx = out_desc->e_idx;
+		DUK_ASSERT(out_desc->e_idx >= 0);
+		out_desc->a_idx = -1;
+		out_desc->flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, obj, e_idx);
+		out_desc->get = NULL;
+		out_desc->set = NULL;
+		if (DUK_UNLIKELY(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(thr);
+			}
+		} 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(thr, tv);
+			}
+		}
+		goto prop_found;
+	}
+
+	/*
+	 *  Try array part.
 	 */
 
 	if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) {
@@ -53361,58 +54919,28 @@
 			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);
+					duk_push_tval(thr, tv);
 				}
 				/* implicit attributes */
 				out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
 				                  DUK_PROPDESC_FLAG_CONFIGURABLE |
 				                  DUK_PROPDESC_FLAG_ENUMERABLE;
-				out_desc->a_idx = arr_idx;
+				out_desc->get = NULL;
+				out_desc->set = NULL;
+				out_desc->e_idx = -1;
+				out_desc->h_idx = -1;
+				out_desc->a_idx = (duk_int_t) arr_idx;  /* XXX: limit 2G due to being signed */
 				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;
-	}
+	}
+
+	DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property"));
 
 	/*
 	 *  Not found as a concrete property, check for virtual properties.
 	 */
 
- prop_not_found_concrete:
-
 	if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) {
 		/* Quick skip. */
 		goto prop_not_found;
@@ -53431,15 +54959,20 @@
 			DUK_DDD(DUK_DDDPRINT("-> found, key is 'length', length exotic behavior"));
 
 			if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
-				duk_push_uint(ctx, (duk_uint_t) a->length);
+				duk_push_uint(thr, (duk_uint_t) a->length);
 			}
 			out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
 			if (DUK_HARRAY_LENGTH_WRITABLE(a)) {
 				out_desc->flags |= DUK_PROPDESC_FLAG_WRITABLE;
 			}
+			out_desc->get = NULL;
+			out_desc->set = NULL;
+			out_desc->e_idx = -1;
+			out_desc->h_idx = -1;
+			out_desc->a_idx = -1;
 
 			DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-			return 1;  /* cannot be arguments exotic */
+			goto prop_found_noexotic;  /* cannot be arguments exotic */
 		}
 	} else if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj)) {
 		DUK_DDD(DUK_DDDPRINT("string object exotic property get for key: %!O, arr_idx: %ld",
@@ -53457,14 +54990,19 @@
 			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] */
+					duk_push_hstring(thr, h_val);
+					duk_substring(thr, -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;
+				out_desc->get = NULL;
+				out_desc->set = NULL;
+				out_desc->e_idx = -1;
+				out_desc->h_idx = -1;
+				out_desc->a_idx = -1;
 
 				DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-				return 1;  /* cannot be e.g. arguments exotic, since exotic 'traits' are mutually exclusive */
+				goto prop_found_noexotic;  /* cannot be arguments exotic */
 			} else {
 				/* index is above internal string length -> property is fully normal */
 				DUK_DDD(DUK_DDDPRINT("array index outside string -> normal property"));
@@ -53477,12 +55015,17 @@
 			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));
+				duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_val));
 			}
 			out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;  /* E5 Section 15.5.5.1 */
+			out_desc->get = NULL;
+			out_desc->set = NULL;
+			out_desc->e_idx = -1;
+			out_desc->h_idx = -1;
+			out_desc->a_idx = -1;
 
 			DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-			return 1;  /* cannot be arguments exotic */
+			goto prop_found_noexotic;  /* cannot be arguments exotic */
 		}
 	}
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
@@ -53504,16 +55047,16 @@
 			 */
 			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;
+				elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);
 				if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) {
 					duk_uint8_t *data;
 
 					if (h_bufobj->buf != NULL && DUK_HBUFOBJ_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_hbufobj_push_validated_read(ctx, h_bufobj, data, elem_size);
+						duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size);
 					} else {
 						DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)"));
-						duk_push_uint(ctx, 0);
+						duk_push_uint(thr, 0);
 					}
 				}
 				out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE |
@@ -53524,9 +55067,14 @@
 					 */
 					out_desc->flags |= DUK_PROPDESC_FLAG_ENUMERABLE;
 				}
+				out_desc->get = NULL;
+				out_desc->set = NULL;
+				out_desc->e_idx = -1;
+				out_desc->h_idx = -1;
+				out_desc->a_idx = -1;
 
 				DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-				return 1;  /* cannot be e.g. arguments exotic, since exotic 'traits' are mutually exclusive */
+				goto prop_found_noexotic;  /* 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"));
@@ -53538,32 +55086,20 @@
 				/* 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);
+				duk_push_uint(thr, h_bufobj->length >> h_bufobj->shift);
 			}
 			out_desc->flags = DUK_PROPDESC_FLAG_VIRTUAL;
+			out_desc->get = NULL;
+			out_desc->set = NULL;
+			out_desc->e_idx = -1;
+			out_desc->h_idx = -1;
+			out_desc->a_idx = -1;
 
 			DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj));
-			return 1;  /* cannot be arguments exotic */
+			goto prop_found_noexotic;  /* cannot be arguments exotic */
 		}
 	}
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
-	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_hnatfunc *) obj)->nargs;
-				duk_push_int(ctx, func_nargs == DUK_HNATFUNC_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.
@@ -53574,15 +55110,16 @@
 	 */
 
 	/*
-	 *  Not found as concrete or virtual
+	 *  Not found as concrete or virtual.
 	 */
 
  prop_not_found:
 	DUK_DDD(DUK_DDDPRINT("-> not found (virtual, entry part, or array part)"));
-	return 0;
-
-	/*
-	 *  Found
+	DUK_STATS_INC(thr->heap, stats_getownpropdesc_miss);
+	return 0;
+
+	/*
+	 *  Found.
 	 *
 	 *  Arguments object has exotic post-processing, see E5 Section 10.6,
 	 *  description of [[GetOwnProperty]] variant for arguments.
@@ -53592,15 +55129,15 @@
 	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
+	 *  - 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
+	 *  - 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)) {
+	if (DUK_UNLIKELY(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,
@@ -53618,13 +55155,15 @@
 		 */
 		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)));
+			                     (duk_tval *) duk_get_tval(thr, -2),
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 			/* [... old_result result] -> [... result] */
-			duk_remove_m2(ctx);
-		}
-	}
-
+			duk_remove_m2(thr);
+		}
+	}
+
+ prop_found_noexotic:
+	DUK_STATS_INC(thr->heap, stats_getownpropdesc_hit);
 	return 1;
 }
 
@@ -53669,6 +55208,8 @@
 	DUK_ASSERT(out_desc != NULL);
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
+	DUK_STATS_INC(thr->heap, stats_getpropdesc_count);
+
 	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, "
@@ -53683,6 +55224,7 @@
 	do {
 		if (duk__get_own_propdesc_raw(thr, curr, key, arr_idx, out_desc, flags)) {
 			/* stack contains value (if requested), 'out_desc' is set */
+			DUK_STATS_INC(thr->heap, stats_getpropdesc_hit);
 			return 1;
 		}
 
@@ -53702,6 +55244,7 @@
 	 * value to determine whether out_desc can be looked up
 	 */
 
+	DUK_STATS_INC(thr->heap, stats_getpropdesc_miss);
 	return 0;
 }
 
@@ -53736,7 +55279,7 @@
 	     !DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(obj) &&
 	     !DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(obj) &&
 	     !DUK_HOBJECT_IS_BUFOBJ(obj) &&
-	     !DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
+	     !DUK_HOBJECT_IS_PROXY(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.
@@ -53859,15 +55402,12 @@
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 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_hbufobj *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_BUFOBJ(obj)) {
 		return 0;
 	}
@@ -53899,14 +55439,14 @@
 	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;
+	elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);
 
 	if (h_bufobj->buf != NULL && DUK_HBUFOBJ_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_hbufobj_push_validated_read(ctx, h_bufobj, data, elem_size);
+		duk_hbufobj_push_validated_read(thr, h_bufobj, data, elem_size);
 	} else {
 		DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (read zero)"));
-		duk_push_uint(ctx, 0);
+		duk_push_uint(thr, 0);
 	}
 
 	return 1;
@@ -53915,15 +55455,12 @@
 
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 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_hbufobj *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_BUFOBJ(obj) &&
 	      DUK_TVAL_IS_NUMBER(tv_val))) {
 		return 0;
@@ -53958,22 +55495,22 @@
 	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;
+	elem_size = (duk_small_uint_t) (1U << 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));
+	duk_push_tval(thr, tv_val);
+	DUK_ASSERT(duk_is_number(thr, -1));
 
 	if (h_bufobj->buf != NULL && DUK_HBUFOBJ_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_hbufobj_validated_write(ctx, h_bufobj, data, elem_size);
+		duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size);
 	} else {
 		DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (write skipped)"));
 	}
 
-	duk_pop(ctx);
+	duk_pop_unsafe(thr);
 	return 1;
 }
 #endif  /* DUK_USE_BUFFEROBJECT_SUPPORT */
@@ -53983,7 +55520,6 @@
  */
 
 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;
@@ -53996,7 +55532,6 @@
 	                     (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);
@@ -54004,6 +55539,8 @@
 
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
+	DUK_STATS_INC(thr->heap, stats_getprop_all);
+
 	/*
 	 *  Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of
 	 *  them being invalidated by a valstack resize.
@@ -54031,7 +55568,7 @@
 		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));
+		               duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
 #endif
 		return 0;
 	}
@@ -54065,23 +55602,24 @@
 			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_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, 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));
+			                     (duk_tval *) duk_get_tval(thr, -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_pop_n_unsafe(thr, pop_count);
+			duk_push_hstring(thr, h);
+			duk_substring(thr, -1, arr_idx, arr_idx + 1);  /* [str] -> [substr] */
+
+			DUK_STATS_INC(thr->heap, stats_getprop_stringidx);
 			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)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 			return 1;
 		}
 
@@ -54089,20 +55627,21 @@
 			/* This is a pretty awkward control flow, but we need to recheck the
 			 * key coercion here.
 			 */
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, 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));
+			                     (duk_tval *) duk_get_tval(thr, -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_pop_unsafe(thr);  /* [key] -> [] */
+			duk_push_uint(thr, (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h));  /* [] -> [res] */
+
+			DUK_STATS_INC(thr->heap, stats_getprop_stringlen);
 			DUK_DDD(DUK_DDDPRINT("-> %!T (base is string, key is 'length' after coercion -> "
 			                     "return string length)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 			return 1;
 		}
 
@@ -54124,11 +55663,12 @@
 #if defined(DUK_USE_ARRAY_PROP_FASTPATH)
 		tmp = duk__getprop_shallow_fastpath_array_tval(thr, curr, tv_key);
 		if (tmp) {
-			duk_push_tval(ctx, tmp);
+			duk_push_tval(thr, tmp);
 
 			DUK_DDD(DUK_DDDPRINT("-> %!T (base is object, key is a number, array part "
 			                     "fast path)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
+			DUK_STATS_INC(thr->heap, stats_getprop_arrayidx);
 			return 1;
 		}
 #endif
@@ -54138,32 +55678,34 @@
 			/* Read value pushed on stack. */
 			DUK_DDD(DUK_DDDPRINT("-> %!T (base is bufobj, key is a number, bufobj "
 			                     "fast path)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
+			DUK_STATS_INC(thr->heap, stats_getprop_bufobjidx);
 			return 1;
 		}
 #endif
 
 #if defined(DUK_USE_ES6_PROXY)
-		if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(curr))) {
+		if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(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*/);
+				DUK_STATS_INC(thr->heap, stats_getprop_proxy);
+				duk_push_hobject(thr, h_target);  /* target */
+				duk_push_tval(thr, tv_key);       /* P */
+				duk_push_tval(thr, tv_obj);       /* Receiver: Proxy object */
+				duk_call_method(thr, 3 /*nargs*/);
 
 				/* Target object must be checked for a conflicting
 				 * non-configurable property.
 				 */
-				arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+				arr_idx = duk__push_tval_to_property_key(thr, 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_tval *tv_hook = duk_require_tval(thr, -3);  /* value from hook */
+					duk_tval *tv_targ = duk_require_tval(thr, -1);  /* value from target */
 					duk_bool_t datadesc_reject;
 					duk_bool_t accdesc_reject;
 
@@ -54186,9 +55728,9 @@
 						DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
 					}
 
-					duk_pop_2(ctx);
+					duk_pop_2_unsafe(thr);
 				} else {
-					duk_pop(ctx);
+					duk_pop_unsafe(thr);
 				}
 				return 1;  /* return value */
 			}
@@ -54199,18 +55741,19 @@
 #endif  /* DUK_USE_ES6_PROXY */
 
 		if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(curr)) {
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 			DUK_ASSERT(key != NULL);
 
+			DUK_STATS_INC(thr->heap, stats_getprop_arguments);
 			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)));
+				                     (duk_tval *) duk_get_tval(thr, -1)));
 
 				/* no need for 'caller' post-check, because 'key' must be an array index */
 
-				duk_remove_m2(ctx);  /* [key result] -> [result] */
+				duk_remove_m2(thr);  /* [key result] -> [result] */
 				return 1;
 			}
 
@@ -54244,22 +55787,22 @@
 			DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx));
 			pop_count = 0;
 		} else {
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 			DUK_ASSERT(key != NULL);
 			DUK_DDD(DUK_DDDPRINT("base object buffer, 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));
+			                     (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
 			pop_count = 1;
 		}
 
 		if (arr_idx != DUK__NO_ARRAY_INDEX &&
 		    arr_idx < DUK_HBUFFER_GET_SIZE(h)) {
-			duk_pop_n(ctx, pop_count);
-			duk_push_uint(ctx, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h))[arr_idx]);
-
+			duk_pop_n_unsafe(thr, pop_count);
+			duk_push_uint(thr, ((duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h))[arr_idx]);
+			DUK_STATS_INC(thr->heap, stats_getprop_bufferidx);
 			DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is an index inside buffer length "
 			                     "after coercion -> return byte as number)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 			return 1;
 		}
 
@@ -54267,20 +55810,21 @@
 			/* This is a pretty awkward control flow, but we need to recheck the
 			 * key coercion here.
 			 */
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 			DUK_ASSERT(key != NULL);
 			DUK_DDD(DUK_DDDPRINT("base object buffer, 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));
+			                     (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
 		}
 
 		if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-			duk_pop(ctx);  /* [key] -> [] */
-			duk_push_uint(ctx, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h));  /* [] -> [res] */
+			duk_pop_unsafe(thr);  /* [key] -> [] */
+			duk_push_uint(thr, (duk_uint_t) DUK_HBUFFER_GET_SIZE(h));  /* [] -> [res] */
+			DUK_STATS_INC(thr->heap, stats_getprop_bufferlen);
 
 			DUK_DDD(DUK_DDDPRINT("-> %!T (base is buffer, key is 'length' "
 			                     "after coercion -> return buffer length)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 			return 1;
 		}
 
@@ -54296,25 +55840,10 @@
 	}
 
 	case DUK_TAG_LIGHTFUNC: {
-		duk_int_t lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_obj);
-
-		/* Must coerce key: if key is an object, it may coerce to e.g. 'length'. */
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
-
-		if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
-			duk_int_t lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
-			duk_pop(ctx);
-			duk_push_int(ctx, lf_len);
-			return 1;
-		} else if (key == DUK_HTHREAD_STRING_NAME(thr)) {
-			duk_pop(ctx);
-			duk_push_lightfunc_name(ctx, tv_obj);
-			return 1;
-		}
-
+		/* Lightfuncs inherit getter .name and .length from %NativeFunctionPrototype%. */
 		DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
-		curr = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
-		goto lookup;  /* avoid double coercion */
+		curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
+		break;
 	}
 
 #if defined(DUK_USE_FASTINT)
@@ -54332,9 +55861,8 @@
 
 	/* key coercion (unless already coerced above) */
 	DUK_ASSERT(key == NULL);
-	arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+	arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 	DUK_ASSERT(key != NULL);
-
 	/*
 	 *  Property lookup
 	 */
@@ -54354,14 +55882,14 @@
 			/* accessor with defined getter */
 			DUK_ASSERT((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) != 0);
 
-			duk_pop(ctx);                     /* [key undefined] -> [key] */
-			duk_push_hobject(ctx, desc.get);
-			duk_push_tval(ctx, tv_obj);       /* note: original, uncoerced base */
+			duk_pop_unsafe(thr);              /* [key undefined] -> [key] */
+			duk_push_hobject(thr, desc.get);
+			duk_push_tval(thr, tv_obj);       /* note: original, uncoerced base */
 #if defined(DUK_USE_NONSTD_GETTER_KEY_ARGUMENT)
-			duk_dup_m3(ctx);
-			duk_call_method(ctx, 1);          /* [key getter this key] -> [key retval] */
-#else
-			duk_call_method(ctx, 0);          /* [key getter this] -> [key retval] */
+			duk_dup_m3(thr);
+			duk_call_method(thr, 1);          /* [key getter this key] -> [key retval] */
+#else
+			duk_call_method(thr, 0);          /* [key getter this] -> [key retval] */
 #endif
 		} else {
 			/* [key value] or [key undefined] */
@@ -54372,7 +55900,7 @@
 
 			/* if accessor without getter, return value is undefined */
 			DUK_ASSERT(((desc.flags & DUK_PROPDESC_FLAG_ACCESSOR) == 0) ||
-			           duk_is_undefined(ctx, -1));
+			           duk_is_undefined(thr, -1));
 
 			/* Note: for an accessor without getter, falling through to
 			 * check for "caller" exotic behavior is unnecessary as
@@ -54397,9 +55925,9 @@
 	 *  Not found
 	 */
 
-	duk_to_undefined(ctx, -1);  /* [key] -> [undefined] (default value) */
-
-	DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_to_undefined(thr, -1);  /* [key] -> [undefined] (default value) */
+
+	DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(thr, -1)));
 	return 0;
 
 	/*
@@ -54453,7 +55981,7 @@
 			 */
 			DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(orig));
 
-			h = duk_get_hobject(ctx, -1);  /* NULL if not an object */
+			h = duk_get_hobject(thr, -1);  /* NULL if not an object */
 			if (h &&
 			    DUK_HOBJECT_IS_FUNCTION(h) &&
 			    DUK_HOBJECT_HAS_STRICT(h)) {
@@ -54464,9 +55992,9 @@
 	}
 #endif   /* !DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */
 
-	duk_remove_m2(ctx);  /* [key result] -> [result] */
-
-	DUK_DDD(DUK_DDDPRINT("-> %!T (found)", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_remove_m2(thr);  /* [key result] -> [result] */
+
+	DUK_DDD(DUK_DDDPRINT("-> %!T (found)", (duk_tval *) duk_get_tval(thr, -1)));
 	return 1;
 }
 
@@ -54478,7 +56006,6 @@
  */
 
 DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval tv_key_copy;
 	duk_hobject *obj;
 	duk_hstring *key;
@@ -54517,27 +56044,23 @@
 		obj = DUK_TVAL_GET_OBJECT(tv_obj);
 		DUK_ASSERT(obj != NULL);
 
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+		arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 	} else if (DUK_TVAL_IS_BUFFER(tv_obj)) {
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+		arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 		if (duk__key_is_plain_buf_ownprop(thr, DUK_TVAL_GET_BUFFER(tv_obj), key, arr_idx)) {
 			rc = 1;
 			goto pop_and_return;
 		}
 		obj = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
 	} else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
-		if (duk__key_is_lightfunc_ownprop(thr, key)) {
-			rc = 1;
-			goto pop_and_return;
-		}
-
-		/* If not found, resume existence check from Function.prototype.
+		arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
+
+		/* If not found, resume existence check from %NativeFunctionPrototype%.
 		 * We can just substitute the value in this case; nothing will
 		 * need the original base value (as would be the case with e.g.
 		 * setters/getters.
 		 */
-		obj = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
+		obj = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
 	} else {
 		/* Note: unconditional throw */
 		DUK_DDD(DUK_DDDPRINT("base object is not an object -> reject"));
@@ -54551,7 +56074,7 @@
 	DUK_UNREF(arr_idx);
 
 #if defined(DUK_USE_ES6_PROXY)
-	if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
+	if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) {
 		duk_hobject *h_target;
 		duk_bool_t tmp_bool;
 
@@ -54563,10 +56086,10 @@
 		if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_HAS, tv_key, &h_target)) {
 			/* [ ... key trap handler ] */
 			DUK_DDD(DUK_DDDPRINT("-> proxy object 'has' for key %!T", (duk_tval *) tv_key));
-			duk_push_hobject(ctx, h_target);  /* target */
-			duk_push_tval(ctx, tv_key);       /* P */
-			duk_call_method(ctx, 2 /*nargs*/);
-			tmp_bool = duk_to_boolean(ctx, -1);
+			duk_push_hobject(thr, h_target);  /* target */
+			duk_push_tval(thr, tv_key);       /* P */
+			duk_call_method(thr, 2 /*nargs*/);
+			tmp_bool = duk_to_boolean(thr, -1);
 			if (!tmp_bool) {
 				/* Target object must be checked for a conflicting
 				 * non-configurable property.
@@ -54589,7 +56112,7 @@
 				}
 			}
 
-			duk_pop_2(ctx);  /* [ key trap_result ] -> [] */
+			duk_pop_2_unsafe(thr);  /* [ key trap_result ] -> [] */
 			return tmp_bool;
 		}
 
@@ -54603,7 +56126,7 @@
 	/* fall through */
 
  pop_and_return:
-	duk_pop(ctx);  /* [ key ] -> [] */
+	duk_pop_unsafe(thr);  /* [ key ] -> [] */
 	return rc;
 }
 
@@ -54656,7 +56179,7 @@
 		/* Very common case. */
 		duk_int64_t fi;
 		fi = DUK_TVAL_GET_FASTINT(tv);
-		if (fi < 0 || fi > 0xffffffffLL) {
+		if (fi < 0 || fi > DUK_I64_CONSTANT(0xffffffff)) {
 			goto fail_range;
 		}
 		return (duk_uint32_t) fi;
@@ -54769,7 +56292,7 @@
 		return 1;
 	} else {
 		/*
-		 *  Entries part is a bit more complex
+		 *  Entries part is a bit more complex.
 		 */
 
 		/* Stage 1: find highest preventing non-configurable entry (if any).
@@ -54888,7 +56411,6 @@
 
 /* XXX: is valstack top best place for argument? */
 DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject *obj) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_harray *a;
 	duk_uint32_t old_len;
 	duk_uint32_t new_len;
@@ -54897,10 +56419,9 @@
 
 	DUK_DDD(DUK_DDDPRINT("handling a put operation to array 'length' exotic property, "
 	                     "new val: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
+	                     (duk_tval *) duk_get_tval(thr, -1)));
+
+	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(obj != NULL);
 
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
@@ -54910,14 +56431,14 @@
 	a = (duk_harray *) obj;
 	DUK_ASSERT_HARRAY_VALID(a);
 
-	DUK_ASSERT(duk_is_valid_index(ctx, -1));
+	DUK_ASSERT(duk_is_valid_index(thr, -1));
 
 	/*
 	 *  Get old and new length
 	 */
 
 	old_len = a->length;
-	new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(ctx, -1));
+	new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1));
 	DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) old_len, (long) new_len));
 
 	/*
@@ -54990,7 +56511,6 @@
  */
 
 DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval tv_obj_copy;
 	duk_tval tv_key_copy;
 	duk_tval tv_val_copy;
@@ -55012,13 +56532,14 @@
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(tv_obj != NULL);
 	DUK_ASSERT(tv_key != NULL);
 	DUK_ASSERT(tv_val != NULL);
 
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
+	DUK_STATS_INC(thr->heap, stats_putprop_all);
+
 	/*
 	 *  Make a copy of tv_obj, tv_key, and tv_val to avoid any issues of
 	 *  them being invalidated by a valstack resize.
@@ -55048,7 +56569,7 @@
 		DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
 #else
 		DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot write property %s of %s",
-		               duk_push_string_tval_readable(ctx, tv_key), duk_push_string_tval_readable(ctx, tv_obj));
+		               duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
 #endif
 		return 0;
 	}
@@ -55068,7 +56589,7 @@
 		 */
 
 		DUK_ASSERT(key == NULL);
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+		arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 		DUK_ASSERT(key != NULL);
 
 		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
@@ -55127,6 +56648,7 @@
 #if defined(DUK_USE_ARRAY_PROP_FASTPATH)
 		if (duk__putprop_shallow_fastpath_array_tval(thr, orig, tv_key, tv_val) != 0) {
 			DUK_DDD(DUK_DDDPRINT("array fast path success"));
+			DUK_STATS_INC(thr->heap, stats_putprop_arrayidx);
 			return 1;
 		}
 #endif
@@ -55134,25 +56656,27 @@
 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
 		if (duk__putprop_fastpath_bufobj_tval(thr, orig, tv_key, tv_val) != 0) {
 			DUK_DDD(DUK_DDDPRINT("base is bufobj, key is a number, bufobj fast path"));
+			DUK_STATS_INC(thr->heap, stats_putprop_bufobjidx);
 			return 1;
 		}
 #endif
 
 #if defined(DUK_USE_ES6_PROXY)
-		if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(orig))) {
+		if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(orig))) {
 			duk_hobject *h_target;
 			duk_bool_t tmp_bool;
 
 			if (duk__proxy_check_prop(thr, orig, DUK_STRIDX_SET, tv_key, &h_target)) {
 				/* -> [ ... trap handler ] */
 				DUK_DDD(DUK_DDDPRINT("-> proxy object 'set' 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_val);       /* V */
-				duk_push_tval(ctx, tv_obj);       /* Receiver: Proxy object */
-				duk_call_method(ctx, 4 /*nargs*/);
-				tmp_bool = duk_to_boolean(ctx, -1);
-				duk_pop(ctx);
+				DUK_STATS_INC(thr->heap, stats_putprop_proxy);
+				duk_push_hobject(thr, h_target);  /* target */
+				duk_push_tval(thr, tv_key);       /* P */
+				duk_push_tval(thr, tv_val);       /* V */
+				duk_push_tval(thr, tv_obj);       /* Receiver: Proxy object */
+				duk_call_method(thr, 4 /*nargs*/);
+				tmp_bool = duk_to_boolean(thr, -1);
+				duk_pop_nodecref_unsafe(thr);
 				if (!tmp_bool) {
 					goto fail_proxy_rejected;
 				}
@@ -55160,11 +56684,11 @@
 				/* Target object must be checked for a conflicting
 				 * non-configurable property.
 				 */
-				arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+				arr_idx = duk__push_tval_to_property_key(thr, 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_targ = duk_require_tval(ctx, -1);
+					duk_tval *tv_targ = duk_require_tval(thr, -1);
 					duk_bool_t datadesc_reject;
 					duk_bool_t accdesc_reject;
 
@@ -55186,9 +56710,9 @@
 						DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
 					}
 
-					duk_pop_2(ctx);
+					duk_pop_2_unsafe(thr);
 				} else {
-					duk_pop(ctx);
+					duk_pop_unsafe(thr);
 				}
 				return 1;  /* success */
 			}
@@ -55223,11 +56747,11 @@
 			DUK_DDD(DUK_DDDPRINT("base object buffer, key is a fast-path number; arr_idx %ld", (long) arr_idx));
 			pop_count = 0;
 		} else {
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 			DUK_ASSERT(key != NULL);
 			DUK_DDD(DUK_DDDPRINT("base object buffer, 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));
+			                     (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
 			pop_count = 1;
 		}
 
@@ -55248,13 +56772,14 @@
 			else
 #endif
 			{
-				duk_push_tval(ctx, tv_val);
-				data[arr_idx] = (duk_uint8_t) duk_to_uint32(ctx, -1);
+				duk_push_tval(thr, tv_val);
+				data[arr_idx] = (duk_uint8_t) duk_to_uint32(thr, -1);
 				pop_count++;
 			}
 
-			duk_pop_n(ctx, pop_count);
+			duk_pop_n_unsafe(thr, pop_count);
 			DUK_DDD(DUK_DDDPRINT("result: success (buffer data write)"));
+			DUK_STATS_INC(thr->heap, stats_putprop_bufferidx);
 			return 1;
 		}
 
@@ -55262,11 +56787,11 @@
 			/* This is a pretty awkward control flow, but we need to recheck the
 			 * key coercion here.
 			 */
-			arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+			arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 			DUK_ASSERT(key != NULL);
 			DUK_DDD(DUK_DDDPRINT("base object buffer, 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));
+			                     (duk_tval *) duk_get_tval(thr, -1), (long) arr_idx));
 		}
 
 		if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
@@ -55285,20 +56810,13 @@
 	}
 
 	case DUK_TAG_LIGHTFUNC: {
-		/* All lightfunc own properties are non-writable and the lightfunc
-		 * is considered non-extensible.  However, the write may be captured
-		 * by an inherited setter which means we can't stop the lookup here.
-		 */
-
-		arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
-
-		if (duk__key_is_lightfunc_ownprop(thr, key)) {
-			goto fail_not_writable;
-		}
-
+		/* Lightfuncs have no own properties and are considered non-extensible.
+		 * However, the write may be captured by an inherited setter which
+		 * means we can't stop the lookup here.
+		 */
 		DUK_DDD(DUK_DDDPRINT("base object is a lightfunc, start lookup from function prototype"));
-		curr = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
-		goto lookup;  /* avoid double coercion */
+		curr = thr->builtins[DUK_BIDX_NATIVE_FUNCTION_PROTOTYPE];
+		break;
 	}
 
 #if defined(DUK_USE_FASTINT)
@@ -55314,7 +56832,7 @@
 	}
 
 	DUK_ASSERT(key == NULL);
-	arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+	arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 	DUK_ASSERT(key != NULL);
 
  lookup:
@@ -55352,16 +56870,16 @@
 			if (!setter) {
 				goto fail_no_setter;
 			}
-			duk_push_hobject(ctx, setter);
-			duk_push_tval(ctx, tv_obj);  /* note: original, uncoerced base */
-			duk_push_tval(ctx, tv_val);  /* [key setter this val] */
+			duk_push_hobject(thr, setter);
+			duk_push_tval(thr, tv_obj);  /* note: original, uncoerced base */
+			duk_push_tval(thr, tv_val);  /* [key setter this val] */
 #if defined(DUK_USE_NONSTD_SETTER_KEY_ARGUMENT)
-			duk_dup_m4(ctx);
-			duk_call_method(ctx, 2);     /* [key setter this val key] -> [key retval] */
-#else
-			duk_call_method(ctx, 1);     /* [key setter this val] -> [key retval] */
-#endif
-			duk_pop(ctx);                /* ignore retval -> [key] */
+			duk_dup_m4(thr);
+			duk_call_method(thr, 2);     /* [key setter this val key] -> [key retval] */
+#else
+			duk_call_method(thr, 1);     /* [key setter this val] -> [key retval] */
+#endif
+			duk_pop_unsafe(thr);         /* ignore retval -> [key] */
 			goto success_no_arguments_exotic;
 		}
 
@@ -55423,9 +56941,9 @@
 					 * compatible with what we need.
 					 */
 
-					duk_push_tval(ctx, tv_val);  /* [key val] */
+					duk_push_tval(thr, tv_val);  /* [key val] */
 					rc = duk__handle_put_array_length(thr, orig);
-					duk_pop(ctx);  /* [key val] -> [key] */
+					duk_pop_unsafe(thr);  /* [key val] -> [key] */
 					if (!rc) {
 						goto fail_array_length_partial;
 					}
@@ -55453,23 +56971,23 @@
 
 						DUK_ASSERT(arr_idx != DUK__NO_ARRAY_INDEX);  /* index/length check guarantees */
 						byte_off = arr_idx << h_bufobj->shift;       /* no wrap assuming h_bufobj->length is valid */
-						elem_size = 1 << h_bufobj->shift;
+						elem_size = (duk_small_uint_t) (1U << h_bufobj->shift);
 
 						/* Coerce to number before validating pointers etc so that the
 						 * number coercions in duk_hbufobj_validated_write() are
 						 * guaranteed to be side effect free and not invalidate the
 						 * pointer checks we do here.
 						 */
-						duk_push_tval(ctx, tv_val);
-						(void) duk_to_number_m1(ctx);
+						duk_push_tval(thr, tv_val);
+						(void) duk_to_number_m1(thr);
 
 						if (h_bufobj->buf != NULL && DUK_HBUFOBJ_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_hbufobj_validated_write(ctx, h_bufobj, data, elem_size);
+							duk_hbufobj_validated_write(thr, h_bufobj, data, elem_size);
 						} else {
 							DUK_D(DUK_DPRINT("bufobj access out of underlying buffer, ignoring (write skipped)"));
 						}
-						duk_pop(ctx);
+						duk_pop_unsafe(thr);
 						goto success_no_arguments_exotic;
 					}
 				}
@@ -55753,7 +57271,7 @@
 	 * refcount; may need a props allocation resize but doesn't
 	 * 'recheck' the valstack.
 	 */
-	e_idx = duk__alloc_entry_checked(thr, orig, key);
+	e_idx = duk__hobject_alloc_entry_checked(thr, orig, key);
 	DUK_ASSERT(e_idx >= 0);
 
 	tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, orig, e_idx);
@@ -55821,16 +57339,16 @@
 		 * rework to use tv_val directly?
 		 */
 
-		duk_push_tval(ctx, tv_val);
+		duk_push_tval(thr, tv_val);
 		(void) duk__check_arguments_map_for_put(thr, orig, key, &desc, throw_flag);
-		duk_pop(ctx);
+		duk_pop_unsafe(thr);
 	}
 	/* fall thru */
 
  success_no_arguments_exotic:
 	/* shared exit path now */
 	DUK_DDD(DUK_DDDPRINT("result: success"));
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 1;
 
 #if defined(DUK_USE_ES6_PROXY)
@@ -55850,10 +57368,10 @@
 		DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
 #else
 		DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot write property %s of %s",
-		               duk_push_string_tval_readable(ctx, tv_key), duk_push_string_tval_readable(ctx, tv_obj));
-#endif
-	}
-	duk_pop(ctx);  /* remove key */
+		               duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
+#endif
+	}
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 
  fail_not_extensible:
@@ -55861,7 +57379,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_NOT_EXTENSIBLE);
 	}
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 
  fail_not_writable:
@@ -55869,7 +57387,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_NOT_WRITABLE);
 	}
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 
 #if defined(DUK_USE_ROM_OBJECTS)
@@ -55886,7 +57404,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
 	}
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 
  fail_no_setter:
@@ -55894,7 +57412,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_SETTER_UNDEFINED);
 	}
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 
  fail_internal:
@@ -55902,7 +57420,7 @@
 	if (throw_flag) {
 		DUK_ERROR_INTERNAL(thr);
 	}
-	duk_pop(ctx);  /* remove key */
+	duk_pop_unsafe(thr);  /* remove key */
 	return 0;
 }
 
@@ -56070,7 +57588,6 @@
  */
 
 DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_bool_t throw_flag) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *key = NULL;
 #if defined(DUK_USE_ES6_PROXY)
 	duk_propdesc desc;
@@ -56083,7 +57600,6 @@
 	                     (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);
@@ -56094,7 +57610,7 @@
 	/* Storing the entry top is cheaper here to ensure stack is correct at exit,
 	 * as there are several paths out.
 	 */
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 
 	if (DUK_TVAL_IS_UNDEFINED(tv_obj) ||
 	    DUK_TVAL_IS_NULL(tv_obj)) {
@@ -56102,16 +57618,16 @@
 		goto fail_invalid_base_uncond;
 	}
 
-	duk_push_tval(ctx, tv_obj);
-	duk_push_tval(ctx, tv_key);
-
-	tv_obj = DUK_GET_TVAL_NEGIDX(ctx, -2);
+	duk_push_tval(thr, tv_obj);
+	duk_push_tval(thr, tv_key);
+
+	tv_obj = DUK_GET_TVAL_NEGIDX(thr, -2);
 	if (DUK_TVAL_IS_OBJECT(tv_obj)) {
 		duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_obj);
 		DUK_ASSERT(obj != NULL);
 
 #if defined(DUK_USE_ES6_PROXY)
-		if (DUK_UNLIKELY(DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj))) {
+		if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(obj))) {
 			duk_hobject *h_target;
 			duk_bool_t tmp_bool;
 
@@ -56120,11 +57636,11 @@
 			if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_DELETE_PROPERTY, tv_key, &h_target)) {
 				/* -> [ ... obj key trap handler ] */
 				DUK_DDD(DUK_DDDPRINT("-> proxy object 'deleteProperty' for key %!T", (duk_tval *) tv_key));
-				duk_push_hobject(ctx, h_target);  /* target */
-				duk_dup_m4(ctx);  /* P */
-				duk_call_method(ctx, 2 /*nargs*/);
-				tmp_bool = duk_to_boolean(ctx, -1);
-				duk_pop(ctx);
+				duk_push_hobject(thr, h_target);  /* target */
+				duk_dup_m4(thr);  /* P */
+				duk_call_method(thr, 2 /*nargs*/);
+				tmp_bool = duk_to_boolean(thr, -1);
+				duk_pop_nodecref_unsafe(thr);
 				if (!tmp_bool) {
 					goto fail_proxy_rejected;  /* retval indicates delete failed */
 				}
@@ -56132,8 +57648,8 @@
 				/* Target object must be checked for a conflicting
 				 * non-configurable property.
 				 */
-				tv_key = DUK_GET_TVAL_NEGIDX(ctx, -1);
-				arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key);
+				tv_key = DUK_GET_TVAL_NEGIDX(thr, -1);
+				arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key);
 				DUK_ASSERT(key != NULL);
 
 				if (duk__get_own_propdesc_raw(thr, h_target, key, arr_idx, &desc, 0 /*flags*/)) {  /* don't push value */
@@ -56159,7 +57675,7 @@
 		}
 #endif  /* DUK_USE_ES6_PROXY */
 
-		arr_idx = duk__to_property_key(ctx, -1, &key);
+		arr_idx = duk__to_property_key(thr, -1, &key);
 		DUK_ASSERT(key != NULL);
 
 		rc = duk_hobject_delprop_raw(thr, obj, key, throw_flag ? DUK_DELPROP_FLAG_THROW : 0);
@@ -56175,7 +57691,7 @@
 		duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj);
 		DUK_ASSERT(h != NULL);
 
-		arr_idx = duk__to_property_key(ctx, -1, &key);
+		arr_idx = duk__to_property_key(thr, -1, &key);
 		DUK_ASSERT(key != NULL);
 
 		if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
@@ -56194,7 +57710,7 @@
 		duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv_obj);
 		DUK_ASSERT(h != NULL);
 
-		arr_idx = duk__to_property_key(ctx, -1, &key);
+		arr_idx = duk__to_property_key(thr, -1, &key);
 		DUK_ASSERT(key != NULL);
 
 		if (key == DUK_HTHREAD_STRING_LENGTH(thr)) {
@@ -56206,16 +57722,13 @@
 			goto fail_not_configurable;
 		}
 	} else if (DUK_TVAL_IS_LIGHTFUNC(tv_obj)) {
-		/* Lightfunc virtual properties are non-configurable, so
-		 * reject if match any of them.
-		 */
-
-		arr_idx = duk__to_property_key(ctx, -1, &key);
+		/* Lightfunc has no virtual properties since Duktape 2.2
+		 * so success.  Still must coerce key for side effects.
+		 */
+
+		arr_idx = duk__to_property_key(thr, -1, &key);
 		DUK_ASSERT(key != NULL);
-
-		if (duk__key_is_lightfunc_ownprop(thr, key)) {
-			goto fail_not_configurable;
-		}
+		DUK_UNREF(key);
 	}
 
 	/* non-object base, no offending virtual property */
@@ -56223,17 +57736,17 @@
 	goto done_rc;
 
  done_rc:
-	duk_set_top(ctx, entry_top);
+	duk_set_top_unsafe(thr, entry_top);
 	return rc;
 
  fail_invalid_base_uncond:
 	/* Note: unconditional throw */
-	DUK_ASSERT(duk_get_top(ctx) == entry_top);
+	DUK_ASSERT(duk_get_top(thr) == entry_top);
 #if defined(DUK_USE_PARANOID_ERRORS)
 	DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BASE);
 #else
 	DUK_ERROR_FMT2(thr, DUK_ERR_TYPE_ERROR, "cannot delete property %s of %s",
-	               duk_push_string_tval_readable(ctx, tv_key), duk_push_string_tval_readable(ctx, tv_obj));
+	               duk_push_string_tval_readable(thr, tv_key), duk_push_string_tval_readable(thr, tv_obj));
 #endif
 	return 0;
 
@@ -56242,7 +57755,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_PROXY_REJECTED);
 	}
-	duk_set_top(ctx, entry_top);
+	duk_set_top_unsafe(thr, entry_top);
 	return 0;
 #endif
 
@@ -56250,7 +57763,7 @@
 	if (throw_flag) {
 		DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE);
 	}
-	duk_set_top(ctx, entry_top);
+	duk_set_top_unsafe(thr, entry_top);
 	return 0;
 }
 
@@ -56274,7 +57787,6 @@
  */
 
 DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_propdesc desc;
 	duk_uint32_t arr_idx;
 	duk_int_t e_idx;
@@ -56284,7 +57796,7 @@
 
 	DUK_DDD(DUK_DDDPRINT("define new property (internal): thr=%p, obj=%!O, key=%!O, flags=0x%02lx, val=%!T",
 	                     (void *) thr, (duk_heaphdr *) obj, (duk_heaphdr *) key,
-	                     (unsigned long) flags, (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (unsigned long) flags, (duk_tval *) duk_get_tval(thr, -1)));
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
@@ -56292,7 +57804,7 @@
 	DUK_ASSERT(key != NULL);
 	DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj));
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
-	DUK_ASSERT(duk_is_valid_index(ctx, -1));  /* contains value */
+	DUK_ASSERT(duk_is_valid_index(thr, -1));  /* contains value */
 
 	arr_idx = DUK_HSTRING_GET_ARRIDX_SLOW(key);
 
@@ -56334,7 +57846,7 @@
 				duk_uint32_t prev_len;
 				prev_len = ((duk_harray *) obj)->length;
 #endif
-				new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(ctx, -1));
+				new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_NEGIDX(thr, -1));
 				((duk_harray *) obj)->length = new_len;
 				DUK_D(DUK_DPRINT("internal define property for array .length: %ld -> %ld",
 				                 (long) prev_len, (long) ((duk_harray *) obj)->length));
@@ -56364,7 +57876,7 @@
 	}
 
 	DUK_DDD(DUK_DDDPRINT("property does not exist, object belongs in entry part -> allocate new entry and write value and attributes"));
-	e_idx = duk__alloc_entry_checked(thr, obj, key);  /* increases key refcount */
+	e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);  /* increases key refcount */
 	DUK_ASSERT(e_idx >= 0);
 	DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, propflags);
 	tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx);
@@ -56375,7 +57887,7 @@
  write_value:
 	/* tv1 points to value storage */
 
-	tv2 = duk_require_tval(ctx, -1);  /* late lookup, avoid side effects */
+	tv2 = duk_require_tval(thr, -1);  /* late lookup, avoid side effects */
 	DUK_DDD(DUK_DDDPRINT("writing/updating value: %!T -> %!T",
 	                     (duk_tval *) tv1, (duk_tval *) tv2));
 
@@ -56383,7 +57895,7 @@
 	goto pop_exit;
 
  pop_exit:
-	duk_pop(ctx);  /* remove in_val */
+	duk_pop_unsafe(thr);  /* remove in_val */
 	return;
 
  error_virtual:  /* share error message */
@@ -56399,14 +57911,13 @@
  */
 
 DUK_INTERNAL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *key;
 	duk_tval *tv1, *tv2;
 
 	DUK_DDD(DUK_DDDPRINT("define new property (internal) arr_idx fast path: thr=%p, obj=%!O, "
 	                     "arr_idx=%ld, flags=0x%02lx, val=%!T",
 	                     (void *) thr, obj, (long) arr_idx, (unsigned long) flags,
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
@@ -56427,23 +57938,23 @@
 
 		DUK_ASSERT(arr_idx < DUK_HOBJECT_GET_ASIZE(obj));
 		tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx);
-		tv2 = duk_require_tval(ctx, -1);
+		tv2 = duk_require_tval(thr, -1);
 
 		DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */
 
-		duk_pop(ctx);  /* [ ...val ] -> [ ... ] */
+		duk_pop_unsafe(thr);  /* [ ...val ] -> [ ... ] */
 		return;
 	}
 
 	DUK_DDD(DUK_DDDPRINT("define property fast path didn't work, use slow path"));
 
-	key = duk_push_uint_to_hstring(ctx, (duk_uint_t) arr_idx);
+	key = duk_push_uint_to_hstring(thr, (duk_uint_t) arr_idx);
 	DUK_ASSERT(key != NULL);
-	duk_insert(ctx, -2);  /* [ ... val key ] -> [ ... key val ] */
+	duk_insert(thr, -2);  /* [ ... val key ] -> [ ... key val ] */
 
 	duk_hobject_define_property_internal(thr, obj, key, flags);
 
-	duk_pop(ctx);  /* [ ... key ] -> [ ... ] */
+	duk_pop_unsafe(thr);  /* [ ... key ] -> [ ... ] */
 }
 
 /*
@@ -56451,10 +57962,9 @@
  */
 
 DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_double_t val;
 
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_ASSERT(obj != NULL);
 
 	/* Fast path for Arrays. */
@@ -56463,13 +57973,13 @@
 	}
 
 	/* Slow path, .length can be e.g. accessor, obj can be a Proxy, etc. */
-	duk_push_hobject(ctx, obj);
-	duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH);
+	duk_push_hobject(thr, obj);
+	duk_push_hstring_stridx(thr, DUK_STRIDX_LENGTH);
 	(void) duk_hobject_getprop(thr,
-	                           DUK_GET_TVAL_NEGIDX(ctx, -2),
-	                           DUK_GET_TVAL_NEGIDX(ctx, -1));
-	val = duk_to_number_m1(ctx);
-	duk_pop_3(ctx);
+	                           DUK_GET_TVAL_NEGIDX(thr, -2),
+	                           DUK_GET_TVAL_NEGIDX(thr, -1));
+	val = duk_to_number_m1(thr);
+	duk_pop_3_unsafe(thr);
 
 	/* This isn't part of Ecmascript semantics; return a value within
 	 * duk_size_t range, or 0 otherwise.
@@ -56521,31 +58031,27 @@
  *  [ ... key ] -> [ ... desc/undefined ]
  */
 
-DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_context *ctx, duk_idx_t obj_idx) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_hthread *thr, duk_idx_t obj_idx) {
 	duk_hobject *obj;
 	duk_hstring *key;
 	duk_propdesc pd;
-	duk_bool_t rc;
-
-	DUK_ASSERT(ctx != NULL);
+
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
 
-	obj = duk_require_hobject_promote_mask(ctx, obj_idx, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-	key = duk_to_property_key_hstring(ctx, -1);
+	obj = duk_require_hobject_promote_mask(thr, obj_idx, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+	key = duk_to_property_key_hstring(thr, -1);
 	DUK_ASSERT(key != NULL);
 
 	DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE);
 
-	rc = duk_hobject_get_own_propdesc(thr, obj, key, &pd, DUK_GETDESC_FLAG_PUSH_VALUE);
-	if (!rc) {
-		duk_push_undefined(ctx);
-		duk_remove_m2(ctx);
-		return;
-	}
-
-	duk_push_object(ctx);
+	if (!duk_hobject_get_own_propdesc(thr, obj, key, &pd, DUK_GETDESC_FLAG_PUSH_VALUE)) {
+		duk_push_undefined(thr);
+		duk_remove_m2(thr);
+		return;
+	}
+
+	duk_push_object(thr);
 
 	/* [ ... key value desc ] */
 
@@ -56554,32 +58060,32 @@
 		 * still have the property present with the value 'undefined'.
 		 */
 		if (pd.get) {
-			duk_push_hobject(ctx, pd.get);
-		} else {
-			duk_push_undefined(ctx);
-		}
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_GET);
+			duk_push_hobject(thr, pd.get);
+		} else {
+			duk_push_undefined(thr);
+		}
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_GET);
 		if (pd.set) {
-			duk_push_hobject(ctx, pd.set);
-		} else {
-			duk_push_undefined(ctx);
-		}
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_SET);
-	} else {
-		duk_dup_m2(ctx);
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_VALUE);
-		duk_push_boolean(ctx, DUK_PROPDESC_IS_WRITABLE(&pd));
-		duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_WRITABLE);
-	}
-	duk_push_boolean(ctx, DUK_PROPDESC_IS_ENUMERABLE(&pd));
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_ENUMERABLE);
-	duk_push_boolean(ctx, DUK_PROPDESC_IS_CONFIGURABLE(&pd));
-	duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_CONFIGURABLE);
+			duk_push_hobject(thr, pd.set);
+		} else {
+			duk_push_undefined(thr);
+		}
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_SET);
+	} else {
+		duk_dup_m2(thr);
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_VALUE);
+		duk_push_boolean(thr, DUK_PROPDESC_IS_WRITABLE(&pd));
+		duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_WRITABLE);
+	}
+	duk_push_boolean(thr, DUK_PROPDESC_IS_ENUMERABLE(&pd));
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_ENUMERABLE);
+	duk_push_boolean(thr, DUK_PROPDESC_IS_CONFIGURABLE(&pd));
+	duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_CONFIGURABLE);
 
 	/* [ ... key value desc ] */
 
-	duk_replace(ctx, -3);
-	duk_pop(ctx);  /* -> [ ... desc ] */
+	duk_replace(thr, -3);
+	duk_pop_unsafe(thr);  /* -> [ ... desc ] */
 }
 
 /*
@@ -56599,13 +58105,12 @@
 /* XXX: very basic optimization -> duk_get_prop_stridx_top */
 
 DUK_INTERNAL
-void duk_hobject_prepare_property_descriptor(duk_context *ctx,
+void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
                                              duk_idx_t idx_in,
                                              duk_uint_t *out_defprop_flags,
                                              duk_idx_t *out_idx_value,
                                              duk_hobject **out_getter,
                                              duk_hobject **out_setter) {
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_idx_t idx_value = -1;
 	duk_hobject *getter = NULL;
 	duk_hobject *setter = NULL;
@@ -56613,7 +58118,6 @@
 	duk_bool_t is_acc_desc = 0;
 	duk_uint_t defprop_flags = 0;
 
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(out_defprop_flags != NULL);
 	DUK_ASSERT(out_idx_value != NULL);
 	DUK_ASSERT(out_getter != NULL);
@@ -56621,8 +58125,8 @@
 	DUK_ASSERT(idx_in <= 0x7fffL);  /* short variants would be OK, but not used to avoid shifts */
 
 	/* Must be an object, otherwise TypeError (E5.1 Section 8.10.5, step 1). */
-	idx_in = duk_require_normalize_index(ctx, idx_in);
-	(void) duk_require_hobject(ctx, idx_in);
+	idx_in = duk_require_normalize_index(thr, idx_in);
+	(void) duk_require_hobject(thr, idx_in);
 
 	/* The coercion order must match the ToPropertyDescriptor() algorithm
 	 * so that side effects in coercion happen in the correct order.
@@ -56630,23 +58134,23 @@
 	 * although it doesn't matter in practice.)
 	 */
 
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_VALUE)) {
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_VALUE)) {
 		is_data_desc = 1;
 		defprop_flags |= DUK_DEFPROP_HAVE_VALUE;
-		idx_value = duk_get_top_index(ctx);
-	}
-
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_WRITABLE)) {
+		idx_value = duk_get_top_index(thr);
+	}
+
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) {
 		is_data_desc = 1;
-		if (duk_to_boolean(ctx, -1)) {
+		if (duk_to_boolean(thr, -1)) {
 			defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE;
 		} else {
 			defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE;
 		}
 	}
 
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_GET)) {
-		duk_tval *tv = duk_require_tval(ctx, -1);
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_GET)) {
+		duk_tval *tv = duk_require_tval(thr, -1);
 		duk_hobject *h_get;
 
 		if (DUK_TVAL_IS_UNDEFINED(tv)) {
@@ -56657,7 +58161,7 @@
 			 * lightfuncs don't fit into a property value slot.  This
 			 * has some side effects, see test-dev-lightfunc-accessor.js.
 			 */
-			h_get = duk_get_hobject_promote_lfunc(ctx, -1);
+			h_get = duk_get_hobject_promote_lfunc(thr, -1);
 			if (h_get == NULL || !DUK_HOBJECT_IS_CALLABLE(h_get)) {
 				goto type_error;
 			}
@@ -56667,8 +58171,8 @@
 		defprop_flags |= DUK_DEFPROP_HAVE_GETTER;
 	}
 
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_SET)) {
-		duk_tval *tv = duk_require_tval(ctx, -1);
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_SET)) {
+		duk_tval *tv = duk_require_tval(thr, -1);
 		duk_hobject *h_set;
 
 		if (DUK_TVAL_IS_UNDEFINED(tv)) {
@@ -56679,7 +58183,7 @@
 			 * lightfuncs don't fit into a property value slot.  This
 			 * has some side effects, see test-dev-lightfunc-accessor.js.
 			 */
-			h_set = duk_get_hobject_promote_lfunc(ctx, -1);
+			h_set = duk_get_hobject_promote_lfunc(thr, -1);
 			if (h_set == NULL || !DUK_HOBJECT_IS_CALLABLE(h_set)) {
 				goto type_error;
 			}
@@ -56689,16 +58193,16 @@
 		defprop_flags |= DUK_DEFPROP_HAVE_SETTER;
 	}
 
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_ENUMERABLE)) {
-		if (duk_to_boolean(ctx, -1)) {
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_ENUMERABLE)) {
+		if (duk_to_boolean(thr, -1)) {
 			defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE;
 		} else {
 			defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE;
 		}
 	}
 
-	if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_CONFIGURABLE)) {
-		if (duk_to_boolean(ctx, -1)) {
+	if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_CONFIGURABLE)) {
+		if (duk_to_boolean(thr, -1)) {
 			defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE;
 		} else {
 			defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE;
@@ -56742,7 +58246,7 @@
 
 /* XXX: this is a major target for size optimization */
 DUK_INTERNAL
-duk_bool_t duk_hobject_define_property_helper(duk_context *ctx,
+duk_bool_t duk_hobject_define_property_helper(duk_hthread *thr,
                                               duk_uint_t defprop_flags,
                                               duk_hobject *obj,
                                               duk_hstring *key,
@@ -56750,7 +58254,6 @@
                                               duk_hobject *get,
                                               duk_hobject *set,
                                               duk_bool_t throw_flag) {
-	duk_hthread *thr = (duk_hthread *) ctx;
 	duk_uint32_t arr_idx;
 	duk_tval tv;
 	duk_bool_t has_enumerable;
@@ -56772,7 +58275,6 @@
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(obj != NULL);
 	DUK_ASSERT(key != NULL);
 	/* idx_value may be < 0 (no value), set and get may be NULL */
@@ -56809,7 +58311,7 @@
 	                     (long) has_enumerable, (long) is_enumerable,
 	                     (long) has_configurable, (long) is_configurable,
 	                     (long) has_writable, (long) is_writable,
-	                     (long) has_value, (duk_tval *) (idx_value >= 0 ? duk_get_tval(ctx, idx_value) : NULL),
+	                     (long) has_value, (duk_tval *) (idx_value >= 0 ? duk_get_tval(thr, idx_value) : NULL),
 	                     (long) has_get, (void *) get, (duk_heaphdr *) get,
 	                     (long) has_set, (void *) set, (duk_heaphdr *) set,
 	                     (long) arr_idx, (long) throw_flag));
@@ -56844,9 +58346,9 @@
 		arrlen_old_len = a->length;
 
 		DUK_ASSERT(idx_value >= 0);
-		arrlen_new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_POSIDX(ctx, idx_value));
-		duk_push_u32(ctx, arrlen_new_len);
-		duk_replace(ctx, idx_value);  /* step 3.e: replace 'Desc.[[Value]]' */
+		arrlen_new_len = duk__to_new_array_length_checked(thr, DUK_GET_TVAL_POSIDX(thr, idx_value));
+		duk_push_u32(thr, arrlen_new_len);
+		duk_replace(thr, idx_value);  /* step 3.e: replace 'Desc.[[Value]]' */
 
 		DUK_DDD(DUK_DDDPRINT("old_len=%ld, new_len=%ld", (long) arrlen_old_len, (long) arrlen_new_len));
 
@@ -56975,7 +58477,7 @@
 			}
 
 			/* write to entry part */
-			e_idx = duk__alloc_entry_checked(thr, obj, key);
+			e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);
 			DUK_ASSERT(e_idx >= 0);
 
 			DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, e_idx, get);
@@ -57005,7 +58507,7 @@
 				new_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
 			}
 			if (has_value) {
-				duk_tval *tv_tmp = duk_require_tval(ctx, idx_value);
+				duk_tval *tv_tmp = duk_require_tval(thr, idx_value);
 				DUK_TVAL_SET_TVAL(&tv, tv_tmp);
 			} else {
 				DUK_TVAL_SET_UNDEFINED(&tv);  /* default value */
@@ -57028,7 +58530,7 @@
 			}
 
 			/* write to entry part */
-			e_idx = duk__alloc_entry_checked(thr, obj, key);
+			e_idx = duk__hobject_alloc_entry_checked(thr, obj, key);
 			DUK_ASSERT(e_idx >= 0);
 			tv2 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, e_idx);
 			DUK_TVAL_SET_TVAL(tv2, &tv);
@@ -57081,8 +58583,8 @@
 			goto need_check;
 		}
 
-		tmp1 = duk_require_tval(ctx, -1);         /* curr value */
-		tmp2 = duk_require_tval(ctx, idx_value);  /* new value */
+		tmp1 = duk_require_tval(thr, -1);         /* curr value */
+		tmp2 = duk_require_tval(thr, idx_value);  /* new value */
 		if (!duk_js_samevalue(tmp1, tmp2)) {
 			goto need_check;
 		}
@@ -57201,7 +58703,7 @@
 			if (curr.a_idx >= 0) {
 				DUK_DDD(DUK_DDDPRINT("property to convert is stored in an array entry, abandon array and re-lookup"));
 				duk__abandon_array_checked(thr, obj);
-				duk_pop(ctx);  /* remove old value */
+				duk_pop_unsafe(thr);  /* remove old value */
 				rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE);
 				DUK_UNREF(rc);
 				DUK_ASSERT(rc != 0);
@@ -57277,8 +58779,8 @@
 				}
 				/* Note: changing from writable to non-writable is OK */
 				if (!(curr.flags & DUK_PROPDESC_FLAG_WRITABLE) && has_value) {
-					duk_tval *tmp1 = duk_require_tval(ctx, -1);         /* curr value */
-					duk_tval *tmp2 = duk_require_tval(ctx, idx_value);  /* new value */
+					duk_tval *tmp1 = duk_require_tval(thr, -1);         /* curr value */
+					duk_tval *tmp2 = duk_require_tval(thr, idx_value);  /* new value */
 					if (!duk_js_samevalue(tmp1, tmp2)) {
 						goto fail_not_configurable;
 					}
@@ -57349,7 +58851,7 @@
 			DUK_ASSERT(!has_get);
 			DUK_ASSERT(idx_value >= 0);  /* must be: if attributes match and we get here the value must differ (otherwise no change) */
 
-			tv2 = duk_require_tval(ctx, idx_value);
+			tv2 = duk_require_tval(thr, idx_value);
 			tv1 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, curr.a_idx);
 			DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects; may invalidate a_idx */
 			goto success_exotics;
@@ -57357,7 +58859,7 @@
 
 		DUK_DDD(DUK_DDDPRINT("array index, new property attributes do not match array defaults, abandon array and re-lookup"));
 		duk__abandon_array_checked(thr, obj);
-		duk_pop(ctx);  /* remove old value */
+		duk_pop_unsafe(thr);  /* remove old value */
 		rc = duk__get_own_propdesc_raw(thr, obj, key, arr_idx, &curr, DUK_GETDESC_FLAG_PUSH_VALUE);
 		DUK_UNREF(rc);
 		DUK_ASSERT(rc != 0);
@@ -57437,7 +58939,7 @@
 
 		if (curr.e_idx >= 0) {
 			DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, obj, curr.e_idx));
-			tv2 = duk_require_tval(ctx, idx_value);
+			tv2 = duk_require_tval(thr, idx_value);
 			tv1 = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx);
 			DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects; may invalidate e_idx */
 		} else {
@@ -57567,17 +59069,17 @@
 				DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', "
 				                     "update bound value (variable/argument)"));
 
-				varname = duk_require_hstring(ctx, -1);
+				varname = duk_require_hstring(thr, -1);
 				DUK_ASSERT(varname != NULL);
 
 				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, idx_value)));
+				                     (duk_tval *) duk_require_tval(thr, idx_value)));
 
 				/* strict flag for putvar comes from our caller (currently: fixed) */
-				duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(ctx, idx_value), 1 /*throw_flag*/);
+				duk_js_putvar_envrec(thr, varenv, varname, duk_require_tval(thr, idx_value), 1 /*throw_flag*/);
 			}
 			if (has_writable && !is_writable) {
 				DUK_DDD(DUK_DDDPRINT("defineProperty successful, key mapped to arguments 'map', "
@@ -57617,23 +59119,22 @@
  *  Object.prototype.hasOwnProperty() and Object.prototype.propertyIsEnumerable().
  */
 
-DUK_INTERNAL duk_bool_t duk_hobject_object_ownprop_helper(duk_context *ctx, duk_small_uint_t required_desc_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL duk_bool_t duk_hobject_object_ownprop_helper(duk_hthread *thr, duk_small_uint_t required_desc_flags) {
 	duk_hstring *h_v;
 	duk_hobject *h_obj;
 	duk_propdesc desc;
 	duk_bool_t ret;
 
 	/* coercion order matters */
-	h_v = duk_to_hstring_acceptsymbol(ctx, 0);
+	h_v = duk_to_hstring_acceptsymbol(thr, 0);
 	DUK_ASSERT(h_v != NULL);
 
-	h_obj = duk_push_this_coercible_to_object(ctx);
+	h_obj = duk_push_this_coercible_to_object(thr);
 	DUK_ASSERT(h_obj != NULL);
 
 	ret = duk_hobject_get_own_propdesc(thr, h_obj, h_v, &desc, 0 /*flags*/);  /* don't push value */
 
-	duk_push_boolean(ctx, ret && ((desc.flags & required_desc_flags) == required_desc_flags));
+	duk_push_boolean(thr, ret && ((desc.flags & required_desc_flags) == required_desc_flags));
 	return 1;
 }
 
@@ -57800,7 +59301,7 @@
 	DUK_ASSERT_DISABLE(pos >= 0);  /* unsigned */
 	DUK_ASSERT(pos < (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h));
 
-	boff = duk_heap_strcache_offset_char2byte(thr, h, (duk_uint32_t) pos);
+	boff = (duk_uint32_t) duk_heap_strcache_offset_char2byte(thr, h, (duk_uint32_t) pos);
 	DUK_DDD(DUK_DDDPRINT("charCodeAt: pos=%ld -> boff=%ld, str=%!O",
 	                     (long) pos, (long) boff, (duk_heaphdr *) h));
 	DUK_ASSERT_DISABLE(boff >= 0);
@@ -57825,7 +59326,7 @@
 			cp2 = 0;  /* If call fails, this is left untouched and won't match cp2 check. */
 			(void) duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp2);
 			if (cp2 >= 0xdc00UL && cp2 <= 0xdfffUL) {
-				cp1 = ((cp1 - 0xd800UL) << 10) + (cp2 - 0xdc00UL) + 0x10000UL;
+				cp1 = (duk_ucodepoint_t) (((cp1 - 0xd800UL) << 10) + (cp2 - 0xdc00UL) + 0x10000UL);
 			}
 		}
 	} else {
@@ -57836,9 +59337,46 @@
 }
 
 /*
- *  duk_hstring charlen access
- */
-
+ *  duk_hstring charlen, when lazy charlen disabled
+ */
+
+#if !defined(DUK_USE_HSTRING_LAZY_CLEN)
+#if !defined(DUK_USE_HSTRING_CLEN)
+#error non-lazy duk_hstring charlen but DUK_USE_HSTRING_CLEN not set
+#endif
+DUK_INTERNAL void duk_hstring_init_charlen(duk_hstring *h) {
+	duk_uint32_t clen;
+
+	DUK_ASSERT(h != NULL);
+	DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(h));
+	DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h));
+
+	clen = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h));
+#if defined(DUK_USE_STRLEN16)
+	DUK_ASSERT(clen <= 0xffffUL);  /* Bytelength checked during interning. */
+	h->clen16 = (duk_uint16_t) clen;
+#else
+	h->clen = (duk_uint32_t) clen;
+#endif
+	if (DUK_LIKELY(clen == DUK_HSTRING_GET_BYTELEN(h))) {
+		DUK_HSTRING_SET_ASCII(h);
+	}
+}
+
+DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) {
+#if defined(DUK_USE_STRLEN16)
+	return h->clen16;
+#else
+	return h->clen;
+#endif
+}
+#endif  /* !DUK_USE_HSTRING_LAZY_CLEN */
+
+/*
+ *  duk_hstring charlen, when lazy charlen enabled
+ */
+
+#if defined(DUK_USE_HSTRING_LAZY_CLEN)
 #if defined(DUK_USE_HSTRING_CLEN)
 DUK_LOCAL DUK_COLD duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) {
 	duk_size_t res;
@@ -57916,6 +59454,27 @@
 	return duk__hstring_get_charlen_slowpath(h);
 }
 #endif  /* DUK_USE_HSTRING_CLEN */
+#endif  /* DUK_USE_HSTRING_LAZY_CLEN */
+
+/*
+ *  Compare duk_hstring to an ASCII cstring.
+ */
+
+DUK_INTERNAL duk_bool_t duk_hstring_equals_ascii_cstring(duk_hstring *h, const char *cstr) {
+	duk_size_t len;
+
+	DUK_ASSERT(h != NULL);
+	DUK_ASSERT(cstr != NULL);
+
+	len = DUK_STRLEN(cstr);
+	if (len != DUK_HSTRING_GET_BYTELEN(h)) {
+		return 0;
+	}
+	if (DUK_MEMCMP((const void *) cstr, (const void *) DUK_HSTRING_GET_DATA(h), len) == 0) {
+		return 1;
+	}
+	return 0;
+}
 #line 1 "duk_hthread_alloc.c"
 /*
  *  duk_hthread allocation and freeing.
@@ -57937,23 +59496,21 @@
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->valstack == NULL);
 	DUK_ASSERT(thr->valstack_end == NULL);
+	DUK_ASSERT(thr->valstack_alloc_end == NULL);
 	DUK_ASSERT(thr->valstack_bottom == NULL);
 	DUK_ASSERT(thr->valstack_top == NULL);
-	DUK_ASSERT(thr->callstack == NULL);
 	DUK_ASSERT(thr->callstack_curr == NULL);
-	DUK_ASSERT(thr->catchstack == NULL);
 
 	/* valstack */
+	DUK_ASSERT(DUK_VALSTACK_API_ENTRY_MINIMUM <= DUK_VALSTACK_INITIAL_SIZE);
 	alloc_size = sizeof(duk_tval) * DUK_VALSTACK_INITIAL_SIZE;
 	thr->valstack = (duk_tval *) DUK_ALLOC(heap, alloc_size);
 	if (!thr->valstack) {
 		goto fail;
 	}
 	DUK_MEMZERO(thr->valstack, alloc_size);
-	thr->valstack_end = thr->valstack + DUK_VALSTACK_INITIAL_SIZE;
-#if !defined(DUK_USE_PREFER_SIZE)
-	thr->valstack_size = DUK_VALSTACK_INITIAL_SIZE;
-#endif
+	thr->valstack_end = thr->valstack + DUK_VALSTACK_API_ENTRY_MINIMUM;
+	thr->valstack_alloc_end = thr->valstack + DUK_VALSTACK_INITIAL_SIZE;
 	thr->valstack_bottom = thr->valstack;
 	thr->valstack_top = thr->valstack;
 
@@ -57961,37 +59518,13 @@
 		DUK_TVAL_SET_UNDEFINED(&thr->valstack[i]);
 	}
 
-	/* callstack */
-	alloc_size = sizeof(duk_activation) * DUK_CALLSTACK_INITIAL_SIZE;
-	thr->callstack = (duk_activation *) DUK_ALLOC(heap, alloc_size);
-	if (!thr->callstack) {
-		goto fail;
-	}
-	DUK_MEMZERO(thr->callstack, alloc_size);
-	thr->callstack_size = DUK_CALLSTACK_INITIAL_SIZE;
-	DUK_ASSERT(thr->callstack_top == 0);
-	DUK_ASSERT(thr->callstack_curr == NULL);
-
-	/* catchstack */
-	alloc_size = sizeof(duk_catcher) * DUK_CATCHSTACK_INITIAL_SIZE;
-	thr->catchstack = (duk_catcher *) DUK_ALLOC(heap, alloc_size);
-	if (!thr->catchstack) {
-		goto fail;
-	}
-	DUK_MEMZERO(thr->catchstack, alloc_size);
-	thr->catchstack_size = DUK_CATCHSTACK_INITIAL_SIZE;
-	DUK_ASSERT(thr->catchstack_top == 0);
-
 	return 1;
 
  fail:
 	DUK_FREE(heap, thr->valstack);
-	DUK_FREE(heap, thr->callstack);
-	DUK_FREE(heap, thr->catchstack);
+	DUK_ASSERT(thr->callstack_curr == NULL);
 
 	thr->valstack = NULL;
-	thr->callstack = NULL;
-	thr->catchstack = NULL;
 	return 0;
 }
 
@@ -58002,18 +59535,6 @@
 	DUK_UNREF(heap);
 	return (void *) thr->valstack;
 }
-
-DUK_INTERNAL void *duk_hthread_get_callstack_ptr(duk_heap *heap, void *ud) {
-	duk_hthread *thr = (duk_hthread *) ud;
-	DUK_UNREF(heap);
-	return (void *) thr->callstack;
-}
-
-DUK_INTERNAL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud) {
-	duk_hthread *thr = (duk_hthread *) ud;
-	DUK_UNREF(heap);
-	return (void *) thr->catchstack;
-}
 #line 1 "duk_hthread_builtins.c"
 /*
  *  Initialize built-in objects.  Current thread must have a valstack
@@ -58051,7 +59572,6 @@
 #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_context *ctx;
 	duk_hobject *h_global;
 #if defined(DUK_USE_ROM_GLOBAL_CLONE)
 	duk_hobject *h_oldglobal;
@@ -58060,13 +59580,11 @@
 #endif
 	duk_hobject *h_objenv;
 
-	ctx = (duk_context *) thr;
-
 	/* 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(ctx,
+	h_global = duk_push_object_helper(thr,
 	                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                                  DUK_HOBJECT_FLAG_FASTREFS |
 	                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
@@ -58077,7 +59595,7 @@
 	 * fully RAM-based global object.  Uses more memory than the inherit
 	 * model but more compliant.
 	 */
-	h_global = duk_push_object_helper(ctx,
+	h_global = duk_push_object_helper(thr,
 	                                  DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                                  DUK_HOBJECT_FLAG_FASTREFS |
 	                                  DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL),
@@ -58128,7 +59646,7 @@
 	                                             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(ctx, h_objenv);
+	duk_push_hobject(thr, h_objenv);
 
 	DUK_ASSERT(h_global != NULL);
 	((duk_hobjenv *) h_objenv)->target = h_global;
@@ -58143,7 +59661,7 @@
 
 	DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) h_objenv);
 
-	duk_pop_2(ctx);  /* Pop global object and global env. */
+	duk_pop_2(thr);  /* Pop global object and global env. */
 }
 #endif  /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */
 
@@ -58170,15 +59688,15 @@
 #endif
 }
 #else  /* DUK_USE_ROM_OBJECTS */
-DUK_LOCAL void duk__push_stridx(duk_context *ctx, duk_bitdecoder_ctx *bd) {
+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(ctx, n);
-}
-DUK_LOCAL void duk__push_string(duk_context *ctx, duk_bitdecoder_ctx *bd) {
+	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.
 	 */
@@ -58186,21 +59704,21 @@
 	duk_small_uint_t len;
 
 	len = duk_bd_decode_bitpacked_string(bd, tmp);
-	duk_push_lstring(ctx, (const char *) tmp, (duk_size_t) len);
-}
-DUK_LOCAL void duk__push_stridx_or_string(duk_context *ctx, duk_bitdecoder_ctx *bd) {
+	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(ctx, bd);
+		duk__push_string(thr, bd);
 	} else {
 		n--;
 		DUK_ASSERT(n < DUK_HEAP_NUM_STRINGS);
-		duk_push_hstring_stridx(ctx, n);
-	}
-}
-DUK_LOCAL void duk__push_double(duk_context *ctx, duk_bitdecoder_ctx *bd) {
+		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;
 
@@ -58211,11 +59729,10 @@
 		du.uc[i] = (duk_uint8_t) duk_bd_decode(bd, 8);
 	}
 
-	duk_push_number(ctx, du.d);  /* push operation normalizes NaNs */
+	duk_push_number(thr, du.d);  /* push operation normalizes NaNs */
 }
 
 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;
@@ -58238,12 +59755,14 @@
 	 *  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).
-	 */
-
-	duk_require_stack(ctx, DUK_NUM_ALL_BUILTINS);
+	 *
+	 *  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(ctx, 0);
+	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 */
@@ -58271,9 +59790,8 @@
 			}
 
 			/* XXX: set magic directly here? (it could share the c_nargs arg) */
-			duk_push_c_function_noexotic(ctx, c_func, c_nargs);
-
-			h = duk_known_hobject(ctx, -1);
+			(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
@@ -58283,14 +59801,14 @@
 
 			/* XXX: function properties */
 
-			duk__push_stridx_or_string(ctx, bd);
+			duk__push_stridx_or_string(thr, bd);
 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
-			duk_xdef_prop_stridx_short(ctx,
+			duk_xdef_prop_stridx_short(thr,
 			                           -2,
 			                           DUK_STRIDX_NAME,
 			                           DUK_PROPDESC_FLAGS_C);
 #else
-			duk_pop(ctx);  /* Not very ideal but good enough for now. */
+			duk_pop(thr);  /* Not very ideal but good enough for now. */
 #endif
 
 			/* Almost all global level Function objects are constructable
@@ -58307,7 +59825,7 @@
 			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(ctx);
+			duk_push_array(thr);
 		} else if (class_num == DUK_HOBJECT_CLASS_OBJENV) {
 			duk_hobjenv *env;
 			duk_hobject *global;
@@ -58319,9 +59837,9 @@
 	                                        DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
 			DUK_ASSERT(env->target == NULL);
-			duk_push_hobject(ctx, (duk_hobject *) env);
-
-			global = duk_known_hobject(ctx, DUK_BIDX_GLOBAL);
+			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);
@@ -58331,14 +59849,14 @@
 		} else {
 			DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_DECENV);
 
-			(void) duk_push_object_helper(ctx,
+			(void) duk_push_object_helper(thr,
 			                              DUK_HOBJECT_FLAG_FASTREFS |
 			                              DUK_HOBJECT_FLAG_EXTENSIBLE,
 			                              -1);  /* no prototype or class yet */
 
 		}
 
-		h = duk_known_hobject(ctx, -1);
+		h = duk_known_hobject(thr, -1);
 		DUK_HOBJECT_SET_CLASS_NUMBER(h, class_num);
 
 		if (i < DUK_NUM_BUILTINS) {
@@ -58358,8 +59876,8 @@
 			 */
 
 			DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_ARRAY);  /* .length is virtual */
-			duk_push_int(ctx, len);
-			duk_xdef_prop_stridx_short(ctx,
+			duk_push_int(thr, len);
+			duk_xdef_prop_stridx_short(thr,
 			                           -2,
 			                           DUK_STRIDX_LENGTH,
 			                           DUK_PROPDESC_FLAGS_C);
@@ -58397,7 +59915,8 @@
 
 	/*
 	 *  Then decode the builtins init data (see genbuiltins.py) to
-	 *  init objects
+	 *  init objects.  Internal prototypes are set at this stage,
+	 *  with thr->builtins[] populated.
 	 */
 
 	DUK_DD(DUK_DDPRINT("initialize built-in object properties"));
@@ -58406,13 +59925,20 @@
 		duk_small_uint_t num;
 
 		DUK_DDD(DUK_DDDPRINT("initializing built-in object at index %ld", (long) i));
-		h = duk_known_hobject(ctx, 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(ctx, 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);
@@ -58424,7 +59950,8 @@
 			 */
 			t--;
 			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);
+			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);
@@ -58436,7 +59963,8 @@
 			 */
 			t--;
 			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);
+			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 */
@@ -58445,7 +59973,7 @@
 		for (j = 0; j < num; j++) {
 			duk_small_uint_t defprop_flags;
 
-			duk__push_stridx_or_string(ctx, bd);
+			duk__push_stridx_or_string(thr, bd);
 
 			/*
 			 *  Property attribute defaults are defined in E5 Section 15 (first
@@ -58475,38 +60003,38 @@
 			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(ctx, -1), (unsigned long) defprop_flags, (long) t));
+			                     (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(ctx, bd);
+				duk__push_double(thr, bd);
 				break;
 			}
 			case DUK__PROP_TYPE_STRING: {
-				duk__push_string(ctx, bd);
+				duk__push_string(thr, bd);
 				break;
 			}
 			case DUK__PROP_TYPE_STRIDX: {
-				duk__push_stridx(ctx, bd);
+				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(ctx, (duk_idx_t) bidx);
+				duk_dup(thr, (duk_idx_t) bidx);
 				break;
 			}
 			case DUK__PROP_TYPE_UNDEFINED: {
-				duk_push_undefined(ctx);
+				duk_push_undefined(thr);
 				break;
 			}
 			case DUK__PROP_TYPE_BOOLEAN_TRUE: {
-				duk_push_true(ctx);
+				duk_push_true(thr);
 				break;
 			}
 			case DUK__PROP_TYPE_BOOLEAN_FALSE: {
-				duk_push_false(ctx);
+				duk_push_false(thr);
 				break;
 			}
 			case DUK__PROP_TYPE_ACCESSOR: {
@@ -58517,18 +60045,18 @@
 				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(ctx, -1), (long) natidx_getter, (long) natidx_setter, (unsigned long) defprop_flags));
+				                     (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_noconstruct_noexotic(ctx, c_func_getter, 0);  /* always 0 args */
-					duk_set_magic(ctx, -1, (duk_int_t) accessor_magic);
+					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_noconstruct_noexotic(ctx, c_func_setter, 1);  /* always 1 arg */
-					duk_set_magic(ctx, -1, (duk_int_t) accessor_magic);
+					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;
 				}
 
@@ -58545,8 +60073,8 @@
 			}
 			}
 
-			duk_def_prop(ctx, i, defprop_flags);
-			DUK_ASSERT_TOP(ctx, DUK_NUM_ALL_BUILTINS);
+			duk_def_prop(thr, (duk_idx_t) i, defprop_flags);
+			DUK_ASSERT_TOP(thr, DUK_NUM_ALL_BUILTINS);
 		}
 
 		/* native function properties */
@@ -58564,13 +60092,13 @@
 			duk_small_int_t lightfunc_eligible;
 #endif
 
-			duk__push_stridx_or_string(ctx, bd);
-			h_key = duk_known_hstring(ctx, -1);
+			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_int32_t) c_length /*def_value*/);
+			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;
 			}
@@ -58590,24 +60118,33 @@
 				(c_length <= DUK_LFUNC_LENGTH_MAX) &&
 				(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX);
 
-			if (h_key == DUK_HTHREAD_STRING_EVAL(thr) ||
-			    h_key == DUK_HTHREAD_STRING_YIELD(thr) ||
-			    h_key == DUK_HTHREAD_STRING_RESUME(thr)) {
-				/* 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: key=%!O, i=%d, j=%d", (duk_heaphdr *) h_key, (int) i, (int) j));
+			/* 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 = (c_nargs == DUK_VARARGS ? DUK_LFUNC_NARGS_VARARGS : c_nargs);
+				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(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)));
+				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;
 			}
 
@@ -58616,10 +60153,21 @@
 
 			/* [ (builtin objects) name ] */
 
-			duk_push_c_function_noconstruct_noexotic(ctx, c_func, c_nargs);
-			h_func = duk_known_hnatfunc(ctx, -1);
+			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)
@@ -58640,16 +60188,16 @@
 
 			/* [ (builtin objects) name func ] */
 
-			duk_push_int(ctx, c_length);
-			duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
-
-			duk_dup_m2(ctx);
-			duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
+			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(ctx, -1)));
+			                   (long) i, (long) j, (duk_tval *) duk_get_tval(thr, -1)));
 
 			/* [ (builtin objects) name func ] */
 
@@ -58662,7 +60210,10 @@
 		 lightfunc_skip:
 #endif
 
-			duk_xdef_prop(ctx, i, DUK_PROPDESC_FLAGS_WC);
+			/* XXX: So far all ES builtins are 'wc' but e.g.
+			 * performance.now() should be 'wec'.
+			 */
+			duk_xdef_prop(thr, (duk_idx_t) i, DUK_PROPDESC_FLAGS_WC);
 
 			/* [ (builtin objects) ] */
 		}
@@ -58686,11 +60237,11 @@
 	 */
 
 #if defined(DUK_USE_DATE_BUILTIN)
-	duk_get_prop_stridx_short(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_UTC_STRING);
-	duk_xdef_prop_stridx_short(ctx, DUK_BIDX_DATE_PROTOTYPE, DUK_STRIDX_TO_GMT_STRING, DUK_PROPDESC_FLAGS_WC);
-#endif
-
-	h = duk_known_hobject(ctx, DUK_BIDX_DOUBLE_ERROR);
+	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)
@@ -58704,7 +60255,7 @@
 #endif
 
 	/* XXX: relocate */
-	duk_push_string(ctx,
+	duk_push_string(thr,
 			/* Endianness indicator */
 #if defined(DUK_USE_INTEGER_LE)
 	                "l"
@@ -58805,7 +60356,7 @@
 	                DUK_USE_OS_STRING
 			" "
 	                DUK_USE_COMPILER_STRING);
-	duk_xdef_prop_stridx_short(ctx, DUK_BIDX_DUKTAPE, DUK_STRIDX_ENV, DUK_PROPDESC_FLAGS_WC);
+	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.
@@ -58813,7 +60364,7 @@
 
 	DUK_DD(DUK_DDPRINT("compact built-ins"));
 	for (i = 0; i < DUK_NUM_ALL_BUILTINS; i++) {
-		duk_hobject_compact_props(thr, duk_known_hobject(ctx, i));
+		duk_hobject_compact_props(thr, duk_known_hobject(thr, (duk_idx_t) i));
 	}
 
 	DUK_D(DUK_DPRINT("INITBUILTINS END"));
@@ -58821,7 +60372,7 @@
 #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(ctx, i)));
+		                   (long) i, (duk_heaphdr *) duk_require_hobject(thr, i)));
 	}
 #endif
 
@@ -58831,8 +60382,8 @@
 	 *  through builtins[].
 	 */
 
-	duk_set_top(ctx, 0);
-	DUK_ASSERT_TOP(ctx, 0);
+	duk_set_top(thr, 0);
+	DUK_ASSERT_TOP(thr, 0);
 }
 #endif  /* DUK_USE_ROM_OBJECTS */
 
@@ -58869,29 +60420,25 @@
 DUK_INTERNAL void duk_hthread_terminate(duk_hthread *thr) {
 	DUK_ASSERT(thr != NULL);
 
-	/* Order of unwinding is important */
-
-	duk_hthread_catchstack_unwind(thr, 0);
-	duk_hthread_callstack_unwind(thr, 0);  /* side effects, possibly errors */
+	while (thr->callstack_curr != NULL) {
+		duk_hthread_activation_unwind_norz(thr);
+	}
 
 	thr->valstack_bottom = thr->valstack;
-	duk_set_top((duk_context *) thr, 0);  /* unwinds valstack, updating refcounts */
+	duk_set_top(thr, 0);  /* unwinds valstack, updating refcounts */
 
 	thr->state = DUK_HTHREAD_STATE_TERMINATED;
 
 	/* Here we could remove references to built-ins, but it may not be
 	 * worth the effort because built-ins are quite likely to be shared
 	 * with another (unterminated) thread, and terminated threads are also
-	 * usually garbage collected quite quickly.  Also, doing DECREFs
-	 * could trigger finalization, which would run on the current thread
-	 * and have access to only some of the built-ins.  Garbage collection
-	 * deals with this correctly already.
-	 */
-
-	/* XXX: Shrink the stacks to minimize memory usage?  May not
-	 * be worth the effort because terminated threads are usually
-	 * garbage collected quite soon.
-	 */
+	 * usually garbage collected quite quickly.
+	 *
+	 * We could also shrink the value stack here, but that also may not
+	 * be worth the effort for the same reason.
+	 */
+
+	DUK_REFZERO_CHECK_SLOW(thr);
 }
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
@@ -58963,585 +60510,411 @@
 }
 #line 1 "duk_hthread_stacks.c"
 /*
- *  Manipulation of thread stacks (valstack, callstack, catchstack).
- *
- *  Ideally unwinding of stacks should have no side effects, which would
- *  then favor separate unwinding and shrink check primitives for each
- *  stack type.  A shrink check may realloc and thus have side effects.
- *
- *  However, currently callstack unwinding itself has side effects, as it
- *  needs to DECREF multiple objects, close environment records, etc.
- *  Stacks must thus be unwound in the correct order by the caller.
- *
- *  (XXX: This should be probably reworked so that there is a shared
- *  unwind primitive which handles all stacks as requested, and knows
- *  the proper order for unwinding.)
- *
- *  Valstack entries above 'top' are always kept initialized to
- *  "undefined unused".  Callstack and catchstack entries above 'top'
- *  are not zeroed and are left as garbage.
- *
- *  Value stack handling is mostly a part of the API implementation.
+ *  Thread stack (mainly call stack) primitives: allocation of activations,
+ *  unwinding catchers and activations, etc.
+ *
+ *  Value stack handling is a part of the API implementation.
  */
 
 /* #include duk_internal.h -> already included */
 
-DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_callstack_grow(duk_hthread *thr) {
-	duk_activation *new_ptr;
-	duk_size_t old_size;
-	duk_size_t new_size;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);   /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->callstack_size >= thr->callstack_top);
-
-	old_size = thr->callstack_size;
-	new_size = old_size + DUK_CALLSTACK_GROW_STEP;
-
-	/* this is a bit approximate (errors out before max is reached); this is OK */
-	if (new_size >= thr->callstack_max) {
-		DUK_ERROR_RANGE(thr, DUK_STR_CALLSTACK_LIMIT);
-	}
-
-	DUK_DD(DUK_DDPRINT("growing callstack %ld -> %ld", (long) old_size, (long) new_size));
-
-	/*
-	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
-	 *  pointer may be changed by mark-and-sweep.
-	 */
-
-	DUK_ASSERT(new_size > 0);
-	new_ptr = (duk_activation *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_callstack_ptr, (void *) thr, sizeof(duk_activation) * new_size);
-	if (!new_ptr) {
-		/* No need for a NULL/zero-size check because new_size > 0) */
-		DUK_ERROR_ALLOC_FAILED(thr);
-	}
-	thr->callstack = new_ptr;
-	thr->callstack_size = new_size;
-
-	if (thr->callstack_top > 0) {
-		thr->callstack_curr = thr->callstack + thr->callstack_top - 1;
-	} else {
-		thr->callstack_curr = NULL;
-	}
-
-	/* note: any entries above the callstack top are garbage and not zeroed */
-}
-
-/* check that there is space for at least one new entry */
-DUK_INTERNAL void duk_hthread_callstack_grow(duk_hthread *thr) {
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);   /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->callstack_size >= thr->callstack_top);
-
-	if (DUK_LIKELY(thr->callstack_top < thr->callstack_size)) {
-		return;
-	}
-	duk__hthread_do_callstack_grow(thr);
-}
-
-DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_callstack_shrink(duk_hthread *thr) {
-	duk_size_t new_size;
-	duk_activation *p;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->callstack_size >= thr->callstack_top);
-
-	new_size = thr->callstack_top + DUK_CALLSTACK_SHRINK_SPARE;
-	DUK_ASSERT(new_size >= thr->callstack_top);
-
-	DUK_DD(DUK_DDPRINT("shrinking callstack %ld -> %ld", (long) thr->callstack_size, (long) new_size));
-
-	/*
-	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
-	 *  pointer may be changed by mark-and-sweep.
-	 */
-
-	/* shrink failure is not fatal */
-	p = (duk_activation *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_callstack_ptr, (void *) thr, sizeof(duk_activation) * new_size);
-	if (p) {
-		thr->callstack = p;
-		thr->callstack_size = new_size;
-
-		if (thr->callstack_top > 0) {
-			thr->callstack_curr = thr->callstack + thr->callstack_top - 1;
-		} else {
-			thr->callstack_curr = NULL;
-		}
-	} else {
-		/* Because new_size != 0, if condition doesn't need to be
-		 * (p != NULL || new_size == 0).
-		 */
-		DUK_ASSERT(new_size != 0);
-		DUK_D(DUK_DPRINT("callstack shrink failed, ignoring"));
-	}
-
-	/* note: any entries above the callstack top are garbage and not zeroed */
-}
-
-DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) {
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->callstack_top >= 0);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->callstack_size >= thr->callstack_top);
-
-	if (DUK_LIKELY(thr->callstack_size - thr->callstack_top < DUK_CALLSTACK_SHRINK_THRESHOLD)) {
-		return;
-	}
-
-	duk__hthread_do_callstack_shrink(thr);
-}
-
-DUK_INTERNAL void duk_hthread_callstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) {
-	duk_size_t idx;
-
-	DUK_DDD(DUK_DDDPRINT("unwind callstack top of thread %p from %ld to %ld",
-	                     (void *) thr,
-	                     (thr != NULL ? (long) thr->callstack_top : (long) -1),
-	                     (long) new_top));
-
-	DUK_ASSERT(thr);
-	DUK_ASSERT(thr->heap);
-	DUK_ASSERT_DISABLE(new_top >= 0);  /* unsigned */
-	DUK_ASSERT((duk_size_t) new_top <= thr->callstack_top);  /* cannot grow */
-
-	/*
-	 *  The loop below must avoid issues with potential callstack
-	 *  reallocations.  A resize (and other side effects) may happen
-	 *  e.g. due to finalizer/errhandler calls caused by a refzero or
-	 *  mark-and-sweep.  Arbitrary finalizers may run, because when
-	 *  an environment record is refzero'd, it may refer to arbitrary
-	 *  values which also become refzero'd.
-	 *
-	 *  So, the pointer 'p' is re-looked-up below whenever a side effect
-	 *  might have changed it.
-	 */
-
-	idx = thr->callstack_top;
-	while (idx > new_top) {
-		duk_activation *act;
-		duk_hobject *func;
-		duk_hobject *tmp;
+/* Unwind the topmost catcher of the current activation (caller must check that
+ * both exist) without side effects.
+ */
+DUK_INTERNAL void duk_hthread_catcher_unwind_norz(duk_hthread *thr, duk_activation *act) {
+	duk_catcher *cat;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(act->cat != NULL);  /* caller must check */
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+
+	DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is done)", (void *) cat));
+
+	if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
+		duk_hobject *env;
+
+		env = act->lex_env;             /* current lex_env of the activation (created for catcher) */
+		DUK_ASSERT(env != NULL);        /* must be, since env was created when catcher was created */
+		act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);  /* prototype is lex_env before catcher created */
+		DUK_HOBJECT_INCREF(thr, act->lex_env);
+		DUK_HOBJECT_DECREF_NORZ(thr, env);
+
+		/* There is no need to decref anything else than 'env': if 'env'
+		 * becomes unreachable, refzero will handle decref'ing its prototype.
+		 */
+	}
+
+	act->cat = cat->parent;
+	duk_hthread_catcher_free(thr, cat);
+}
+
+/* Same as above, but caller is certain no catcher-related lexenv may exist. */
+DUK_INTERNAL void duk_hthread_catcher_unwind_nolexenv_norz(duk_hthread *thr, duk_activation *act) {
+	duk_catcher *cat;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(act->cat != NULL);  /* caller must check */
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+
+	DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is not done)", (void *) cat));
+
+	DUK_ASSERT(!DUK_CAT_HAS_LEXENV_ACTIVE(cat));
+
+	act->cat = cat->parent;
+	duk_hthread_catcher_free(thr, cat);
+}
+
+DUK_LOCAL
+#if defined(DUK_USE_CACHE_CATCHER)
+DUK_NOINLINE
+#endif
+duk_catcher *duk__hthread_catcher_alloc_slow(duk_hthread *thr) {
+	duk_catcher *cat;
+
+	cat = (duk_catcher *) DUK_ALLOC_CHECKED(thr, sizeof(duk_catcher));
+	DUK_ASSERT(cat != NULL);
+	return cat;
+}
+
+#if defined(DUK_USE_CACHE_CATCHER)
+DUK_INTERNAL DUK_INLINE duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) {
+	duk_catcher *cat;
+
+	DUK_ASSERT(thr != NULL);
+
+	cat = thr->heap->catcher_free;
+	if (DUK_LIKELY(cat != NULL)) {
+		thr->heap->catcher_free = cat->parent;
+		return cat;
+	}
+
+	return duk__hthread_catcher_alloc_slow(thr);
+}
+#else  /* DUK_USE_CACHE_CATCHER */
+DUK_INTERNAL duk_catcher *duk_hthread_catcher_alloc(duk_hthread *thr) {
+	return duk__hthread_catcher_alloc_slow(thr);
+}
+#endif  /* DUK_USE_CACHE_CATCHER */
+
+DUK_INTERNAL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat) {
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(cat != NULL);
+
+#if defined(DUK_USE_CACHE_CATCHER)
+	/* Unconditional caching for now; freed in mark-and-sweep. */
+	cat->parent = thr->heap->catcher_free;
+	thr->heap->catcher_free = cat;
+#else
+	DUK_FREE_CHECKED(thr, (void *) cat);
+#endif
+}
+
+DUK_LOCAL
+#if defined(DUK_USE_CACHE_ACTIVATION)
+DUK_NOINLINE
+#endif
+duk_activation *duk__hthread_activation_alloc_slow(duk_hthread *thr) {
+	duk_activation *act;
+
+	act = (duk_activation *) DUK_ALLOC_CHECKED(thr, sizeof(duk_activation));
+	DUK_ASSERT(act != NULL);
+	return act;
+}
+
+#if defined(DUK_USE_CACHE_ACTIVATION)
+DUK_INTERNAL DUK_INLINE duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) {
+	duk_activation *act;
+
+	DUK_ASSERT(thr != NULL);
+
+	act = thr->heap->activation_free;
+	if (DUK_LIKELY(act != NULL)) {
+		thr->heap->activation_free = act->parent;
+		return act;
+	}
+
+	return duk__hthread_activation_alloc_slow(thr);
+}
+#else  /* DUK_USE_CACHE_ACTIVATION */
+DUK_INTERNAL duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) {
+	return duk__hthread_activation_alloc_slow(thr);
+}
+#endif  /* DUK_USE_CACHE_ACTIVATION */
+
+
+DUK_INTERNAL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act) {
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(act != NULL);
+
+#if defined(DUK_USE_CACHE_ACTIVATION)
+	/* Unconditional caching for now; freed in mark-and-sweep. */
+	act->parent = thr->heap->activation_free;
+	thr->heap->activation_free = act;
+#else
+	DUK_FREE_CHECKED(thr, (void *) act);
+#endif
+}
+
+/* Internal helper: process the unwind for the topmost activation of a thread,
+ * but leave the duk_activation in place for possible tailcall reuse.
+ */
+DUK_LOCAL void duk__activation_unwind_nofree_norz(duk_hthread *thr) {
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
-		duk_heap *heap;
-#endif
-
-		idx--;
-		DUK_ASSERT_DISABLE(idx >= 0);  /* unsigned */
-		DUK_ASSERT((duk_size_t) idx < thr->callstack_size);  /* true, despite side effect resizes */
-
-		act = thr->callstack + idx;
-		/* With lightfuncs, act 'func' may be NULL */
+	duk_heap *heap;
+#endif
+	duk_activation *act;
+	duk_hobject *func;
+	duk_hobject *tmp;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(thr->callstack_curr != NULL);  /* caller must check */
+	DUK_ASSERT(thr->callstack_top > 0);
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	/* With lightfuncs, act 'func' may be NULL. */
+
+	/* With duk_activation records allocated separately, 'act' is a stable
+	 * pointer and not affected by side effects.
+	 */
 
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-		/*
-		 *  Restore 'caller' property for non-strict callee functions.
-		 */
-
-		func = DUK_ACT_GET_FUNC(act);
-		if (func != NULL && !DUK_HOBJECT_HAS_STRICT(func)) {
-			duk_tval *tv_caller;
-			duk_tval tv_tmp;
-			duk_hobject *h_tmp;
-
-			tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_CALLER(thr));
-
-			/* The act->prev_caller should only be set if the entry for 'caller'
-			 * exists (as it is only set in that case, and the property is not
-			 * configurable), but handle all the cases anyway.
-			 */
-
-			if (tv_caller) {
-				DUK_TVAL_SET_TVAL(&tv_tmp, tv_caller);
-				if (act->prev_caller) {
-					/* Just transfer the refcount from act->prev_caller to tv_caller,
-					 * so no need for a refcount update.  This is the expected case.
-					 */
-					DUK_TVAL_SET_OBJECT(tv_caller, act->prev_caller);
-					act->prev_caller = NULL;
-				} else {
-					DUK_TVAL_SET_NULL(tv_caller);   /* no incref needed */
-					DUK_ASSERT(act->prev_caller == NULL);
-				}
-				DUK_TVAL_DECREF_NORZ(thr, &tv_tmp);
-			} else {
-				h_tmp = act->prev_caller;
-				if (h_tmp) {
-					act->prev_caller = NULL;
-					DUK_HOBJECT_DECREF_NORZ(thr, h_tmp);
-				}
-			}
-			act = thr->callstack + idx;  /* avoid side effects */
-			DUK_ASSERT(act->prev_caller == NULL);
-		}
-#endif
-
-		/*
-		 *  Unwind debugger state.  If we unwind while stepping
-		 *  (either step over or step into), pause execution.
-		 */
+	/*
+	 *  Restore 'caller' property for non-strict callee functions.
+	 */
+
+	func = DUK_ACT_GET_FUNC(act);
+	if (func != NULL && !DUK_HOBJECT_HAS_STRICT(func)) {
+		duk_tval *tv_caller;
+		duk_tval tv_tmp;
+		duk_hobject *h_tmp;
+
+		tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_CALLER(thr));
+
+		/* The act->prev_caller should only be set if the entry for 'caller'
+		 * exists (as it is only set in that case, and the property is not
+		 * configurable), but handle all the cases anyway.
+		 */
+
+		if (tv_caller) {
+			DUK_TVAL_SET_TVAL(&tv_tmp, tv_caller);
+			if (act->prev_caller) {
+				/* Just transfer the refcount from act->prev_caller to tv_caller,
+				 * so no need for a refcount update.  This is the expected case.
+				 */
+				DUK_TVAL_SET_OBJECT(tv_caller, act->prev_caller);
+				act->prev_caller = NULL;
+			} else {
+				DUK_TVAL_SET_NULL(tv_caller);   /* no incref needed */
+				DUK_ASSERT(act->prev_caller == NULL);
+			}
+			DUK_TVAL_DECREF_NORZ(thr, &tv_tmp);
+		} else {
+			h_tmp = act->prev_caller;
+			if (h_tmp) {
+				act->prev_caller = NULL;
+				DUK_HOBJECT_DECREF_NORZ(thr, h_tmp);
+			}
+		}
+		DUK_ASSERT(act->prev_caller == NULL);
+	}
+#endif
+
+	/*
+	 *  Unwind debugger state.  If we unwind while stepping
+	 *  (for any step type), pause execution.  This is the
+	 *  only place explicitly handling a step out.
+	 */
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
-		heap = thr->heap;
-		if (heap->dbg_step_thread == thr &&
-		    heap->dbg_step_csindex == idx) {
-			/* Pause for all step types: step into, step over, step out.
-			 * This is the only place explicitly handling a step out.
-			 */
-			if (duk_debug_is_paused(heap)) {
-				DUK_D(DUK_DPRINT("step pause trigger but already paused, ignoring"));
-			} else {
-				duk_debug_set_paused(heap);
-				DUK_ASSERT(heap->dbg_step_thread == NULL);
-			}
-		}
-#endif
-
-		/*
-		 *  Close environment record(s) if they exist.
-		 *
-		 *  Only variable environments are closed.  If lex_env != var_env, it
-		 *  cannot currently contain any register bound declarations.
-		 *
-		 *  Only environments created for a NEWENV function are closed.  If an
-		 *  environment is created for e.g. an eval call, it must not be closed.
-		 */
-
-		func = DUK_ACT_GET_FUNC(act);
-		if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) {
-			DUK_DDD(DUK_DDDPRINT("skip closing environments, envs not owned by this activation"));
-			goto skip_env_close;
-		}
-		/* func is NULL for lightfunc */
-
-		/* Catch sites are required to clean up their environments
-		 * in FINALLY part before propagating, so this should
-		 * always hold here.
-		 */
-		DUK_ASSERT(act->lex_env == act->var_env);
-
-		if (act->var_env != NULL) {
-			DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O",
-			                     (void *) act->var_env, (duk_heaphdr *) act->var_env));
-			duk_js_close_environment_record(thr, act->var_env);
-			act = thr->callstack + idx;  /* avoid side effect issues */
-		}
-
-	 skip_env_close:
-
-		/*
-		 *  Update preventcount
-		 */
-
-		if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
-			DUK_ASSERT(thr->callstack_preventcount >= 1);
-			thr->callstack_preventcount--;
-		}
-
-		/*
-		 *  Reference count updates, using NORZ macros so we don't
-		 *  need to handle side effects.
-		 */
-
-		DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->var_env);
-		act->var_env = NULL;
-		DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->lex_env);
-		act->lex_env = NULL;
-
-		/* Note: this may cause a corner case situation where a finalizer
-		 * may see a currently reachable activation whose 'func' is NULL.
-		 */
-		tmp = DUK_ACT_GET_FUNC(act);
-		DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
-		DUK_UNREF(tmp);
-		act->func = NULL;
-	}
-
-	thr->callstack_top = new_top;
-	if (new_top > 0) {
-		thr->callstack_curr = thr->callstack + new_top - 1;
-	} else {
-		thr->callstack_curr = NULL;
-	}
-
-	/*
-	 *  We could clear the book-keeping variables for the topmost activation,
-	 *  but don't do so now.
-	 */
-#if 0
-	if (thr->callstack_curr != NULL) {
-		duk_activation *act = thr->callstack_curr;
-		act->idx_retval = 0;
-	}
-#endif
-
-	/* Note: any entries above the callstack top are garbage and not zeroed.
-	 * Also topmost activation idx_retval is garbage (not zeroed), and must
-	 * be ignored.
-	 */
-}
-
-DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_top) {
-	duk_hthread_callstack_unwind_norz(thr, new_top);
-	DUK_REFZERO_CHECK_FAST(thr);
-}
-
-DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_catchstack_grow(duk_hthread *thr) {
-	duk_catcher *new_ptr;
-	duk_size_t old_size;
-	duk_size_t new_size;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->catchstack_top);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
-
-	old_size = thr->catchstack_size;
-	new_size = old_size + DUK_CATCHSTACK_GROW_STEP;
-
-	/* this is a bit approximate (errors out before max is reached); this is OK */
-	if (new_size >= thr->catchstack_max) {
-		DUK_ERROR_RANGE(thr, DUK_STR_CATCHSTACK_LIMIT);
-	}
-
-	DUK_DD(DUK_DDPRINT("growing catchstack %ld -> %ld", (long) old_size, (long) new_size));
-
-	/*
-	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
-	 *  pointer may be changed by mark-and-sweep.
-	 */
-
-	DUK_ASSERT(new_size > 0);
-	new_ptr = (duk_catcher *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_catchstack_ptr, (void *) thr, sizeof(duk_catcher) * new_size);
-	if (!new_ptr) {
-		/* No need for a NULL/zero-size check because new_size > 0) */
-		DUK_ERROR_ALLOC_FAILED(thr);
-	}
-	thr->catchstack = new_ptr;
-	thr->catchstack_size = new_size;
-
-	/* note: any entries above the catchstack top are garbage and not zeroed */
-}
-
-DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->catchstack_top);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
-
-	if (DUK_LIKELY(thr->catchstack_top < thr->catchstack_size)) {
-		return;
-	}
-
-	duk__hthread_do_catchstack_grow(thr);
-}
-
-DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_catchstack_shrink(duk_hthread *thr) {
-	duk_size_t new_size;
-	duk_catcher *p;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->catchstack_top >= 0);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
-
-	new_size = thr->catchstack_top + DUK_CATCHSTACK_SHRINK_SPARE;
-	DUK_ASSERT(new_size >= thr->catchstack_top);
-
-	DUK_DD(DUK_DDPRINT("shrinking catchstack %ld -> %ld", (long) thr->catchstack_size, (long) new_size));
-
-	/*
-	 *  Note: must use indirect variant of DUK_REALLOC() because underlying
-	 *  pointer may be changed by mark-and-sweep.
-	 */
-
-	/* shrink failure is not fatal */
-	p = (duk_catcher *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_catchstack_ptr, (void *) thr, sizeof(duk_catcher) * new_size);
-	if (p) {
-		thr->catchstack = p;
-		thr->catchstack_size = new_size;
-	} else {
-		/* Because new_size != 0, if condition doesn't need to be
-		 * (p != NULL || new_size == 0).
-		 */
-		DUK_ASSERT(new_size != 0);
-		DUK_D(DUK_DPRINT("catchstack shrink failed, ignoring"));
-	}
-
-	/* note: any entries above the catchstack top are garbage and not zeroed */
-}
-
-DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) {
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(thr->catchstack_top >= 0);  /* avoid warning (unsigned) */
-	DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
-
-	if (DUK_LIKELY(thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD)) {
-		return;
-	}
-
-	duk__hthread_do_catchstack_shrink(thr);
-}
-
-DUK_INTERNAL void duk_hthread_catchstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) {
-	duk_size_t idx;
-
-	DUK_DDD(DUK_DDDPRINT("unwind catchstack top of thread %p from %ld to %ld",
-	                     (void *) thr,
-	                     (thr != NULL ? (long) thr->catchstack_top : (long) -1),
-	                     (long) new_top));
-
-	DUK_ASSERT(thr);
-	DUK_ASSERT(thr->heap);
-	DUK_ASSERT_DISABLE(new_top >= 0);  /* unsigned */
-	DUK_ASSERT((duk_size_t) new_top <= thr->catchstack_top);  /* cannot grow */
-
-	/*
+	heap = thr->heap;
+	if (heap->dbg_pause_act == thr->callstack_curr) {
+		if (heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_EXIT) {
+			DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function exit"));
+			duk_debug_set_paused(heap);
+		} else {
+			DUK_D(DUK_DPRINT("unwound past dbg_pause_act, set to NULL"));
+			heap->dbg_pause_act = NULL;  /* avoid stale pointers */
+		}
+		DUK_ASSERT(heap->dbg_pause_act == NULL);
+	}
+#endif
+
+	/*
+	 *  Unwind catchers.
+	 *
 	 *  Since there are no references in the catcher structure,
 	 *  unwinding is quite simple.  The only thing we need to
 	 *  look out for is popping a possible lexical environment
 	 *  established for an active catch clause.
 	 */
 
-	idx = thr->catchstack_top;
-	while (idx > new_top) {
-		duk_catcher *p;
-		duk_activation *act;
-		duk_hobject *env;
-
-		idx--;
-		DUK_ASSERT_DISABLE(idx >= 0);  /* unsigned */
-		DUK_ASSERT((duk_size_t) idx < thr->catchstack_size);
-
-		p = thr->catchstack + idx;
-
-		if (DUK_CAT_HAS_LEXENV_ACTIVE(p)) {
-			DUK_DDD(DUK_DDDPRINT("unwinding catchstack idx %ld, callstack idx %ld, callstack top %ld: lexical environment active",
-			                     (long) idx, (long) p->callstack_index, (long) thr->callstack_top));
-
-			/* XXX: Here we have a nasty dependency: the need to manipulate
-			 * the callstack means that catchstack must always be unwound by
-			 * the caller before unwinding the callstack.  This should be fixed
-			 * later.
-			 */
-
-			/* Note that multiple catchstack entries may refer to the same
-			 * callstack entry.
-			 */
-			act = thr->callstack + p->callstack_index;
-			DUK_ASSERT(act >= thr->callstack);
-			DUK_ASSERT(act < thr->callstack + thr->callstack_top);
-
-			DUK_DDD(DUK_DDDPRINT("catchstack_index=%ld, callstack_index=%ld, lex_env=%!iO",
-			                     (long) idx, (long) p->callstack_index,
-			                     (duk_heaphdr *) act->lex_env));
-
-			env = act->lex_env;             /* current lex_env of the activation (created for catcher) */
-			DUK_ASSERT(env != NULL);        /* must be, since env was created when catcher was created */
-			act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);  /* prototype is lex_env before catcher created */
-			DUK_HOBJECT_INCREF(thr, act->lex_env);
-			DUK_HOBJECT_DECREF_NORZ(thr, env);
-
-			/* There is no need to decref anything else than 'env': if 'env'
-			 * becomes unreachable, refzero will handle decref'ing its prototype.
-			 */
-		}
-	}
-
-	thr->catchstack_top = new_top;
-
-	/* note: any entries above the catchstack top are garbage and not zeroed */
-}
-
-DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new_top) {
-	duk_hthread_catchstack_unwind_norz(thr, new_top);
-	DUK_REFZERO_CHECK_FAST(thr);
+	while (act->cat != NULL) {
+		duk_hthread_catcher_unwind_norz(thr, act);
+	}
+
+	/*
+	 *  Close environment record(s) if they exist.
+	 *
+	 *  Only variable environments are closed.  If lex_env != var_env, it
+	 *  cannot currently contain any register bound declarations.
+	 *
+	 *  Only environments created for a NEWENV function are closed.  If an
+	 *  environment is created for e.g. an eval call, it must not be closed.
+	 */
+
+	func = DUK_ACT_GET_FUNC(act);
+	if (func != NULL && !DUK_HOBJECT_HAS_NEWENV(func)) {
+		DUK_DDD(DUK_DDDPRINT("skip closing environments, envs not owned by this activation"));
+		goto skip_env_close;
+	}
+	/* func is NULL for lightfunc */
+
+	/* Catch sites are required to clean up their environments
+	 * in FINALLY part before propagating, so this should
+	 * always hold here.
+	 */
+	DUK_ASSERT(act->lex_env == act->var_env);
+
+	/* XXX: Closing the environment record copies values from registers
+	 * into the scope object.  It's side effect free as such, but may
+	 * currently run out of memory which causes an error throw.  This is
+	 * an actual sandboxing problem for error unwinds, and needs to be
+	 * fixed e.g. by preallocating the scope property slots.
+	 */
+	if (act->var_env != NULL) {
+		DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O",
+		                     (void *) act->var_env, (duk_heaphdr *) act->var_env));
+		duk_js_close_environment_record(thr, act->var_env);
+	}
+
+ skip_env_close:
+
+	/*
+	 *  Update preventcount
+	 */
+
+	if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
+		DUK_ASSERT(thr->callstack_preventcount >= 1);
+		thr->callstack_preventcount--;
+	}
+
+	/*
+	 *  Reference count updates, using NORZ macros so we don't
+	 *  need to handle side effects.
+	 *
+	 *  duk_activation pointers like act->var_env are intentionally
+	 *  left as garbage and not NULLed.  Without side effects they
+	 *  can't be used when the values are dangling/garbage.
+	 */
+
+	DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->var_env);
+	DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->lex_env);
+	tmp = DUK_ACT_GET_FUNC(act);
+	DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
+	DUK_UNREF(tmp);
+}
+
+/* Unwind topmost duk_activation of a thread, caller must ensure that an
+ * activation exists.  The call is side effect free, except that scope
+ * closure may currently throw an out-of-memory error.
+ */
+DUK_INTERNAL void duk_hthread_activation_unwind_norz(duk_hthread *thr) {
+	duk_activation *act;
+
+	duk__activation_unwind_nofree_norz(thr);
+
+	DUK_ASSERT(thr->callstack_curr != NULL);
+	DUK_ASSERT(thr->callstack_top > 0);
+	act = thr->callstack_curr;
+	thr->callstack_curr = act->parent;
+	thr->callstack_top--;
+
+	/* Ideally we'd restore value stack reserve here to caller's value.
+	 * This doesn't work for current unwind call sites however, because
+	 * the current (unwound) value stack top may be above the reserve.
+	 * Thus value stack reserve is restored by the call sites.
+	 */
+
+	/* XXX: inline for performance builds? */
+	duk_hthread_activation_free(thr, act);
+
+	/* We could clear the book-keeping variables like retval_byteoff for
+	 * the topmost activation, but don't do so now as it's not necessary.
+	 */
+}
+
+DUK_INTERNAL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr) {
+	duk__activation_unwind_nofree_norz(thr);
+}
+
+/* Get duk_activation for given callstack level or NULL if level is invalid
+ * or deeper than the call stack.  Level -1 refers to current activation, -2
+ * to its caller, etc.  Starting from Duktape 2.2 finding the activation is
+ * a linked list scan which gets more expensive the deeper the lookup is.
+ */
+DUK_INTERNAL duk_activation *duk_hthread_get_activation_for_level(duk_hthread *thr, duk_int_t level) {
+	duk_activation *act;
+
+	if (level >= 0) {
+		return NULL;
+	}
+	act = thr->callstack_curr;
+	for (;;) {
+		if (act == NULL) {
+			return act;
+		}
+		if (level == -1) {
+			return act;
+		}
+		level++;
+		act = act->parent;
+	}
+	/* never here */
 }
 
 #if defined(DUK_USE_FINALIZER_TORTURE)
 DUK_INTERNAL void duk_hthread_valstack_torture_realloc(duk_hthread *thr) {
 	duk_size_t alloc_size;
 	duk_tval *new_ptr;
+	duk_ptrdiff_t alloc_end_off;
 	duk_ptrdiff_t end_off;
 	duk_ptrdiff_t bottom_off;
 	duk_ptrdiff_t top_off;
 
 	if (thr->valstack == NULL) {
-		return;
-	}
-
+		DUK_D(DUK_DPRINT("skip valstack torture realloc, valstack is NULL"));
+		return;
+	}
+
+	alloc_end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_alloc_end - (duk_uint8_t *) thr->valstack);
 	end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
 	bottom_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
 	top_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack);
-	alloc_size = (duk_size_t) end_off;
+	alloc_size = (duk_size_t) alloc_end_off;
 	if (alloc_size == 0) {
-		return;
-	}
-
-	new_ptr = (duk_tval *) DUK_ALLOC(thr->heap, alloc_size);
+		DUK_D(DUK_DPRINT("skip valstack torture realloc, alloc_size is zero"));
+		return;
+	}
+
+	/* Use DUK_ALLOC_RAW() to avoid side effects. */
+	new_ptr = (duk_tval *) DUK_ALLOC_RAW(thr->heap, alloc_size);
 	if (new_ptr != NULL) {
 		DUK_MEMCPY((void *) new_ptr, (const void *) thr->valstack, alloc_size);
 		DUK_MEMSET((void *) thr->valstack, 0x55, alloc_size);
-		DUK_FREE(thr->heap, (void *) thr->valstack);
+		DUK_FREE_CHECKED(thr, (void *) thr->valstack);
 		thr->valstack = new_ptr;
+		thr->valstack_alloc_end = (duk_tval *) ((duk_uint8_t *) new_ptr + alloc_end_off);
 		thr->valstack_end = (duk_tval *) ((duk_uint8_t *) new_ptr + end_off);
 		thr->valstack_bottom = (duk_tval *) ((duk_uint8_t *) new_ptr + bottom_off);
 		thr->valstack_top = (duk_tval *) ((duk_uint8_t *) new_ptr + top_off);
-		/* No change in size. */
 	} else {
 		DUK_D(DUK_DPRINT("failed to realloc valstack for torture, ignore"));
 	}
 }
-
-DUK_INTERNAL void duk_hthread_callstack_torture_realloc(duk_hthread *thr) {
-	duk_size_t alloc_size;
-	duk_activation *new_ptr;
-	duk_ptrdiff_t curr_off;
-
-	if (thr->callstack == NULL) {
-		return;
-	}
-
-	curr_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->callstack_curr - (duk_uint8_t *) thr->callstack);
-	alloc_size = sizeof(duk_activation) * thr->callstack_size;
-	if (alloc_size == 0) {
-		return;
-	}
-
-	new_ptr = (duk_activation *) DUK_ALLOC(thr->heap, alloc_size);
-	if (new_ptr != NULL) {
-		DUK_MEMCPY((void *) new_ptr, (const void *) thr->callstack, alloc_size);
-		DUK_MEMSET((void *) thr->callstack, 0x55, alloc_size);
-		DUK_FREE(thr->heap, (void *) thr->callstack);
-		thr->callstack = new_ptr;
-		thr->callstack_curr = (duk_activation *) ((duk_uint8_t *) new_ptr + curr_off);
-		/* No change in size. */
-	} else {
-		DUK_D(DUK_DPRINT("failed to realloc callstack for torture, ignore"));
-	}
-}
-
-DUK_INTERNAL void duk_hthread_catchstack_torture_realloc(duk_hthread *thr) {
-	duk_size_t alloc_size;
-	duk_catcher *new_ptr;
-
-	if (thr->catchstack == NULL) {
-		return;
-	}
-
-	alloc_size = sizeof(duk_catcher) * thr->catchstack_size;
-	if (alloc_size == 0) {
-		return;
-	}
-
-	new_ptr = (duk_catcher *) DUK_ALLOC(thr->heap, alloc_size);
-	if (new_ptr != NULL) {
-		DUK_MEMCPY((void *) new_ptr, (const void *) thr->catchstack, alloc_size);
-		DUK_MEMSET((void *) thr->catchstack, 0x55, alloc_size);
-		DUK_FREE(thr->heap, (void *) thr->catchstack);
-		thr->catchstack = new_ptr;
-		/* No change in size. */
-	} else {
-		DUK_D(DUK_DPRINT("failed to realloc catchstack for torture, ignore"));
-	}
-}
 #endif  /* DUK_USE_FINALIZER_TORTURE */
 #line 1 "duk_js_arith.c"
 /*
@@ -59685,22 +61058,30 @@
 /*
  *  Call handling.
  *
- *  Main functions are:
- *
- *    - duk_handle_call_unprotected(): unprotected call to Ecmascript or
- *      Duktape/C function
- *    - duk_handle_call_protected(): protected call to Ecmascript or
- *      Duktape/C function
- *    - duk_handle_safe_call(): make a protected C call within current
- *      activation
- *    - duk_handle_ecma_call_setup(): Ecmascript-to-Ecmascript calls
- *      (not always possible), including tail calls and coroutine resume
+ *  duk_handle_call_unprotected():
+ *
+ *    - Unprotected call to Ecmascript or Duktape/C function, from native
+ *      code or bytecode executor.
+ *
+ *    - Also handles Ecma-to-Ecma calls which reuses a currently running
+ *      executor instance to avoid native recursion.  Call setup is done
+ *      normally, but just before calling the bytecode executor a special
+ *      return code is used to indicate that a calling executor is reused.
+ *
+ *    - Also handles tailcalls, i.e. reuse of current duk_activation.
+ *
+ *    - Also handles setup for initial Duktape.Thread.resume().
+ *
+ *  duk_handle_safe_call():
+ *
+ *    - Protected C call within current activation.
+ *
+ *  setjmp() and local variables have a nasty interaction, see execution.rst;
+ *  non-volatile locals modified after setjmp() call are not guaranteed to
+ *  keep their value and can cause compiler or compiler version specific
+ *  difficult to replicate issues.
  *
  *  See 'execution.rst'.
- *
- *  Note: setjmp() and local variables have a nasty interaction,
- *  see execution.rst; non-volatile locals modified after setjmp()
- *  call are not guaranteed to keep their value.
  */
 
 /* #include duk_internal.h -> already included */
@@ -59708,46 +61089,78 @@
 /* XXX: heap->error_not_allowed for success path too? */
 
 /*
- *  Forward declarations.
- */
-
-DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
-                                      duk_idx_t num_stack_args,
-                                      duk_small_uint_t call_flags,
-                                      duk_idx_t idx_func);
-DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
-                                      duk_size_t entry_valstack_bottom_index,
-                                      duk_size_t entry_valstack_end,
-                                      duk_size_t entry_catchstack_top,
-                                      duk_size_t entry_callstack_top,
-                                      duk_int_t entry_call_recursion_depth,
-                                      duk_hthread *entry_curr_thread,
-                                      duk_uint_fast8_t entry_thread_state,
-                                      duk_instr_t **entry_ptr_curr_pc,
-                                      duk_idx_t idx_func,
-                                      duk_jmpbuf *old_jmpbuf_ptr);
-DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
-                                           duk_safe_call_function func,
-                                           void *udata,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top);
-DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
-                                           duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top,
-                                           duk_jmpbuf *old_jmpbuf_ptr);
-DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
-                                            duk_idx_t idx_retbase,
-                                            duk_idx_t num_stack_rets,
-                                            duk_int_t entry_call_recursion_depth,
-                                            duk_hthread *entry_curr_thread,
-                                            duk_uint_fast8_t entry_thread_state,
-                                            duk_instr_t **entry_ptr_curr_pc);
+ *  Limit check helpers.
+ */
+
+/* Allow headroom for calls during error augmentation (see GH-191).
+ * We allow space for 10 additional recursions, with one extra
+ * for, e.g. a print() call at the deepest level, and an extra
+ * +1 for protected call wrapping.
+ */
+#define DUK__AUGMENT_CALL_RELAX_COUNT  (10 + 2)
+
+DUK_LOCAL DUK_NOINLINE void duk__call_c_recursion_limit_check_slowpath(duk_hthread *thr) {
+	/* When augmenting an error, the effective limit is a bit higher.
+	 * Check for it only if the fast path check fails.
+	 */
+#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
+	if (thr->heap->augmenting_error) {
+		if (thr->heap->call_recursion_depth < thr->heap->call_recursion_limit + DUK__AUGMENT_CALL_RELAX_COUNT) {
+			DUK_D(DUK_DPRINT("C recursion limit reached but augmenting error and within relaxed limit"));
+			return;
+		}
+	}
+#endif
+
+	DUK_D(DUK_DPRINT("call prevented because C recursion limit reached"));
+	DUK_ERROR_RANGE(thr, DUK_STR_C_CALLSTACK_LIMIT);
+}
+
+DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_c_recursion_limit_check(duk_hthread *thr) {
+	DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
+	DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit);
+
+	/* This check is forcibly inlined because it's very cheap and almost
+	 * always passes.  The slow path is forcibly noinline.
+	 */
+	if (DUK_LIKELY(thr->heap->call_recursion_depth < thr->heap->call_recursion_limit)) {
+		return;
+	}
+
+	duk__call_c_recursion_limit_check_slowpath(thr);
+}
+
+DUK_LOCAL DUK_NOINLINE void duk__call_callstack_limit_check_slowpath(duk_hthread *thr) {
+	/* When augmenting an error, the effective limit is a bit higher.
+	 * Check for it only if the fast path check fails.
+	 */
+#if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE)
+	if (thr->heap->augmenting_error) {
+		if (thr->callstack_top < DUK_USE_CALLSTACK_LIMIT + DUK__AUGMENT_CALL_RELAX_COUNT) {
+			DUK_D(DUK_DPRINT("call stack limit reached but augmenting error and within relaxed limit"));
+			return;
+		}
+	}
+#endif
+
+	/* XXX: error message is a bit misleading: we reached a recursion
+	 * limit which is also essentially the same as a C callstack limit
+	 * (except perhaps with some relaxed threading assumptions).
+	 */
+	DUK_D(DUK_DPRINT("call prevented because call stack limit reached"));
+	DUK_ERROR_RANGE(thr, DUK_STR_CALLSTACK_LIMIT);
+}
+
+DUK_LOCAL DUK_ALWAYS_INLINE void duk__call_callstack_limit_check(duk_hthread *thr) {
+	/* This check is forcibly inlined because it's very cheap and almost
+	 * always passes.  The slow path is forcibly noinline.
+	 */
+	if (DUK_LIKELY(thr->callstack_top < DUK_USE_CALLSTACK_LIMIT)) {
+		return;
+	}
+
+	duk__call_callstack_limit_check_slowpath(thr);
+}
 
 /*
  *  Interrupt counter fixup (for development only).
@@ -59796,9 +61209,7 @@
 DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr,
                                             duk_hobject *func,
                                             duk_hobject *varenv,
-                                            duk_idx_t idx_argbase,        /* idx of first argument on stack */
-                                            duk_idx_t num_stack_args) {   /* num args starting from idx_argbase */
-	duk_context *ctx = (duk_context *) thr;
+                                            duk_idx_t idx_args) {
 	duk_hobject *arg;          /* 'arguments' */
 	duk_hobject *formals;      /* formals for 'func' (may be NULL if func is a C function) */
 	duk_idx_t i_arg;
@@ -59808,30 +61219,30 @@
 	duk_idx_t i_argbase;
 	duk_idx_t n_formals;
 	duk_idx_t idx;
+	duk_idx_t num_stack_args;
 	duk_bool_t need_map;
 
-	DUK_DDD(DUK_DDDPRINT("creating arguments object for func=%!iO, varenv=%!iO, "
-	                     "idx_argbase=%ld, num_stack_args=%ld",
-	                     (duk_heaphdr *) func, (duk_heaphdr *) varenv,
-	                     (long) idx_argbase, (long) num_stack_args));
-
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(func != NULL);
 	DUK_ASSERT(DUK_HOBJECT_IS_NONBOUND_FUNCTION(func));
 	DUK_ASSERT(varenv != NULL);
-	DUK_ASSERT(idx_argbase >= 0);  /* assumed to bottom relative */
-	DUK_ASSERT(num_stack_args >= 0);
+
+	/* [ ... func this arg1(@idx_args) ... argN envobj ]
+	 * [ arg1(@idx_args) ... argN envobj ] (for tailcalls)
+	 */
 
 	need_map = 0;
 
-	i_argbase = idx_argbase;
+	i_argbase = idx_args;
+	num_stack_args = duk_get_top(thr) - i_argbase - 1;
 	DUK_ASSERT(i_argbase >= 0);
-
-	duk_push_hobject(ctx, func);
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_FORMALS);
-	formals = duk_get_hobject(ctx, -1);
+	DUK_ASSERT(num_stack_args >= 0);
+
+	duk_push_hobject(thr, func);
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_FORMALS);
+	formals = duk_get_hobject(thr, -1);
 	if (formals) {
-		n_formals = (duk_idx_t) duk_get_length(ctx, -1);
+		n_formals = (duk_idx_t) duk_get_length(thr, -1);
 	} else {
 		/* This shouldn't happen without tampering of internal
 		 * properties: if a function accesses 'arguments', _Formals
@@ -59841,8 +61252,8 @@
 		DUK_D(DUK_DPRINT("_Formals is undefined when creating arguments, use n_formals == 0"));
 		n_formals = 0;
 	}
-	duk_remove_m2(ctx);  /* leave formals on stack for later use */
-	i_formals = duk_require_top_index(ctx);
+	duk_remove_m2(thr);  /* leave formals on stack for later use */
+	i_formals = duk_require_top_index(thr);
 
 	DUK_ASSERT(n_formals >= 0);
 	DUK_ASSERT(formals != NULL || n_formals == 0);
@@ -59860,24 +61271,24 @@
 	 *    - 'mappedNames' object: temporary value used during construction
 	 */
 
-	arg = duk_push_object_helper(ctx,
+	arg = duk_push_object_helper(thr,
 	                             DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                             DUK_HOBJECT_FLAG_FASTREFS |
 	                             DUK_HOBJECT_FLAG_ARRAY_PART |
 	                             DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARGUMENTS),
 	                             DUK_BIDX_OBJECT_PROTOTYPE);
 	DUK_ASSERT(arg != NULL);
-	(void) duk_push_object_helper(ctx,
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
 	                              -1);  /* no prototype */
-	(void) duk_push_object_helper(ctx,
+	(void) duk_push_object_helper(thr,
 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                              DUK_HOBJECT_FLAG_FASTREFS |
 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
 	                              -1);  /* no prototype */
-	i_arg = duk_get_top(ctx) - 3;
+	i_arg = duk_get_top(thr) - 3;
 	i_map = i_arg + 1;
 	i_mappednames = i_arg + 2;
 
@@ -59887,19 +61298,19 @@
 	                     "arguments at index %ld -> %!O "
 	                     "map at index %ld -> %!O "
 	                     "mappednames at index %ld -> %!O",
-	                     (long) i_arg, (duk_heaphdr *) duk_get_hobject(ctx, i_arg),
-	                     (long) i_map, (duk_heaphdr *) duk_get_hobject(ctx, i_map),
-	                     (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(ctx, i_mappednames)));
+	                     (long) i_arg, (duk_heaphdr *) duk_get_hobject(thr, i_arg),
+	                     (long) i_map, (duk_heaphdr *) duk_get_hobject(thr, i_map),
+	                     (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(thr, i_mappednames)));
 
 	/*
 	 *  Init arguments properties, map, etc.
 	 */
 
-	duk_push_int(ctx, num_stack_args);
-	duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);
-
-	/*
-	 *  Init argument related properties
+	duk_push_int(thr, num_stack_args);
+	duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_WC);
+
+	/*
+	 *  Init argument related properties.
 	 */
 
 	/* step 11 */
@@ -59909,8 +61320,8 @@
 		                     (long) idx, (long) i_argbase, (long) (i_argbase + idx)));
 
 		DUK_DDD(DUK_DDDPRINT("define arguments[%ld]=arg", (long) idx));
-		duk_dup(ctx, i_argbase + idx);
-		duk_xdef_prop_index_wec(ctx, i_arg, (duk_uarridx_t) idx);
+		duk_dup(thr, i_argbase + idx);
+		duk_xdef_prop_index_wec(thr, i_arg, (duk_uarridx_t) idx);
 		DUK_DDD(DUK_DDDPRINT("defined arguments[%ld]=arg", (long) idx));
 
 		/* step 11.c is relevant only if non-strict (checked in 11.c.ii) */
@@ -59920,12 +61331,12 @@
 			DUK_DDD(DUK_DDDPRINT("strict function, index within formals (%ld < %ld)",
 			                     (long) idx, (long) n_formals));
 
-			duk_get_prop_index(ctx, i_formals, idx);
-			DUK_ASSERT(duk_is_string(ctx, -1));
-
-			duk_dup_top(ctx);  /* [ ... name name ] */
-
-			if (!duk_has_prop(ctx, i_mappednames)) {
+			duk_get_prop_index(thr, i_formals, (duk_uarridx_t) idx);
+			DUK_ASSERT(duk_is_string(thr, -1));
+
+			duk_dup_top(thr);  /* [ ... name name ] */
+
+			if (!duk_has_prop(thr, i_mappednames)) {
 				/* steps 11.c.ii.1 - 11.c.ii.4, but our internal book-keeping
 				 * differs from the reference model
 				 */
@@ -59935,23 +61346,23 @@
 				need_map = 1;
 
 				DUK_DDD(DUK_DDDPRINT("set mappednames[%s]=%ld",
-				                     (const char *) duk_get_string(ctx, -1),
+				                     (const char *) duk_get_string(thr, -1),
 				                     (long) idx));
-				duk_dup_top(ctx);                      /* name */
-				(void) duk_push_uint_to_hstring(ctx, (duk_uint_t) idx);  /* index */
-				duk_xdef_prop_wec(ctx, i_mappednames);  /* out of spec, must be configurable */
+				duk_dup_top(thr);                      /* name */
+				(void) duk_push_uint_to_hstring(thr, (duk_uint_t) idx);  /* index */
+				duk_xdef_prop_wec(thr, i_mappednames);  /* out of spec, must be configurable */
 
 				DUK_DDD(DUK_DDDPRINT("set map[%ld]=%s",
 				                     (long) idx,
-				                     duk_get_string(ctx, -1)));
-				duk_dup_top(ctx);         /* name */
-				duk_xdef_prop_index_wec(ctx, i_map, (duk_uarridx_t) idx);  /* out of spec, must be configurable */
+				                     duk_get_string(thr, -1)));
+				duk_dup_top(thr);         /* name */
+				duk_xdef_prop_index_wec(thr, i_map, (duk_uarridx_t) idx);  /* out of spec, must be configurable */
 			} else {
 				/* duk_has_prop() popped the second 'name' */
 			}
 
 			/* [ ... name ] */
-			duk_pop(ctx);  /* pop 'name' */
+			duk_pop(thr);  /* pop 'name' */
 		}
 
 		idx--;
@@ -59966,8 +61377,8 @@
 		/* should never happen for a strict callee */
 		DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(func));
 
-		duk_dup(ctx, i_map);
-		duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_INT_MAP, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
+		duk_dup(thr, i_map);
+		duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_MAP, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
 
 		/* The variable environment for magic variable bindings needs to be
 		 * given by the caller and recorded in the arguments object.
@@ -59978,8 +61389,8 @@
 		 * an explicit (internal) callee property is not needed.
 		 */
 
-		duk_push_hobject(ctx, varenv);
-		duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_INT_VARENV, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
+		duk_push_hobject(thr, varenv);
+		duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_INT_VARENV, DUK_PROPDESC_FLAGS_NONE);  /* out of spec, don't care */
 	}
 
 	/* steps 13-14 */
@@ -59999,12 +61410,12 @@
 
 		DUK_DDD(DUK_DDDPRINT("strict function, setting caller/callee to throwers"));
 
-		duk_xdef_prop_stridx_thrower(ctx, i_arg, DUK_STRIDX_CALLER);
-		duk_xdef_prop_stridx_thrower(ctx, i_arg, DUK_STRIDX_CALLEE);
+		duk_xdef_prop_stridx_thrower(thr, i_arg, DUK_STRIDX_CALLER);
+		duk_xdef_prop_stridx_thrower(thr, i_arg, DUK_STRIDX_CALLEE);
 	} else {
 		DUK_DDD(DUK_DDDPRINT("non-strict function, setting callee to actual value"));
-		duk_push_hobject(ctx, func);
-		duk_xdef_prop_stridx(ctx, i_arg, DUK_STRIDX_CALLEE, DUK_PROPDESC_FLAGS_WC);
+		duk_push_hobject(thr, func);
+		duk_xdef_prop_stridx(thr, i_arg, DUK_STRIDX_CALLEE, DUK_PROPDESC_FLAGS_WC);
 	}
 
 	/* set exotic behavior only after we're done */
@@ -60032,47 +61443,42 @@
 	                     "arguments at index %ld -> %!O "
 	                     "map at index %ld -> %!O "
 	                     "mappednames at index %ld -> %!O",
-	                     (long) i_arg, (duk_heaphdr *) duk_get_hobject(ctx, i_arg),
-	                     (long) i_map, (duk_heaphdr *) duk_get_hobject(ctx, i_map),
-	                     (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(ctx, i_mappednames)));
-
-	/* [ args(n) [crud] formals arguments map mappednames ] */
-
-	duk_pop_2(ctx);
-	duk_remove_m2(ctx);
-
-	/* [ args [crud] arguments ] */
+	                     (long) i_arg, (duk_heaphdr *) duk_get_hobject(thr, i_arg),
+	                     (long) i_map, (duk_heaphdr *) duk_get_hobject(thr, i_map),
+	                     (long) i_mappednames, (duk_heaphdr *) duk_get_hobject(thr, i_mappednames)));
+
+	/* [ args(n) envobj formals arguments map mappednames ] */
+
+	duk_pop_2(thr);
+	duk_remove_m2(thr);
+
+	/* [ args(n) envobj arguments ] */
 }
 
 /* Helper for creating the arguments object and adding it to the env record
- * on top of the value stack.  This helper has a very strict dependency on
- * the shape of the input stack.
+ * on top of the value stack.
  */
 DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr,
                                                duk_hobject *func,
                                                duk_hobject *env,
-                                               duk_idx_t num_stack_args) {
-	duk_context *ctx = (duk_context *) thr;
-
+                                               duk_idx_t idx_args) {
 	DUK_DDD(DUK_DDDPRINT("creating arguments object for function call"));
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(func != NULL);
 	DUK_ASSERT(env != NULL);
 	DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
-	DUK_ASSERT(duk_get_top(ctx) >= num_stack_args + 1);
 
 	/* [ ... arg1 ... argN envobj ] */
 
 	duk__create_arguments_object(thr,
 	                             func,
 	                             env,
-	                             duk_get_top(ctx) - num_stack_args - 1,    /* idx_argbase */
-	                             num_stack_args);
+	                             idx_args);
 
 	/* [ ... arg1 ... argN envobj argobj ] */
 
-	duk_xdef_prop_stridx_short(ctx,
+	duk_xdef_prop_stridx_short(thr,
 	                           -2,
 	                           DUK_STRIDX_LC_ARGUMENTS,
 	                           DUK_HOBJECT_HAS_STRICT(func) ? DUK_PROPDESC_FLAGS_E :   /* strict: non-deletable, non-writable */
@@ -60081,117 +61487,189 @@
 }
 
 /*
- *  Helper for handling a "bound function" chain when a call is being made.
- *
- *  Follows the bound function chain until a non-bound function is found.
- *  Prepends the bound arguments to the value stack (at idx_func + 2),
- *  updating 'num_stack_args' in the process.  The 'this' binding is also
- *  updated if necessary (at idx_func + 1).  Note that for constructor calls
- *  the 'this' binding is never updated by [[BoundThis]].
- *
- *  XXX: bound function chains could be collapsed at bound function creation
- *  time so that each bound function would point directly to a non-bound
- *  function.  This would make call time handling much easier.
+ *  Helpers for constructor call handling.
+ *
+ *  There are two [[Construct]] operations in the specification:
+ *
+ *    - E5 Section 13.2.2: for Function objects
+ *    - E5 Section 15.3.4.5.2: for "bound" Function objects
+ *
+ *  The chain of bound functions is resolved in Section 15.3.4.5.2,
+ *  with arguments "piling up" until the [[Construct]] internal
+ *  method is called on the final, actual Function object.  Note
+ *  that the "prototype" property is looked up *only* from the
+ *  final object, *before* calling the constructor.
+ *
+ *  Since Duktape 2.2 bound functions are represented with the
+ *  duk_hboundfunc internal type, and bound function chains are
+ *  collapsed when a bound function is created.  As a result, the
+ *  direct target of a duk_hboundfunc is always non-bound and the
+ *  this/argument lists have been resolved.
+ *
+ *  When constructing new Array instances, an unnecessary object is
+ *  created and discarded now: the standard [[Construct]] creates an
+ *  object, and calls the Array constructor.  The Array constructor
+ *  returns an Array instance, which is used as the result value for
+ *  the "new" operation; the object created before the Array constructor
+ *  call is discarded.
+ *
+ *  This would be easy to fix, e.g. by knowing that the Array constructor
+ *  will always create a replacement object and skip creating the fallback
+ *  object in that case.
+ */
+
+/* Update default instance prototype for constructor call. */
+DUK_LOCAL void duk__update_default_instance_proto(duk_hthread *thr, duk_idx_t idx_func) {
+	duk_hobject *proto;
+	duk_hobject *fallback;
+
+	DUK_ASSERT(duk_is_constructable(thr, idx_func));
+
+	duk_get_prop_stridx_short(thr, idx_func, DUK_STRIDX_PROTOTYPE);
+	proto = duk_get_hobject(thr, -1);
+	if (proto == NULL) {
+		DUK_DDD(DUK_DDDPRINT("constructor has no 'prototype' property, or value not an object "
+		                     "-> leave standard Object prototype as fallback prototype"));
+	} else {
+		DUK_DDD(DUK_DDDPRINT("constructor has 'prototype' property with object value "
+		                     "-> set fallback prototype to that value: %!iO", (duk_heaphdr *) proto));
+		/* Original fallback (default instance) is untouched when
+		 * resolving bound functions etc.
+		 */
+		fallback = duk_known_hobject(thr, idx_func + 1);
+		DUK_ASSERT(fallback != NULL);
+		DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, fallback, proto);
+	}
+	duk_pop(thr);
+}
+
+/* Postprocess: return value special handling, error augmentation. */
+DUK_INTERNAL void duk_call_construct_postprocess(duk_hthread *thr, duk_small_uint_t proxy_invariant) {
+	/* Use either fallback (default instance) or retval depending
+	 * on retval type.  Needs to be called before unwind because
+	 * the default instance is read from the current (immutable)
+	 * 'this' binding.
+	 *
+	 * For Proxy 'construct' calls the return value must be an
+	 * Object (we accept object-like values like buffers and
+	 * lightfuncs too).  If not, TypeError.
+	 */
+	if (duk_check_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT |
+	                                 DUK_TYPE_MASK_BUFFER |
+	                                 DUK_TYPE_MASK_LIGHTFUNC)) {
+		DUK_DDD(DUK_DDDPRINT("replacement value"));
+	} else {
+		if (DUK_UNLIKELY(proxy_invariant != 0U)) {
+			/* Proxy 'construct' return value invariant violated. */
+			DUK_ERROR_TYPE_INVALID_TRAP_RESULT(thr);
+		}
+		/* XXX: direct value stack access */
+		duk_pop(thr);
+		duk_push_this(thr);
+	}
+
+#if defined(DUK_USE_AUGMENT_ERROR_CREATE)
+	/* Augment created errors upon creation, not when they are thrown or
+	 * rethrown.  __FILE__ and __LINE__ are not desirable here; the call
+	 * stack reflects the caller which is correct.  Skip topmost, unwound
+	 * activation when creating a traceback.  If thr->ptr_curr_pc was !=
+	 * NULL we'd need to sync the current PC so that the traceback comes
+	 * out right; however it is always synced here so just assert for it.
+	 */
+	DUK_ASSERT(thr->ptr_curr_pc == NULL);
+	duk_err_augment_error_create(thr, thr, NULL, 0, DUK_AUGMENT_FLAG_NOBLAME_FILELINE |
+	                                                DUK_AUGMENT_FLAG_SKIP_ONE);
+#endif
+}
+
+/*
+ *  Helper for handling a bound function when a call is being made.
+ *
+ *  Assumes that bound function chains have been "collapsed" so that either
+ *  the target is non-bound or there is one bound function that points to a
+ *  nonbound target.
+ *
+ *  Prepends the bound arguments to the value stack (at idx_func + 2).
+ *  The 'this' binding is also updated if necessary (at idx_func + 1).
+ *  Note that for constructor calls the 'this' binding is never updated by
+ *  [[BoundThis]].
  */
 
 DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
                                                 duk_idx_t idx_func,
-                                                duk_idx_t *p_num_stack_args,   /* may be changed by call */
                                                 duk_bool_t is_constructor_call) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_idx_t num_stack_args;
 	duk_tval *tv_func;
 	duk_hobject *func;
-	duk_uint_t sanity;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(p_num_stack_args != NULL);
+	duk_idx_t len;
+
+	DUK_ASSERT(thr != NULL);
 
 	/* On entry, item at idx_func is a bound, non-lightweight function,
 	 * but we don't rely on that below.
 	 */
 
-	num_stack_args = *p_num_stack_args;
-
-	sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
-	do {
-		duk_idx_t i, len;
-
-		tv_func = duk_require_tval(ctx, idx_func);
-		DUK_ASSERT(tv_func != NULL);
-
-		if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
-			/* Lightweight function: never bound, so terminate. */
-			break;
-		} else if (DUK_TVAL_IS_OBJECT(tv_func)) {
-			func = DUK_TVAL_GET_OBJECT(tv_func);
-			if (!DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
-				/* Normal non-bound function. */
-				break;
-			}
-		} else {
-			/* Function.prototype.bind() should never let this happen,
-			 * ugly error message is enough.
-			 */
-			DUK_ERROR_INTERNAL(thr);
-		}
-		DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv_func) != NULL);
-
-		/* XXX: this could be more compact by accessing the internal properties
-		 * directly as own properties (they cannot be inherited, and are not
-		 * externally visible).
-		 */
-
-		DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p, num_stack_args=%ld: %!T",
-		                     (void *) DUK_TVAL_GET_OBJECT(tv_func), (long) num_stack_args, tv_func));
-
-		/* [ ... func this arg1 ... argN ] */
-
-		if (is_constructor_call) {
-			/* See: tests/ecmascript/test-spec-bound-constructor.js */
-			DUK_DDD(DUK_DDDPRINT("constructor call: don't update this binding"));
-		} else {
-			duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_THIS);
-			duk_replace(ctx, idx_func + 1);  /* idx_this = idx_func + 1 */
-		}
-
-		/* [ ... func this arg1 ... argN ] */
-
-		/* XXX: duk_get_length? */
-		duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_ARGS);  /* -> [ ... func this arg1 ... argN _Args ] */
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_LENGTH);          /* -> [ ... func this arg1 ... argN _Args length ] */
-		len = (duk_idx_t) duk_require_int(ctx, -1);
-		duk_pop(ctx);
-		for (i = 0; i < len; i++) {
-			/* XXX: very slow - better to bulk allocate a gap, and copy
-			 * from args_array directly (we know it has a compact array
-			 * part, etc).
-			 */
-
-			/* [ ... func this <some bound args> arg1 ... argN _Args ] */
-			duk_get_prop_index(ctx, -1, i);
-			duk_insert(ctx, idx_func + 2 + i);  /* idx_args = idx_func + 2 */
-		}
-		num_stack_args += len;  /* must be updated to work properly (e.g. creation of 'arguments') */
-		duk_pop(ctx);
-
-		/* [ ... func this <bound args> arg1 ... argN ] */
-
-		duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_TARGET);
-		duk_replace(ctx, idx_func);  /* replace in stack */
-
-		DUK_DDD(DUK_DDDPRINT("bound function handled, num_stack_args=%ld, idx_func=%ld, curr func=%!T",
-		                     (long) num_stack_args, (long) idx_func, duk_get_tval(ctx, idx_func)));
-	} while (--sanity > 0);
-
-	if (DUK_UNLIKELY(sanity == 0)) {
-		DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
-	}
-
-	DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(ctx, idx_func)));
-
-#if defined(DUK_USE_ASSERTIONS)
-	tv_func = duk_require_tval(ctx, idx_func);
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+
+	tv_func = duk_require_tval(thr, idx_func);
+	DUK_ASSERT(tv_func != NULL);
+
+	if (DUK_TVAL_IS_OBJECT(tv_func)) {
+		func = DUK_TVAL_GET_OBJECT(tv_func);
+
+		/* XXX: separate helper function, out of fast path? */
+		if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
+			duk_hboundfunc *h_bound;
+			duk_tval *tv_args;
+			duk_tval *tv_gap;
+
+			h_bound = (duk_hboundfunc *) func;
+			tv_args = h_bound->args;
+			len = h_bound->nargs;
+			DUK_ASSERT(len == 0 || tv_args != NULL);
+
+			DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p: %!T",
+			                     (void *) DUK_TVAL_GET_OBJECT(tv_func), tv_func));
+
+			/* [ ... func this arg1 ... argN ] */
+
+			if (is_constructor_call) {
+				/* See: tests/ecmascript/test-spec-bound-constructor.js */
+				DUK_DDD(DUK_DDDPRINT("constructor call: don't update this binding"));
+			} else {
+				/* XXX: duk_replace_tval */
+				duk_push_tval(thr, &h_bound->this_binding);
+				duk_replace(thr, idx_func + 1);  /* idx_this = idx_func + 1 */
+			}
+
+			/* [ ... func this arg1 ... argN ] */
+
+			duk_require_stack(thr, len);
+
+			tv_gap = duk_reserve_gap(thr, idx_func + 2, len);
+			duk_copy_tvals_incref(thr, tv_gap, tv_args, (duk_size_t) len);
+
+			/* [ ... func this <bound args> arg1 ... argN ] */
+
+			duk_push_tval(thr, &h_bound->target);
+			duk_replace(thr, idx_func);  /* replace in stack */
+
+			DUK_DDD(DUK_DDDPRINT("bound function handled, idx_func=%ld, curr func=%!T",
+			                     (long) idx_func, duk_get_tval(thr, idx_func)));
+		}
+	} else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
+		/* Lightweight function: never bound, so terminate. */
+		;
+	} else {
+		/* Shouldn't happen, so ugly error is enough. */
+		DUK_ERROR_INTERNAL(thr);
+	}
+
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+
+	DUK_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(thr, idx_func)));
+
+#if defined(DUK_USE_ASSERTIONS)
+	tv_func = duk_require_tval(thr, idx_func);
 	DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func) || DUK_TVAL_IS_OBJECT(tv_func));
 	if (DUK_TVAL_IS_OBJECT(tv_func)) {
 		func = DUK_TVAL_GET_OBJECT(tv_func);
@@ -60201,10 +61679,324 @@
 		           DUK_HOBJECT_HAS_NATFUNC(func));
 	}
 #endif
-
-	/* write back */
-	*p_num_stack_args = num_stack_args;
-}
+}
+
+/*
+ *  Helper for inline handling of .call(), .apply(), and .construct().
+ */
+
+DUK_LOCAL duk_bool_t duk__handle_specialfuncs_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_hobject *func, duk_small_uint_t *call_flags, duk_bool_t first) {
+#if defined(DUK_USE_ASSERTIONS)
+	duk_c_function natfunc;
+#endif
+	duk_tval *tv_args;
+
+	DUK_ASSERT(func != NULL);
+	DUK_ASSERT((*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0);  /* Caller. */
+
+#if defined(DUK_USE_ASSERTIONS)
+	natfunc = ((duk_hnatfunc *) func)->func;
+	DUK_ASSERT(natfunc != NULL);
+#endif
+
+	/* On every round of function resolution at least target function and
+	 * 'this' binding are set.  We can assume that here, and must guarantee
+	 * it on exit.  Value stack reserve is extended for bound function and
+	 * .apply() unpacking so we don't need to extend it here when we need a
+	 * few slots.
+	 */
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+
+	/* Handle native 'eval' specially.  A direct eval check is only made
+	 * for the first resolution attempt; e.g. a bound eval call is -not-
+	 * a direct eval call.
+	 */
+	if (DUK_UNLIKELY(((duk_hnatfunc *) func)->magic == 15)) {
+		/* For now no special handling except for direct eval
+		 * detection.
+		 */
+		DUK_ASSERT(((duk_hnatfunc *) func)->func == duk_bi_global_object_eval);
+		if (first && (*call_flags & DUK_CALL_FLAG_CALLED_AS_EVAL)) {
+			*call_flags = (*call_flags & ~DUK_CALL_FLAG_CALLED_AS_EVAL) | DUK_CALL_FLAG_DIRECT_EVAL;
+		}
+		DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+		return 1;  /* stop resolving */
+	}
+
+	/* Handle special functions based on the DUK_HOBJECT_FLAG_SPECIAL_CALL
+	 * flag; their magic value is used for switch-case.
+	 *
+	 * NOTE: duk_unpack_array_like() reserves value stack space
+	 * for the result values (unlike most other value stack calls).
+	 */
+	switch (((duk_hnatfunc *) func)->magic) {
+	case 0: {  /* 0=Function.prototype.call() */
+		/* Value stack:
+		 * idx_func + 0: Function.prototype.call()  [removed]
+		 * idx_func + 1: this binding for .call (target function)
+		 * idx_func + 2: 1st argument to .call, desired 'this' binding
+		 * idx_func + 3: 2nd argument to .call, desired 1st argument for ultimate target
+		 * ...
+		 *
+		 * Remove idx_func + 0 to get:
+		 * idx_func + 0: target function
+		 * idx_func + 1: this binding
+		 * idx_func + 2: call arguments
+		 * ...
+		 */
+		DUK_ASSERT(natfunc == duk_bi_function_prototype_call);
+		duk_remove_unsafe(thr, idx_func);
+		tv_args = thr->valstack_bottom + idx_func + 2;
+		if (thr->valstack_top < tv_args) {
+			DUK_ASSERT(tv_args <= thr->valstack_end);
+			thr->valstack_top = tv_args;  /* at least target function and 'this' binding present */
+		}
+		break;
+	}
+	case 1: {  /* 1=Function.prototype.apply() */
+		/* Value stack:
+		 * idx_func + 0: Function.prototype.apply()  [removed]
+		 * idx_func + 1: this binding for .apply (target function)
+		 * idx_func + 2: 1st argument to .apply, desired 'this' binding
+		 * idx_func + 3: 2nd argument to .apply, argArray
+		 * [anything after this MUST be ignored]
+		 *
+		 * Remove idx_func + 0 and unpack the argArray to get:
+		 * idx_func + 0: target function
+		 * idx_func + 1: this binding
+		 * idx_func + 2: call arguments
+		 * ...
+		 */
+		DUK_ASSERT(natfunc == duk_bi_function_prototype_apply);
+		duk_remove_unsafe(thr, idx_func);
+		goto apply_shared;
+	}
+#if defined(DUK_USE_REFLECT_BUILTIN)
+	case 2: {  /* 2=Reflect.apply() */
+		/* Value stack:
+		 * idx_func + 0: Reflect.apply()  [removed]
+		 * idx_func + 1: this binding for .apply (ignored, usually Reflect)  [removed]
+		 * idx_func + 2: 1st argument to .apply, target function
+		 * idx_func + 3: 2nd argument to .apply, desired 'this' binding
+		 * idx_func + 4: 3rd argument to .apply, argArray
+		 * [anything after this MUST be ignored]
+		 *
+		 * Remove idx_func + 0 and idx_func + 1, and unpack the argArray to get:
+		 * idx_func + 0: target function
+		 * idx_func + 1: this binding
+		 * idx_func + 2: call arguments
+		 * ...
+		 */
+		DUK_ASSERT(natfunc == duk_bi_reflect_apply);
+		duk_remove_n_unsafe(thr, idx_func, 2);
+		goto apply_shared;
+	}
+	case 3: {  /* 3=Reflect.construct() */
+		/* Value stack:
+		 * idx_func + 0: Reflect.construct()  [removed]
+		 * idx_func + 1: this binding for .construct (ignored, usually Reflect)  [removed]
+		 * idx_func + 2: 1st argument to .construct, target function
+		 * idx_func + 3: 2nd argument to .construct, argArray
+		 * idx_func + 4: 3rd argument to .construct, newTarget
+		 * [anything after this MUST be ignored]
+		 *
+		 * Remove idx_func + 0 and idx_func + 1, unpack the argArray,
+		 * and insert default instance (prototype not yet updated), to get:
+		 * idx_func + 0: target function
+		 * idx_func + 1: this binding (default instance)
+		 * idx_func + 2: constructor call arguments
+		 * ...
+		 *
+		 * Call flags must be updated to reflect the fact that we're
+		 * now dealing with a constructor call, and e.g. the 'this'
+		 * binding cannot be overwritten if the target is bound.
+		 *
+		 * newTarget is checked but not yet passed onwards.
+		 */
+
+		duk_idx_t top;
+
+		DUK_ASSERT(natfunc == duk_bi_reflect_construct);
+		*call_flags |= DUK_CALL_FLAG_CONSTRUCT;
+		duk_remove_n_unsafe(thr, idx_func, 2);
+		top = duk_get_top(thr);
+		if (!duk_is_constructable(thr, idx_func)) {
+			/* Target constructability must be checked before
+			 * unpacking argArray (which may cause side effects).
+			 * Just return; caller will throw the error.
+			 */
+			duk_set_top_unsafe(thr, idx_func + 2);  /* satisfy asserts */
+			break;
+		}
+		duk_push_object(thr);
+		duk_insert(thr, idx_func + 1);  /* default instance */
+
+		/* [ ... func default_instance argArray newTarget? ] */
+
+		top = duk_get_top(thr);
+		if (top < idx_func + 3) {
+			/* argArray is a mandatory argument for Reflect.construct(). */
+			DUK_ERROR_TYPE_INVALID_ARGS(thr);
+		}
+		if (top > idx_func + 3) {
+			if (!duk_strict_equals(thr, idx_func, idx_func + 3)) {
+				/* XXX: [[Construct]] newTarget currently unsupported */
+				DUK_ERROR_UNSUPPORTED(thr);
+			}
+			duk_set_top_unsafe(thr, idx_func + 3);  /* remove any args beyond argArray */
+		}
+		DUK_ASSERT(duk_get_top(thr) == idx_func + 3);
+		DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2));
+		(void) duk_unpack_array_like(thr, idx_func + 2);  /* XXX: should also remove target to be symmetric with duk_pack()? */
+		duk_remove(thr, idx_func + 2);
+		DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+		break;
+	}
+#endif  /* DUK_USE_REFLECT_BUILTIN */
+	default: {
+		DUK_ASSERT(0);
+		DUK_UNREACHABLE();
+	}
+	}
+
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+	return 0;  /* keep resolving */
+
+ apply_shared:
+	tv_args = thr->valstack_bottom + idx_func + 2;
+	if (thr->valstack_top <= tv_args) {
+		DUK_ASSERT(tv_args <= thr->valstack_end);
+		thr->valstack_top = tv_args;  /* at least target func and 'this' binding present */
+		/* No need to check for argArray. */
+	} else {
+		DUK_ASSERT(duk_get_top(thr) >= idx_func + 3);  /* idx_func + 2 covered above */
+		if (thr->valstack_top > tv_args + 1) {
+			duk_set_top_unsafe(thr, idx_func + 3);  /* remove any args beyond argArray */
+		}
+		DUK_ASSERT(duk_is_valid_index(thr, idx_func + 2));
+		if (!duk_is_callable(thr, idx_func)) {
+			/* Avoid unpack side effects if the target isn't callable.
+			 * Calling code will throw the actual error.
+			 */
+		} else {
+			(void) duk_unpack_array_like(thr, idx_func + 2);
+			duk_remove(thr, idx_func + 2);
+		}
+	}
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+	return 0;  /* keep resolving */
+}
+
+/*
+ *  Helper for Proxy handling.
+ */
+
+#if defined(DUK_USE_ES6_PROXY)
+DUK_LOCAL void duk__handle_proxy_for_call(duk_hthread *thr, duk_idx_t idx_func, duk_hproxy *h_proxy, duk_small_uint_t *call_flags) {
+	duk_bool_t rc;
+
+	/* Value stack:
+	 * idx_func + 0: Proxy object
+	 * idx_func + 1: this binding for call
+	 * idx_func + 2: 1st argument for call
+	 * idx_func + 3: 2nd argument for call
+	 * ...
+	 *
+	 * If Proxy doesn't have a trap for the call ('apply' or 'construct'),
+	 * replace Proxy object with target object.
+	 *
+	 * If we're dealing with a normal call and the Proxy has an 'apply'
+	 * trap, manipulate value stack to:
+	 *
+	 * idx_func + 0: trap
+	 * idx_func + 1: Proxy's handler
+	 * idx_func + 2: Proxy's target
+	 * idx_func + 3: this binding for call (from idx_func + 1)
+	 * idx_func + 4: call arguments packed to an array
+	 *
+	 * If we're dealing with a constructor call and the Proxy has a
+	 * 'construct' trap, manipulate value stack to:
+	 *
+	 * idx_func + 0: trap
+	 * idx_func + 1: Proxy's handler
+	 * idx_func + 2: Proxy's target
+	 * idx_func + 3: call arguments packed to an array
+	 * idx_func + 4: newTarget == Proxy object here
+	 *
+	 * As we don't yet have proper newTarget support, the newTarget at
+	 * idx_func + 3 is just the original constructor being called, i.e.
+	 * the Proxy object (not the target).  Note that the default instance
+	 * (original 'this' binding) is dropped and ignored.
+	 */
+
+	duk_push_hobject(thr, h_proxy->handler);
+	rc = duk_get_prop_stridx_short(thr, -1, (*call_flags & DUK_CALL_FLAG_CONSTRUCT) ? DUK_STRIDX_CONSTRUCT : DUK_STRIDX_APPLY);
+	if (rc == 0) {
+		/* Not found, continue to target.  If this is a construct
+		 * call, update default instance prototype using the Proxy,
+		 * not the target.
+		 */
+		if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+			if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) {
+				*call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED;
+				duk__update_default_instance_proto(thr, idx_func);
+			}
+		}
+		duk_pop_2(thr);
+		duk_push_hobject(thr, h_proxy->target);
+		duk_replace(thr, idx_func);
+		return;
+	}
+
+	/* Here we must be careful not to replace idx_func while
+	 * h_proxy is still needed, otherwise h_proxy may become
+	 * dangling.  This could be improved e.g. using a
+	 * duk_pack_slice() with a freeform slice.
+	 */
+
+	/* Here:
+	 * idx_func + 0: Proxy object
+	 * idx_func + 1: this binding for call
+	 * idx_func + 2: 1st argument for call
+	 * idx_func + 3: 2nd argument for call
+	 * ...
+	 * idx_func + N: handler
+	 * idx_func + N + 1: trap
+	 */
+
+	duk_insert(thr, idx_func + 1);
+	duk_insert(thr, idx_func + 2);
+	duk_push_hobject(thr, h_proxy->target);
+	duk_insert(thr, idx_func + 3);
+	duk_pack(thr, duk_get_top(thr) - (idx_func + 5));
+
+	/* Here:
+	 * idx_func + 0: Proxy object
+	 * idx_func + 1: trap
+	 * idx_func + 2: Proxy's handler
+	 * idx_func + 3: Proxy's target
+	 * idx_func + 4: this binding for call
+	 * idx_func + 5: arguments array
+	 */
+	DUK_ASSERT(duk_get_top(thr) == idx_func + 6);
+
+	if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+		*call_flags |= DUK_CALL_FLAG_CONSTRUCT_PROXY;  /* Enable 'construct' trap return invariant check. */
+		*call_flags &= ~(DUK_CALL_FLAG_CONSTRUCT);     /* Resume as non-constructor call to the trap. */
+
+		/* 'apply' args: target, thisArg, argArray
+		 * 'construct' args: target, argArray, newTarget
+		 */
+		duk_remove(thr, idx_func + 4);
+		duk_push_hobject(thr, (duk_hobject *) h_proxy);
+	}
+
+	/* Finalize value stack layout by removing Proxy reference. */
+	duk_remove(thr, idx_func);
+	h_proxy = NULL;  /* invalidated */
+	DUK_ASSERT(duk_get_top(thr) == idx_func + 5);
+}
+#endif  /* DUK_USE_ES6_PROXY */
 
 /*
  *  Helper for setting up var_env and lex_env of an activation,
@@ -60261,7 +62053,7 @@
 	DUK_ASSERT(thr->callstack_top > 0);
 	act_callee = thr->callstack_curr;
 	DUK_ASSERT(act_callee != NULL);
-	act_caller = (thr->callstack_top >= 2 ? act_callee - 1 : NULL);
+	act_caller = (thr->callstack_top >= 2 ? act_callee->parent : NULL);
 
 	/* XXX: check .caller writability? */
 
@@ -60278,7 +62070,7 @@
 		 * because 'func' has been resolved to a non-bound function.
 		 */
 
-		if (act_caller) {
+		if (act_caller != NULL) {
 			/* act_caller->func may be NULL in some finalization cases,
 			 * just treat like we don't know the caller.
 			 */
@@ -60299,7 +62091,7 @@
 			 * is transferred to prev_caller.
 			 */
 
-			if (act_caller) {
+			if (act_caller != NULL) {
 				DUK_ASSERT(act_caller->func != NULL);
 				DUK_TVAL_SET_OBJECT(tv_caller, act_caller->func);
 				DUK_TVAL_INCREF(thr, tv_caller);
@@ -60310,7 +62102,7 @@
 			/* 'caller' must only take on 'null' or function value */
 			DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv_caller));
 			DUK_ASSERT(act_callee->prev_caller == NULL);
-			if (act_caller && act_caller->func) {
+			if (act_caller != NULL && act_caller->func) {
 				/* Tolerate act_caller->func == NULL which happens in
 				 * some finalization cases; treat like unknown caller.
 				 */
@@ -60325,15 +62117,21 @@
 #endif  /* DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */
 
 /*
- *  Determine the effective 'this' binding and coerce the current value
- *  on the valstack to the effective one (in-place, at idx_this).
- *
- *  The current this value in the valstack (at idx_this) represents either:
- *    - the caller's requested 'this' binding; or
- *    - a 'this' binding accumulated from the bound function chain
- *
- *  The final 'this' binding for the target function may still be
- *  different, and is determined as described in E5 Section 10.4.3.
+ *  Shared helpers for resolving the final, non-bound target function of the
+ *  call and the effective 'this' binding.  Resolves bound functions and
+ *  applies .call(), .apply(), and .construct() inline.
+ *
+ *  Proxy traps are also handled inline so that if the target is a Proxy with
+ *  a 'call' or 'construct' trap, the trap handler is called with a modified
+ *  argument list.
+ *
+ *  Once the bound function / .call() / .apply() / .construct() sequence has
+ *  been resolved, the value at idx_func + 1 may need coercion described in
+ *  E5 Section 10.4.3.
+ *
+ *  A call that begins as a non-constructor call may be converted into a
+ *  constructor call during the resolution process if Reflect.construct()
+ *  is invoked.  This is handled by updating the caller's call_flags.
  *
  *  For global and eval code (E5 Sections 10.4.1 and 10.4.2), we assume
  *  that the caller has provided the correct 'this' binding explicitly
@@ -60343,24 +62141,14 @@
  *    - direct eval: this=copy from eval() caller's this binding
  *    - other eval:  this=global object
  *
- *  Note: this function may cause a recursive function call with arbitrary
+ *  The 'this' coercion may cause a recursive function call with arbitrary
  *  side effects, because ToObject() may be called.
  */
 
-DUK_LOCAL void duk__coerce_effective_this_binding(duk_hthread *thr,
-                                                  duk_hobject *func,
-                                                  duk_idx_t idx_this) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_LOCAL DUK_INLINE void duk__coerce_nonstrict_this_binding(duk_hthread *thr, duk_idx_t idx_this) {
 	duk_tval *tv_this;
 	duk_hobject *obj_global;
 
-	if (func == NULL || DUK_HOBJECT_HAS_STRICT(func)) {
-		/* Lightfuncs are always considered strict. */
-		DUK_DDD(DUK_DDDPRINT("this binding: strict -> use directly"));
-		return;
-	}
-
-	/* XXX: byte offset */
 	tv_this = thr->valstack_bottom + idx_this;
 	switch (DUK_TVAL_GET_TAG(tv_this)) {
 	case DUK_TAG_OBJECT:
@@ -60387,144 +62175,238 @@
 	default:
 		/* Plain buffers and lightfuncs are object coerced.  Lightfuncs
 		 * very rarely come here however, because the call target would
-		 * need to be a strict non-lightfunc (lightfuncs are considered
+		 * need to be a non-strict non-lightfunc (lightfuncs are considered
 		 * strict) with an explicit lightfunc 'this' binding.
 		 */
 		DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_this));
 		DUK_DDD(DUK_DDDPRINT("this binding: non-strict, not object/undefined/null -> use ToObject(value)"));
-		duk_to_object(ctx, idx_this);  /* may have side effects */
-		break;
-	}
-}
-
-/*
- *  Shared helper for non-bound func lookup.
- *
- *  Returns duk_hobject * to the final non-bound function (NULL for lightfunc).
- */
-
-DUK_LOCAL duk_hobject *duk__nonbound_func_lookup(duk_context *ctx,
-                                                 duk_idx_t idx_func,
-                                                 duk_idx_t *out_num_stack_args,
-                                                 duk_tval **out_tv_func,
-                                                 duk_small_uint_t call_flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+		duk_to_object(thr, idx_this);  /* may have side effects */
+		break;
+	}
+}
+
+DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__resolve_target_fastpath_check(duk_hthread *thr, duk_idx_t idx_func, duk_hobject **out_func, duk_small_uint_t call_flags) {
+#if defined(DUK_USE_PREFER_SIZE)
+	DUK_UNREF(thr);
+	DUK_UNREF(idx_func);
+	DUK_UNREF(out_func);
+	DUK_UNREF(call_flags);
+#else  /* DUK_USE_PREFER_SIZE */
 	duk_tval *tv_func;
 	duk_hobject *func;
 
-	for (;;) {
-		/* Use loop to minimize code size of relookup after bound function case */
-		tv_func = DUK_GET_TVAL_POSIDX(ctx, idx_func);
+	if (DUK_UNLIKELY(call_flags & DUK_CALL_FLAG_CONSTRUCT)) {
+		return 0;
+	}
+
+	tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
+	DUK_ASSERT(tv_func != NULL);
+
+	if (DUK_LIKELY(DUK_TVAL_IS_OBJECT(tv_func))) {
+		func = DUK_TVAL_GET_OBJECT(tv_func);
+		if (DUK_HOBJECT_IS_CALLABLE(func) &&
+		    !DUK_HOBJECT_HAS_BOUNDFUNC(func) &&
+		    !DUK_HOBJECT_HAS_SPECIAL_CALL(func)) {
+			*out_func = func;
+
+			if (DUK_HOBJECT_HAS_STRICT(func)) {
+				/* Strict function: no 'this' coercion. */
+				return 1;
+			}
+
+			duk__coerce_nonstrict_this_binding(thr, idx_func + 1);
+			return 1;
+		}
+	} else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
+		*out_func = NULL;
+
+		/* Lightfuncs are considered strict, so 'this' binding is
+		 * used as is.  They're never bound, always constructable,
+		 * and never special functions.
+		 */
+		return 1;
+	}
+#endif  /* DUK_USE_PREFER_SIZE */
+	return 0;  /* let slow path deal with it */
+}
+
+DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_hthread *thr,
+                                                                 duk_idx_t idx_func,
+                                                                 duk_small_uint_t *call_flags) {
+	duk_tval *tv_func;
+	duk_hobject *func;
+	duk_bool_t first;
+
+	DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+
+	for (first = 1;; first = 0) {
+		DUK_ASSERT(duk_get_top(thr) >= idx_func + 2);
+
+		tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
 		DUK_ASSERT(tv_func != NULL);
 
 		if (DUK_TVAL_IS_OBJECT(tv_func)) {
 			func = DUK_TVAL_GET_OBJECT(tv_func);
-			if (!DUK_HOBJECT_IS_CALLABLE(func)) {
-				goto not_callable_error;
-			}
+
+			if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+				if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func))) {
+					goto not_constructable;
+				}
+			} else {
+				if (DUK_UNLIKELY(!DUK_HOBJECT_IS_CALLABLE(func))) {
+					goto not_callable;
+				}
+			}
+
+			if (DUK_LIKELY(!DUK_HOBJECT_HAS_BOUNDFUNC(func) &&
+			               !DUK_HOBJECT_HAS_SPECIAL_CALL(func) &&
+			               !DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func))) {
+				/* Common case, so test for using a single bitfield test.
+				 * Break out to handle this coercion etc.
+				 */
+				break;
+			}
+
+			/* XXX: could set specialcall for boundfuncs too, simplify check above */
+
 			if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
-				duk__handle_bound_chain_for_call(thr, idx_func, out_num_stack_args, call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL);
-
-				/* The final object may be a normal function or a lightfunc.
-				 * We need to re-lookup tv_func because it may have changed
-				 * (also value stack may have been resized).  Loop again to
-				 * do that; we're guaranteed not to come here again.
+				DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_CALL(func));
+				DUK_ASSERT(!DUK_HOBJECT_IS_NATFUNC(func));
+
+				/* Callable/constructable flags are the same
+				 * for the bound function and its target, so
+				 * we don't need to check them here, we can
+				 * check them from the target only.
 				 */
-				DUK_ASSERT(DUK_TVAL_IS_OBJECT(duk_require_tval(ctx, idx_func)) ||
-				           DUK_TVAL_IS_LIGHTFUNC(duk_require_tval(ctx, idx_func)));
-				continue;
-			}
+				duk__handle_bound_chain_for_call(thr, idx_func, *call_flags & DUK_CALL_FLAG_CONSTRUCT);
+
+				DUK_ASSERT(DUK_TVAL_IS_OBJECT(duk_require_tval(thr, idx_func)) ||
+				           DUK_TVAL_IS_LIGHTFUNC(duk_require_tval(thr, idx_func)));
+			} else {
+				DUK_ASSERT(DUK_HOBJECT_HAS_SPECIAL_CALL(func));
+
+#if defined(DUK_USE_ES6_PROXY)
+				if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(func)) {
+					/* If no trap, resume processing from Proxy trap.
+					 * If trap exists, helper converts call into a trap
+					 * call; this may change a constructor call into a
+					 * normal (non-constructor) trap call.  We must
+					 * continue processing even when a trap is found as
+					 * the trap may be bound.
+					 */
+					duk__handle_proxy_for_call(thr, idx_func, (duk_hproxy *) func, call_flags);
+				}
+				else
+#endif
+				{
+					DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func));
+					DUK_ASSERT(DUK_HOBJECT_HAS_CALLABLE(func));
+					DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func));
+					/* Constructable check already done above. */
+
+					if (duk__handle_specialfuncs_for_call(thr, idx_func, func, call_flags, first) != 0) {
+						/* Encountered native eval call, normal call
+						 * context.  Break out, handle this coercion etc.
+						 */
+						break;
+					}
+				}
+			}
+			/* Retry loop. */
 		} else if (DUK_TVAL_IS_LIGHTFUNC(tv_func)) {
+			/* Lightfuncs are:
+			 *   - Always strict, so no 'this' coercion.
+			 *   - Always callable.
+			 *   - Always constructable.
+			 *   - Never specialfuncs.
+			 */
 			func = NULL;
-		} else {
-			goto not_callable_error;
-		}
-		break;
-	}
-
-	DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_func) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_func))) ||
-	           DUK_TVAL_IS_LIGHTFUNC(tv_func));
-	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
-	DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
-	                            DUK_HOBJECT_IS_NATFUNC(func)));
-
-	*out_tv_func = tv_func;
+			goto finished;
+		} else {
+			goto not_callable;
+		}
+	}
+
+	DUK_ASSERT(func != NULL);
+
+	if (!DUK_HOBJECT_HAS_STRICT(func)) {
+		/* Non-strict target needs 'this' coercion.
+		 * This has potential side effects invalidating
+		 * 'tv_func'.
+		 */
+		duk__coerce_nonstrict_this_binding(thr, idx_func + 1);
+	}
+	if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+		if (!(*call_flags & DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED)) {
+			*call_flags |= DUK_CALL_FLAG_DEFAULT_INSTANCE_UPDATED;
+			duk__update_default_instance_proto(thr, idx_func);
+		}
+	}
+
+ finished:
+
+#if defined(DUK_USE_ASSERTIONS)
+	{
+		duk_tval *tv_tmp;
+
+		tv_tmp = duk_get_tval(thr, idx_func);
+		DUK_ASSERT(tv_tmp != NULL);
+
+		DUK_ASSERT((DUK_TVAL_IS_OBJECT(tv_tmp) && DUK_HOBJECT_IS_CALLABLE(DUK_TVAL_GET_OBJECT(tv_tmp))) ||
+		           DUK_TVAL_IS_LIGHTFUNC(tv_tmp));
+		DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
+		DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
+		                            DUK_HOBJECT_IS_NATFUNC(func)));
+		DUK_ASSERT(func == NULL || (DUK_HOBJECT_HAS_CONSTRUCTABLE(func) ||
+		                            (*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0));
+	}
+#endif
+
 	return func;
 
- not_callable_error:
+ not_callable:
 	DUK_ASSERT(tv_func != NULL);
+
+#if defined(DUK_USE_VERBOSE_ERRORS)
+	/* GETPROPC delayed error handling: when target is not callable,
+	 * GETPROPC replaces idx_func+0 with an Error (non-callable) with
+	 * a hidden Symbol to signify it's to be thrown as is here.  The
+	 * hidden Symbol is only checked as an own property, not inherited
+	 * (which would be dangerous).
+	 */
+	if (DUK_TVAL_IS_OBJECT(tv_func)) {
+		if (duk_hobject_find_existing_entry_tval_ptr(thr->heap, DUK_TVAL_GET_OBJECT(tv_func), DUK_HTHREAD_STRING_INT_TARGET(thr)) != NULL) {
+			duk_push_tval(thr, tv_func);
+			(void) duk_throw(thr);
+		}
+	}
+#endif
+
+#if defined(DUK_USE_VERBOSE_ERRORS)
 #if defined(DUK_USE_PARANOID_ERRORS)
+	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_get_type_name(thr, idx_func));
+#else
+	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(thr, tv_func));
+#endif
+#else
 	DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
-#else
-	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(ctx, tv_func));
 #endif
 	DUK_UNREACHABLE();
 	return NULL;  /* never executed */
-}
-
-/*
- *  Value stack resize and stack top adjustment helper.
- *
- *  XXX: This should all be merged to duk_valstack_resize_raw().
- */
-
-DUK_LOCAL void duk__adjust_valstack_and_top(duk_hthread *thr,
-                                            duk_idx_t num_stack_args,
-                                            duk_idx_t idx_args,
-                                            duk_idx_t nregs,
-                                            duk_idx_t nargs,
-                                            duk_hobject *func) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_size_t vs_min_size;
-	duk_bool_t adjusted_top = 0;
-
-	vs_min_size = (thr->valstack_bottom - thr->valstack) +  /* bottom of current func */
-	              idx_args;                                 /* bottom of new func */
-
-	if (nregs >= 0) {
-		DUK_ASSERT(nargs >= 0);
-		DUK_ASSERT(nregs >= nargs);
-		vs_min_size += nregs;
-	} else {
-		/* 'func' wants stack "as is" */
-		vs_min_size += num_stack_args;  /* num entries of new func at entry */
-	}
-	if (func == NULL || DUK_HOBJECT_IS_NATFUNC(func)) {
-		vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM;  /* Duktape/C API guaranteed entries (on top of args) */
-	}
-	vs_min_size += DUK_VALSTACK_INTERNAL_EXTRA;             /* + spare */
-
-	/* XXX: We can't resize the value stack to a size smaller than the
-	 * current top, so the order of the resize and adjusting the stack
-	 * top depends on the current vs. final size of the value stack.
-	 * The operations could be combined to avoid this, but the proper
-	 * fix is to only grow the value stack on a function call, and only
-	 * shrink it (without throwing if the shrink fails) on function
-	 * return.
-	 */
-
-	if (vs_min_size < (duk_size_t) (thr->valstack_top  - thr->valstack)) {
-		DUK_DDD(DUK_DDDPRINT(("final size smaller, set top before resize")));
-
-		DUK_ASSERT(nregs >= 0);  /* can't happen when keeping current stack size */
-		duk_set_top(ctx, idx_args + nargs);  /* clamp anything above nargs */
-		duk_set_top(ctx, idx_args + nregs);  /* extend with undefined */
-		adjusted_top = 1;
-	}
-
-	(void) duk_valstack_resize_raw((duk_context *) thr,
-	                               vs_min_size,
-	                               DUK_VSRESIZE_FLAG_SHRINK |      /* flags */
-	                               0 /* no compact */ |
-	                               DUK_VSRESIZE_FLAG_THROW);
-
-	if (!adjusted_top) {
-		if (nregs >= 0) {
-			DUK_ASSERT(nregs >= nargs);
-			duk_set_top(ctx, idx_args + nargs);  /* clamp anything above nargs */
-			duk_set_top(ctx, idx_args + nregs);  /* extend with undefined */
-		}
-	}
+
+ not_constructable:
+	/* For now GETPROPC delayed error not needed for constructor calls. */
+#if defined(DUK_USE_VERBOSE_ERRORS)
+#if defined(DUK_USE_PARANOID_ERRORS)
+	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_get_type_name(thr, idx_func));
+#else
+	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_tval_readable(thr, tv_func));
+#endif
+#else
+	DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONSTRUCTABLE);
+#endif
+	DUK_UNREACHABLE();
+	return NULL;  /* never executed */
 }
 
 /*
@@ -60538,7 +62420,6 @@
  */
 
 DUK_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t idx_retbase, duk_idx_t num_stack_rets, duk_idx_t num_actual_rets) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_idx_t idx_rcbase;
 
 	DUK_ASSERT(thr != NULL);
@@ -60546,540 +62427,399 @@
 	DUK_ASSERT(num_stack_rets >= 0);
 	DUK_ASSERT(num_actual_rets >= 0);
 
-	idx_rcbase = duk_get_top(ctx) - num_actual_rets;  /* base of known return values */
+	idx_rcbase = duk_get_top(thr) - num_actual_rets;  /* base of known return values */
+	if (DUK_UNLIKELY(idx_rcbase < 0)) {
+		DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC);
+	}
 
 	DUK_DDD(DUK_DDDPRINT("adjust valstack after func call: "
 	                     "num_stack_rets=%ld, num_actual_rets=%ld, stack_top=%ld, idx_retbase=%ld, idx_rcbase=%ld",
-	                     (long) num_stack_rets, (long) num_actual_rets, (long) duk_get_top(ctx),
+	                     (long) num_stack_rets, (long) num_actual_rets, (long) duk_get_top(thr),
 	                     (long) idx_retbase, (long) idx_rcbase));
 
 	DUK_ASSERT(idx_rcbase >= 0);  /* caller must check */
 
-	/* Ensure space for final configuration (idx_retbase + num_stack_rets)
-	 * and intermediate configurations.
-	 */
-	duk_require_stack_top(ctx,
-	                      (idx_rcbase > idx_retbase ? idx_rcbase : idx_retbase) +
-	                      num_stack_rets);
-
-	/* Chop extra retvals away / extend with undefined. */
-	duk_set_top(ctx, idx_rcbase + num_stack_rets);
-
-	if (idx_rcbase >= idx_retbase) {
+	/* Space for num_stack_rets was reserved before the safe call.
+	 * Because value stack reserve cannot shrink except in call returns,
+	 * the reserve is still in place.  Adjust valstack, carefully
+	 * ensuring we don't overstep the reserve.
+	 */
+
+	/* Match idx_rcbase with idx_retbase so that the return values
+	 * start at the correct index.
+	 */
+	if (idx_rcbase > idx_retbase) {
 		duk_idx_t count = idx_rcbase - idx_retbase;
-		duk_idx_t i;
 
 		DUK_DDD(DUK_DDDPRINT("elements at/after idx_retbase have enough to cover func retvals "
 		                     "(idx_retbase=%ld, idx_rcbase=%ld)", (long) idx_retbase, (long) idx_rcbase));
 
-		/* nuke values at idx_retbase to get the first retval (initially
-		 * at idx_rcbase) to idx_retbase
-		 */
-
-		DUK_ASSERT(count >= 0);
-
-		for (i = 0; i < count; i++) {
-			/* XXX: inefficient; block remove primitive */
-			duk_remove(ctx, idx_retbase);
-		}
+		/* Remove values between irc_rcbase (start of intended return
+		 * values) and idx_retbase to lower return values to idx_retbase.
+		 */
+		DUK_ASSERT(count > 0);
+		duk_remove_n(thr, idx_retbase, count);  /* may be NORZ */
 	} else {
 		duk_idx_t count = idx_retbase - idx_rcbase;
-		duk_idx_t i;
 
 		DUK_DDD(DUK_DDDPRINT("not enough elements at/after idx_retbase to cover func retvals "
 		                     "(idx_retbase=%ld, idx_rcbase=%ld)", (long) idx_retbase, (long) idx_rcbase));
 
-		/* insert 'undefined' values at idx_rcbase to get the
-		 * return values to idx_retbase
-		 */
-
-		DUK_ASSERT(count > 0);
-
-		for (i = 0; i < count; i++) {
-			/* XXX: inefficient; block insert primitive */
-			duk_push_undefined(ctx);
-			duk_insert(ctx, idx_rcbase);
-		}
-	}
-}
-
-/*
- *  Misc shared helpers.
- */
-
-/* Get valstack index for the func argument or throw if insane stack. */
-DUK_LOCAL duk_idx_t duk__get_idx_func(duk_hthread *thr, duk_idx_t num_stack_args) {
-	duk_size_t off_stack_top;
-	duk_size_t off_stack_args;
-	duk_size_t off_stack_all;
-	duk_idx_t idx_func;         /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
-
-	/* Argument validation and func/args offset. */
-	off_stack_top = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack_bottom);
-	off_stack_args = (duk_size_t) ((duk_size_t) num_stack_args * sizeof(duk_tval));
-	off_stack_all = off_stack_args + 2 * sizeof(duk_tval);
-	if (DUK_UNLIKELY(off_stack_all > off_stack_top)) {
-		/* Since stack indices are not reliable, we can't do anything useful
-		 * here.  Invoke the existing setjmp catcher, or if it doesn't exist,
-		 * call the fatal error handler.
-		 */
-		DUK_ERROR_TYPE_INVALID_ARGS(thr);
-		return 0;
-	}
-	idx_func = (duk_idx_t) ((off_stack_top - off_stack_all) / sizeof(duk_tval));
-	return idx_func;
-}
-
-/*
- *  duk_handle_call_protected() and duk_handle_call_unprotected():
- *  call into a Duktape/C or an Ecmascript function from any state.
- *
- *  Input stack (thr):
- *
- *    [ func this arg1 ... argN ]
- *
- *  Output stack (thr):
- *
- *    [ retval ]         (DUK_EXEC_SUCCESS)
- *    [ errobj ]         (DUK_EXEC_ERROR (normal error), protected call)
- *
- *  Even when executing a protected call an error may be thrown in rare cases
- *  such as an insane num_stack_args argument.  If there is no catchpoint for
- *  such errors, the fatal error handler is called.
- *
- *  The error handling path should be error free, even for out-of-memory
- *  errors, to ensure safe sandboxing.  (As of Duktape 1.4.0 this is not
- *  yet the case, see XXX notes below.)
- */
-
-DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
-                                                 duk_idx_t num_stack_args,
-                                                 duk_small_uint_t call_flags) {
-	duk_context *ctx;
-	duk_size_t entry_valstack_bottom_index;
-	duk_size_t entry_valstack_end;
-	duk_size_t entry_callstack_top;
-	duk_size_t entry_catchstack_top;
-	duk_int_t entry_call_recursion_depth;
-	duk_hthread *entry_curr_thread;
-	duk_uint_fast8_t entry_thread_state;
-	duk_instr_t **entry_ptr_curr_pc;
-	duk_jmpbuf *old_jmpbuf_ptr = NULL;
-	duk_jmpbuf our_jmpbuf;
-	duk_idx_t idx_func;  /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
-
-	/* XXX: Multiple tv_func lookups are now avoided by making a local
-	 * copy of tv_func.  Another approach would be to compute an offset
-	 * for tv_func from valstack bottom and recomputing the tv_func
-	 * pointer quickly as valstack + offset instead of calling duk_get_tval().
-	 */
-
-	ctx = (duk_context *) thr;
-	DUK_UNREF(ctx);
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(num_stack_args >= 0);
-	/* XXX: currently NULL allocations are not supported; remove if later allowed */
-	DUK_ASSERT(thr->valstack != NULL);
-	DUK_ASSERT(thr->callstack != NULL);
-	DUK_ASSERT(thr->catchstack != NULL);
-
-	/* Argument validation and func/args offset. */
-	idx_func = duk__get_idx_func(thr, num_stack_args);
-
-	/* Preliminaries, required by setjmp() handler.  Must be careful not
-	 * to throw an unintended error here.
-	 */
-
-	entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack);
-#if defined(DUK_USE_PREFER_SIZE)
-	entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-	DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size);
-	entry_valstack_end = thr->valstack_size;
-#endif
-	entry_callstack_top = thr->callstack_top;
-	entry_catchstack_top = thr->catchstack_top;
-	entry_call_recursion_depth = thr->heap->call_recursion_depth;
-	entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if first call */
-	entry_thread_state = thr->state;
-	entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-
-	DUK_DD(DUK_DDPRINT("duk_handle_call_protected: thr=%p, num_stack_args=%ld, "
-	                   "call_flags=0x%08lx (ignorerec=%ld, constructor=%ld), "
-	                   "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, "
-	                   "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-	                   "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld",
-	                   (void *) thr,
-	                   (long) num_stack_args,
-	                   (unsigned long) call_flags,
-	                   (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) != 0 ? 1 : 0),
-	                   (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0),
-	                   (long) duk_get_top(ctx),
-	                   (long) idx_func,
-	                   (long) (idx_func + 2),
-	                   (long) thr->heap->call_recursion_depth,
-	                   (long) thr->heap->call_recursion_limit,
-	                   (long) entry_valstack_bottom_index,
-	                   (long) entry_callstack_top,
-	                   (long) entry_catchstack_top,
-	                   (long) entry_call_recursion_depth,
-	                   (void *) entry_curr_thread,
-	                   (long) entry_thread_state));
-
-	old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
-	thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	try {
-#else
-	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
-	if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
-#endif
-		/* Call handling and success path.  Success path exit cleans
-		 * up almost all state.
-		 */
-		duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func);
-
-		thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-		return DUK_EXEC_SUCCESS;
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	} catch (duk_internal_exception &exc) {
-#else
-	} else {
-#endif
-		/* Error; error value is in heap->lj.value1. */
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-		DUK_UNREF(exc);
-#endif
-
-		duk__handle_call_error(thr,
-		                       entry_valstack_bottom_index,
-		                       entry_valstack_end,
-		                       entry_catchstack_top,
-		                       entry_callstack_top,
-		                       entry_call_recursion_depth,
-		                       entry_curr_thread,
-		                       entry_thread_state,
-		                       entry_ptr_curr_pc,
-		                       idx_func,
-		                       old_jmpbuf_ptr);
-
-		return DUK_EXEC_ERROR;
-	}
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	catch (std::exception &exc) {
-		const char *what = exc.what();
-		if (!what) {
-			what = "unknown";
-		}
-		DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
-		try {
-			DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
-		} catch (duk_internal_exception exc) {
-			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
-			DUK_UNREF(exc);
-			duk__handle_call_error(thr,
-			                       entry_valstack_bottom_index,
-			                       entry_valstack_end,
-			                       entry_catchstack_top,
-			                       entry_callstack_top,
-			                       entry_call_recursion_depth,
-			                       entry_curr_thread,
-			                       entry_thread_state,
-			                       entry_ptr_curr_pc,
-			                       idx_func,
-			                       old_jmpbuf_ptr);
-
-			return DUK_EXEC_ERROR;
-		}
-	} catch (...) {
-		DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
-		try {
-			DUK_ERROR_TYPE(thr, "caught invalid c++ exception (perhaps thrown by user code)");
-		} catch (duk_internal_exception exc) {
-			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
-			DUK_UNREF(exc);
-			duk__handle_call_error(thr,
-			                       entry_valstack_bottom_index,
-			                       entry_valstack_end,
-			                       entry_catchstack_top,
-			                       entry_callstack_top,
-			                       entry_call_recursion_depth,
-			                       entry_curr_thread,
-			                       entry_thread_state,
-			                       entry_ptr_curr_pc,
-			                       idx_func,
-			                       old_jmpbuf_ptr);
-
-			return DUK_EXEC_ERROR;
-		}
-	}
-#endif
-}
-
-DUK_INTERNAL void duk_handle_call_unprotected(duk_hthread *thr,
-                                              duk_idx_t num_stack_args,
-                                              duk_small_uint_t call_flags) {
-	duk_idx_t idx_func;         /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
-
-	/* Argument validation and func/args offset. */
-	idx_func = duk__get_idx_func(thr, num_stack_args);
-
-	duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func);
-}
-
-DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
-                                      duk_idx_t num_stack_args,
-                                      duk_small_uint_t call_flags,
-                                      duk_idx_t idx_func) {
-	duk_context *ctx;
-	duk_size_t entry_valstack_bottom_index;
-	duk_size_t entry_valstack_end;
-	duk_size_t entry_callstack_top;
-	duk_size_t entry_catchstack_top;
-	duk_int_t entry_call_recursion_depth;
-	duk_hthread *entry_curr_thread;
-	duk_uint_fast8_t entry_thread_state;
-	duk_instr_t **entry_ptr_curr_pc;
-	duk_idx_t nargs;            /* # argument registers target function wants (< 0 => "as is") */
-	duk_idx_t nregs;            /* # total registers target function wants on entry (< 0 => "as is") */
-	duk_hobject *func;          /* 'func' on stack (borrowed reference) */
-	duk_tval *tv_func;          /* duk_tval ptr for 'func' on stack (borrowed reference) or tv_func_copy */
-	duk_tval tv_func_copy;      /* to avoid relookups */
+		/* Insert 'undefined' at idx_rcbase (start of intended return
+		 * values) to lift return values to idx_retbase.
+		 */
+		DUK_ASSERT(count >= 0);
+		DUK_ASSERT(thr->valstack_end - thr->valstack_top >= count);  /* reserve cannot shrink */
+		duk_insert_undefined_n(thr, idx_rcbase, count);
+	}
+
+	/* Chop extra retvals away / extend with undefined. */
+	duk_set_top_unsafe(thr, idx_retbase + num_stack_rets);
+}
+
+/*
+ *  Activation setup for tailcalls and non-tailcalls.
+ */
+
+#if defined(DUK_USE_TAILCALL)
+DUK_LOCAL duk_small_uint_t duk__call_setup_act_attempt_tailcall(duk_hthread *thr,
+                                                                duk_small_uint_t call_flags,
+                                                                duk_idx_t idx_func,
+                                                                duk_hobject *func,
+                                                                duk_size_t entry_valstack_bottom_byteoff,
+                                                                duk_size_t entry_valstack_end_byteoff,
+                                                                duk_idx_t *out_nargs,
+                                                                duk_idx_t *out_nregs,
+                                                                duk_size_t *out_vs_min_bytes,
+                                                                duk_activation **out_act) {
 	duk_activation *act;
-	duk_hobject *env;
-	duk_ret_t rc;
-
-	ctx = (duk_context *) thr;
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_ASSERT(ctx != NULL);
-	DUK_ASSERT(num_stack_args >= 0);
-	/* XXX: currently NULL allocations are not supported; remove if later allowed */
-	DUK_ASSERT(thr->valstack != NULL);
-	DUK_ASSERT(thr->callstack != NULL);
-	DUK_ASSERT(thr->catchstack != NULL);
-
-	DUK_DD(DUK_DDPRINT("duk__handle_call_inner: num_stack_args=%ld, call_flags=0x%08lx, top=%ld",
-	                   (long) num_stack_args, (long) call_flags, (long) duk_get_top(ctx)));
-
-	/*
-	 *  Store entry state.
-	 */
-
-	entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack);
-#if defined(DUK_USE_PREFER_SIZE)
-	entry_valstack_end = (duk_size_t) (thr->valstack_end - thr->valstack);
-#else
-	DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size);
-	entry_valstack_end = thr->valstack_size;
-#endif
-	entry_callstack_top = thr->callstack_top;
-	entry_catchstack_top = thr->catchstack_top;
-	entry_call_recursion_depth = thr->heap->call_recursion_depth;
-	entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if first call */
-	entry_thread_state = thr->state;
-	entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-
-	/* If thr->ptr_curr_pc is set, sync curr_pc to act->pc.  Then NULL
-	 * thr->ptr_curr_pc so that it's not accidentally used with an incorrect
-	 * activation when side effects occur.
-	 */
-	duk_hthread_sync_and_null_currpc(thr);
-
-	DUK_DD(DUK_DDPRINT("duk__handle_call_inner: thr=%p, num_stack_args=%ld, "
-	                   "call_flags=0x%08lx (ignorerec=%ld, constructor=%ld), "
-	                   "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, "
-	                   "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-	                   "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld",
-	                   (void *) thr,
-	                   (long) num_stack_args,
-	                   (unsigned long) call_flags,
-	                   (long) ((call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) != 0 ? 1 : 0),
-	                   (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) != 0 ? 1 : 0),
-	                   (long) duk_get_top(ctx),
-	                   (long) idx_func,
-	                   (long) (idx_func + 2),
-	                   (long) thr->heap->call_recursion_depth,
-	                   (long) thr->heap->call_recursion_limit,
-	                   (long) entry_valstack_bottom_index,
-	                   (long) entry_callstack_top,
-	                   (long) entry_catchstack_top,
-	                   (long) entry_call_recursion_depth,
-	                   (void *) entry_curr_thread,
-	                   (long) entry_thread_state));
-
-
-	/*
-	 *  Thread state check and book-keeping.
-	 */
-
-	if (thr == thr->heap->curr_thread) {
-		/* same thread */
-		if (thr->state != DUK_HTHREAD_STATE_RUNNING) {
-			/* should actually never happen, but check anyway */
-			goto thread_state_error;
-		}
-	} else {
-		/* different thread */
-		DUK_ASSERT(thr->heap->curr_thread == NULL ||
-		           thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING);
-		if (thr->state != DUK_HTHREAD_STATE_INACTIVE) {
-			goto thread_state_error;
-		}
-		DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
-		thr->state = DUK_HTHREAD_STATE_RUNNING;
-
-		/* Note: multiple threads may be simultaneously in the RUNNING
-		 * state, but not in the same "resume chain".
-		 */
-	}
-	DUK_ASSERT(thr->heap->curr_thread == thr);
-	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
-
-	/*
-	 *  C call recursion depth check, which provides a reasonable upper
-	 *  bound on maximum C stack size (arbitrary C stack growth is only
-	 *  possible by recursive handle_call / handle_safe_call calls).
-	 */
-
-	/* XXX: remove DUK_CALL_FLAG_IGNORE_RECLIMIT flag: there's now the
-	 * reclimit bump?
-	 */
-
-	DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
-	DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit);
-	if (call_flags & DUK_CALL_FLAG_IGNORE_RECLIMIT) {
-		DUK_DD(DUK_DDPRINT("ignoring reclimit for this call (probably an errhandler call)"));
-	} else {
-		if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit) {
-			/* XXX: error message is a bit misleading: we reached a recursion
-			 * limit which is also essentially the same as a C callstack limit
-			 * (except perhaps with some relaxed threading assumptions).
-			 */
-			DUK_ERROR_RANGE(thr, DUK_STR_C_CALLSTACK_LIMIT);
-		}
-		thr->heap->call_recursion_depth++;
-	}
-
-	/*
-	 *  Check the function type, handle bound function chains, and prepare
-	 *  parameters for the rest of the call handling.  Also figure out the
-	 *  effective 'this' binding, which replaces the current value at
-	 *  idx_func + 1.
-	 *
-	 *  If the target function is a 'bound' one, follow the chain of 'bound'
-	 *  functions until a non-bound function is found.  During this process,
-	 *  bound arguments are 'prepended' to existing ones, and the "this"
-	 *  binding is overridden.  See E5 Section 15.3.4.5.1.
-	 *
-	 *  Lightfunc detection happens here too.  Note that lightweight functions
-	 *  can be wrapped by (non-lightweight) bound functions so we must resolve
-	 *  the bound function chain first.
-	 */
-
-	func = duk__nonbound_func_lookup(ctx, idx_func, &num_stack_args, &tv_func, call_flags);
-	DUK_TVAL_SET_TVAL(&tv_func_copy, tv_func);
-	tv_func = &tv_func_copy;  /* local copy to avoid relookups */
-
-	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
-	DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
-	                            DUK_HOBJECT_IS_NATFUNC(func)));
-
-	duk__coerce_effective_this_binding(thr, func, idx_func + 1);
-	DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T",
-	                     (duk_tval *) duk_get_tval(ctx, idx_func + 1)));
-
-	/* [ ... func this arg1 ... argN ] */
-
-	/*
-	 *  Setup a preliminary activation and figure out nargs/nregs.
-	 *
-	 *  Don't touch valstack_bottom or valstack_top yet so that Duktape API
-	 *  calls work normally.
-	 */
-
-	duk_hthread_callstack_grow(thr);
+	duk_tval *tv1, *tv2;
+	duk_idx_t idx_args;
+	duk_small_uint_t flags1, flags2;
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+	duk_activation *prev_pause_act;
+#endif
+
+	DUK_UNREF(entry_valstack_end_byteoff);
+
+	/* Tailcall cannot be flagged to resume calls, and a
+	 * previous frame must exist.
+	 */
+	DUK_ASSERT(thr->callstack_top >= 1);
+
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	*out_act = act;
+
+	if (func == NULL || !DUK_HOBJECT_IS_COMPFUNC(func)) {
+		DUK_DDD(DUK_DDDPRINT("tail call prevented by target not being ecma function"));
+		return 0;
+	}
+	if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
+		DUK_DDD(DUK_DDDPRINT("tail call prevented by current activation having DUK_ACT_FLAG_PREVENT_YIELD"));
+		return 0;
+	}
+	/* Tailcall is only allowed if current and candidate
+	 * function have identical return value handling.  There
+	 * are three possible return value handling cases:
+	 *   1. Normal function call, no special return value handling.
+	 *   2. Constructor call, return value replacement object check.
+	 *   3. Proxy 'construct' trap call, return value invariant check.
+	 */
+	flags1 = (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT) ? 1 : 0)
+#if defined(DUK_USE_ES6_PROXY)
+	         | (duk_small_uint_t) ((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) ? 2 : 0)
+#endif
+	         ;
+	flags2 = (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) ? 1 : 0)
+#if defined(DUK_USE_ES6_PROXY)
+	         | (duk_small_uint_t) ((call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) ? 2 : 0);
+#endif
+	         ;
+	if (flags1 != flags2) {
+		DUK_DDD(DUK_DDDPRINT("tail call prevented by incompatible return value handling"));
+		return 0;
+	}
+	DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT) && (call_flags & DUK_CALL_FLAG_CONSTRUCT)) ||
+	           (!(act->flags & DUK_ACT_FLAG_CONSTRUCT) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT)));
+	DUK_ASSERT(((act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY)) ||
+	           (!(act->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY) && !(call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY)));
+	if (DUK_HOBJECT_HAS_NOTAIL(func)) {
+		/* See: test-bug-tailcall-preventyield-assert.c. */
+		DUK_DDD(DUK_DDDPRINT("tail call prevented by function having a notail flag"));
+		return 0;
+	}
+
+	/*
+	 *  Tailcall handling
+	 *
+	 *  Although the callstack entry is reused, we need to explicitly unwind
+	 *  the current activation (or simulate an unwind).  In particular, the
+	 *  current activation must be closed, otherwise something like
+	 *  test-bug-reduce-judofyr.js results.  Also catchers need to be unwound
+	 *  because there may be non-error-catching label entries in valid tail calls.
+	 *
+	 *  Special attention is needed for debugger and pause behavior when
+	 *  reusing an activation.
+	 *    - Disable StepOut processing for the activation unwind because
+	 *      we reuse the activation, see:
+	 *      https://github.com/svaarala/duktape/issues/1684.
+	 *    - Disable line change pause flag permanently if act == dbg_pause_act
+	 *      (if set) because it would no longer be relevant, see:
+	 *      https://github.com/svaarala/duktape/issues/1726,
+	 *      https://github.com/svaarala/duktape/issues/1786.
+	 *    - Check for function entry (e.g. StepInto) pause flag here, because
+	 *      the executor pause check won't trigger due to shared activation, see:
+	 *      https://github.com/svaarala/duktape/issues/1726.
+	 */
+
+	DUK_DDD(DUK_DDDPRINT("is tail call, reusing activation at callstack top, at index %ld",
+                             (long) (thr->callstack_top - 1)));
+
+	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
+	DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
+	DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
+	DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
+	DUK_ASSERT(call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA);
+
+	/* Unwind the topmost callstack entry before reusing it.  This
+	 * also unwinds the catchers related to the topmost entry.
+	 */
+	DUK_ASSERT(thr->callstack_top > 0);
+	DUK_ASSERT(thr->callstack_curr != NULL);
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+	if (act == thr->heap->dbg_pause_act) {
+		thr->heap->dbg_pause_flags &= ~DUK_PAUSE_FLAG_LINE_CHANGE;
+	}
+
+	prev_pause_act = thr->heap->dbg_pause_act;
+	thr->heap->dbg_pause_act = NULL;
+	if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) {
+		DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry (tailcall)"));
+		duk_debug_set_paused(thr->heap);
+	}
+#endif
+	duk_hthread_activation_unwind_reuse_norz(thr);
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+	thr->heap->dbg_pause_act = prev_pause_act;
+#endif
+	DUK_ASSERT(act == thr->callstack_curr);
+
+	/* XXX: We could restore the caller's value stack reserve
+	 * here, as if we did an actual unwind-and-call.  Without
+	 * the restoration, value stack reserve may remain higher
+	 * than would otherwise be possible until we return to a
+	 * non-tailcall.
+	 */
+
+	/* Then reuse the unwound activation. */
+	act->cat = NULL;
+	act->var_env = NULL;
+	act->lex_env = NULL;
+	DUK_ASSERT(func != NULL);
+	DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
+	act->func = func;  /* don't want an intermediate exposed state with func == NULL */
+#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
+	act->prev_caller = NULL;
+#endif
+	/* don't want an intermediate exposed state with invalid pc */
+	act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+	act->prev_line = 0;
+#endif
+	DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
+	DUK_HOBJECT_INCREF(thr, func);
+
+	act->flags = DUK_ACT_FLAG_TAILCALLED;
+	if (DUK_HOBJECT_HAS_STRICT(func)) {
+		act->flags |= DUK_ACT_FLAG_STRICT;
+	}
+	if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+		act->flags |= DUK_ACT_FLAG_CONSTRUCT;
+	}
+#if defined(DUK_USE_ES6_PROXY)
+	if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) {
+		act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY;
+	}
+#endif
+
+	DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func);      /* already updated */
+	DUK_ASSERT(act->var_env == NULL);
+	DUK_ASSERT(act->lex_env == NULL);
+	act->bottom_byteoff = entry_valstack_bottom_byteoff;  /* tail call -> reuse current "frame" */
+#if 0
+	/* Topmost activation retval_byteoff is considered garbage, no need to init. */
+	act->retval_byteoff = 0;
+#endif
+	/* Filled in when final reserve is known, dummy value doesn't matter
+	 * even in error unwind because reserve_byteoff is only used when
+	 * returning to -this- activation.
+	 */
+	act->reserve_byteoff = 0;
+
+	/*
+	 *  Manipulate valstack so that args are on the current bottom and the
+	 *  previous caller's 'this' binding (which is the value preceding the
+	 *  current bottom) is replaced with the new 'this' binding:
+	 *
+	 *       [ ... this_old | (crud) func this_new arg1 ... argN ]
+	 *  -->  [ ... this_new | arg1 ... argN ]
+	 *
+	 *  For tail calling to work properly, the valstack bottom must not grow
+	 *  here; otherwise crud would accumulate on the valstack.
+	 */
+
+	tv1 = thr->valstack_bottom - 1;
+	tv2 = thr->valstack_bottom + idx_func + 1;
+	DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);  /* tv1 is -below- valstack_bottom */
+	DUK_ASSERT(tv2 >= thr->valstack_bottom && tv2 < thr->valstack_top);
+	DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */
+
+	idx_args = idx_func + 2;
+	duk_remove_n(thr, 0, idx_args);  /* may be NORZ */
+
+	idx_func = 0; DUK_UNREF(idx_func);  /* really 'not applicable' anymore, should not be referenced after this */
+	idx_args = 0;
+
+	*out_nargs = ((duk_hcompfunc *) func)->nargs;
+	*out_nregs = ((duk_hcompfunc *) func)->nregs;
+	DUK_ASSERT(*out_nregs >= 0);
+	DUK_ASSERT(*out_nregs >= *out_nargs);
+	*out_vs_min_bytes = entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA);
+
+
+#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
+#if defined(DUK_USE_TAILCALL)
+#error incorrect options: tail calls enabled with function caller property
+#endif
+	/* XXX: This doesn't actually work properly for tail calls, so
+	 * tail calls are disabled when DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
+	 * is in use.
+	 */
+	duk__update_func_caller_prop(thr, func);
+#endif
+
+	/* [ ... this_new | arg1 ... argN ] */
+
+	return 1;
+}
+#endif  /* DUK_USE_TAILCALL */
+
+DUK_LOCAL void duk__call_setup_act_not_tailcall(duk_hthread *thr,
+                                                duk_small_uint_t call_flags,
+                                                duk_idx_t idx_func,
+                                                duk_hobject *func,
+                                                duk_size_t entry_valstack_bottom_byteoff,
+                                                duk_size_t entry_valstack_end_byteoff,
+                                                duk_idx_t *out_nargs,
+                                                duk_idx_t *out_nregs,
+                                                duk_size_t *out_vs_min_bytes,
+                                                duk_activation **out_act) {
+	duk_activation *act;
+	duk_activation *new_act;
+
+	DUK_UNREF(entry_valstack_end_byteoff);
+
+	DUK_DDD(DUK_DDDPRINT("not a tail call, pushing a new activation to callstack, to index %ld",
+	                     (long) (thr->callstack_top)));
+
+	duk__call_callstack_limit_check(thr);
+	new_act = duk_hthread_activation_alloc(thr);
+	DUK_ASSERT(new_act != NULL);
 
 	act = thr->callstack_curr;
 	if (act != NULL) {
 		/*
-		 *  Update idx_retval of current activation.
+		 *  Update return value stack index of current activation (if any).
 		 *
 		 *  Although it might seem this is not necessary (bytecode executor
 		 *  does this for Ecmascript-to-Ecmascript calls; other calls are
 		 *  handled here), this turns out to be necessary for handling yield
 		 *  and resume.  For them, an Ecmascript-to-native call happens, and
-		 *  the Ecmascript call's idx_retval must be set for things to work.
-		 */
-
-		act->idx_retval = entry_valstack_bottom_index + idx_func;
-	}
-
-	DUK_ASSERT(thr->callstack_top < thr->callstack_size);
-	act = thr->callstack + thr->callstack_top;
+		 *  the Ecmascript call's retval_byteoff must be set for things to work.
+		 */
+
+		act->retval_byteoff = entry_valstack_bottom_byteoff + (duk_size_t) idx_func * sizeof(duk_tval);
+	}
+
+	new_act->parent = act;
+	thr->callstack_curr = new_act;
 	thr->callstack_top++;
-	thr->callstack_curr = act;
-	DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
+	act = new_act;
+	*out_act = act;
+
 	DUK_ASSERT(thr->valstack_top > thr->valstack_bottom);  /* at least effective 'this' */
 	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
 
+	act->cat = NULL;
+
 	act->flags = 0;
-
-	/* For now all calls except Ecma-to-Ecma calls prevent a yield. */
-	act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
-	if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) {
+	if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
 		act->flags |= DUK_ACT_FLAG_CONSTRUCT;
 	}
+#if defined(DUK_USE_ES6_PROXY)
+	if (call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY) {
+		act->flags |= DUK_ACT_FLAG_CONSTRUCT_PROXY;
+	}
+#endif
 	if (call_flags & DUK_CALL_FLAG_DIRECT_EVAL) {
 		act->flags |= DUK_ACT_FLAG_DIRECT_EVAL;
 	}
 
-	/* These base values are never used, but if the compiler doesn't know
-	 * that DUK_ERROR() won't return, these are needed to silence warnings.
-	 * On the other hand, scan-build will warn about the values not being
-	 * used, so add a DUK_UNREF.
-	 */
-	nargs = 0; DUK_UNREF(nargs);
-	nregs = 0; DUK_UNREF(nregs);
-
+	/* start of arguments: idx_func + 2. */
+	act->func = func;  /* NULL for lightfunc */
 	if (DUK_LIKELY(func != NULL)) {
+		DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
 		if (DUK_HOBJECT_HAS_STRICT(func)) {
 			act->flags |= DUK_ACT_FLAG_STRICT;
 		}
 		if (DUK_HOBJECT_IS_COMPFUNC(func)) {
-			nargs = ((duk_hcompfunc *) func)->nargs;
-			nregs = ((duk_hcompfunc *) func)->nregs;
-			DUK_ASSERT(nregs >= nargs);
+			*out_nargs = ((duk_hcompfunc *) func)->nargs;
+			*out_nregs = ((duk_hcompfunc *) func)->nregs;
+			DUK_ASSERT(*out_nregs >= 0);
+			DUK_ASSERT(*out_nregs >= *out_nargs);
+			*out_vs_min_bytes = entry_valstack_bottom_byteoff +
+				sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + DUK_VALSTACK_INTERNAL_EXTRA);
 		} else {
 			/* True because of call target lookup checks. */
 			DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func));
 
-			/* Note: nargs (and nregs) may be negative for a native,
-			 * function, which indicates that the function wants the
-			 * input stack "as is" (i.e. handles "vararg" arguments).
-			 */
-			nargs = ((duk_hnatfunc *) func)->nargs;
-			nregs = nargs;
+			*out_nargs = ((duk_hnatfunc *) func)->nargs;
+			*out_nregs = *out_nargs;
+			if (*out_nargs >= 0) {
+				*out_vs_min_bytes = entry_valstack_bottom_byteoff +
+					sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nregs + DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
+			} else {
+				/* Vararg function. */
+				duk_size_t valstack_top_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack));
+				*out_vs_min_bytes = valstack_top_byteoff +
+					sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
+			}
 		}
 	} else {
 		duk_small_uint_t lf_flags;
-
-		DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
-		lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func);
-		nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
-		if (nargs == DUK_LFUNC_NARGS_VARARGS) {
-			nargs = -1;  /* vararg */
-		}
-		nregs = nargs;
+		duk_tval *tv_func;
 
 		act->flags |= DUK_ACT_FLAG_STRICT;
-	}
-
-	act->func = func;  /* NULL for lightfunc */
+
+		tv_func = DUK_GET_TVAL_POSIDX(thr, idx_func);
+		DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
+		DUK_TVAL_SET_TVAL(&act->tv_func, tv_func);  /* borrowed, no refcount */
+
+		lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv_func);
+		*out_nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
+		if (*out_nargs != DUK_LFUNC_NARGS_VARARGS) {
+			*out_vs_min_bytes = entry_valstack_bottom_byteoff +
+				sizeof(duk_tval) * ((duk_size_t) idx_func + 2U + (duk_size_t) *out_nargs + DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
+		} else {
+			duk_size_t valstack_top_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_top - ((duk_uint8_t *) thr->valstack));
+			*out_vs_min_bytes = valstack_top_byteoff +
+				sizeof(duk_tval) * (DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
+			*out_nargs = -1;  /* vararg */
+		}
+		*out_nregs = *out_nargs;
+	}
+
 	act->var_env = NULL;
 	act->lex_env = NULL;
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
@@ -61089,20 +62829,15 @@
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 	act->prev_line = 0;
 #endif
-	act->idx_bottom = entry_valstack_bottom_index + idx_func + 2;
-#if 0  /* topmost activation idx_retval is considered garbage, no need to init */
-	act->idx_retval = 0;
-#endif
-	DUK_TVAL_SET_TVAL(&act->tv_func, tv_func);  /* borrowed, no refcount */
-
-	/* XXX: remove the preventcount and make yield walk the callstack?
-	 * Or perhaps just use a single flag, not a counter, faster to just
-	 * set and restore?
-	 */
-	if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
-		/* duk_hthread_callstack_unwind() will decrease this on unwind */
-		thr->callstack_preventcount++;
-	}
+	act->bottom_byteoff = entry_valstack_bottom_byteoff + sizeof(duk_tval) * ((duk_size_t) idx_func + 2U);
+#if 0
+	act->retval_byteoff = 0;   /* topmost activation retval_byteoff is considered garbage, no need to init */
+#endif
+	/* Filled in when final reserve is known, dummy value doesn't matter
+	 * even in error unwind because reserve_byteoff is only used when
+	 * returning to -this- activation.
+	 */
+	act->reserve_byteoff = 0;  /* filled in by caller */
 
 	/* XXX: Is this INCREF necessary? 'func' is always a borrowed
 	 * reference reachable through the value stack?  If changed, stack
@@ -61113,9 +62848,302 @@
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
 	if (func) {
 		duk__update_func_caller_prop(thr, func);
-		act = thr->callstack_curr;
-	}
-#endif
+	}
+#endif
+}
+
+/*
+ *  Environment setup.
+ */
+
+DUK_LOCAL void duk__call_env_setup(duk_hthread *thr, duk_hobject *func, duk_activation *act, duk_idx_t idx_args) {
+	duk_hobject *env;
+
+	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound function has already been resolved */
+
+	if (DUK_LIKELY(func != NULL)) {
+		if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) {
+			if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) {
+				/* Use a new environment but there's no 'arguments' object;
+				 * delayed environment initialization.  This is the most
+				 * common case.
+				 */
+				DUK_ASSERT(act->lex_env == NULL);
+				DUK_ASSERT(act->var_env == NULL);
+			} else {
+				/* Use a new environment and there's an 'arguments' object.
+				 * We need to initialize it right now.
+				 */
+
+				/* third arg: absolute index (to entire valstack) of bottom_byteoff of new activation */
+				env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff);
+				DUK_ASSERT(env != NULL);
+
+				/* [ ... func this arg1 ... argN envobj ] */
+
+				DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
+				duk__handle_createargs_for_call(thr, func, env, idx_args);
+
+				/* [ ... func this arg1 ... argN envobj ] */
+
+				act->lex_env = env;
+				act->var_env = env;
+				DUK_HOBJECT_INCREF(thr, env);
+				DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (2) directly */
+				duk_pop(thr);
+			}
+		} else {
+			/* Use existing env (e.g. for non-strict eval); cannot have
+			 * an own 'arguments' object (but can refer to an existing one).
+			 */
+
+			DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));
+
+			duk__handle_oldenv_for_call(thr, func, act);
+
+			DUK_ASSERT(act->lex_env != NULL);
+			DUK_ASSERT(act->var_env != NULL);
+		}
+	} else {
+		/* Lightfuncs are always native functions and have "newenv". */
+		DUK_ASSERT(act->lex_env == NULL);
+		DUK_ASSERT(act->var_env == NULL);
+	}
+}
+
+/*
+ *  Misc shared helpers.
+ */
+
+/* Check thread state, update current thread. */
+DUK_LOCAL void duk__call_thread_state_update(duk_hthread *thr) {
+	DUK_ASSERT(thr != NULL);
+
+	if (DUK_LIKELY(thr == thr->heap->curr_thread)) {
+		if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_RUNNING)) {
+			/* Should actually never happen, but check anyway. */
+			goto thread_state_error;
+		}
+	} else {
+		DUK_ASSERT(thr->heap->curr_thread == NULL ||
+		           thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING);
+		if (DUK_UNLIKELY(thr->state != DUK_HTHREAD_STATE_INACTIVE)) {
+			goto thread_state_error;
+		}
+		DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
+		thr->state = DUK_HTHREAD_STATE_RUNNING;
+
+		/* Multiple threads may be simultaneously in the RUNNING
+		 * state, but not in the same "resume chain".
+		 */
+	}
+	DUK_ASSERT(thr->heap->curr_thread == thr);
+	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
+	return;
+
+ thread_state_error:
+	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state (%ld)", (long) thr->state);
+	DUK_UNREACHABLE();
+}
+
+/*
+ *  Main unprotected call handler, handles:
+ *
+ *    - All combinations of native/Ecmascript caller and native/Ecmascript
+ *      target.
+ *
+ *    - Optimized Ecmascript-to-Ecmascript call where call handling only
+ *      sets up a new duk_activation but reuses an existing bytecode executor
+ *      (the caller) without native recursion.
+ *
+ *    - Tailcalls, where an activation is reused without increasing call
+ *      stack (duk_activation) depth.
+ *
+ *    - Setup for an initial Duktape.Thread.resume().
+ *
+ *  The call handler doesn't provide any protection guarantees, protected calls
+ *  must be implemented e.g. by wrapping the call in a duk_safe_call().
+ *  Call setup may fail at any stage, even when the new activation is in
+ *  place; the only guarantee is that the state is consistent for unwinding.
+ */
+
+DUK_LOCAL duk_int_t duk__handle_call_raw(duk_hthread *thr,
+                                         duk_idx_t idx_func,
+                                         duk_small_uint_t call_flags) {
+#if defined(DUK_USE_ASSERTIONS)
+	duk_activation *entry_act;
+	duk_size_t entry_callstack_top;
+#endif
+	duk_size_t entry_valstack_bottom_byteoff;
+	duk_size_t entry_valstack_end_byteoff;
+	duk_int_t entry_call_recursion_depth;
+	duk_hthread *entry_curr_thread;
+	duk_uint_fast8_t entry_thread_state;
+	duk_instr_t **entry_ptr_curr_pc;
+	duk_idx_t idx_args;
+	duk_idx_t nargs;            /* # argument registers target function wants (< 0 => "as is") */
+	duk_idx_t nregs;            /* # total registers target function wants on entry (< 0 => "as is") */
+	duk_size_t vs_min_bytes;    /* minimum value stack size (bytes) for handling call */
+	duk_hobject *func;          /* 'func' on stack (borrowed reference) */
+	duk_activation *act;
+	duk_ret_t rc;
+	duk_small_uint_t use_tailcall;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(thr->heap != NULL);
+	/* Asserts for heap->curr_thread omitted: it may be NULL, 'thr', or
+	 * any other thread (e.g. when heap thread is used to run finalizers).
+	 */
+	DUK_ASSERT_CTX_VALID(thr);
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+	DUK_ASSERT(idx_func >= 0);
+
+	DUK_STATS_INC(thr->heap, stats_call_all);
+
+	/* If a tail call:
+	 *   - an Ecmascript activation must be on top of the callstack
+	 *   - there cannot be any catch stack entries that would catch
+	 *     a return
+	 */
+#if defined(DUK_USE_ASSERTIONS)
+	if (call_flags & DUK_CALL_FLAG_TAILCALL) {
+		duk_activation *tmp_act;
+		duk_catcher *tmp_cat;
+
+		DUK_ASSERT(thr->callstack_top >= 1);
+		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
+		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
+
+		/* No entry in the catch stack which would actually catch a
+		 * throw can refer to the callstack entry being reused.
+		 * There *can* be catch stack entries referring to the current
+		 * callstack entry as long as they don't catch (e.g. label sites).
+		 */
+
+		tmp_act = thr->callstack_curr;
+		for (tmp_cat = tmp_act->cat; tmp_cat != NULL; tmp_cat = tmp_cat->parent) {
+			DUK_ASSERT(DUK_CAT_GET_TYPE(tmp_cat) == DUK_CAT_TYPE_LABEL); /* a non-catching entry */
+		}
+	}
+#endif  /* DUK_USE_ASSERTIONS */
+
+	/*
+	 *  Store entry state.
+	 */
+
+#if defined(DUK_USE_ASSERTIONS)
+	entry_act = thr->callstack_curr;
+	entry_callstack_top = thr->callstack_top;
+#endif
+	entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
+	entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
+	entry_call_recursion_depth = thr->heap->call_recursion_depth;
+	entry_curr_thread = thr->heap->curr_thread;  /* may be NULL if first call */
+	entry_thread_state = thr->state;
+	entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
+
+	/* If thr->ptr_curr_pc is set, sync curr_pc to act->pc.  Then NULL
+	 * thr->ptr_curr_pc so that it's not accidentally used with an incorrect
+	 * activation when side effects occur.
+	 */
+	duk_hthread_sync_and_null_currpc(thr);
+	DUK_ASSERT(thr->ptr_curr_pc == NULL);
+
+	DUK_DD(DUK_DDPRINT("duk__handle_call_raw: thr=%p, idx_func=%ld, "
+	                   "call_flags=0x%08lx (constructor=%ld), "
+	                   "valstack_top=%ld, idx_func=%ld, idx_args=%ld, rec_depth=%ld/%ld, "
+	                   "entry_valstack_bottom_byteoff=%ld, entry_valstack_end_byteoff=%ld, "
+	                   "entry_call_recursion_depth=%ld, "
+	                   "entry_curr_thread=%p, entry_thread_state=%ld",
+	                   (void *) thr,
+	                   (long) idx_func,
+	                   (unsigned long) call_flags,
+	                   (long) ((call_flags & DUK_CALL_FLAG_CONSTRUCT) != 0 ? 1 : 0),
+	                   (long) duk_get_top(thr),
+	                   (long) idx_func,
+	                   (long) (idx_func + 2),
+	                   (long) thr->heap->call_recursion_depth,
+	                   (long) thr->heap->call_recursion_limit,
+	                   (long) entry_valstack_bottom_byteoff,
+	                   (long) entry_valstack_end_byteoff,
+	                   (long) entry_call_recursion_depth,
+	                   (void *) entry_curr_thread,
+	                   (long) entry_thread_state));
+
+	/*
+	 *  Thread state check and book-keeping.
+	 */
+
+	duk__call_thread_state_update(thr);
+
+	/*
+	 *  Resolve final target function; handle bound functions and special
+	 *  functions like .call() and .apply().  Also figure out the effective
+	 *  'this' binding, which replaces the current value at idx_func + 1.
+	 */
+
+	if (DUK_LIKELY(duk__resolve_target_fastpath_check(thr, idx_func, &func, call_flags) != 0U)) {
+		DUK_DDD(DUK_DDDPRINT("fast path target resolve"));
+	} else {
+		DUK_DDD(DUK_DDDPRINT("slow path target resolve"));
+		func = duk__resolve_target_func_and_this_binding(thr, idx_func, &call_flags);
+	}
+	DUK_ASSERT(duk_get_top(thr) - idx_func >= 2);  /* at least func and this present */
+
+	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
+	DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) ||
+	                            DUK_HOBJECT_IS_NATFUNC(func)));
+
+	/* [ ... func this arg1 ... argN ] */
+
+	/*
+	 *  Setup a preliminary activation and figure out nargs/nregs and
+	 *  value stack minimum size.
+	 *
+	 *  Don't touch valstack_bottom or valstack_top yet so that Duktape API
+	 *  calls work normally.
+	 *
+	 *  Because 'act' is not zeroed, all fields must be filled in.
+	 */
+
+#if defined(DUK_USE_TAILCALL)
+	use_tailcall = (call_flags & DUK_CALL_FLAG_TAILCALL);
+	if (use_tailcall) {
+		use_tailcall = duk__call_setup_act_attempt_tailcall(thr,
+		                                                    call_flags,
+		                                                    idx_func,
+		                                                    func,
+		                                                    entry_valstack_bottom_byteoff,
+		                                                    entry_valstack_end_byteoff,
+		                                                    &nargs,
+		                                                    &nregs,
+		                                                    &vs_min_bytes,
+		                                                    &act);
+	}
+#else
+	DUK_ASSERT((call_flags & DUK_CALL_FLAG_TAILCALL) == 0);  /* compiler ensures this */
+	use_tailcall = 0;
+#endif
+
+	if (use_tailcall) {
+		idx_args = 0;
+		DUK_STATS_INC(thr->heap, stats_call_tailcall);
+	} else {
+		duk__call_setup_act_not_tailcall(thr,
+		                                 call_flags,
+		                                 idx_func,
+		                                 func,
+		                                 entry_valstack_bottom_byteoff,
+		                                 entry_valstack_end_byteoff,
+		                                 &nargs,
+		                                 &nregs,
+		                                 &vs_min_bytes,
+		                                 &act);
+		idx_args = idx_func + 2;
+	}
+	/* After this point idx_func is no longer valid for tailcalls. */
+
+	DUK_ASSERT(act != NULL);
 
 	/* [ ... func this arg1 ... argN ] */
 
@@ -61132,103 +63160,73 @@
 	 *  Delayed creation (on demand) is handled in duk_js_var.c.
 	 */
 
-	DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound function chain has already been resolved */
-
-	if (DUK_LIKELY(func != NULL)) {
-		if (DUK_LIKELY(DUK_HOBJECT_HAS_NEWENV(func))) {
-			if (DUK_LIKELY(!DUK_HOBJECT_HAS_CREATEARGS(func))) {
-				/* Use a new environment but there's no 'arguments' object;
-				 * delayed environment initialization.  This is the most
-				 * common case.
-				 */
-				DUK_ASSERT(act->lex_env == NULL);
-				DUK_ASSERT(act->var_env == NULL);
-			} else {
-				/* Use a new environment and there's an 'arguments' object.
-				 * We need to initialize it right now.
-				 */
-
-				/* third arg: absolute index (to entire valstack) of idx_bottom of new activation */
-				env = duk_create_activation_environment_record(thr, func, act->idx_bottom);
-				DUK_ASSERT(env != NULL);
-
-				/* [ ... func this arg1 ... argN envobj ] */
-
-				DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
-				duk__handle_createargs_for_call(thr, func, env, num_stack_args);
-
-				/* [ ... func this arg1 ... argN envobj ] */
-
-				act = thr->callstack_curr;
-				act->lex_env = env;
-				act->var_env = env;
-				DUK_HOBJECT_INCREF(thr, env);
-				DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (2) directly */
-				duk_pop(ctx);
-			}
-		} else {
-			/* Use existing env (e.g. for non-strict eval); cannot have
-			 * an own 'arguments' object (but can refer to an existing one).
-			 */
-
-			DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(func));
-
-			duk__handle_oldenv_for_call(thr, func, act);
-			/* No need to re-lookup 'act' at present: no side effects. */
-
-			DUK_ASSERT(act->lex_env != NULL);
-			DUK_ASSERT(act->var_env != NULL);
-		}
-	} else {
-		/* Lightfuncs are always native functions and have "newenv". */
-		DUK_ASSERT(act->lex_env == NULL);
-		DUK_ASSERT(act->var_env == NULL);
-	}
+	duk__call_env_setup(thr, func, act, idx_args);
 
 	/* [ ... func this arg1 ... argN ] */
 
 	/*
-	 *  Setup value stack: clamp to 'nargs', fill up to 'nregs'
-	 *
-	 *  Value stack may either grow or shrink, depending on the
-	 *  number of func registers and the number of actual arguments.
-	 *  If nregs >= 0, func wants args clamped to 'nargs'; else it
-	 *  wants all args (= 'num_stack_args').
-	 */
-
-	/* XXX: optimize value stack operation */
-	/* XXX: don't want to shrink allocation here */
-
-	duk__adjust_valstack_and_top(thr,
-	                             num_stack_args,
-	                             idx_func + 2,
-	                             nregs,
-	                             nargs,
-	                             func);
-
-	/*
-	 *  Determine call type, then finalize activation, shift to
-	 *  new value stack bottom, and call the target.
-	 */
-
-	act = thr->callstack_curr;
+	 *  Setup value stack: clamp to 'nargs', fill up to 'nregs',
+	 *  ensure value stack size matches target requirements, and
+	 *  switch value stack bottom.  Valstack top is kept.
+	 *
+	 *  Value stack can only grow here.
+	 */
+
+	duk_valstack_grow_check_throw(thr, vs_min_bytes);
+	act->reserve_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
+
+	if (use_tailcall) {
+		DUK_ASSERT(nregs >= 0);
+		DUK_ASSERT(nregs >= nargs);
+		duk_set_top_and_wipe(thr, nregs, nargs);
+	} else {
+		if (nregs >= 0) {
+			DUK_ASSERT(nregs >= nargs);
+			duk_set_top_and_wipe(thr, idx_func + 2 + nregs, idx_func + 2 + nargs);
+		} else {
+			;
+		}
+		thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
+	}
+	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+
+	/*
+	 *  Make the actual call.  For Ecma-to-Ecma calls detect that
+	 *  setup is complete, then return with a status code that allows
+	 *  the caller to reuse the running executor.
+	 */
+
 	if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) {
 		/*
-		 *  Ecmascript call
-		 */
-
-		duk_tval *tv_ret;
-		duk_tval *tv_funret;
+		 *  Ecmascript call.
+		 */
 
 		DUK_ASSERT(func != NULL);
 		DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
 		act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);
 
-		thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
-		/* keep current valstack_top */
-		DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-		DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-		DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+		if (call_flags & DUK_CALL_FLAG_ALLOW_ECMATOECMA) {
+			DUK_DD(DUK_DDPRINT("avoid native call, use existing executor"));
+			DUK_STATS_INC(thr->heap, stats_call_ecmatoecma);
+			DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
+			DUK_REFZERO_CHECK_FAST(thr);
+			DUK_ASSERT(thr->ptr_curr_pc == NULL);
+			return 1;  /* 1=reuse executor */
+		}
+		DUK_ASSERT(use_tailcall == 0);
+
+		/* duk_hthread_activation_unwind_norz() will decrease this on unwind */
+		DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
+		act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
+		thr->callstack_preventcount++;
+
+		/* XXX: we could just do this on entry regardless of reuse, as long
+		 * as recursion depth is decreased for e2e case.
+		 */
+		duk__call_c_recursion_limit_check(thr);
+		thr->heap->call_recursion_depth++;
 
 		/* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */
 
@@ -61249,26 +63247,100 @@
 		DUK_DDD(DUK_DDDPRINT("entering bytecode execution"));
 		duk_js_execute_bytecode(thr);
 		DUK_DDD(DUK_DDDPRINT("returned from bytecode execution"));
-
-		/* Unwind. */
-
-		DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);  /* may need unwind */
-		DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
-		duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
-		duk_hthread_catchstack_shrink_check(thr);
-		duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);  /* XXX: may now fail */
-		duk_hthread_callstack_shrink_check(thr);
-
-		thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
-		/* keep current valstack_top */
-		DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-		DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-		DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-		DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1);
-
-		/* Return value handling. */
-
-		/* [ ... func this (crud) retval ] */
+	} else {
+		/*
+		 *  Native call.
+		 */
+
+		DUK_ASSERT(func == NULL || ((duk_hnatfunc *) func)->func != NULL);
+		DUK_ASSERT(use_tailcall == 0);
+
+		/* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */
+
+		/* duk_hthread_activation_unwind_norz() will decrease this on unwind */
+		DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
+		act->flags |= DUK_ACT_FLAG_PREVENT_YIELD;
+		thr->callstack_preventcount++;
+
+		/* XXX: we could just do this on entry regardless of reuse, as long
+		 * as recursion depth is decreased for e2e case.
+		 */
+		duk__call_c_recursion_limit_check(thr);
+		thr->heap->call_recursion_depth++;
+
+		/* For native calls must be NULL so we don't sync back */
+		DUK_ASSERT(thr->ptr_curr_pc == NULL);
+
+		/* XXX: native funcptr could come out of call setup. */
+		if (func) {
+			rc = ((duk_hnatfunc *) func)->func(thr);
+		} else {
+			duk_tval *tv_func;
+			duk_c_function funcptr;
+
+			tv_func = &act->tv_func;
+			DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv_func));
+			funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func);
+			rc = funcptr(thr);
+		}
+
+		/* Automatic error throwing, retval check. */
+
+		if (rc == 0) {
+			DUK_ASSERT(thr->valstack < thr->valstack_end);
+			DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));
+			thr->valstack_top++;
+		} else if (rc == 1) {
+			;
+		} else if (rc < 0) {
+			duk_error_throw_from_negative_rc(thr, rc);
+			DUK_UNREACHABLE();
+		} else {
+			DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC);
+		}
+	}
+	DUK_ASSERT(thr->ptr_curr_pc == NULL);
+	DUK_ASSERT(use_tailcall == 0);
+
+	/*
+	 *  Constructor call post processing.
+	 */
+
+#if defined(DUK_USE_ES6_PROXY)
+	if (call_flags & (DUK_CALL_FLAG_CONSTRUCT | DUK_CALL_FLAG_CONSTRUCT_PROXY)) {
+		duk_call_construct_postprocess(thr, call_flags & DUK_CALL_FLAG_CONSTRUCT_PROXY);
+	}
+#else
+	if (call_flags & DUK_CALL_FLAG_CONSTRUCT) {
+		duk_call_construct_postprocess(thr, 0);
+	}
+#endif
+
+	/*
+	 *  Unwind, restore valstack bottom and other book-keeping.
+	 */
+
+	DUK_ASSERT(thr->callstack_curr != NULL);
+	DUK_ASSERT(thr->callstack_curr->parent == entry_act);
+	DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
+	duk_hthread_activation_unwind_norz(thr);
+	DUK_ASSERT(thr->callstack_curr == entry_act);
+	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
+
+	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff);
+	/* keep current valstack_top */
+	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+	DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1);
+
+	/* Return value handling. */
+
+	/* [ ... func this (crud) retval ] */
+
+	{
+		duk_tval *tv_ret;
+		duk_tval *tv_funret;
 
 		tv_ret = thr->valstack_bottom + idx_func;
 		tv_funret = thr->valstack_top - 1;
@@ -61277,95 +63349,20 @@
 		DUK_TVAL_CHKFAST_INPLACE_FAST(tv_funret);
 #endif
 		DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret);  /* side effects */
-	} else {
-		/*
-		 *  Native call.
-		 */
-
-		duk_tval *tv_ret;
-		duk_tval *tv_funret;
-
-		thr->valstack_bottom = thr->valstack_bottom + idx_func + 2;
-		/* keep current valstack_top */
-		DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-		DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-		DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-		DUK_ASSERT(func == NULL || ((duk_hnatfunc *) func)->func != NULL);
-
-		/* [ ... func this | arg1 ... argN ] ('this' must precede new bottom) */
-
-		/* For native calls must be NULL so we don't sync back */
-		DUK_ASSERT(thr->ptr_curr_pc == NULL);
-
-		if (func) {
-			rc = ((duk_hnatfunc *) func)->func((duk_context *) thr);
-		} else {
-			duk_c_function funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func);
-			rc = funcptr((duk_context *) thr);
-		}
-
-		/* Automatic error throwing, retval check. */
-
-		if (rc < 0) {
-			duk_error_throw_from_negative_rc(thr, rc);
-			DUK_UNREACHABLE();
-		} else if (rc > 1) {
-			DUK_ERROR_TYPE(thr, "c function returned invalid rc");
-		}
-		DUK_ASSERT(rc == 0 || rc == 1);
-
-		/* Unwind. */
-
-		DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);  /* no need to unwind */
-		DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
-		duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
-		duk_hthread_callstack_shrink_check(thr);
-
-		thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
-		/* keep current valstack_top */
-		DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-		DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-		DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-		DUK_ASSERT(thr->valstack_top - thr->valstack_bottom >= idx_func + 1);
-
-		/* Return value handling. */
-
-		/* XXX: should this happen in the callee's activation or after unwinding? */
-		tv_ret = thr->valstack_bottom + idx_func;
-		if (rc == 0) {
-			DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv_ret);  /* side effects */
-		} else {
-			/* [ ... func this (crud) retval ] */
-			tv_funret = thr->valstack_top - 1;
-#if defined(DUK_USE_FASTINT)
-			/* Explicit check for fastint downgrade. */
-			DUK_TVAL_CHKFAST_INPLACE_FAST(tv_funret);
-#endif
-			DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret);  /* side effects */
-		}
-	}
-
-	duk_set_top(ctx, idx_func + 1);  /* XXX: unnecessary, handle in adjust */
+	}
+
+	duk_set_top_unsafe(thr, idx_func + 1);
 
 	/* [ ... retval ] */
 
-	/* Ensure there is internal valstack spare before we exit; this may
-	 * throw an alloc error.  The same guaranteed size must be available
-	 * as before the call.  This is not optimal now: we store the valstack
-	 * allocated size during entry; this value may be higher than the
-	 * minimal guarantee for an application.
-	 */
-
-	/* XXX: we should never shrink here; when we error out later, we'd
-	 * need to potentially grow the value stack in error unwind which could
-	 * cause another error.
-	 */
-
-	(void) duk_valstack_resize_raw((duk_context *) thr,
-	                               entry_valstack_end,                    /* same as during entry */
-	                               DUK_VSRESIZE_FLAG_SHRINK |             /* flags */
-	                               DUK_VSRESIZE_FLAG_COMPACT |
-	                               DUK_VSRESIZE_FLAG_THROW);
+	/* Restore caller's value stack reserve (cannot fail). */
+	DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff >= (duk_uint8_t *) thr->valstack_top);
+	DUK_ASSERT((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff <= (duk_uint8_t *) thr->valstack_alloc_end);
+	thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_end_byteoff);
+
+	/* XXX: Trial value stack shrink would be OK here, but we'd need
+	 * to prevent side effects of the potential realloc.
+	 */
 
 	/* Restore entry thread executor curr_pc stack frame pointer. */
 	thr->ptr_curr_pc = entry_ptr_curr_pc;
@@ -61407,162 +63404,29 @@
 	/* Restored by success path. */
 	DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
 	DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
-
 	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
 
 	DUK_REFZERO_CHECK_FAST(thr);
 
-	return;
-
- thread_state_error:
-	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for call (%ld)", (long) thr->state);
-	DUK_UNREACHABLE();
-	return;  /* never executed */
-}
-
-DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
-                                      duk_size_t entry_valstack_bottom_index,
-                                      duk_size_t entry_valstack_end,
-                                      duk_size_t entry_catchstack_top,
-                                      duk_size_t entry_callstack_top,
-                                      duk_int_t entry_call_recursion_depth,
-                                      duk_hthread *entry_curr_thread,
-                                      duk_uint_fast8_t entry_thread_state,
-                                      duk_instr_t **entry_ptr_curr_pc,
-                                      duk_idx_t idx_func,
-                                      duk_jmpbuf *old_jmpbuf_ptr) {
-	duk_context *ctx;
-	duk_tval *tv_ret;
-
-	ctx = (duk_context *) thr;
-
-	DUK_DDD(DUK_DDDPRINT("error caught during duk__handle_call_inner(): %!T",
-	                     (duk_tval *) &thr->heap->lj.value1));
-
-	/* Other longjmp types are handled by executor before propagating
-	 * the error here.
-	 */
-	DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
-	DUK_ASSERT_LJSTATE_SET(thr->heap);
-	DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-	DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
-
-	/* We don't need to sync back thr->ptr_curr_pc here because
-	 * the bytecode executor always has a setjmp catchpoint which
-	 * does that before errors propagate to here.
-	 */
-	DUK_ASSERT(thr->ptr_curr_pc == NULL);
-
-	/* Restore the previous setjmp catcher so that any error in
-	 * error handling will propagate outwards rather than re-enter
-	 * the same handler.  However, the error handling path must be
-	 * designed to be error free so that sandboxing guarantees are
-	 * reliable, see e.g. https://github.com/svaarala/duktape/issues/476.
-	 */
-	thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-	/* XXX: callstack unwind may now throw an error when closing
-	 * scopes; this is a sandboxing issue, described in:
-	 * https://github.com/svaarala/duktape/issues/476
-	 */
-	duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
-	duk_hthread_catchstack_shrink_check(thr);
-	duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
-	duk_hthread_callstack_shrink_check(thr);
-
-	thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
-	tv_ret = thr->valstack_bottom + idx_func;  /* XXX: byte offset? */
-	DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, &thr->heap->lj.value1);  /* side effects */
-#if defined(DUK_USE_FASTINT)
-	/* Explicit check for fastint downgrade. */
-	DUK_TVAL_CHKFAST_INPLACE_FAST(tv_ret);
-#endif
-	duk_set_top(ctx, idx_func + 1);  /* XXX: could be eliminated with valstack adjust */
-
-	/* [ ... errobj ] */
-
-	/* Ensure there is internal valstack spare before we exit; this may
-	 * throw an alloc error.  The same guaranteed size must be available
-	 * as before the call.  This is not optimal now: we store the valstack
-	 * allocated size during entry; this value may be higher than the
-	 * minimal guarantee for an application.
-	 */
-
-	/* XXX: this needs to be reworked so that we never shrink the value
-	 * stack on function entry so that we never need to grow it here.
-	 * Needing to grow here is a sandboxing issue because we need to
-	 * allocate which may cause an error in the error handling path
-	 * and thus propagate an error out of a protected call.
-	 */
-
-	(void) duk_valstack_resize_raw((duk_context *) thr,
-	                               entry_valstack_end,                    /* same as during entry */
-	                               DUK_VSRESIZE_FLAG_SHRINK |             /* flags */
-	                               DUK_VSRESIZE_FLAG_COMPACT |
-	                               DUK_VSRESIZE_FLAG_THROW);
-
-
-	/* These are just convenience "wiping" of state.  Side effects should
-	 * not be an issue here: thr->heap and thr->heap->lj have a stable
-	 * pointer.  Finalizer runs etc capture even out-of-memory errors so
-	 * nothing should throw here.
-	 */
-	thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
-	thr->heap->lj.iserror = 0;
-	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1);  /* side effects */
-	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2);  /* side effects */
-
-	/* Restore entry thread executor curr_pc stack frame pointer. */
-	thr->ptr_curr_pc = entry_ptr_curr_pc;
-
-	DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
-	thr->state = (duk_uint8_t) entry_thread_state;
-
-	/* Disabled assert: triggered with some torture tests. */
-#if 0
-	DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) ||  /* first call */
-	           (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) ||  /* other call */
-	           (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr));     /* current thread */
-#endif
-
-	thr->heap->call_recursion_depth = entry_call_recursion_depth;
-
-	/* If the debugger is active we need to force an interrupt so that
-	 * debugger breakpoints are rechecked.  This is important for function
-	 * calls caused by side effects (e.g. when doing a DUK_OP_GETPROP), see
-	 * GH-303.  Only needed for success path, error path always causes a
-	 * breakpoint recheck in the executor.  It would be enough to set this
-	 * only when returning to an Ecmascript activation, but setting the flag
-	 * on every return should have no ill effect.
-	 */
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-	if (duk_debug_is_attached(thr->heap)) {
-		DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt"));
-		DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
-		thr->interrupt_init -= thr->interrupt_counter;
-		thr->interrupt_counter = 0;
-		thr->heap->dbg_force_restart = 1;
-	}
-#endif
-
-#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
-	duk__interrupt_fixup(thr, entry_curr_thread);
-#endif
-
-	/* Error handling complete, remove side effect protections and
-	 * process pending finalizers.
-	 */
-#if defined(DUK_USE_ASSERTIONS)
-	DUK_ASSERT(thr->heap->error_not_allowed == 1);
-	thr->heap->error_not_allowed = 0;
-#endif
-	DUK_ASSERT(thr->heap->pf_prevent_count > 0);
-	thr->heap->pf_prevent_count--;
-	DUK_DD(DUK_DDPRINT("call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count));
-
-	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
-
-	DUK_REFZERO_CHECK_SLOW(thr);
+	return 0;  /* 0=call handled inline */
+}
+
+DUK_INTERNAL duk_int_t duk_handle_call_unprotected_nargs(duk_hthread *thr,
+                                                         duk_idx_t nargs,
+                                                         duk_small_uint_t call_flags) {
+	duk_idx_t idx_func;
+	DUK_ASSERT(duk_get_top(thr) >= nargs + 2);
+	idx_func = duk_get_top(thr) - (nargs + 2);
+	DUK_ASSERT(idx_func >= 0);
+	return duk_handle_call_unprotected(thr, idx_func, call_flags);
+}
+
+DUK_INTERNAL duk_int_t duk_handle_call_unprotected(duk_hthread *thr,
+                                                   duk_idx_t idx_func,
+                                                   duk_small_uint_t call_flags) {
+	DUK_ASSERT(duk_is_valid_index(thr, idx_func));
+	DUK_ASSERT(idx_func >= 0);
+	return duk__handle_call_raw(thr, idx_func, call_flags);
 }
 
 /*
@@ -61570,247 +63434,52 @@
  *  current activation.
  *
  *  The allowed thread states for making a call are the same as for
- *  duk_handle_call_xxx().
- *
- *  Error handling is similar to duk_handle_call_xxx(); errors may be thrown
- *  (and result in a fatal error) for insane arguments.
- */
-
-/* XXX: bump preventcount by one for the duration of this call? */
-
-DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
-                                            duk_safe_call_function func,
-                                            void *udata,
-                                            duk_idx_t num_stack_args,
-                                            duk_idx_t num_stack_rets) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_size_t entry_valstack_bottom_index;
-	duk_size_t entry_callstack_top;
-	duk_size_t entry_catchstack_top;
-	duk_int_t entry_call_recursion_depth;
-	duk_hthread *entry_curr_thread;
-	duk_uint_fast8_t entry_thread_state;
-	duk_instr_t **entry_ptr_curr_pc;
-	duk_jmpbuf *old_jmpbuf_ptr = NULL;
-	duk_jmpbuf our_jmpbuf;
-	duk_idx_t idx_retbase;
-	duk_int_t retval;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
-
-	/* Note: careful with indices like '-x'; if 'x' is zero, it refers to bottom */
-	entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack);
-	entry_callstack_top = thr->callstack_top;
-	entry_catchstack_top = thr->catchstack_top;
-	entry_call_recursion_depth = thr->heap->call_recursion_depth;
-	entry_curr_thread = thr->heap->curr_thread;  /* Note: may be NULL if first call */
-	entry_thread_state = thr->state;
-	entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
-	idx_retbase = duk_get_top(ctx) - num_stack_args;  /* Note: not a valid stack index if num_stack_args == 0 */
-
-	/* Note: cannot portably debug print a function pointer, hence 'func' not printed! */
-	DUK_DD(DUK_DDPRINT("duk_handle_safe_call: thr=%p, num_stack_args=%ld, num_stack_rets=%ld, "
-	                   "valstack_top=%ld, idx_retbase=%ld, rec_depth=%ld/%ld, "
-	                   "entry_valstack_bottom_index=%ld, entry_callstack_top=%ld, entry_catchstack_top=%ld, "
-	                   "entry_call_recursion_depth=%ld, entry_curr_thread=%p, entry_thread_state=%ld",
-	                   (void *) thr,
-	                   (long) num_stack_args,
-	                   (long) num_stack_rets,
-	                   (long) duk_get_top(ctx),
-	                   (long) idx_retbase,
-	                   (long) thr->heap->call_recursion_depth,
-	                   (long) thr->heap->call_recursion_limit,
-	                   (long) entry_valstack_bottom_index,
-	                   (long) entry_callstack_top,
-	                   (long) entry_catchstack_top,
-	                   (long) entry_call_recursion_depth,
-	                   (void *) entry_curr_thread,
-	                   (long) entry_thread_state));
-
-	if (idx_retbase < 0) {
-		/* Since stack indices are not reliable, we can't do anything useful
-		 * here.  Invoke the existing setjmp catcher, or if it doesn't exist,
-		 * call the fatal error handler.
-		 */
-
-		DUK_ERROR_TYPE_INVALID_ARGS(thr);
-	}
-
-	/* setjmp catchpoint setup */
-
-	old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
-	thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;
-
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	try {
-#else
-	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
-	if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
-		/* Success path. */
-#endif
-		DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete"));
-
-		duk__handle_safe_call_inner(thr,
-		                            func,
-		                            udata,
-		                            idx_retbase,
-		                            num_stack_rets,
-		                            entry_valstack_bottom_index,
-		                            entry_callstack_top,
-		                            entry_catchstack_top);
-
-		/* Note: either pointer may be NULL (at entry), so don't assert */
-		thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
-
-		retval = DUK_EXEC_SUCCESS;
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	} catch (duk_internal_exception &exc) {
-		DUK_UNREF(exc);
-#else
-	} else {
-		/* Error path. */
-#endif
-		duk__handle_safe_call_error(thr,
-		                            idx_retbase,
-		                            num_stack_rets,
-		                            entry_valstack_bottom_index,
-		                            entry_callstack_top,
-		                            entry_catchstack_top,
-		                            old_jmpbuf_ptr);
-
-		retval = DUK_EXEC_ERROR;
-	}
-#if defined(DUK_USE_CPP_EXCEPTIONS)
-	catch (std::exception &exc) {
-		const char *what = exc.what();
-		if (!what) {
-			what = "unknown";
-		}
-		DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
-		try {
-			DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
-		} catch (duk_internal_exception exc) {
-			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
-			DUK_UNREF(exc);
-			duk__handle_safe_call_error(thr,
-			                            idx_retbase,
-			                            num_stack_rets,
-			                            entry_valstack_bottom_index,
-			                            entry_callstack_top,
-			                            entry_catchstack_top,
-			                            old_jmpbuf_ptr);
-			retval = DUK_EXEC_ERROR;
-		}
-	} catch (...) {
-		DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
-		try {
-			DUK_ERROR_TYPE(thr, "caught invalid c++ exception (perhaps thrown by user code)");
-		} catch (duk_internal_exception exc) {
-			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
-			DUK_UNREF(exc);
-			duk__handle_safe_call_error(thr,
-			                            idx_retbase,
-			                            num_stack_rets,
-			                            entry_valstack_bottom_index,
-			                            entry_callstack_top,
-			                            entry_catchstack_top,
-			                            old_jmpbuf_ptr);
-			retval = DUK_EXEC_ERROR;
-		}
-	}
-#endif
-
-	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr);  /* success/error path both do this */
-
-	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
-
-	duk__handle_safe_call_shared(thr,
-	                             idx_retbase,
-	                             num_stack_rets,
-	                             entry_call_recursion_depth,
-	                             entry_curr_thread,
-	                             entry_thread_state,
-	                             entry_ptr_curr_pc);
-
-	return retval;
-}
+ *  duk_handle_call_protected().
+ *
+ *  Even though this call is protected, errors are thrown for insane arguments
+ *  and may result in a fatal error unless there's another protected call which
+ *  catches such errors.
+ *
+ *  The error handling path should be error free, even for out-of-memory
+ *  errors, to ensure safe sandboxing.  (As of Duktape 2.2.0 this is not
+ *  yet the case for environment closing which may run out of memory, see
+ *  XXX notes below.)
+ */
 
 DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
                                            duk_safe_call_function func,
                                            void *udata,
+#if defined(DUK_USE_ASSERTIONS)
+                                           duk_size_t entry_valstack_bottom_byteoff,
+                                           duk_size_t entry_callstack_top,
+#endif
+                                           duk_hthread *entry_curr_thread,
+                                           duk_uint_fast8_t entry_thread_state,
                                            duk_idx_t idx_retbase,
-                                           duk_idx_t num_stack_rets,
-                                           duk_size_t entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top) {
-	duk_context *ctx;
+                                           duk_idx_t num_stack_rets) {
 	duk_ret_t rc;
 
 	DUK_ASSERT(thr != NULL);
-	ctx = (duk_context *) thr;
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(entry_valstack_bottom_index);
-	DUK_UNREF(entry_callstack_top);
-	DUK_UNREF(entry_catchstack_top);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	/*
 	 *  Thread state check and book-keeping.
 	 */
 
-	if (thr == thr->heap->curr_thread) {
-		/* same thread */
-		if (thr->state != DUK_HTHREAD_STATE_RUNNING) {
-			/* should actually never happen, but check anyway */
-			goto thread_state_error;
-		}
-	} else {
-		/* different thread */
-		DUK_ASSERT(thr->heap->curr_thread == NULL ||
-		           thr->heap->curr_thread->state == DUK_HTHREAD_STATE_RUNNING);
-		if (thr->state != DUK_HTHREAD_STATE_INACTIVE) {
-			goto thread_state_error;
-		}
-		DUK_HEAP_SWITCH_THREAD(thr->heap, thr);
-		thr->state = DUK_HTHREAD_STATE_RUNNING;
-
-		/* Note: multiple threads may be simultaneously in the RUNNING
-		 * state, but not in the same "resume chain".
-		 */
-	}
-
-	DUK_ASSERT(thr->heap->curr_thread == thr);
-	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
+	duk__call_thread_state_update(thr);
 
 	/*
 	 *  Recursion limit check.
-	 *
-	 *  Note: there is no need for an "ignore recursion limit" flag
-	 *  for duk_handle_safe_call now.
-	 */
-
-	DUK_ASSERT(thr->heap->call_recursion_depth >= 0);
-	DUK_ASSERT(thr->heap->call_recursion_depth <= thr->heap->call_recursion_limit);
-	if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit) {
-		/* XXX: error message is a bit misleading: we reached a recursion
-		 * limit which is also essentially the same as a C callstack limit
-		 * (except perhaps with some relaxed threading assumptions).
-		 */
-		DUK_ERROR_RANGE(thr, DUK_STR_C_CALLSTACK_LIMIT);
-	}
+	 */
+
+	duk__call_c_recursion_limit_check(thr);
 	thr->heap->call_recursion_depth++;
 
 	/*
-	 *  Valstack spare check
-	 */
-
-	duk_require_stack(ctx, 0);  /* internal spare */
-
-	/*
-	 *  Make the C call
-	 */
-
-	rc = func(ctx, udata);
+	 *  Make the C call.
+	 */
+
+	rc = func(thr, udata);
 
 	DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc));
 
@@ -61820,48 +63489,35 @@
 
 	/* we're running inside the caller's activation, so no change in call/catch stack or valstack bottom */
 	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
-	DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);
 	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-	DUK_ASSERT((duk_size_t) (thr->valstack_bottom - thr->valstack) == entry_valstack_bottom_index);
+	DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == entry_valstack_bottom_byteoff);
 	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
 	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
 
-	if (rc < 0) {
+	if (DUK_UNLIKELY(rc < 0)) {
 		duk_error_throw_from_negative_rc(thr, rc);
 	}
 	DUK_ASSERT(rc >= 0);
 
-	if (duk_get_top(ctx) < rc) {
-		DUK_ERROR_RANGE(thr, "not enough stack values for safe_call rc");
-	}
-
-	DUK_ASSERT(thr->catchstack_top == entry_catchstack_top);  /* no need to unwind */
-	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
-
-	duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc);
-
-	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
-
-	DUK_REFZERO_CHECK_FAST(thr);
-	return;
-
- thread_state_error:
-	DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "invalid thread state for safe_call (%ld)", (long) thr->state);
-	DUK_UNREACHABLE();
+	duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc);  /* throws for insane rc */
+
+	DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
+	thr->state = (duk_uint8_t) entry_thread_state;
 }
 
 DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
+                                           duk_activation *entry_act,
+#if defined(DUK_USE_ASSERTIONS)
+                                           duk_size_t entry_callstack_top,
+#endif
+                                           duk_hthread *entry_curr_thread,
+                                           duk_uint_fast8_t entry_thread_state,
                                            duk_idx_t idx_retbase,
                                            duk_idx_t num_stack_rets,
-                                           duk_size_t entry_valstack_bottom_index,
-                                           duk_size_t entry_callstack_top,
-                                           duk_size_t entry_catchstack_top,
+                                           duk_size_t entry_valstack_bottom_byteoff,
                                            duk_jmpbuf *old_jmpbuf_ptr) {
-	duk_context *ctx;
-
-	DUK_ASSERT(thr != NULL);
-	ctx = (duk_context *) thr;
-	DUK_ASSERT_CTX_VALID(ctx);
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_CTX_VALID(thr);
 
 	/*
 	 *  Error during call.  The error value is at heap->lj.value1.
@@ -61878,51 +63534,56 @@
 	 */
 	DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
 	DUK_ASSERT_LJSTATE_SET(thr->heap);
-	DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-	DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
-
-	/* Note: either pointer may be NULL (at entry), so don't assert. */
+
+	/* Either pointer may be NULL (at entry), so don't assert. */
 	thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
 
-	DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
+	/* XXX: callstack unwind may now throw an error when closing
+	 * scopes; this is a sandboxing issue, described in:
+	 * https://github.com/svaarala/duktape/issues/476
+	 */
+	/* XXX: "unwind to" primitive? */
+
 	DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
-	duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
-	duk_hthread_catchstack_shrink_check(thr);
-	duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
-	duk_hthread_callstack_shrink_check(thr);
-	thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
+	while (thr->callstack_curr != entry_act) {
+		DUK_ASSERT(thr->callstack_curr != NULL);
+		duk_hthread_activation_unwind_norz(thr);
+	}
+	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
+
+	/* Switch active thread before any side effects to avoid a
+	 * dangling curr_thread pointer.
+	 */
+	DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
+	thr->state = (duk_uint8_t) entry_thread_state;
+
+	DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread);
+	DUK_ASSERT(thr->state == entry_thread_state);
+
+	/* Restore valstack bottom. */
+	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + entry_valstack_bottom_byteoff);
 
 	/* [ ... | (crud) ] */
 
-	/* XXX: space in valstack?  see discussion in duk_handle_call_xxx(). */
-	duk_push_tval(ctx, &thr->heap->lj.value1);
+	/* XXX: ensure space in valstack (now relies on internal reserve)? */
+	duk_push_tval(thr, &thr->heap->lj.value1);
 
 	/* [ ... | (crud) errobj ] */
 
-	DUK_ASSERT(duk_get_top(ctx) >= 1);  /* at least errobj must be on stack */
-
-	/* check that the valstack has space for the final amount and any
-	 * intermediate space needed; this is unoptimal but should be safe
-	 */
-	duk_require_stack_top(ctx, idx_retbase + num_stack_rets);  /* final configuration */
-	duk_require_stack(ctx, num_stack_rets);
+	DUK_ASSERT(duk_get_top(thr) >= 1);  /* at least errobj must be on stack */
 
 	duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, 1);  /* 1 = num actual 'return values' */
 
 	/* [ ... | ] or [ ... | errobj (M * undefined)] where M = num_stack_rets - 1 */
 
-	/* These are just convenience "wiping" of state.  Side effects should
-	 * not be an issue here: thr->heap and thr->heap->lj have a stable
-	 * pointer.  Finalizer runs etc capture even out-of-memory errors so
-	 * nothing should throw here.
-	 */
+	/* Reset longjmp state. */
 	thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN;
 	thr->heap->lj.iserror = 0;
-	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1);  /* side effects */
-	DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2);  /* side effects */
-
-	/* Error handling complete, remove side effect protections and
-	 * process pending finalizers.
+	DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value1);
+	DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, &thr->heap->lj.value2);
+
+	/* Error handling complete, remove side effect protections.  Caller
+	 * will process pending finalizers.
 	 */
 #if defined(DUK_USE_ASSERTIONS)
 	DUK_ASSERT(thr->heap->error_not_allowed == 1);
@@ -61932,47 +63593,39 @@
 	thr->heap->pf_prevent_count--;
 	DUK_DD(DUK_DDPRINT("safe call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count));
 
-	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
-
-	DUK_REFZERO_CHECK_SLOW(thr);
-}
-
-DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
-                                            duk_idx_t idx_retbase,
-                                            duk_idx_t num_stack_rets,
-                                            duk_int_t entry_call_recursion_depth,
-                                            duk_hthread *entry_curr_thread,
-                                            duk_uint_fast8_t entry_thread_state,
-                                            duk_instr_t **entry_ptr_curr_pc) {
-	duk_context *ctx;
-
-	DUK_ASSERT(thr != NULL);
-	ctx = (duk_context *) thr;
-	DUK_ASSERT_CTX_VALID(ctx);
-	DUK_UNREF(ctx);
+	/* thr->ptr_curr_pc is restored by
+	 * duk__handle_safe_call_shared_unwind() which is also used for
+	 * success path.
+	 */
+}
+
+DUK_LOCAL void duk__handle_safe_call_shared_unwind(duk_hthread *thr,
+                                                   duk_idx_t idx_retbase,
+                                                   duk_idx_t num_stack_rets,
+#if defined(DUK_USE_ASSERTIONS)
+                                                   duk_size_t entry_callstack_top,
+#endif
+                                                   duk_int_t entry_call_recursion_depth,
+                                                   duk_hthread *entry_curr_thread,
+                                                   duk_instr_t **entry_ptr_curr_pc) {
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT_CTX_VALID(thr);
 	DUK_UNREF(idx_retbase);
 	DUK_UNREF(num_stack_rets);
-
-	/* Restore entry thread executor curr_pc stack frame pointer. */
+	DUK_UNREF(entry_curr_thread);
+
+	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
+
+	/* Restore entry thread executor curr_pc stack frame pointer.
+	 * XXX: would be enough to do in error path only, should nest
+	 * cleanly in success path.
+	 */
 	thr->ptr_curr_pc = entry_ptr_curr_pc;
 
-	/* XXX: because we unwind stacks above, thr->heap->curr_thread is at
-	 * risk of pointing to an already freed thread.  This was indeed the
-	 * case in test-bug-multithread-valgrind.c, until duk_handle_call()
-	 * was fixed to restore thr->heap->curr_thread before rethrowing an
-	 * uncaught error.
-	 */
-	DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread);  /* may be NULL */
-	thr->state = (duk_uint8_t) entry_thread_state;
-
-	DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) ||  /* first call */
-	           (thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) ||  /* other call */
-	           (thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr));     /* current thread */
-
 	thr->heap->call_recursion_depth = entry_call_recursion_depth;
 
 	/* stack discipline consistency check */
-	DUK_ASSERT(duk_get_top(ctx) == idx_retbase + num_stack_rets);
+	DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets);
 
 	/* A debugger forced interrupt check is not needed here, as
 	 * problematic safe calls are not caused by side effects.
@@ -61981,479 +63634,293 @@
 #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
 	duk__interrupt_fixup(thr, entry_curr_thread);
 #endif
-
-	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
-}
-
-/*
- *  Helper for handling an Ecmascript-to-Ecmascript call or an Ecmascript
- *  function (initial) Duktape.Thread.resume().
- *
- *  Compared to normal calls handled by duk_handle_call(), there are a
- *  bunch of differences:
- *
- *    - the call is never protected
- *    - there is no C recursion depth increase (hence an "ignore recursion
- *      limit" flag is not applicable)
- *    - instead of making the call, this helper just performs the thread
- *      setup and returns; the bytecode executor then restarts execution
- *      internally
- *    - ecmascript functions are never 'vararg' functions (they access
- *      varargs through the 'arguments' object)
- *
- *  The callstack of the target contains an earlier Ecmascript call in case
- *  of an Ecmascript-to-Ecmascript call (whose idx_retval is updated), or
- *  is empty in case of an initial Duktape.Thread.resume().
- *
- *  The first thing to do here is to figure out whether an ecma-to-ecma
- *  call is actually possible.  It's not always the case if the target is
- *  a bound function; the final function may be native.  In that case,
- *  return an error so caller can fall back to a normal call path.
- */
-
-DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
-                                                   duk_idx_t num_stack_args,
-                                                   duk_small_uint_t call_flags) {
-	duk_context *ctx = (duk_context *) thr;
-	duk_size_t entry_valstack_bottom_index;
-	duk_idx_t idx_func;     /* valstack index of 'func' and retval (relative to entry valstack_bottom) */
-	duk_idx_t idx_args;     /* valstack index of start of args (arg1) (relative to entry valstack_bottom) */
-	duk_idx_t nargs;        /* # argument registers target function wants (< 0 => never for ecma calls) */
-	duk_idx_t nregs;        /* # total registers target function wants on entry (< 0 => never for ecma calls) */
-	duk_hobject *func;      /* 'func' on stack (borrowed reference) */
-	duk_tval *tv_func;      /* duk_tval ptr for 'func' on stack (borrowed reference) */
-	duk_activation *act;
-	duk_hobject *env;
-	duk_bool_t use_tailcall;
+}
+
+DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
+                                            duk_safe_call_function func,
+                                            void *udata,
+                                            duk_idx_t num_stack_args,
+                                            duk_idx_t num_stack_rets) {
+	duk_activation *entry_act;
+	duk_size_t entry_valstack_bottom_byteoff;
+#if defined(DUK_USE_ASSERTIONS)
+	duk_size_t entry_valstack_end_byteoff;
+	duk_size_t entry_callstack_top;
+	duk_size_t entry_callstack_preventcount;
+#endif
+	duk_int_t entry_call_recursion_depth;
+	duk_hthread *entry_curr_thread;
+	duk_uint_fast8_t entry_thread_state;
 	duk_instr_t **entry_ptr_curr_pc;
-
-	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
-	DUK_ASSERT(!((call_flags & DUK_CALL_FLAG_IS_RESUME) != 0 && (call_flags & DUK_CALL_FLAG_IS_TAILCALL) != 0));
-
-	/* XXX: assume these? */
-	DUK_ASSERT(thr->valstack != NULL);
-	DUK_ASSERT(thr->callstack != NULL);
-	DUK_ASSERT(thr->catchstack != NULL);
-
-	/* no need to handle thread state book-keeping here */
-	DUK_ASSERT((call_flags & DUK_CALL_FLAG_IS_RESUME) != 0 ||
-	           (thr->state == DUK_HTHREAD_STATE_RUNNING &&
-	            thr->heap->curr_thread == thr));
-
-	/* If thr->ptr_curr_pc is set, sync curr_pc to act->pc.  Then NULL
-	 * thr->ptr_curr_pc so that it's not accidentally used with an incorrect
-	 * activation when side effects occur.  If we end up not making the
-	 * call we must restore the value.
-	 */
-	entry_ptr_curr_pc = thr->ptr_curr_pc;
-	duk_hthread_sync_and_null_currpc(thr);
-
-	/* if a tail call:
-	 *   - an Ecmascript activation must be on top of the callstack
-	 *   - there cannot be any active catchstack entries
-	 */
-#if defined(DUK_USE_ASSERTIONS)
-	if (call_flags & DUK_CALL_FLAG_IS_TAILCALL) {
-		duk_size_t our_callstack_index;
-		duk_size_t i;
-
-		DUK_ASSERT(thr->callstack_top >= 1);
-		our_callstack_index = thr->callstack_top - 1;
-		DUK_ASSERT_DISABLE(our_callstack_index >= 0);
-		DUK_ASSERT(our_callstack_index < thr->callstack_size);
-		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + our_callstack_index) != NULL);
-		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + our_callstack_index)));
-
-		/* No entry in the catchstack which would actually catch a
-		 * throw can refer to the callstack entry being reused.
-		 * There *can* be catchstack entries referring to the current
-		 * callstack entry as long as they don't catch (e.g. label sites).
-		 */
-
-		for (i = 0; i < thr->catchstack_top; i++) {
-			DUK_ASSERT(thr->catchstack[i].callstack_index < our_callstack_index ||  /* refer to callstack entries below current */
-			           DUK_CAT_GET_TYPE(thr->catchstack + i) == DUK_CAT_TYPE_LABEL); /* or a non-catching entry */
-		}
-	}
-#endif  /* DUK_USE_ASSERTIONS */
-
-	entry_valstack_bottom_index = (duk_size_t) (thr->valstack_bottom - thr->valstack);
-	/* XXX: rework */
-	idx_func = duk_normalize_index(thr, -num_stack_args - 2);
-	idx_args = idx_func + 2;
-
-	DUK_DD(DUK_DDPRINT("handle_ecma_call_setup: thr=%p, "
-	                   "num_stack_args=%ld, call_flags=0x%08lx (resume=%ld, tailcall=%ld), "
-	                   "idx_func=%ld, idx_args=%ld, entry_valstack_bottom_index=%ld",
+	duk_jmpbuf *old_jmpbuf_ptr = NULL;
+	duk_jmpbuf our_jmpbuf;
+	duk_idx_t idx_retbase;
+	duk_int_t retval;
+
+	DUK_ASSERT(thr != NULL);
+	DUK_ASSERT(duk_get_top(thr) >= num_stack_args);  /* Caller ensures. */
+
+	DUK_STATS_INC(thr->heap, stats_safecall_all);
+
+	/* Value stack reserve handling: safe call assumes caller has reserved
+	 * space for nrets (assuming optimal unwind processing).  Value stack
+	 * reserve is not stored/restored as for normal calls because a safe
+	 * call conceptually happens in the same activation.
+	 */
+
+	/* Careful with indices like '-x'; if 'x' is zero, it refers to bottom */
+	entry_act = thr->callstack_curr;
+	entry_valstack_bottom_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
+#if defined(DUK_USE_ASSERTIONS)
+	entry_valstack_end_byteoff = (duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
+	entry_callstack_top = thr->callstack_top;
+	entry_callstack_preventcount = thr->callstack_preventcount;
+#endif
+	entry_call_recursion_depth = thr->heap->call_recursion_depth;
+	entry_curr_thread = thr->heap->curr_thread;  /* may be NULL if first call */
+	entry_thread_state = thr->state;
+	entry_ptr_curr_pc = thr->ptr_curr_pc;  /* may be NULL */
+	idx_retbase = duk_get_top(thr) - num_stack_args;  /* not a valid stack index if num_stack_args == 0 */
+	DUK_ASSERT(idx_retbase >= 0);
+
+	DUK_ASSERT((duk_idx_t) (thr->valstack_top - thr->valstack_bottom) >= num_stack_args);  /* Caller ensures. */
+	DUK_ASSERT((duk_idx_t) (thr->valstack_end - (thr->valstack_bottom + idx_retbase)) >= num_stack_rets);  /* Caller ensures. */
+
+	/* Cannot portably debug print a function pointer, hence 'func' not printed! */
+	DUK_DD(DUK_DDPRINT("duk_handle_safe_call: thr=%p, num_stack_args=%ld, num_stack_rets=%ld, "
+	                   "valstack_top=%ld, idx_retbase=%ld, rec_depth=%ld/%ld, "
+	                   "entry_act=%p, entry_valstack_bottom_byteoff=%ld, entry_call_recursion_depth=%ld, "
+	                   "entry_curr_thread=%p, entry_thread_state=%ld",
 	                   (void *) thr,
 	                   (long) num_stack_args,
-	                   (unsigned long) call_flags,
-	                   (long) ((call_flags & DUK_CALL_FLAG_IS_RESUME) != 0 ? 1 : 0),
-	                   (long) ((call_flags & DUK_CALL_FLAG_IS_TAILCALL) != 0 ? 1 : 0),
-	                   (long) idx_func,
-	                   (long) idx_args,
-	                   (long) entry_valstack_bottom_index));
-
-	if (DUK_UNLIKELY(idx_func < 0 || idx_args < 0)) {
-		/* XXX: assert? compiler is responsible for this never happening */
-		DUK_ERROR_TYPE_INVALID_ARGS(thr);
-	}
-
-	/*
-	 *  Check the function type, handle bound function chains, and prepare
-	 *  parameters for the rest of the call handling.  Also figure out the
-	 *  effective 'this' binding, which replaces the current value at
-	 *  idx_func + 1.
-	 *
-	 *  If the target function is a 'bound' one, follow the chain of 'bound'
-	 *  functions until a non-bound function is found.  During this process,
-	 *  bound arguments are 'prepended' to existing ones, and the "this"
-	 *  binding is overridden.  See E5 Section 15.3.4.5.1.
-	 *
-	 *  If the final target function cannot be handled by an ecma-to-ecma
-	 *  call, return to the caller with a return value indicating this case.
-	 *  The bound chain is resolved and the caller can resume with a plain
-	 *  function call.
-	 */
-
-	func = duk__nonbound_func_lookup(ctx, idx_func, &num_stack_args, &tv_func, call_flags);
-	if (func == NULL || !DUK_HOBJECT_IS_COMPFUNC(func)) {
-		DUK_DDD(DUK_DDDPRINT("final target is a lightfunc/nativefunc, cannot do ecma-to-ecma call"));
-		thr->ptr_curr_pc = entry_ptr_curr_pc;
-		return 0;
-	}
-	/* XXX: tv_func is not actually needed */
-
-	DUK_ASSERT(func != NULL);
-	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
-	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(func));
-
-	duk__coerce_effective_this_binding(thr, func, idx_func + 1);
-	DUK_DDD(DUK_DDDPRINT("effective 'this' binding is: %!T",
-	                     duk_get_tval(ctx, idx_func + 1)));
-
-	nargs = ((duk_hcompfunc *) func)->nargs;
-	nregs = ((duk_hcompfunc *) func)->nregs;
-	DUK_ASSERT(nregs >= nargs);
-
-	/* [ ... func this arg1 ... argN ] */
-
-	/*
-	 *  Preliminary activation record and valstack manipulation.
-	 *  The concrete actions depend on whether the we're dealing
-	 *  with a tail call (reuse an existing activation), a resume,
-	 *  or a normal call.
-	 *
-	 *  The basic actions, in varying order, are:
-	 *
-	 *    - Check stack size for call handling
-	 *    - Grow call stack if necessary (non-tail-calls)
-	 *    - Update current activation (idx_retval) if necessary
-	 *      (non-tail, non-resume calls)
-	 *    - Move start of args (idx_args) to valstack bottom
-	 *      (tail calls)
-	 *
-	 *  Don't touch valstack_bottom or valstack_top yet so that Duktape API
-	 *  calls work normally.
-	 */
-
-	/* XXX: some overlapping code; cleanup */
-	use_tailcall = call_flags & DUK_CALL_FLAG_IS_TAILCALL;
-#if !defined(DUK_USE_TAILCALL)
-	DUK_ASSERT(use_tailcall == 0);  /* compiler ensures this */
-#endif
-	if (use_tailcall) {
-		/* tailcall cannot be flagged to resume calls, and a
-		 * previous frame must exist
-		 */
-		DUK_ASSERT(thr->callstack_top >= 1);
-		DUK_ASSERT((call_flags & DUK_CALL_FLAG_IS_RESUME) == 0);
-
-		act = thr->callstack_curr;
-		DUK_ASSERT(act != NULL);
-		if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
-			/* See: test-bug-tailcall-preventyield-assert.c. */
-			DUK_DDD(DUK_DDDPRINT("tail call prevented by current activation having DUK_ACT_FLAG_PREVENTYIELD"));
-			use_tailcall = 0;
-		} else if (DUK_HOBJECT_HAS_NOTAIL(func)) {
-			DUK_D(DUK_DPRINT("tail call prevented by function having a notail flag"));
-			use_tailcall = 0;
-		}
-	}
-
-	if (use_tailcall) {
-		duk_tval *tv1, *tv2;
-		duk_size_t cs_index;
-		duk_int_t i_stk;  /* must be signed for loop structure */
-		duk_idx_t i_arg;
-
-		/*
-		 *  Tailcall handling
-		 *
-		 *  Although the callstack entry is reused, we need to explicitly unwind
-		 *  the current activation (or simulate an unwind).  In particular, the
-		 *  current activation must be closed, otherwise something like
-		 *  test-bug-reduce-judofyr.js results.  Also catchstack needs be unwound
-		 *  because there may be non-error-catching label entries in valid tail calls.
-		 */
-
-		DUK_DDD(DUK_DDDPRINT("is tail call, reusing activation at callstack top, at index %ld",
-		                     (long) (thr->callstack_top - 1)));
-
-		/* 'act' already set above */
-
-		DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
-		DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
-		DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
-		DUK_ASSERT((act->flags & DUK_ACT_FLAG_PREVENT_YIELD) == 0);
-
-		/* Unwind catchstack entries referring to the callstack entry we're reusing */
-		cs_index = thr->callstack_top - 1;
-		DUK_ASSERT(thr->catchstack_top <= DUK_INT_MAX);  /* catchstack limits */
-		for (i_stk = (duk_int_t) (thr->catchstack_top - 1); i_stk >= 0; i_stk--) {
-			duk_catcher *cat = thr->catchstack + i_stk;
-			if (cat->callstack_index != cs_index) {
-				/* 'i' is the first entry we'll keep */
-				break;
-			}
-		}
-		duk_hthread_catchstack_unwind_norz(thr, i_stk + 1);
-
-		/* Unwind the topmost callstack entry before reusing it */
-		DUK_ASSERT(thr->callstack_top > 0);
-		duk_hthread_callstack_unwind_norz(thr, thr->callstack_top - 1);
-
-		/* Then reuse the unwound activation; callstack was not shrunk so there is always space */
-		DUK_ASSERT(thr->callstack_top < thr->callstack_size);
-		act = thr->callstack + thr->callstack_top;
-		thr->callstack_top++;
-		thr->callstack_curr = act;
-
-		/* Start filling in the activation */
-		act->func = func;  /* don't want an intermediate exposed state with func == NULL */
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-		act->prev_caller = NULL;
-#endif
-		DUK_ASSERT(func != NULL);
-		DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
-		/* don't want an intermediate exposed state with invalid pc */
-		act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-		act->prev_line = 0;
-#endif
-		DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
-#if defined(DUK_USE_REFERENCE_COUNTING)
-		DUK_HOBJECT_INCREF(thr, func);
-		act = thr->callstack_curr;  /* side effects (currently none though) */
-#endif
-
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-#if defined(DUK_USE_TAILCALL)
-#error incorrect options: tail calls enabled with function caller property
-#endif
-		/* XXX: this doesn't actually work properly for tail calls, so
-		 * tail calls are disabled when DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
-		 * is in use.
-		 */
-		duk__update_func_caller_prop(thr, func);
-		act = thr->callstack_curr;
-#endif
-
-		act->flags = (DUK_HOBJECT_HAS_STRICT(func) ?
-		              DUK_ACT_FLAG_STRICT | DUK_ACT_FLAG_TAILCALLED :
-		              DUK_ACT_FLAG_TAILCALLED);
-
-		DUK_ASSERT(DUK_ACT_GET_FUNC(act) == func);      /* already updated */
-		DUK_ASSERT(act->var_env == NULL);   /* already NULLed (by unwind) */
-		DUK_ASSERT(act->lex_env == NULL);   /* already NULLed (by unwind) */
-		act->idx_bottom = entry_valstack_bottom_index;  /* tail call -> reuse current "frame" */
-		DUK_ASSERT(nregs >= 0);
-#if 0  /* topmost activation idx_retval is considered garbage, no need to init */
-		act->idx_retval = 0;
-#endif
-
-		/*
-		 *  Manipulate valstack so that args are on the current bottom and the
-		 *  previous caller's 'this' binding (which is the value preceding the
-		 *  current bottom) is replaced with the new 'this' binding:
-		 *
-		 *       [ ... this_old | (crud) func this_new arg1 ... argN ]
-		 *  -->  [ ... this_new | arg1 ... argN ]
-		 *
-		 *  For tail calling to work properly, the valstack bottom must not grow
-		 *  here; otherwise crud would accumulate on the valstack.
-		 */
-
-		tv1 = thr->valstack_bottom - 1;
-		tv2 = thr->valstack_bottom + idx_func + 1;
-		DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);  /* tv1 is -below- valstack_bottom */
-		DUK_ASSERT(tv2 >= thr->valstack_bottom && tv2 < thr->valstack_top);
-		DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */
-
-		for (i_arg = 0; i_arg < idx_args; i_arg++) {
-			/* XXX: block removal API primitive */
-			/* Note: 'func' is popped from valstack here, but it is
-			 * already reachable from the activation.
-			 */
-			duk_remove(ctx, 0);
-		}
-		idx_func = 0; DUK_UNREF(idx_func);  /* really 'not applicable' anymore, should not be referenced after this */
-		idx_args = 0;
-
-		/* [ ... this_new | arg1 ... argN ] */
-	} else {
-		DUK_DDD(DUK_DDDPRINT("not a tail call, pushing a new activation to callstack, to index %ld",
-		                     (long) (thr->callstack_top)));
-
-		duk_hthread_callstack_grow(thr);
-
-		if (call_flags & DUK_CALL_FLAG_IS_RESUME) {
-			DUK_DDD(DUK_DDDPRINT("is resume -> no update to current activation (may not even exist)"));
-		} else {
-			DUK_DDD(DUK_DDDPRINT("update to current activation idx_retval"));
-			DUK_ASSERT(thr->callstack_top < thr->callstack_size);
-			DUK_ASSERT(thr->callstack_top >= 1);
-			act = thr->callstack_curr;
-			DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
-			DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
-			act->idx_retval = entry_valstack_bottom_index + idx_func;
-		}
-
-		DUK_ASSERT(thr->callstack_top < thr->callstack_size);
-		act = thr->callstack + thr->callstack_top;
-		thr->callstack_top++;
-		thr->callstack_curr = act;
-		DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
-
-		DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
-		DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(func));
-		DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
-
-		act->flags = (DUK_HOBJECT_HAS_STRICT(func) ?
-		              DUK_ACT_FLAG_STRICT :
-		              0);
-		act->func = func;
-		act->var_env = NULL;
-		act->lex_env = NULL;
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-		act->prev_caller = NULL;
-#endif
-		DUK_ASSERT(func != NULL);
-		DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(func));
-		act->curr_pc = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, (duk_hcompfunc *) func);
-#if defined(DUK_USE_DEBUGGER_SUPPORT)
-		act->prev_line = 0;
-#endif
-		act->idx_bottom = entry_valstack_bottom_index + idx_args;
-		DUK_ASSERT(nregs >= 0);
-#if 0  /* topmost activation idx_retval is considered garbage, no need to init */
-		act->idx_retval = 0;
-#endif
-		DUK_TVAL_SET_OBJECT(&act->tv_func, func);  /* borrowed, no refcount */
-
-		DUK_HOBJECT_INCREF(thr, func);  /* act->func */
-
-#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
-		duk__update_func_caller_prop(thr, func);
-		act = thr->callstack_curr;
-#endif
-	}
-
-	/* [ ... func this arg1 ... argN ]  (not tail call)
-	 * [ this | arg1 ... argN ]         (tail call)
-	 *
-	 * idx_args updated to match
-	 */
-
-	/*
-	 *  Environment record creation and 'arguments' object creation.
-	 *  Named function expression name binding is handled by the
-	 *  compiler; the compiled function's parent env will contain
-	 *  the (immutable) binding already.
-	 *
-	 *  Delayed creation (on demand) is handled in duk_js_var.c.
-	 */
-
-	/* XXX: unify handling with native call. */
-
-	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound function chain has already been resolved */
-
-	if (!DUK_HOBJECT_HAS_NEWENV(func)) {
-		/* use existing env (e.g. for non-strict eval); cannot have
-		 * an own 'arguments' object (but can refer to the existing one)
-		 */
-
-		duk__handle_oldenv_for_call(thr, func, act);
-		/* No need to re-lookup 'act' at present: no side effects. */
-
-		DUK_ASSERT(act->lex_env != NULL);
-		DUK_ASSERT(act->var_env != NULL);
-		goto env_done;
-	}
-
-	DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
-
-	if (!DUK_HOBJECT_HAS_CREATEARGS(func)) {
-		/* no need to create environment record now; leave as NULL */
-		DUK_ASSERT(act->lex_env == NULL);
-		DUK_ASSERT(act->var_env == NULL);
-		goto env_done;
-	}
-
-	/* third arg: absolute index (to entire valstack) of idx_bottom of new activation */
-	env = duk_create_activation_environment_record(thr, func, act->idx_bottom);
-	DUK_ASSERT(env != NULL);
-
-	/* [ ... arg1 ... argN envobj ] */
-
-	/* original input stack before nargs/nregs handling must be
-	 * intact for 'arguments' object
-	 */
-	DUK_ASSERT(DUK_HOBJECT_HAS_CREATEARGS(func));
-	duk__handle_createargs_for_call(thr, func, env, num_stack_args);
-
-	/* [ ... arg1 ... argN envobj ] */
-
-	act = thr->callstack_curr;
-	act->lex_env = env;
-	act->var_env = env;
-	DUK_HOBJECT_INCREF(thr, act->lex_env);
-	DUK_HOBJECT_INCREF(thr, act->var_env);
-	duk_pop(ctx);
-
- env_done:
-	/* [ ... arg1 ... argN ] */
-
-	/*
-	 *  Setup value stack: clamp to 'nargs', fill up to 'nregs'
-	 */
-
-	duk__adjust_valstack_and_top(thr,
-	                             num_stack_args,
-	                             idx_args,
-	                             nregs,
-	                             nargs,
-	                             func);
-
-	/*
-	 *  Shift to new valstack_bottom.
-	 */
-
-	thr->valstack_bottom = thr->valstack_bottom + idx_args;
-	/* keep current valstack_top */
-	DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
-	DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
-	DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
-
-	/*
-	 *  Return to bytecode executor, which will resume execution from
-	 *  the topmost activation.
-	 */
-
+	                   (long) num_stack_rets,
+	                   (long) duk_get_top(thr),
+	                   (long) idx_retbase,
+	                   (long) thr->heap->call_recursion_depth,
+	                   (long) thr->heap->call_recursion_limit,
+	                   (void *) entry_act,
+	                   (long) entry_valstack_bottom_byteoff,
+	                   (long) entry_call_recursion_depth,
+	                   (void *) entry_curr_thread,
+	                   (long) entry_thread_state));
+
+	/* Setjmp catchpoint setup. */
+	old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr;
+	thr->heap->lj.jmpbuf_ptr = &our_jmpbuf;
+
+	/* Prevent yields for the duration of the safe call.  This only
+	 * matters if the executor makes safe calls to functions that
+	 * yield, this doesn't currently happen.
+	 */
+	thr->callstack_preventcount++;
+
+#if defined(DUK_USE_CPP_EXCEPTIONS)
+	try {
+#else
+	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf);
+	if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
+		/* Success path. */
+#endif
+		DUK_DDD(DUK_DDDPRINT("safe_call setjmp catchpoint setup complete"));
+
+		duk__handle_safe_call_inner(thr,
+		                            func,
+		                            udata,
+#if defined(DUK_USE_ASSERTIONS)
+		                            entry_valstack_bottom_byteoff,
+		                            entry_callstack_top,
+#endif
+		                            entry_curr_thread,
+		                            entry_thread_state,
+		                            idx_retbase,
+		                            num_stack_rets);
+
+		DUK_STATS_INC(thr->heap, stats_safecall_nothrow);
+
+		/* Either pointer may be NULL (at entry), so don't assert */
+		thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
+
+		/* If calls happen inside the safe call, these are restored by
+		 * whatever calls are made.  Reserve cannot decrease.
+		 */
+		DUK_ASSERT(thr->callstack_curr == entry_act);
+		DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+
+		retval = DUK_EXEC_SUCCESS;
+#if defined(DUK_USE_CPP_EXCEPTIONS)
+	} catch (duk_internal_exception &exc) {
+		DUK_UNREF(exc);
+#else
+	} else {
+		/* Error path. */
+#endif
+		DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+
+		DUK_STATS_INC(thr->heap, stats_safecall_throw);
+
+		duk__handle_safe_call_error(thr,
+		                            entry_act,
+#if defined(DUK_USE_ASSERTIONS)
+		                            entry_callstack_top,
+#endif
+		                            entry_curr_thread,
+		                            entry_thread_state,
+		                            idx_retbase,
+		                            num_stack_rets,
+		                            entry_valstack_bottom_byteoff,
+		                            old_jmpbuf_ptr);
+
+		retval = DUK_EXEC_ERROR;
+	}
+#if defined(DUK_USE_CPP_EXCEPTIONS)
+	catch (std::exception &exc) {
+		const char *what = exc.what();
+		DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+		DUK_STATS_INC(thr->heap, stats_safecall_throw);
+		if (!what) {
+			what = "unknown";
+		}
+		DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
+		try {
+			DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
+		} catch (duk_internal_exception exc) {
+			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
+			DUK_UNREF(exc);
+			duk__handle_safe_call_error(thr,
+			                            entry_act,
+#if defined(DUK_USE_ASSERTIONS)
+			                            entry_callstack_top,
+#endif
+			                            entry_curr_thread,
+			                            entry_thread_state,
+			                            idx_retbase,
+			                            num_stack_rets,
+			                            entry_valstack_bottom_byteoff,
+			                            old_jmpbuf_ptr);
+			retval = DUK_EXEC_ERROR;
+		}
+	} catch (...) {
+		DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
+		DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+		DUK_STATS_INC(thr->heap, stats_safecall_throw);
+		try {
+			DUK_ERROR_TYPE(thr, "caught invalid c++ exception (perhaps thrown by user code)");
+		} catch (duk_internal_exception exc) {
+			DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
+			DUK_UNREF(exc);
+			duk__handle_safe_call_error(thr,
+			                            entry_act,
+#if defined(DUK_USE_ASSERTIONS)
+			                            entry_callstack_top,
+#endif
+			                            entry_curr_thread,
+			                            entry_thread_state,
+			                            idx_retbase,
+			                            num_stack_rets,
+			                            entry_valstack_bottom_byteoff,
+			                            old_jmpbuf_ptr);
+			retval = DUK_EXEC_ERROR;
+		}
+	}
+#endif
+
+	DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr);  /* success/error path both do this */
+
+	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+	DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+	duk__handle_safe_call_shared_unwind(thr,
+	                                    idx_retbase,
+	                                    num_stack_rets,
+#if defined(DUK_USE_ASSERTIONS)
+	                                    entry_callstack_top,
+#endif
+	                                    entry_call_recursion_depth,
+	                                    entry_curr_thread,
+	                                    entry_ptr_curr_pc);
+
+	/* Restore preventcount. */
+	thr->callstack_preventcount--;
+	DUK_ASSERT(thr->callstack_preventcount == entry_callstack_preventcount);
+
+	/* Final asserts. */
+	DUK_ASSERT(thr->callstack_curr == entry_act);
+	DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack) == entry_valstack_bottom_byteoff);
+	DUK_ASSERT((duk_size_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack) >= entry_valstack_end_byteoff);
+	DUK_ASSERT(thr->callstack_top == entry_callstack_top);
+	DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
+	DUK_ASSERT(thr->heap->curr_thread == entry_curr_thread);
+	DUK_ASSERT(thr->state == entry_thread_state);
+	DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
+	DUK_ASSERT(duk_get_top(thr) == idx_retbase + num_stack_rets);
+	DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+	/* Pending side effects. */
 	DUK_REFZERO_CHECK_FAST(thr);
-	return 1;
-}
+
+	return retval;
+}
+
+/*
+ *  Property-based call (foo.noSuch()) error setup: replace target function
+ *  on stack top with a specially tagged (hidden Symbol) error which gets
+ *  thrown in call handling at the proper spot to follow Ecmascript semantics.
+ */
+
+#if defined(DUK_USE_VERBOSE_ERRORS)
+DUK_INTERNAL DUK_NOINLINE DUK_COLD void duk_call_setup_propcall_error(duk_hthread *thr, duk_tval *tv_targ, duk_tval *tv_base, duk_tval *tv_key) {
+	const char *str1, *str2, *str3;
+	duk_idx_t entry_top;
+
+	entry_top = duk_get_top(thr);
+
+	/* Must stabilize pointers first.  Argument convention is a bit awkward,
+	 * it comes from the executor call site where some arguments may not be
+	 * on the value stack (consts).
+	 */
+	duk_push_tval(thr, tv_base);
+	duk_push_tval(thr, tv_key);
+	duk_push_tval(thr, tv_targ);
+
+	DUK_GC_TORTURE(thr->heap);
+
+	/* We only push an error, replacing the call target (at idx_func)
+	 * with the error to ensure side effects come out correctly:
+	 * - Property read
+	 * - Call argument evaluation
+	 * - Callability check and error thrown.
+	 *
+	 * A hidden Symbol on the error object pushed here is used by
+	 * call handling to figure out the error is to be thrown as is.
+	 * It is CRITICAL that the hidden Symbol can never occur on a
+	 * user visible object that may get thrown.
+	 */
+
+#if defined(DUK_USE_PARANOID_ERRORS)
+	str1 = duk_get_type_name(thr, -1);
+	str2 = duk_get_type_name(thr, -2);
+	str3 = duk_get_type_name(thr, -3);
+	duk_push_error_object(thr, DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, "%s not callable (property %s of %s)", str1, str2, str3);
+#else
+	str1 = duk_push_string_readable(thr, -1);
+	str2 = duk_push_string_readable(thr, -3);
+	str3 = duk_push_string_readable(thr, -5);
+	duk_push_error_object(thr, DUK_ERR_TYPE_ERROR | DUK_ERRCODE_FLAG_NOBLAME_FILELINE, "%s not callable (property %s of %s)", str1, str2, str3);
+#endif
+
+	duk_push_true(thr);
+	duk_put_prop_stridx(thr, -2, DUK_STRIDX_INT_TARGET);  /* Marker property, reuse _Target. */
+
+	/* [ <nregs> propValue <variable> error ] */
+	duk_replace(thr, entry_top - 1);
+	duk_set_top(thr, entry_top);
+
+	DUK_ASSERT(!duk_is_callable(thr, -1));  /* Critical so that call handling will throw the error. */
+}
+#endif  /* DUK_USE_VERBOSE_ERRORS */
+
+/* automatic undefs */
+#undef DUK__AUGMENT_CALL_RELAX_COUNT
 #line 1 "duk_js_compiler.c"
 /*
  *  Ecmascript compiler.
@@ -62478,8 +63945,8 @@
  *
  *  A few typing notes:
  *
- *    - duk_regconst_t: unsigned, no marker value for "none"
- *    - duk_reg_t: signed, < 0 = none
+ *    - duk_regconst_t: signed, highest bit set (< 0) means constant,
+ *      some call sites use -1 for "none" (equivalent to constant 0x7fffffff)
  *    - PC values: duk_int_t, negative values used as markers
  */
 
@@ -62487,15 +63954,16 @@
 
 /* If highest bit of a register number is set, it refers to a constant instead.
  * When interpreted as a signed value, this means const values are always
- * negative (when interpreted as two's complement).  For example DUK__ISTEMP()
+ * negative (when interpreted as two's complement).  For example DUK__ISREG_TEMP()
  * uses this approach to avoid an explicit DUK__ISREG() check (the condition is
  * logically "'x' is a register AND 'x' >= temp_first").
  */
 #define DUK__CONST_MARKER                   DUK_REGCONST_CONST_MARKER
-#define DUK__ISREG(x)                       (((x) & DUK__CONST_MARKER) == 0)
-#define DUK__ISCONST(x)                     (((x) & DUK__CONST_MARKER) != 0)
 #define DUK__REMOVECONST(x)                 ((x) & ~DUK__CONST_MARKER)
-#define DUK__ISTEMP(comp_ctx,x)             ((duk_int32_t) (x) >= (duk_int32_t) ((comp_ctx)->curr_func.temp_first))  /* Avoid DUK__ISREG() check by interpreting as negative value. */
+#define DUK__ISREG(x)                       ((x) >= 0)
+#define DUK__ISCONST(x)                     ((x) < 0)
+#define DUK__ISREG_TEMP(comp_ctx,x)         ((duk_int32_t) (x) >= (duk_int32_t) ((comp_ctx)->curr_func.temp_first))   /* Check for x >= temp_first && x >= 0 by comparing as signed. */
+#define DUK__ISREG_NOTTEMP(comp_ctx,x)      ((duk_uint32_t) (x) < (duk_uint32_t) ((comp_ctx)->curr_func.temp_first))  /* Check for x >= 0 && x < temp_first by interpreting as unsigned. */
 #define DUK__GETTEMP(comp_ctx)              ((comp_ctx)->curr_func.temp_next)
 #define DUK__SETTEMP(comp_ctx,x)            ((comp_ctx)->curr_func.temp_next = (x))  /* dangerous: must only lower (temp_max not updated) */
 #define DUK__SETTEMP_CHECKMAX(comp_ctx,x)   duk__settemp_checkmax((comp_ctx),(x))
@@ -62525,12 +63993,12 @@
 
 #define DUK__RECURSION_INCREASE(comp_ctx,thr)  do { \
 		DUK_DDD(DUK_DDDPRINT("RECURSION INCREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \
-		duk__recursion_increase((comp_ctx)); \
+		duk__comp_recursion_increase((comp_ctx)); \
 	} while (0)
 
 #define DUK__RECURSION_DECREASE(comp_ctx,thr)  do { \
 		DUK_DDD(DUK_DDDPRINT("RECURSION DECREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \
-		duk__recursion_decrease((comp_ctx)); \
+		duk__comp_recursion_decrease((comp_ctx)); \
 	} while (0)
 
 /* Value stack slot limits: these are quite approximate right now, and
@@ -62563,7 +64031,7 @@
 /* function helpers */
 DUK_LOCAL_DECL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx);
 DUK_LOCAL_DECL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx);
-DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_reg_t *out_stmt_value_reg);
+DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg);
 DUK_LOCAL_DECL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx);
 DUK_LOCAL_DECL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx);
 
@@ -62577,13 +64045,13 @@
 DUK_LOCAL_DECL void duk__emit_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b, duk_regconst_t c);
 #if 0  /* unused */
 DUK_LOCAL_DECL void duk__emit_a(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a);
-#endif
 DUK_LOCAL_DECL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b);
+#endif
 DUK_LOCAL_DECL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc);
 DUK_LOCAL_DECL void duk__emit_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t bc);
 DUK_LOCAL_DECL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc);
-DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val);
-DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val);
+DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val);
+DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val);
 DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc);
 DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx);
 DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc);
@@ -62601,41 +64069,41 @@
 DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h);
 DUK_LOCAL_DECL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst);
 DUK_LOCAL_DECL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst);
-DUK_LOCAL_DECL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num);
-DUK_LOCAL_DECL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx);
-DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next);
+DUK_LOCAL_DECL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num);
+DUK_LOCAL_DECL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx);
+DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next);
 DUK_LOCAL_DECL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx);
 DUK_LOCAL_DECL
 duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                          duk_ispec *x,
-                                         duk_reg_t forced_reg,
+                                         duk_regconst_t forced_reg,
                                          duk_small_uint_t flags);
-DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_reg_t forced_reg);
-DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_reg_t forced_reg);
+DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg);
+DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg);
 DUK_LOCAL_DECL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 DUK_LOCAL_DECL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 DUK_LOCAL_DECL
 duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                           duk_ivalue *x,
-                                          duk_reg_t forced_reg,
+                                          duk_regconst_t forced_reg,
                                           duk_small_uint_t flags);
-DUK_LOCAL_DECL duk_reg_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
+DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 #if 0  /* unused */
-DUK_LOCAL_DECL duk_reg_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
+DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 #endif
 DUK_LOCAL_DECL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg);
 DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x);
 
 /* identifier handling */
-DUK_LOCAL_DECL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx);
-DUK_LOCAL_DECL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *ctx, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname);
+DUK_LOCAL_DECL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx);
+DUK_LOCAL_DECL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname);
 
 /* label handling */
 DUK_LOCAL_DECL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id);
 DUK_LOCAL_DECL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags);
 DUK_LOCAL_DECL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_bool_t is_break, duk_int_t *out_label_id, duk_int_t *out_label_catch_depth, duk_int_t *out_label_pc, duk_bool_t *out_is_closest);
-DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len);
+DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len);
 
 /* top-down expression parser */
 DUK_LOCAL_DECL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
@@ -62649,23 +64117,23 @@
 
 /* convenience helpers */
 #if 0  /* unused */
-DUK_LOCAL_DECL duk_reg_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
+DUK_LOCAL_DECL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 #endif
 #if 0  /* unused */
-DUK_LOCAL_DECL duk_reg_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
-#endif
-DUK_LOCAL_DECL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg);
+DUK_LOCAL_DECL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
+#endif
+DUK_LOCAL_DECL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg);
 DUK_LOCAL_DECL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 #if 0  /* unused */
 DUK_LOCAL_DECL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 #endif
 DUK_LOCAL_DECL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 DUK_LOCAL_DECL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
-DUK_LOCAL_DECL duk_reg_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
+DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 #if 0  /* unused */
-DUK_LOCAL_DECL duk_reg_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
-#endif
-DUK_LOCAL_DECL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg);
+DUK_LOCAL_DECL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
+#endif
+DUK_LOCAL_DECL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg);
 DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
 #if 0  /* unused */
 DUK_LOCAL_DECL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags);
@@ -62677,7 +64145,7 @@
 DUK_LOCAL_DECL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res);
 
 /* statement parsing */
-DUK_LOCAL_DECL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname);
+DUK_LOCAL_DECL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname);
 DUK_LOCAL_DECL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags);
 DUK_LOCAL_DECL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
 DUK_LOCAL_DECL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site);
@@ -62744,7 +64212,7 @@
 #define DUK__TOKEN_LBP_BP_MASK         0x1f
 #define DUK__TOKEN_LBP_FLAG_NO_REGEXP  (1 << 5)   /* regexp literal must not follow this token */
 #define DUK__TOKEN_LBP_FLAG_TERMINATES (1 << 6)   /* terminates expression; e.g. post-increment/-decrement */
-#define DUK__TOKEN_LBP_FLAG_UNUSED     (1 << 7)   /* spare */
+#define DUK__TOKEN_LBP_FLAG_UNUSED     (1 << 7)   /* unused */
 
 #define DUK__TOKEN_LBP_GET_BP(x)       ((duk_small_uint_t) (((x) & DUK__TOKEN_LBP_BP_MASK) * 2))
 
@@ -62860,7 +64328,7 @@
  *  Misc helpers
  */
 
-DUK_LOCAL void duk__recursion_increase(duk_compiler_ctx *comp_ctx) {
+DUK_LOCAL void duk__comp_recursion_increase(duk_compiler_ctx *comp_ctx) {
 	DUK_ASSERT(comp_ctx != NULL);
 	DUK_ASSERT(comp_ctx->recursion_depth >= 0);
 	if (comp_ctx->recursion_depth >= comp_ctx->recursion_limit) {
@@ -62869,7 +64337,7 @@
 	comp_ctx->recursion_depth++;
 }
 
-DUK_LOCAL void duk__recursion_decrease(duk_compiler_ctx *comp_ctx) {
+DUK_LOCAL void duk__comp_recursion_decrease(duk_compiler_ctx *comp_ctx) {
 	DUK_ASSERT(comp_ctx != NULL);
 	DUK_ASSERT(comp_ctx->recursion_depth > 0);
 	comp_ctx->recursion_depth--;
@@ -62897,10 +64365,10 @@
 
 DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_bool_t regexp;
 
-	DUK_ASSERT(comp_ctx->curr_token.t >= 0 && comp_ctx->curr_token.t <= DUK_TOK_MAXVAL);  /* MAXVAL is inclusive */
+	DUK_ASSERT_DISABLE(comp_ctx->curr_token.t >= 0);  /* unsigned */
+	DUK_ASSERT(comp_ctx->curr_token.t <= DUK_TOK_MAXVAL);  /* MAXVAL is inclusive */
 
 	/*
 	 *  Use current token to decide whether a RegExp can follow.
@@ -62920,7 +64388,7 @@
 		regexp = 0;
 	}
 
-	if (expect >= 0 && comp_ctx->curr_token.t != expect) {
+	if (expect >= 0 && comp_ctx->curr_token.t != (duk_small_uint_t) expect) {
 		DUK_D(DUK_DPRINT("parse error: expect=%ld, got=%ld",
 		                 (long) expect, (long) comp_ctx->curr_token.t));
 		DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR);
@@ -62928,8 +64396,8 @@
 
 	/* make current token the previous; need to fiddle with valstack "backing store" */
 	DUK_MEMCPY(&comp_ctx->prev_token, &comp_ctx->curr_token, sizeof(duk_token));
-	duk_copy(ctx, comp_ctx->tok11_idx, comp_ctx->tok21_idx);
-	duk_copy(ctx, comp_ctx->tok12_idx, comp_ctx->tok22_idx);
+	duk_copy(thr, comp_ctx->tok11_idx, comp_ctx->tok21_idx);
+	duk_copy(thr, comp_ctx->tok12_idx, comp_ctx->tok22_idx);
 
 	/* parse new token */
 	duk_lexer_parse_js_input_element(&comp_ctx->lex,
@@ -62943,14 +64411,14 @@
 	                     (long) comp_ctx->curr_token.t_nores,
 	                     (long) comp_ctx->curr_token.start_line,
 	                     (long) comp_ctx->curr_token.lineterm,
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->tok11_idx),
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->tok12_idx),
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->tok11_idx),
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->tok12_idx),
 	                     (long) comp_ctx->prev_token.t,
 	                     (long) comp_ctx->prev_token.t_nores,
 	                     (long) comp_ctx->prev_token.start_line,
 	                     (long) comp_ctx->prev_token.lineterm,
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->tok21_idx),
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->tok22_idx)));
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->tok21_idx),
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->tok22_idx)));
 }
 
 /* advance, expecting current token to be a specific token; parse next token in regexp context */
@@ -62971,10 +64439,9 @@
 DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) {
 	duk_compiler_func *func = &comp_ctx->curr_func;
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_idx_t entry_top;
 
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 
 	DUK_MEMZERO(func, sizeof(*func));  /* intentional overlap with earlier memzero */
 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
@@ -62988,46 +64455,46 @@
 	func->h_varmap = NULL;
 #endif
 
-	duk_require_stack(ctx, DUK__FUNCTION_INIT_REQUIRE_SLOTS);
+	duk_require_stack(thr, DUK__FUNCTION_INIT_REQUIRE_SLOTS);
 
 	DUK_BW_INIT_PUSHBUF(thr, &func->bw_code, DUK__BC_INITIAL_INSTS * sizeof(duk_compiler_instr));
 	/* code_idx = entry_top + 0 */
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	func->consts_idx = entry_top + 1;
-	func->h_consts = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 1);
+	func->h_consts = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 1);
 	DUK_ASSERT(func->h_consts != NULL);
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	func->funcs_idx = entry_top + 2;
-	func->h_funcs = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 2);
+	func->h_funcs = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 2);
 	DUK_ASSERT(func->h_funcs != NULL);
 	DUK_ASSERT(func->fnum_next == 0);
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	func->decls_idx = entry_top + 3;
-	func->h_decls = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 3);
+	func->h_decls = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 3);
 	DUK_ASSERT(func->h_decls != NULL);
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	func->labelnames_idx = entry_top + 4;
-	func->h_labelnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 4);
+	func->h_labelnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 4);
 	DUK_ASSERT(func->h_labelnames != NULL);
 
-	duk_push_dynamic_buffer(ctx, 0);
+	duk_push_dynamic_buffer(thr, 0);
 	func->labelinfos_idx = entry_top + 5;
-	func->h_labelinfos = (duk_hbuffer_dynamic *) duk_known_hbuffer(ctx, entry_top + 5);
+	func->h_labelinfos = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 5);
 	DUK_ASSERT(func->h_labelinfos != NULL);
 	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(func->h_labelinfos) && !DUK_HBUFFER_HAS_EXTERNAL(func->h_labelinfos));
 
-	duk_push_array(ctx);
+	duk_push_array(thr);
 	func->argnames_idx = entry_top + 6;
-	func->h_argnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 6);
+	func->h_argnames = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 6);
 	DUK_ASSERT(func->h_argnames != NULL);
 
-	duk_push_bare_object(ctx);
+	duk_push_bare_object(thr);
 	func->varmap_idx = entry_top + 7;
-	func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 7);
+	func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, entry_top + 7);
 	DUK_ASSERT(func->h_varmap != NULL);
 }
 
@@ -63035,25 +64502,24 @@
 DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) {
 	duk_compiler_func *func = &comp_ctx->curr_func;
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 
 	/* reset bytecode buffer but keep current size; pass 2 will
 	 * require same amount or more.
 	 */
 	DUK_BW_RESET_SIZE(thr, &func->bw_code);
 
-	duk_set_length(ctx, func->consts_idx, 0);
+	duk_set_length(thr, func->consts_idx, 0);
 	/* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */
 	func->fnum_next = 0;
-	/* duk_set_length(ctx, func->funcs_idx, 0); */
-	duk_set_length(ctx, func->labelnames_idx, 0);
+	/* duk_set_length(thr, func->funcs_idx, 0); */
+	duk_set_length(thr, func->labelnames_idx, 0);
 	duk_hbuffer_reset(thr, func->h_labelinfos);
 	/* keep func->h_argnames; it is fixed for all passes */
 
 	/* truncated in case pass 3 needed */
-	duk_push_bare_object(ctx);
-	duk_replace(ctx, func->varmap_idx);
-	func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, func->varmap_idx);
+	duk_push_bare_object(thr);
+	duk_replace(thr, func->varmap_idx);
+	func->h_varmap = DUK_GET_HOBJECT_POSIDX(thr, func->varmap_idx);
 	DUK_ASSERT(func->h_varmap != NULL);
 }
 
@@ -63062,7 +64528,6 @@
  */
 DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_hobject *h_varmap;
 	duk_hstring *h_key;
 	duk_tval *tv;
@@ -63071,7 +64536,7 @@
 
 	/* [ ... varmap ] */
 
-	h_varmap = DUK_GET_HOBJECT_NEGIDX(ctx, -1);
+	h_varmap = DUK_GET_HOBJECT_NEGIDX(thr, -1);
 	DUK_ASSERT(h_varmap != NULL);
 
 	ret = 0;
@@ -63102,7 +64567,7 @@
 		}
 	}
 
-	duk_compact_m1(ctx);
+	duk_compact_m1(thr);
 
 	return ret;
 }
@@ -63114,7 +64579,6 @@
 DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) {
 	duk_compiler_func *func = &comp_ctx->curr_func;
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_hcompfunc *h_res;
 	duk_hbuffer_fixed *h_data;
 	duk_size_t consts_count;
@@ -63142,7 +64606,7 @@
 
 	/* Valstack should suffice here, required on function valstack init */
 
-	h_res = duk_push_hcompfunc(ctx);
+	h_res = duk_push_hcompfunc(thr);
 	DUK_ASSERT(h_res != NULL);
 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_res) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) h_res, NULL);  /* Function templates are "bare objects". */
@@ -63225,8 +64689,8 @@
 	                     (long) funcs_count, (long) sizeof(duk_hobject *),
 	                     (long) code_size, (long) data_size));
 
-	duk_push_fixed_buffer_nozero(ctx, data_size);
-	h_data = (duk_hbuffer_fixed *) duk_known_hbuffer(ctx, -1);
+	duk_push_fixed_buffer_nozero(thr, data_size);
+	h_data = (duk_hbuffer_fixed *) duk_known_hbuffer(thr, -1);
 
 	DUK_HCOMPFUNC_SET_DATA(thr->heap, h_res, (duk_hbuffer *) h_data);
 	DUK_HEAPHDR_INCREF(thr, h_data);
@@ -63273,7 +64737,7 @@
 
 	DUK_ASSERT((duk_uint8_t *) (p_instr + code_count) == DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data) + data_size);
 
-	duk_pop(ctx);  /* 'data' (and everything in it) is reachable through h_res now */
+	duk_pop(thr);  /* 'data' (and everything in it) is reachable through h_res now */
 
 	/*
 	 *  Init non-property result fields
@@ -63330,16 +64794,16 @@
 
 	if (keep_varmap) {
 		duk_int_t num_used;
-		duk_dup(ctx, func->varmap_idx);
+		duk_dup(thr, func->varmap_idx);
 		num_used = duk__cleanup_varmap(comp_ctx);
 		DUK_DDD(DUK_DDDPRINT("cleaned up varmap: %!T (num_used=%ld)",
-		                     (duk_tval *) duk_get_tval(ctx, -1), (long) num_used));
+		                     (duk_tval *) duk_get_tval(thr, -1), (long) num_used));
 
 		if (num_used > 0) {
-			duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
+			duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
 		} else {
 			DUK_DD(DUK_DDPRINT("varmap is empty after cleanup -> no need to add"));
-			duk_pop(ctx);
+			duk_pop(thr);
 		}
 	}
 
@@ -63355,7 +64819,7 @@
 	DUK_DD(DUK_DDPRINT("keeping _Formals because debugger support is enabled"));
 	keep_formals = 1;
 #else
-	formals_length = duk_get_length(ctx, func->argnames_idx);
+	formals_length = duk_get_length(thr, func->argnames_idx);
 	if (formals_length != (duk_size_t) h_res->nargs) {
 		/* Nargs not enough for closure .length: keep _Formals regardless
 		 * of its length.  Shouldn't happen in practice at the moment.
@@ -63376,16 +64840,16 @@
 #endif
 
 	if (keep_formals) {
-		duk_dup(ctx, func->argnames_idx);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
+		duk_dup(thr, func->argnames_idx);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
 	}
 
 	/* name */
 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
 	if (func->h_name) {
-		duk_push_hstring(ctx, func->h_name);
-		DUK_DD(DUK_DDPRINT("setting function template .name to %!T", duk_get_tval(ctx, -1)));
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_hstring(thr, func->h_name);
+		DUK_DD(DUK_DDPRINT("setting function template .name to %!T", duk_get_tval(thr, -1)));
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
 	}
 #endif  /* DUK_USE_FUNC_NAME_PROPERTY */
 
@@ -63431,8 +64895,8 @@
 		 */
 
 #if 0
-		duk_push_string(ctx, "XXX");
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_string(thr, "XXX");
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);
 #endif
 	}
 #endif  /* DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY */
@@ -63446,7 +64910,7 @@
 
 		DUK_ASSERT(code_count <= DUK_COMPILER_MAX_BYTECODE_LENGTH);
 		duk_hobject_pc2line_pack(thr, q_instr, (duk_uint_fast32_t) code_count);  /* -> pushes fixed buffer */
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE);
 
 		/* XXX: if assertions enabled, walk through all valid PCs
 		 * and check line mapping.
@@ -63461,19 +64925,19 @@
 		 *  Source filename (or equivalent), for identifying thrown errors.
 		 */
 
-		duk_push_hstring(ctx, comp_ctx->h_filename);
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_hstring(thr, comp_ctx->h_filename);
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE);
 	}
 #endif
 
 	DUK_DD(DUK_DDPRINT("converted function: %!ixT",
-	                   (duk_tval *) duk_get_tval(ctx, -1)));
+	                   (duk_tval *) duk_get_tval(thr, -1)));
 
 	/*
 	 *  Compact the function template.
 	 */
 
-	duk_compact_m1(ctx);
+	duk_compact_m1(thr);
 
 	/*
 	 *  Debug dumping
@@ -63484,7 +64948,7 @@
 		duk_hcompfunc *h;
 		duk_instr_t *p, *p_start, *p_end;
 
-		h = (duk_hcompfunc *) duk_get_hobject(ctx, -1);
+		h = (duk_hcompfunc *) duk_get_hobject(thr, -1);
 		p_start = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, h);
 		p_end = (duk_instr_t *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, h);
 
@@ -63598,7 +65062,7 @@
 
 	instr->ins = ins;
 #if defined(DUK_USE_PC2LINE)
-	instr->line = line;
+	instr->line = (duk_uint32_t) line;
 #endif
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 	if (line < comp_ctx->curr_func.min_line) {
@@ -63667,7 +65131,7 @@
 	duk_int_t b_out = -1;
 	duk_int_t c_out = -1;
 	duk_int_t tmp;
-	duk_small_int_t op = op_flags & 0xff;
+	duk_small_uint_t op = op_flags & 0xffU;
 
 	DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld",
 	                     (unsigned long) op_flags, (long) a, (long) b, (long) c));
@@ -63681,6 +65145,9 @@
 
 	DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN);  /* unsigned */
 	DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX);
+	DUK_ASSERT(DUK__ISREG(a));
+	DUK_ASSERT(b != -1);  /* Not 'none'. */
+	DUK_ASSERT(c != -1);  /* Not 'none'. */
 
 	/* Input shuffling happens before the actual operation, while output
 	 * shuffling happens afterwards.  Output shuffling decisions are still
@@ -63712,7 +65179,7 @@
 			 * consecutive.
 			 */
 			DUK_ASSERT((comp_ctx->curr_func.shuffle1 == 0 && comp_ctx->curr_func.shuffle2 == 0) ||
-			           comp_ctx->curr_func.shuffle2 == comp_ctx->curr_func.shuffle1 + 1);
+			           (comp_ctx->curr_func.shuffle2 == comp_ctx->curr_func.shuffle1 + 1));
 			if (op == DUK_OP_CSVAR) {
 				/* For CSVAR the limit is one smaller because output shuffle
 				 * must be able to express 'a + 1' in BC.
@@ -63730,7 +65197,7 @@
 
 	/* Slot B: reg/const support, mapped to bit 0 of opcode. */
 
-	if (b & DUK__CONST_MARKER) {
+	if ((b & DUK__CONST_MARKER) != 0) {
 		DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) == 0);
 		DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0);
 		b = b & ~DUK__CONST_MARKER;
@@ -63800,7 +65267,7 @@
 
 	/* Slot C: reg/const support, mapped to bit 1 of opcode. */
 
-	if (c & DUK__CONST_MARKER) {
+	if ((c & DUK__CONST_MARKER) != 0) {
 		DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0);
 		DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0);
 		c = c & ~DUK__CONST_MARKER;
@@ -63856,11 +65323,11 @@
 
 	/* Main operation */
 
-	DUK_ASSERT_DISABLE(a >= DUK_BC_A_MIN);  /* unsigned */
+	DUK_ASSERT(a >= DUK_BC_A_MIN);
 	DUK_ASSERT(a <= DUK_BC_A_MAX);
-	DUK_ASSERT_DISABLE(b >= DUK_BC_B_MIN);  /* unsigned */
+	DUK_ASSERT(b >= DUK_BC_B_MIN);
 	DUK_ASSERT(b <= DUK_BC_B_MAX);
-	DUK_ASSERT_DISABLE(c >= DUK_BC_C_MIN);  /* unsigned */
+	DUK_ASSERT(c >= DUK_BC_C_MIN);
 	DUK_ASSERT(c <= DUK_BC_C_MAX);
 
 	ins |= DUK_ENC_OP_A_B_C(op_flags & 0xff, a, b, c);
@@ -63945,23 +65412,26 @@
 }
 #endif
 
+#if 0  /* unused */
 DUK_LOCAL void duk__emit_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t b) {
 #if defined(DUK_USE_SHUFFLE_TORTURE)
 	op_flags |= DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C;
 #endif
 	duk__emit_a_b_c(comp_ctx, op_flags, 0, b, 0);
 }
+#endif
 
 DUK_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc) {
 	duk_instr_t ins;
 	duk_int_t tmp;
 
 	/* allow caller to give a const number with the DUK__CONST_MARKER */
+	DUK_ASSERT(bc != -1);  /* Not 'none'. */
 	bc = bc & (~DUK__CONST_MARKER);
 
 	DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN);  /* unsigned */
 	DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX);
-	DUK_ASSERT_DISABLE(bc >= DUK_BC_BC_MIN);  /* unsigned */
+	DUK_ASSERT(bc >= DUK_BC_BC_MIN);
 	DUK_ASSERT(bc <= DUK_BC_BC_MAX);
 	DUK_ASSERT((bc & DUK__CONST_MARKER) == 0);
 
@@ -63981,6 +65451,13 @@
 		duk__emit(comp_ctx, ins);
 	} else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) {
 		goto error_outofregs;
+	} else if ((op_flags & 0xf0U) == DUK_OP_CALL0) {
+		comp_ctx->curr_func.needs_shuffle = 1;
+		tmp = comp_ctx->curr_func.shuffle1;
+		duk__emit_load_int32_noshuffle(comp_ctx, tmp, a);
+		op_flags |= DUK_BC_CALL_FLAG_INDIRECT;
+		ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc);
+		duk__emit(comp_ctx, ins);
 	} else if (a <= DUK_BC_BC_MAX) {
 		comp_ctx->curr_func.needs_shuffle = 1;
 		tmp = comp_ctx->curr_func.shuffle1;
@@ -64016,6 +65493,7 @@
 	DUK_ASSERT_DISABLE(abc >= DUK_BC_ABC_MIN);  /* unsigned */
 	DUK_ASSERT(abc <= DUK_BC_ABC_MAX);
 	DUK_ASSERT((abc & DUK__CONST_MARKER) == 0);
+	DUK_ASSERT(abc != -1);  /* Not 'none'. */
 
 	if (abc <= DUK_BC_ABC_MAX) {
 		;
@@ -64034,7 +65512,7 @@
 	DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT);
 }
 
-DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val, duk_small_uint_t op_flags) {
+DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val, duk_small_uint_t op_flags) {
 	/* XXX: Shuffling support could be implemented here so that LDINT+LDINTX
 	 * would only shuffle once (instead of twice).  The current code works
 	 * though, and has a smaller compiler footprint.
@@ -64055,7 +65533,7 @@
 	}
 }
 
-DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) {
+DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
 	duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/);
 }
 
@@ -64063,11 +65541,11 @@
 /* Used by duk__emit*() calls so that we don't shuffle the loadints that
  * are needed to handle indirect opcodes.
  */
-DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) {
+DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
 	duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/);
 }
 #else
-DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) {
+DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_regconst_t reg, duk_int32_t val) {
 	/* When torture not enabled, can just use the same helper because
 	 * 'reg' won't get spilled.
 	 */
@@ -64105,7 +65583,8 @@
 	duk_compiler_instr *instr;
 	duk_size_t offset;
 
-	offset = jump_pc * sizeof(duk_compiler_instr),
+	DUK_ASSERT(jump_pc >= 0);
+	offset = (duk_size_t) jump_pc * sizeof(duk_compiler_instr);
 	instr = (duk_compiler_instr *) (void *)
 	        DUK_BW_INSERT_ENSURE_AREA(comp_ctx->thr,
 	                                  &comp_ctx->curr_func.bw_code,
@@ -64117,7 +65596,7 @@
 #endif
 	instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, 0);
 #if defined(DUK_USE_PC2LINE)
-	instr->line = line;
+	instr->line = (duk_uint32_t) line;
 #endif
 
 	DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr));
@@ -64164,7 +65643,7 @@
 DUK_LOCAL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t ldconst_pc, duk_int_t trycatch_pc, duk_regconst_t reg_catch, duk_regconst_t const_varname, duk_small_uint_t flags) {
 	duk_compiler_instr *instr;
 
-	DUK_ASSERT((reg_catch & DUK__CONST_MARKER) == 0);
+	DUK_ASSERT(DUK__ISREG(reg_catch));
 
 	instr = duk__get_instr_ptr(comp_ctx, ldconst_pc);
 	DUK_ASSERT(DUK_DEC_OP(instr->ins) == DUK_OP_LDCONST);
@@ -64251,7 +65730,7 @@
 				continue;
 			}
 
-			target_pc1 = i + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS;
+			target_pc1 = i + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS;
 			DUK_DDD(DUK_DDDPRINT("consider jump at pc %ld; target_pc=%ld", (long) i, (long) target_pc1));
 			DUK_ASSERT(target_pc1 >= 0);
 			DUK_ASSERT(target_pc1 < n);
@@ -64266,7 +65745,7 @@
 				continue;
 			}
 
-			target_pc2 = target_pc1 + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS;
+			target_pc2 = target_pc1 + 1 + (duk_int_t) DUK_DEC_ABC(ins) - (duk_int_t) DUK_BC_JUMP_BIAS;
 
 			DUK_DDD(DUK_DDDPRINT("optimizing jump at pc %ld; old target is %ld -> new target is %ld",
 			                     (long) i, (long) target_pc1, (long) target_pc2));
@@ -64292,11 +65771,14 @@
  * is not needed, the forced_reg argument suffices and generates better
  * code (it is checked as it is used).
  */
+/* XXX: DUK__IVAL_FLAG_REQUIRE_SHORT is passed but not currently implemented
+ * by ispec/ivalue operations.
+ */
 #define DUK__IVAL_FLAG_ALLOW_CONST          (1 << 0)  /* allow a constant to be returned */
 #define DUK__IVAL_FLAG_REQUIRE_TEMP         (1 << 1)  /* require a (mutable) temporary as a result (or a const if allowed) */
 #define DUK__IVAL_FLAG_REQUIRE_SHORT        (1 << 2)  /* require a short (8-bit) reg/const which fits into bytecode B/C slot */
 
-/* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(ctx,x) */
+/* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(thr,x) */
 
 #if 0  /* enable manually for dumping */
 #define DUK__DUMP_ISPEC(compctx,ispec) do { duk__dump_ispec((compctx), (ispec)); } while (0)
@@ -64305,7 +65787,7 @@
 DUK_LOCAL void duk__dump_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *x) {
 	DUK_D(DUK_DPRINT("ispec dump: t=%ld regconst=0x%08lx, valstack_idx=%ld, value=%!T",
 	                 (long) x->t, (unsigned long) x->regconst, (long) x->valstack_idx,
-	                 duk_get_tval((duk_context *) comp_ctx->thr, x->valstack_idx)));
+	                 duk_get_tval(comp_ctx->thr, x->valstack_idx)));
 }
 DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
 	DUK_D(DUK_DPRINT("ivalue dump: t=%ld op=%ld "
@@ -64313,9 +65795,9 @@
 	                 "x2={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T}",
 		         (long) x->t, (long) x->op,
 	                 (long) x->x1.t, (unsigned long) x->x1.regconst, (long) x->x1.valstack_idx,
-	                 duk_get_tval((duk_context *) comp_ctx->thr, x->x1.valstack_idx),
+	                 duk_get_tval(comp_ctx->thr, x->x1.valstack_idx),
 	                 (long) x->x2.t, (unsigned long) x->x2.regconst, (long) x->x2.valstack_idx,
-	                 duk_get_tval((duk_context *) comp_ctx->thr, x->x2.valstack_idx)));
+	                 duk_get_tval(comp_ctx->thr, x->x2.valstack_idx)));
 }
 #else
 #define DUK__DUMP_ISPEC(comp_ctx,x) do {} while (0)
@@ -64325,50 +65807,46 @@
 DUK_LOCAL void duk__ivalue_regconst(duk_ivalue *x, duk_regconst_t regconst) {
 	x->t = DUK_IVAL_PLAIN;
 	x->x1.t = DUK_ISPEC_REGCONST;
-	x->x1.regconst = (duk_regconst_t) regconst;
+	x->x1.regconst = regconst;
 }
 
 DUK_LOCAL void duk__ivalue_plain_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
 	x->t = DUK_IVAL_PLAIN;
 	x->x1.t = DUK_ISPEC_VALUE;
-	duk_replace((duk_context *) comp_ctx->thr, x->x1.valstack_idx);
+	duk_replace(comp_ctx->thr, x->x1.valstack_idx);
 }
 
 DUK_LOCAL void duk__ivalue_var_fromstack(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
 	x->t = DUK_IVAL_VAR;
 	x->x1.t = DUK_ISPEC_VALUE;
-	duk_replace((duk_context *) comp_ctx->thr, x->x1.valstack_idx);
+	duk_replace(comp_ctx->thr, x->x1.valstack_idx);
 }
 
 DUK_LOCAL_DECL void duk__ivalue_var_hstring(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_hstring *h) {
 	DUK_ASSERT(h != NULL);
-	duk_push_hstring((duk_context *) comp_ctx->thr, h);
+	duk_push_hstring(comp_ctx->thr, h);
 	duk__ivalue_var_fromstack(comp_ctx, x);
 }
 
 DUK_LOCAL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst) {
-	duk_context *ctx = (duk_context *) comp_ctx->thr;
-
 	dst->t = src->t;
 	dst->regconst = src->regconst;
-	duk_copy(ctx, src->valstack_idx, dst->valstack_idx);
+	duk_copy(comp_ctx->thr, src->valstack_idx, dst->valstack_idx);
 }
 
 DUK_LOCAL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst) {
-	duk_context *ctx = (duk_context *) comp_ctx->thr;
-
 	dst->t = src->t;
 	dst->op = src->op;
 	dst->x1.t = src->x1.t;
 	dst->x1.regconst = src->x1.regconst;
 	dst->x2.t = src->x2.t;
 	dst->x2.regconst = src->x2.regconst;
-	duk_copy(ctx, src->x1.valstack_idx, dst->x1.valstack_idx);
-	duk_copy(ctx, src->x2.valstack_idx, dst->x2.valstack_idx);
-}
-
-DUK_LOCAL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) {
-	duk_reg_t res;
+	duk_copy(comp_ctx->thr, src->x1.valstack_idx, dst->x1.valstack_idx);
+	duk_copy(comp_ctx->thr, src->x2.valstack_idx, dst->x2.valstack_idx);
+}
+
+DUK_LOCAL duk_regconst_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) {
+	duk_regconst_t res;
 
 	res = comp_ctx->curr_func.temp_next;
 	comp_ctx->curr_func.temp_next += num;
@@ -64385,11 +65863,11 @@
 	return res;
 }
 
-DUK_LOCAL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx) {
+DUK_LOCAL duk_regconst_t duk__alloctemp(duk_compiler_ctx *comp_ctx) {
 	return duk__alloctemps(comp_ctx, 1);
 }
 
-DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next) {
+DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_regconst_t temp_next) {
 	comp_ctx->curr_func.temp_next = temp_next;
 	if (temp_next > comp_ctx->curr_func.temp_max) {
 		comp_ctx->curr_func.temp_max = temp_next;
@@ -64399,14 +65877,13 @@
 /* get const for value at valstack top */
 DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_compiler_func *f = &comp_ctx->curr_func;
 	duk_tval *tv1;
 	duk_int_t i, n, n_check;
 
-	n = (duk_int_t) duk_get_length(ctx, f->consts_idx);
-
-	tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	n = (duk_int_t) duk_get_length(thr, f->consts_idx);
+
+	tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
 	DUK_ASSERT(tv1 != NULL);
 
 #if defined(DUK_USE_FASTINT)
@@ -64428,8 +65905,8 @@
 		if (duk_js_samevalue(tv1, tv2)) {
 			DUK_DDD(DUK_DDDPRINT("reused existing constant for %!T -> const index %ld",
 			                     (duk_tval *) tv1, (long) i));
-			duk_pop(ctx);
-			return (duk_regconst_t) (i | DUK__CONST_MARKER);
+			duk_pop(thr);
+			return (duk_regconst_t) i | (duk_regconst_t) DUK__CONST_MARKER;
 		}
 	}
 
@@ -64439,20 +65916,19 @@
 
 	DUK_DDD(DUK_DDDPRINT("allocating new constant for %!T -> const index %ld",
 	                     (duk_tval *) tv1, (long) n));
-	(void) duk_put_prop_index(ctx, f->consts_idx, n);  /* invalidates tv1, tv2 */
-	return (duk_regconst_t) (n | DUK__CONST_MARKER);
+	(void) duk_put_prop_index(thr, f->consts_idx, (duk_uarridx_t) n);  /* invalidates tv1, tv2 */
+	return (duk_regconst_t) n | (duk_regconst_t) DUK__CONST_MARKER;
 }
 
 DUK_LOCAL duk_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_regconst_t rc) {
 #if defined(DUK_USE_REFERENCE_COUNTING)
-	duk_context *ctx = (duk_context *) comp_ctx->thr;
 	duk_compiler_func *f = &comp_ctx->curr_func;
 	duk_bool_t ret;
 
 	DUK_ASSERT((rc & DUK__CONST_MARKER) == 0);  /* caller removes const marker */
-	(void) duk_get_prop_index(ctx, f->consts_idx, (duk_uarridx_t) rc);
-	ret = !duk_is_number(ctx, -1);  /* now only number/string, so conservative check */
-	duk_pop(ctx);
+	(void) duk_get_prop_index(comp_ctx->thr, f->consts_idx, (duk_uarridx_t) rc);
+	ret = !duk_is_number(comp_ctx->thr, -1);  /* now only number/string, so conservative check */
+	duk_pop(comp_ctx->thr);
 	return ret;
 #else
 	DUK_UNREF(comp_ctx);
@@ -64481,16 +65957,15 @@
 DUK_LOCAL
 duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                          duk_ispec *x,
-                                         duk_reg_t forced_reg,
+                                         duk_regconst_t forced_reg,
                                          duk_small_uint_t flags) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 
 	DUK_DDD(DUK_DDDPRINT("duk__ispec_toregconst_raw(): x={%ld:%ld:%!T}, "
 	                     "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld",
 	                     (long) x->t,
 	                     (long) x->regconst,
-	                     (duk_tval *) duk_get_tval(ctx, x->valstack_idx),
+	                     (duk_tval *) duk_get_tval(thr, x->valstack_idx),
 	                     (long) forced_reg,
 	                     (unsigned long) flags,
 	                     (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0),
@@ -64501,7 +65976,7 @@
 	case DUK_ISPEC_VALUE: {
 		duk_tval *tv;
 
-		tv = DUK_GET_TVAL_POSIDX(ctx, x->valstack_idx);
+		tv = DUK_GET_TVAL_POSIDX(thr, x->valstack_idx);
 		DUK_ASSERT(tv != NULL);
 
 		switch (DUK_TVAL_GET_TAG(tv)) {
@@ -64510,21 +65985,21 @@
 			 * values can occur during compilation as a result of e.g.
 			 * the 'void' operator.
 			 */
-			duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
-			duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, (duk_regconst_t) dest);
-			return (duk_regconst_t) dest;
+			duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
+			duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, dest);
+			return dest;
 		}
 		case DUK_TAG_NULL: {
-			duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
-			duk__emit_bc(comp_ctx, DUK_OP_LDNULL, (duk_regconst_t) dest);
-			return (duk_regconst_t) dest;
+			duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
+			duk__emit_bc(comp_ctx, DUK_OP_LDNULL, dest);
+			return dest;
 		}
 		case DUK_TAG_BOOLEAN: {
-			duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
+			duk_regconst_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
 			duk__emit_bc(comp_ctx,
 			             (DUK_TVAL_GET_BOOLEAN(tv) ? DUK_OP_LDTRUE : DUK_OP_LDFALSE),
-			             (duk_regconst_t) dest);
-			return (duk_regconst_t) dest;
+			             dest);
+			return dest;
 		}
 		case DUK_TAG_POINTER: {
 			DUK_UNREACHABLE();
@@ -64532,7 +66007,7 @@
 		}
 		case DUK_TAG_STRING: {
 			duk_hstring *h;
-			duk_reg_t dest;
+			duk_regconst_t dest;
 			duk_regconst_t constidx;
 
 			h = DUK_TVAL_GET_STRING(tv);
@@ -64547,7 +66022,7 @@
 				/* Encode into a double constant (53 bits can encode 6*8 = 48 bits + 3-bit length */
 			}
 #endif
-			duk_dup(ctx, x->valstack_idx);
+			duk_dup(thr, x->valstack_idx);
 			constidx = duk__getconst(comp_ctx);
 
 			if (flags & DUK__IVAL_FLAG_ALLOW_CONST) {
@@ -64555,8 +66030,8 @@
 			}
 
 			dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
-			duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx);
-			return (duk_regconst_t) dest;
+			duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx);
+			return dest;
 		}
 		case DUK_TAG_OBJECT: {
 			DUK_UNREACHABLE();
@@ -64575,7 +66050,7 @@
 #endif
 		default: {
 			/* number */
-			duk_reg_t dest;
+			duk_regconst_t dest;
 			duk_regconst_t constidx;
 			duk_double_t dval;
 			duk_int32_t ival;
@@ -64594,50 +66069,50 @@
 				if (duk_is_whole_get_int32_nonegzero(dval, &ival)) {
 					dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
 					duk__emit_load_int32(comp_ctx, dest, ival);
-					return (duk_regconst_t) dest;
-				}
-			}
-
-			duk_dup(ctx, x->valstack_idx);
+					return dest;
+				}
+			}
+
+			duk_dup(thr, x->valstack_idx);
 			constidx = duk__getconst(comp_ctx);
 
 			if (flags & DUK__IVAL_FLAG_ALLOW_CONST) {
 				return constidx;
 			} else {
 				dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
-				duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx);
-				return (duk_regconst_t) dest;
+				duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, constidx);
+				return dest;
 			}
 		}
 		}  /* end switch */
 	}
 	case DUK_ISPEC_REGCONST: {
 		if (forced_reg >= 0) {
-			if (x->regconst & DUK__CONST_MARKER) {
+			if (DUK__ISCONST(x->regconst)) {
 				duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, forced_reg, x->regconst);
-			} else if (x->regconst != (duk_regconst_t) forced_reg) {
+			} else if (x->regconst != forced_reg) {
 				duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, forced_reg, x->regconst);
 			} else {
 				; /* already in correct reg */
 			}
-			return (duk_regconst_t) forced_reg;
+			return forced_reg;
 		}
 
 		DUK_ASSERT(forced_reg < 0);
-		if (x->regconst & DUK__CONST_MARKER) {
+		if (DUK__ISCONST(x->regconst)) {
 			if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) {
-				duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx);
-				duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, x->regconst);
-				return (duk_regconst_t) dest;
+				duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx);
+				duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, x->regconst);
+				return dest;
 			}
 			return x->regconst;
 		}
 
-		DUK_ASSERT(forced_reg < 0 && !(x->regconst & DUK__CONST_MARKER));
-		if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISTEMP(comp_ctx, x->regconst)) {
-			duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx);
-			duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, (duk_regconst_t) dest, x->regconst);
-			return (duk_regconst_t) dest;
+		DUK_ASSERT(forced_reg < 0 && !DUK__ISCONST(x->regconst));
+		if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISREG_TEMP(comp_ctx, x->regconst)) {
+			duk_regconst_t dest = DUK__ALLOCTEMP(comp_ctx);
+			duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, dest, x->regconst);
+			return dest;
 		}
 		return x->regconst;
 	}
@@ -64650,7 +66125,7 @@
 	return 0;
 }
 
-DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_reg_t forced_reg) {
+DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_regconst_t forced_reg) {
 	DUK_ASSERT(forced_reg >= 0);
 	(void) duk__ispec_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/);
 }
@@ -64660,17 +66135,16 @@
  * The duk_ivalue argument ('x') is converted into a plain value as a
  * side effect.
  */
-DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_reg_t forced_reg) {
+DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_regconst_t forced_reg) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 
 	DUK_DDD(DUK_DDDPRINT("duk__ivalue_toplain_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, "
 	                     "forced_reg=%ld",
 	                     (long) x->t, (long) x->op,
 	                     (long) x->x1.t, (long) x->x1.regconst,
-	                     (duk_tval *) duk_get_tval(ctx, x->x1.valstack_idx),
+	                     (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx),
 	                     (long) x->x2.t, (long) x->x2.regconst,
-	                     (duk_tval *) duk_get_tval(ctx, x->x2.valstack_idx),
+	                     (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx),
 	                     (long) forced_reg));
 
 	switch (x->t) {
@@ -64681,7 +66155,7 @@
 	case DUK_IVAL_ARITH: {
 		duk_regconst_t arg1;
 		duk_regconst_t arg2;
-		duk_reg_t dest;
+		duk_regconst_t dest;
 		duk_tval *tv1;
 		duk_tval *tv2;
 
@@ -64690,8 +66164,8 @@
 		/* inline arithmetic check for constant values */
 		/* XXX: use the exactly same arithmetic function here as in executor */
 		if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) {
-			tv1 = DUK_GET_TVAL_POSIDX(ctx, x->x1.valstack_idx);
-			tv2 = DUK_GET_TVAL_POSIDX(ctx, x->x2.valstack_idx);
+			tv1 = DUK_GET_TVAL_POSIDX(thr, x->x1.valstack_idx);
+			tv2 = DUK_GET_TVAL_POSIDX(thr, x->x2.valstack_idx);
 			DUK_ASSERT(tv1 != NULL);
 			DUK_ASSERT(tv2 != NULL);
 
@@ -64750,10 +66224,10 @@
 				/* Inline string concatenation.  No need to check for
 				 * symbols, as all inputs are valid Ecmascript strings.
 				 */
-				duk_dup(ctx, x->x1.valstack_idx);
-				duk_dup(ctx, x->x2.valstack_idx);
-				duk_concat(ctx, 2);
-				duk_replace(ctx, x->x1.valstack_idx);
+				duk_dup(thr, x->x1.valstack_idx);
+				duk_dup(thr, x->x2.valstack_idx);
+				duk_concat(thr, 2);
+				duk_replace(thr, x->x1.valstack_idx);
 				x->t = DUK_IVAL_PLAIN;
 				DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE);
 				return;
@@ -64768,25 +66242,25 @@
 		 */
 		if (forced_reg >= 0) {
 			dest = forced_reg;
-		} else if (DUK__ISTEMP(comp_ctx, arg1)) {
-			dest = (duk_reg_t) arg1;
-		} else if (DUK__ISTEMP(comp_ctx, arg2)) {
-			dest = (duk_reg_t) arg2;
+		} else if (DUK__ISREG_TEMP(comp_ctx, arg1)) {
+			dest = arg1;
+		} else if (DUK__ISREG_TEMP(comp_ctx, arg2)) {
+			dest = arg2;
 		} else {
 			dest = DUK__ALLOCTEMP(comp_ctx);
 		}
 
 		DUK_ASSERT(DUK__ISREG(dest));
-		duk__emit_a_b_c(comp_ctx, x->op | DUK__EMIT_FLAG_BC_REGCONST, (duk_regconst_t) dest, arg1, arg2);
-
-		duk__ivalue_regconst(x, (duk_regconst_t) dest);
+		duk__emit_a_b_c(comp_ctx, x->op | DUK__EMIT_FLAG_BC_REGCONST, dest, arg1, arg2);
+
+		duk__ivalue_regconst(x, dest);
 		return;
 	}
 	case DUK_IVAL_PROP: {
 		/* XXX: very similar to DUK_IVAL_ARITH - merge? */
 		duk_regconst_t arg1;
 		duk_regconst_t arg2;
-		duk_reg_t dest;
+		duk_regconst_t dest;
 
 		/* Need a short reg/const, does not have to be a mutable temp. */
 		arg1 = duk__ispec_toregconst_raw(comp_ctx, &x->x1, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/);
@@ -64803,38 +66277,38 @@
 
 		if (forced_reg >= 0) {
 			dest = forced_reg;
-		} else if (DUK__ISTEMP(comp_ctx, arg1)) {
-			dest = (duk_reg_t) arg1;
-		} else if (DUK__ISTEMP(comp_ctx, arg2)) {
-			dest = (duk_reg_t) arg2;
+		} else if (DUK__ISREG_TEMP(comp_ctx, arg1)) {
+			dest = arg1;
+		} else if (DUK__ISREG_TEMP(comp_ctx, arg2)) {
+			dest = arg2;
 		} else {
 			dest = DUK__ALLOCTEMP(comp_ctx);
 		}
 
 		duk__emit_a_b_c(comp_ctx,
 		                DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST,
-		                (duk_regconst_t) dest,
+		                dest,
 		                arg1,
 		                arg2);
 
-		duk__ivalue_regconst(x, (duk_regconst_t) dest);
+		duk__ivalue_regconst(x, dest);
 		return;
 	}
 	case DUK_IVAL_VAR: {
 		/* x1 must be a string */
-		duk_reg_t dest;
-		duk_reg_t reg_varbind;
+		duk_regconst_t dest;
+		duk_regconst_t reg_varbind;
 		duk_regconst_t rc_varname;
 
 		DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE);
 
-		duk_dup(ctx, x->x1.valstack_idx);
+		duk_dup(thr, x->x1.valstack_idx);
 		if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
-			duk__ivalue_regconst(x, (duk_regconst_t) reg_varbind);
+			duk__ivalue_regconst(x, reg_varbind);
 		} else {
 			dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx));
-			duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, (duk_regconst_t) dest, rc_varname);
-			duk__ivalue_regconst(x, (duk_regconst_t) dest);
+			duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, dest, rc_varname);
+			duk__ivalue_regconst(x, dest);
 		}
 		return;
 	}
@@ -64856,7 +66330,7 @@
 
 /* evaluate to final form (e.g. coerce GETPROP to code), throw away temp */
 DUK_LOCAL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
-	duk_reg_t temp;
+	duk_regconst_t temp;
 
 	/* If duk__ivalue_toplain_raw() allocates a temp, forget it and
 	 * restore next temp state.
@@ -64875,21 +66349,19 @@
 DUK_LOCAL
 duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx,
                                           duk_ivalue *x,
-                                          duk_reg_t forced_reg,
+                                          duk_regconst_t forced_reg,
                                           duk_small_uint_t flags) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_regconst_t reg;
 	DUK_UNREF(thr);
-	DUK_UNREF(ctx);
 
 	DUK_DDD(DUK_DDDPRINT("duk__ivalue_toregconst_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, "
 	                     "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld",
 	                     (long) x->t, (long) x->op,
 	                     (long) x->x1.t, (long) x->x1.regconst,
-	                     (duk_tval *) duk_get_tval(ctx, x->x1.valstack_idx),
+	                     (duk_tval *) duk_get_tval(thr, x->x1.valstack_idx),
 	                     (long) x->x2.t, (long) x->x2.regconst,
-	                     (duk_tval *) duk_get_tval(ctx, x->x2.valstack_idx),
+	                     (duk_tval *) duk_get_tval(thr, x->x2.valstack_idx),
 	                     (long) forced_reg,
 	                     (unsigned long) flags,
 	                     (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0),
@@ -64902,17 +66374,17 @@
 
 	/* then to a register */
 	reg = duk__ispec_toregconst_raw(comp_ctx, &x->x1, forced_reg, flags);
-	duk__ivalue_regconst(x, (duk_regconst_t) reg);
+	duk__ivalue_regconst(x, reg);
 
 	return reg;
 }
 
-DUK_LOCAL duk_reg_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
+DUK_LOCAL duk_regconst_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
 	return duk__ivalue_toregconst_raw(comp_ctx, x, -1, 0 /*flags*/);
 }
 
 #if 0  /* unused */
-DUK_LOCAL duk_reg_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
+DUK_LOCAL duk_regconst_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x) {
 	return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/);
 }
 #endif
@@ -64942,20 +66414,19 @@
  *  Identifier handling
  */
 
-DUK_LOCAL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx) {
+DUK_LOCAL duk_regconst_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *h_varname;
-	duk_reg_t ret;
+	duk_regconst_t ret;
 
 	DUK_DDD(DUK_DDDPRINT("resolving identifier reference to '%!T'",
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	/*
 	 *  Special name handling
 	 */
 
-	h_varname = duk_known_hstring(ctx, -1);
+	h_varname = duk_known_hstring(thr, -1);
 
 	if (h_varname == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)) {
 		DUK_DDD(DUK_DDDPRINT("flagging function as accessing 'arguments'"));
@@ -64980,12 +66451,12 @@
 	 *  name will use slow path.
 	 */
 
-	duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx);
-	if (duk_is_number(ctx, -1)) {
-		ret = duk_to_int(ctx, -1);
-		duk_pop(ctx);
-	} else {
-		duk_pop(ctx);
+	duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
+	if (duk_is_number(thr, -1)) {
+		ret = duk_to_int(thr, -1);
+		duk_pop(thr);
+	} else {
+		duk_pop(thr);
 		if (comp_ctx->curr_func.catch_depth > 0 || comp_ctx->curr_func.with_depth > 0) {
 			DUK_DDD(DUK_DDDPRINT("slow path access from inside a try-catch or with needs _Varmap"));
 			goto slow_path_own;
@@ -65006,14 +66477,14 @@
 	DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, not own variable"));
 
 	comp_ctx->curr_func.id_access_slow = 1;
-	return (duk_reg_t) -1;
+	return (duk_regconst_t) -1;
 
  slow_path_own:
 	DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path, may be own variable"));
 
 	comp_ctx->curr_func.id_access_slow = 1;
 	comp_ctx->curr_func.id_access_slow_own = 1;
-	return (duk_reg_t) -1;
+	return (duk_regconst_t) -1;
 }
 
 /* Lookup an identifier name in the current varmap, indicating whether the
@@ -65024,21 +66495,20 @@
  * return code is 0 or out_reg_varbind is < 0; this is becuase out_rc_varname
  * is unsigned and doesn't have a "unused" / none value.
  */
-DUK_LOCAL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
+DUK_LOCAL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
-	duk_reg_t reg_varbind;
+	duk_regconst_t reg_varbind;
 	duk_regconst_t rc_varname;
 
 	/* [ ... varname ] */
 
-	duk_dup_top(ctx);
+	duk_dup_top(thr);
 	reg_varbind = duk__lookup_active_register_binding(comp_ctx);
 
 	if (reg_varbind >= 0) {
 		*out_reg_varbind = reg_varbind;
 		*out_rc_varname = 0;  /* duk_regconst_t is unsigned, so use 0 as dummy value (ignored by caller) */
-		duk_pop(ctx);
+		duk_pop(thr);
 		return 1;
 	} else {
 		rc_varname = duk__getconst(comp_ctx);
@@ -65058,7 +66528,6 @@
 
 DUK_LOCAL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_size_t n;
 	duk_size_t new_size;
 	duk_uint8_t *p;
@@ -65086,13 +66555,13 @@
 		}
 	}
 
-	duk_push_hstring(ctx, h_label);
+	duk_push_hstring(thr, h_label);
 	DUK_ASSERT(n <= DUK_UARRIDX_MAX);  /* label limits */
-	(void) duk_put_prop_index(ctx, comp_ctx->curr_func.labelnames_idx, (duk_uarridx_t) n);
+	(void) duk_put_prop_index(thr, comp_ctx->curr_func.labelnames_idx, (duk_uarridx_t) n);
 
 	new_size = (n + 1) * sizeof(duk_labelinfo);
 	duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size);
-	/* XXX: spare handling, slow now */
+	/* XXX: slack handling, slow now */
 
 	/* relookup after possible realloc */
 	p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos);
@@ -65163,7 +66632,6 @@
 /* XXX: awkward, especially the bunch of separate output values -> output struct? */
 DUK_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_bool_t is_break, duk_int_t *out_label_id, duk_int_t *out_label_catch_depth, duk_int_t *out_label_pc, duk_bool_t *out_is_closest) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_uint8_t *p;
 	duk_labelinfo *li_start, *li_end, *li;
 	duk_bool_t match = 0;
@@ -65171,7 +66639,7 @@
 	DUK_DDD(DUK_DDDPRINT("looking up active label: label='%!O', is_break=%ld",
 	                     (duk_heaphdr *) h_label, (long) is_break));
 
-	DUK_UNREF(ctx);
+	DUK_UNREF(thr);
 
 	p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos);
 	li_start = (duk_labelinfo *) (void *) p;
@@ -65234,12 +66702,11 @@
 	*out_is_closest = (li == li_end - 1);
 }
 
-DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len) {
+DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_size_t len) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
-
-	duk_set_length(ctx, comp_ctx->curr_func.labelnames_idx, (duk_size_t) len);
-	duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, sizeof(duk_labelinfo) * (duk_size_t) len);
+
+	duk_set_length(thr, comp_ctx->curr_func.labelnames_idx, len);
+	duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, sizeof(duk_labelinfo) * len);
 }
 
 /*
@@ -65257,15 +66724,19 @@
 
 DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_reg_t reg_obj;                 /* result reg */
-	duk_reg_t reg_temp;                /* temp reg */
-	duk_reg_t temp_start;              /* temp reg value for start of loop */
+	duk_regconst_t reg_obj;                 /* result reg */
+	duk_regconst_t reg_temp;                /* temp reg */
+	duk_regconst_t temp_start;              /* temp reg value for start of loop */
 	duk_small_uint_t max_init_values;  /* max # of values initialized in one MPUTARR set */
 	duk_small_uint_t num_values;       /* number of values in current MPUTARR set */
 	duk_uarridx_t curr_idx;            /* current (next) array index */
 	duk_uarridx_t start_idx;           /* start array index of current MPUTARR set */
 	duk_uarridx_t init_idx;            /* last array index explicitly initialized, +1 */
 	duk_bool_t require_comma;          /* next loop requires a comma */
+#if !defined(DUK_USE_PREFER_SIZE)
+	duk_int_t pc_newarr;
+	duk_compiler_instr *instr;
+#endif
 
 	/* DUK_TOK_LBRACKET already eaten, current token is right after that */
 	DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LBRACKET);
@@ -65273,6 +66744,9 @@
 	max_init_values = DUK__MAX_ARRAY_INIT_VALUES;  /* XXX: depend on available temps? */
 
 	reg_obj = DUK__ALLOCTEMP(comp_ctx);
+#if !defined(DUK_USE_PREFER_SIZE)
+	pc_newarr = duk__get_current_pc(comp_ctx);
+#endif
 	duk__emit_bc(comp_ctx, DUK_OP_NEWARR, reg_obj);  /* XXX: patch initial size hint afterwards? */
 	temp_start = DUK__GETTEMP(comp_ctx);
 
@@ -65363,8 +66837,8 @@
 			                DUK_OP_MPUTARR |
 			                    DUK__EMIT_FLAG_NO_SHUFFLE_C |
 			                    DUK__EMIT_FLAG_A_IS_SOURCE,
-			                (duk_regconst_t) reg_obj,
-			                (duk_regconst_t) temp_start,
+			                reg_obj,
+			                temp_start,
 			                (duk_regconst_t) (num_values + 1));
 			init_idx = start_idx + num_values;
 
@@ -65372,6 +66846,14 @@
 		}
 	}
 
+	/* Update initil size for NEWARR, doesn't need to be exact and is
+	 * capped at A field limit.
+	 */
+#if !defined(DUK_USE_PREFER_SIZE)
+	instr = duk__get_instr_ptr(comp_ctx, pc_newarr);
+	instr->ins |= DUK_ENC_OP_A(0, curr_idx > DUK_BC_A_MAX ? DUK_BC_A_MAX : curr_idx);
+#endif
+
 	DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RBRACKET);
 	duk__advance(comp_ctx);
 
@@ -65386,13 +66868,13 @@
 		duk__emit_load_int32(comp_ctx, reg_temp, (duk_int_t) curr_idx);
 		duk__emit_a_bc(comp_ctx,
 		               DUK_OP_SETALEN | DUK__EMIT_FLAG_A_IS_SOURCE,
-		               (duk_regconst_t) reg_obj,
-		               (duk_regconst_t) reg_temp);
+		               reg_obj,
+		               reg_temp);
 	}
 
 	DUK__SETTEMP(comp_ctx, temp_start);
 
-	duk__ivalue_regconst(res, (duk_regconst_t) reg_obj);
+	duk__ivalue_regconst(res, reg_obj);
 	return;
 
  syntax_error:
@@ -65400,9 +66882,10 @@
 }
 
 typedef struct {
-	duk_reg_t reg_obj;
-	duk_reg_t temp_start;
+	duk_regconst_t reg_obj;
+	duk_regconst_t temp_start;
 	duk_small_uint_t num_pairs;
+	duk_small_uint_t num_total_pairs;
 } duk__objlit_state;
 
 DUK_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_state *st) {
@@ -65422,20 +66905,21 @@
 		                    DUK__EMIT_FLAG_A_IS_SOURCE,
 		                st->reg_obj,
 		                st->temp_start,
-		                st->num_pairs * 2);
+		                (duk_regconst_t) (st->num_pairs * 2));
+		st->num_total_pairs += st->num_pairs;
 		st->num_pairs = 0;
 	}
 	DUK__SETTEMP(comp_ctx, st->temp_start);
 }
 
-DUK_LOCAL duk_bool_t duk__objlit_load_key(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_token *tok, duk_reg_t reg_temp) {
+DUK_LOCAL duk_bool_t duk__objlit_load_key(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_token *tok, duk_regconst_t reg_temp) {
 	if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t_nores == DUK_TOK_STRING) {
 		/* same handling for identifiers and strings */
 		DUK_ASSERT(tok->str1 != NULL);
-		duk_push_hstring((duk_context *) comp_ctx->thr, tok->str1);
+		duk_push_hstring(comp_ctx->thr, tok->str1);
 	} else if (tok->t == DUK_TOK_NUMBER) {
 		/* numbers can be loaded as numbers and coerced on the fly */
-		duk_push_number((duk_context *) comp_ctx->thr, tok->num);
+		duk_push_number(comp_ctx->thr, tok->num);
 	} else {
 		return 1;  /* error */
 	}
@@ -65450,10 +66934,14 @@
 DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_hthread *thr = comp_ctx->thr;
 	duk__objlit_state st;
-	duk_reg_t reg_temp;               /* temp reg */
+	duk_regconst_t reg_temp;          /* temp reg */
 	duk_small_uint_t max_init_pairs;  /* max # of key-value pairs initialized in one MPUTOBJ set */
 	duk_bool_t first;                 /* first value: comma must not precede the value */
 	duk_bool_t is_set, is_get;        /* temps */
+#if !defined(DUK_USE_PREFER_SIZE)
+	duk_int_t pc_newobj;
+	duk_compiler_instr *instr;
+#endif
 
 	DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LCURLY);
 
@@ -65462,8 +66950,12 @@
 	st.reg_obj = DUK__ALLOCTEMP(comp_ctx);    /* target object */
 	st.temp_start = DUK__GETTEMP(comp_ctx);   /* start of MPUTOBJ argument list */
 	st.num_pairs = 0;                         /* number of key/value pairs emitted for current MPUTOBJ set */
-
-	duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj);  /* XXX: patch initial size hint afterwards? */
+	st.num_total_pairs = 0;                   /* number of key/value pairs emitted overall */
+
+#if !defined(DUK_USE_PREFER_SIZE)
+	pc_newobj = duk__get_current_pc(comp_ctx);
+#endif
+	duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj);
 
 	/*
 	 *  Emit initializers in sets of maximum max_init_pairs keys.
@@ -65529,7 +67021,7 @@
 		/* Reset temp register state and reserve reg_temp and
 		 * reg_temp + 1 for handling the current property.
 		 */
-		DUK__SETTEMP(comp_ctx, st.temp_start + 2 * st.num_pairs);
+		DUK__SETTEMP(comp_ctx, st.temp_start + 2 * (duk_regconst_t) st.num_pairs);
 		reg_temp = DUK__ALLOCTEMPS(comp_ctx, 2);
 
 		/* NOTE: "get" and "set" are not officially ReservedWords and the lexer
@@ -65559,7 +67051,7 @@
 
 			duk__emit_a_bc(comp_ctx,
 			               DUK_OP_CLOSURE,
-			               (duk_regconst_t) (st.temp_start + 1),
+			               st.temp_start + 1,
 			               (duk_regconst_t) fnum);
 
 			/* Slot C is used in a non-standard fashion (range of regs),
@@ -65607,7 +67099,7 @@
 
 			duk__emit_a_bc(comp_ctx,
 			               DUK_OP_CLOSURE,
-			               (duk_regconst_t) (reg_temp + 1),
+			               reg_temp + 1,
 			               (duk_regconst_t) fnum);
 
 			st.num_pairs++;
@@ -65651,10 +67143,21 @@
 	DUK_ASSERT(st.num_pairs == 0);
 	DUK_ASSERT(DUK__GETTEMP(comp_ctx) == st.temp_start);
 
+	/* Update initial size for NEWOBJ.  The init size doesn't need to be
+	 * exact as the purpose is just to avoid object resizes in common
+	 * cases.  The size is capped to field A limit, and will be too high
+	 * if the object literal contains duplicate keys (this is harmless but
+	 * increases memory traffic if the object is compacted later on).
+	 */
+#if !defined(DUK_USE_PREFER_SIZE)
+	instr = duk__get_instr_ptr(comp_ctx, pc_newobj);
+	instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > DUK_BC_A_MAX ? DUK_BC_A_MAX : st.num_total_pairs);
+#endif
+
 	DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY);
 	duk__advance(comp_ctx);
 
-	duk__ivalue_regconst(res, (duk_regconst_t) st.reg_obj);
+	duk__ivalue_regconst(res, st.reg_obj);
 	return;
 
  syntax_error:
@@ -65667,7 +67170,7 @@
  */
 DUK_LOCAL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_int_t nargs = 0;
-	duk_reg_t reg_temp;
+	duk_regconst_t reg_temp;
 
 	/* Note: expect that caller has already eaten the left paren */
 
@@ -65717,10 +67220,9 @@
 
 DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_token *tk;
-	duk_reg_t temp_at_entry;
-	duk_small_int_t tok;
+	duk_regconst_t temp_at_entry;
+	duk_small_uint_t tok;
 	duk_uint32_t args;  /* temp variable to pass constants and flags to shared code */
 
 	/*
@@ -65746,12 +67248,12 @@
 	/* PRIMARY EXPRESSIONS */
 
 	case DUK_TOK_THIS: {
-		duk_reg_t reg_temp;
+		duk_regconst_t reg_temp;
 		reg_temp = DUK__ALLOCTEMP(comp_ctx);
 		duk__emit_bc(comp_ctx,
 		             DUK_OP_LDTHIS,
-		             (duk_regconst_t) reg_temp);
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+		             reg_temp);
+		duk__ivalue_regconst(res, reg_temp);
 		return;
 	}
 	case DUK_TOK_IDENTIFIER: {
@@ -65759,29 +67261,29 @@
 		return;
 	}
 	case DUK_TOK_NULL: {
-		duk_push_null(ctx);
+		duk_push_null(thr);
 		goto plain_value;
 	}
 	case DUK_TOK_TRUE: {
-		duk_push_true(ctx);
+		duk_push_true(thr);
 		goto plain_value;
 	}
 	case DUK_TOK_FALSE: {
-		duk_push_false(ctx);
+		duk_push_false(thr);
 		goto plain_value;
 	}
 	case DUK_TOK_NUMBER: {
-		duk_push_number(ctx, tk->num);
+		duk_push_number(thr, tk->num);
 		goto plain_value;
 	}
 	case DUK_TOK_STRING: {
 		DUK_ASSERT(tk->str1 != NULL);
-		duk_push_hstring(ctx, tk->str1);
+		duk_push_hstring(thr, tk->str1);
 		goto plain_value;
 	}
 	case DUK_TOK_REGEXP: {
 #if defined(DUK_USE_REGEXP_SUPPORT)
-		duk_reg_t reg_temp;
+		duk_regconst_t reg_temp;
 		duk_regconst_t rc_re_bytecode;  /* const */
 		duk_regconst_t rc_re_source;    /* const */
 
@@ -65793,8 +67295,8 @@
 		                     (duk_heaphdr *) tk->str2));
 
 		reg_temp = DUK__ALLOCTEMP(comp_ctx);
-		duk_push_hstring(ctx, tk->str1);
-		duk_push_hstring(ctx, tk->str2);
+		duk_push_hstring(thr, tk->str1);
+		duk_push_hstring(thr, tk->str2);
 
 		/* [ ... pattern flags ] */
 
@@ -65807,11 +67309,11 @@
 
 		duk__emit_a_b_c(comp_ctx,
 		                DUK_OP_REGEXP | DUK__EMIT_FLAG_BC_REGCONST,
-		                (duk_regconst_t) reg_temp /*a*/,
+		                reg_temp /*a*/,
 		                rc_re_bytecode /*b*/,
 		                rc_re_source /*c*/);
 
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+		duk__ivalue_regconst(res, reg_temp);
 		return;
 #else  /* DUK_USE_REGEXP_SUPPORT */
 		goto syntax_error;
@@ -65859,14 +67361,41 @@
 		 *  and testcases/test-dev-new.js for a bunch of documented tests.
 		 */
 
-		duk_reg_t reg_target;
+		duk_regconst_t reg_target;
 		duk_int_t nargs;
 
 		DUK_DDD(DUK_DDDPRINT("begin parsing new expression"));
 
-		reg_target = DUK__ALLOCTEMP(comp_ctx);
+		reg_target = DUK__ALLOCTEMPS(comp_ctx, 2);
+
+#if defined(DUK_USE_ES6)
+		if (comp_ctx->curr_token.t == DUK_TOK_PERIOD) {
+			/* new.target */
+			DUK_DDD(DUK_DDDPRINT("new.target"));
+			duk__advance(comp_ctx);
+			if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER ||
+			    !duk_hstring_equals_ascii_cstring(comp_ctx->curr_token.str1, "target")) {
+				goto syntax_error_newtarget;
+			}
+			if (comp_ctx->curr_func.is_global) {
+				goto syntax_error_newtarget;
+			}
+			duk__advance(comp_ctx);
+			duk__emit_bc(comp_ctx,
+			             DUK_OP_NEWTARGET,
+			             reg_target);
+			duk__ivalue_regconst(res, reg_target);
+			return;
+		}
+#endif  /* DUK_USE_ES6 */
+
 		duk__expr_toforcedreg(comp_ctx, res, DUK__BP_CALL /*rbp_flags*/, reg_target /*forced_reg*/);
-		DUK__SETTEMP(comp_ctx, reg_target + 1);
+		duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, reg_target + 1);  /* default instance */
+		DUK__SETTEMP(comp_ctx, reg_target + 2);
+
+		/* XXX: 'new obj.noSuch()' doesn't use GETPROPC now which
+		 * makes the error message worse than for obj.noSuch().
+		 */
 
 		if (comp_ctx->curr_token.t == DUK_TOK_LPAREN) {
 			/* 'new' MemberExpression Arguments */
@@ -65880,17 +67409,14 @@
 			nargs = 0;
 		}
 
-		/* Opcode slot C is used in a non-standard way, so shuffling
-		 * is not allowed.
-		 */
 		duk__emit_a_bc(comp_ctx,
-		              DUK_OP_NEW | DUK__EMIT_FLAG_NO_SHUFFLE_A,
+		              DUK_OP_CALL0 | DUK_BC_CALL_FLAG_CONSTRUCT,
 		              nargs /*num_args*/,
 		              reg_target /*target*/);
 
 		DUK_DDD(DUK_DDDPRINT("end parsing new expression"));
 
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_target);
+		duk__ivalue_regconst(res, reg_target);
 		return;
 	}
 
@@ -65908,7 +67434,7 @@
 		 * duk__parse_func_like_fnum().
 		 */
 
-		duk_reg_t reg_temp;
+		duk_regconst_t reg_temp;
 		duk_int_t fnum;
 
 		reg_temp = DUK__ALLOCTEMP(comp_ctx);
@@ -65919,10 +67445,10 @@
 
 		duk__emit_a_bc(comp_ctx,
 		               DUK_OP_CLOSURE,
-		               (duk_regconst_t) reg_temp /*a*/,
+		               reg_temp /*a*/,
 		               (duk_regconst_t) fnum /*bc*/);
 
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+		duk__ivalue_regconst(res, reg_temp);
 		return;
 	}
 
@@ -65941,8 +67467,8 @@
 			 * resolving cases (the specification description is a bit confusing).
 			 */
 
-			duk_reg_t reg_temp;
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_temp;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
 
 			if (comp_ctx->curr_func.is_strict) {
@@ -65952,24 +67478,24 @@
 			DUK__SETTEMP(comp_ctx, temp_at_entry);
 			reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
-			duk_dup(ctx, res->x1.valstack_idx);
+			duk_dup(thr, res->x1.valstack_idx);
 			if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 				/* register bound variables are non-configurable -> always false */
 				duk__emit_bc(comp_ctx,
 				             DUK_OP_LDFALSE,
-				             (duk_regconst_t) reg_temp);
-			} else {
-				duk_dup(ctx, res->x1.valstack_idx);
+				             reg_temp);
+			} else {
+				duk_dup(thr, res->x1.valstack_idx);
 				rc_varname = duk__getconst(comp_ctx);
 				duk__emit_a_bc(comp_ctx,
 				               DUK_OP_DELVAR,
-				               (duk_regconst_t) reg_temp,
-				               (duk_regconst_t) rc_varname);
-			}
-			duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+				               reg_temp,
+				               rc_varname);
+			}
+			duk__ivalue_regconst(res, reg_temp);
 		} else if (res->t == DUK_IVAL_PROP) {
-			duk_reg_t reg_temp;
-			duk_reg_t reg_obj;
+			duk_regconst_t reg_temp;
+			duk_regconst_t reg_obj;
 			duk_regconst_t rc_key;
 
 			DUK__SETTEMP(comp_ctx, temp_at_entry);
@@ -65978,21 +67504,21 @@
 			rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 			duk__emit_a_b_c(comp_ctx,
 			                DUK_OP_DELPROP | DUK__EMIT_FLAG_BC_REGCONST,
-			                (duk_regconst_t) reg_temp,
-			                (duk_regconst_t) reg_obj,
+			                reg_temp,
+			                reg_obj,
 			                rc_key);
 
-			duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+			duk__ivalue_regconst(res, reg_temp);
 		} else {
 			/* non-Reference deletion is always 'true', even in strict mode */
-			duk_push_true(ctx);
+			duk_push_true(thr);
 			goto plain_value;
 		}
 		return;
 	}
 	case DUK_TOK_VOID: {
 		duk__expr_toplain_ignore(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 		goto plain_value;
 	}
 	case DUK_TOK_TYPEOF: {
@@ -66004,11 +67530,11 @@
 		duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
 
 		if (res->t == DUK_IVAL_VAR) {
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
-			duk_reg_t reg_temp;
-
-			duk_dup(ctx, res->x1.valstack_idx);
+			duk_regconst_t reg_temp;
+
+			duk_dup(thr, res->x1.valstack_idx);
 			if (!duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 				DUK_DDD(DUK_DDDPRINT("typeof for an identifier name which could not be resolved "
 				                     "at compile time, need to use special run-time handling"));
@@ -66017,7 +67543,7 @@
 				               DUK_OP_TYPEOFID,
 				               reg_temp,
 				               rc_varname);
-				duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+				duk__ivalue_regconst(res, reg_temp);
 				return;
 			}
 		}
@@ -66037,7 +67563,7 @@
 		/* unary plus */
 		duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
 		if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE &&
-		    duk_is_number(ctx, res->x1.valstack_idx)) {
+		    duk_is_number(thr, res->x1.valstack_idx)) {
 			/* unary plus of a number is identity */
 			return;
 		}
@@ -66048,14 +67574,14 @@
 		/* unary minus */
 		duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
 		if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE &&
-		    duk_is_number(ctx, res->x1.valstack_idx)) {
+		    duk_is_number(thr, res->x1.valstack_idx)) {
 			/* this optimization is important to handle negative literals
 			 * (which are not directly provided by the lexical grammar)
 			 */
 			duk_tval *tv_num;
 			duk_double_union du;
 
-			tv_num = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx);
+			tv_num = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx);
 			DUK_ASSERT(tv_num != NULL);
 			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_num));
 			du.d = DUK_TVAL_GET_NUMBER(tv_num);
@@ -66080,7 +67606,7 @@
 			 */
 			duk_tval *tv_val;
 
-			tv_val = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx);
+			tv_val = DUK_GET_TVAL_POSIDX(thr, res->x1.valstack_idx);
 			DUK_ASSERT(tv_val != NULL);
 			if (DUK_TVAL_IS_NUMBER(tv_val)) {
 				duk_double_t d;
@@ -66096,7 +67622,7 @@
 					return;
 				}
 			} else if (DUK_TVAL_IS_BOOLEAN(tv_val)) {
-				duk_small_int_t v;
+				duk_small_uint_t v;
 				v = DUK_TVAL_GET_BOOLEAN(tv_val);
 				DUK_DDD(DUK_DDDPRINT("inlined lnot boolean: %ld", (long) v));
 				DUK_ASSERT(v == 0 || v == 1);
@@ -66120,10 +67646,10 @@
 		 * bits of the opcode.
 		 */
 
-		duk_reg_t reg_src, reg_res;
+		duk_regconst_t reg_src, reg_res;
 
 		reg_src = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, 0 /*flags*/);
-		if (DUK__ISTEMP(comp_ctx, reg_src)) {
+		if (DUK__ISREG_TEMP(comp_ctx, reg_src)) {
 			reg_res = reg_src;
 		} else {
 			reg_res = DUK__ALLOCTEMP(comp_ctx);
@@ -66131,15 +67657,15 @@
 		duk__emit_a_bc(comp_ctx,
 		             args,
 		             reg_res,
-		             (duk_regconst_t) reg_src);
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_res);
+		             reg_src);
+		duk__ivalue_regconst(res, reg_res);
 		return;
 	}
 
  preincdec:
 	{
 		/* preincrement and predecrement */
-		duk_reg_t reg_res;
+		duk_regconst_t reg_res;
 		duk_small_uint_t args_op1 = args & 0xff;  /* DUK_OP_PREINCR/DUK_OP_PREDECR */
 		duk_small_uint_t args_op2 = args >> 8;    /* DUK_OP_PREINCP_RR/DUK_OP_PREDECP_RR */
 
@@ -66152,39 +67678,39 @@
 		duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/);  /* UnaryExpression */
 		if (res->t == DUK_IVAL_VAR) {
 			duk_hstring *h_varname;
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
 
-			h_varname = duk_known_hstring(ctx, res->x1.valstack_idx);
+			h_varname = duk_known_hstring(thr, res->x1.valstack_idx);
 
 			if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
 				goto syntax_error;
 			}
 
-			duk_dup(ctx, res->x1.valstack_idx);
+			duk_dup(thr, res->x1.valstack_idx);
 			if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 				duk__emit_a_bc(comp_ctx,
 				               args_op1,  /* e.g. DUK_OP_PREINCR */
-				               (duk_regconst_t) reg_res,
-				               (duk_regconst_t) reg_varbind);
+				               reg_res,
+				               reg_varbind);
 			} else {
 				duk__emit_a_bc(comp_ctx,
 				                args_op1 + 4,  /* e.g. DUK_OP_PREINCV */
-				                (duk_regconst_t) reg_res,
+				                reg_res,
 				                rc_varname);
 			}
 
 			DUK_DDD(DUK_DDDPRINT("preincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
 			                     (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
 		} else if (res->t == DUK_IVAL_PROP) {
-			duk_reg_t reg_obj;  /* allocate to reg only (not const) */
+			duk_regconst_t reg_obj;  /* allocate to reg only (not const) */
 			duk_regconst_t rc_key;
 			reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
 			rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 			duk__emit_a_b_c(comp_ctx,
 			                args_op2 | DUK__EMIT_FLAG_BC_REGCONST,  /* e.g. DUK_OP_PREINCP */
-			                (duk_regconst_t) reg_res,
-			                (duk_regconst_t) reg_obj,
+			                reg_res,
+			                reg_obj,
 			                rc_key);
 		} else {
 			/* Technically return value is not needed because INVLHS will
@@ -66201,7 +67727,7 @@
 			                  DUK_OP_INVLHS);
 		}
 		DUK__SETTEMP(comp_ctx, reg_res + 1);
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_res);
+		duk__ivalue_regconst(res, reg_res);
 		return;
 	}
 
@@ -66212,6 +67738,11 @@
 		return;
 	}
 
+#if defined(DUK_USE_ES6)
+ syntax_error_newtarget:
+	DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_NEWTARGET);
+#endif
+
  syntax_error:
 	DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION);
 }
@@ -66222,9 +67753,8 @@
  */
 DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_token *tk;
-	duk_small_int_t tok;
+	duk_small_uint_t tok;
 	duk_uint32_t args;  /* temp variable to pass constants and flags to shared code */
 
 	/*
@@ -66274,8 +67804,8 @@
 		res->t = DUK_IVAL_PROP;
 		duk__copy_ispec(comp_ctx, &left->x1, &res->x1);  /* left.x1 -> res.x1 */
 		DUK_ASSERT(comp_ctx->curr_token.str1 != NULL);
-		duk_push_hstring(ctx, comp_ctx->curr_token.str1);
-		duk_replace(ctx, res->x2.valstack_idx);
+		duk_push_hstring(thr, comp_ctx->curr_token.str1);
+		duk_replace(thr, res->x2.valstack_idx);
 		res->x2.t = DUK_ISPEC_VALUE;
 
 		/* special RegExp literal handling after IdentifierName */
@@ -66317,9 +67847,9 @@
 	}
 	case DUK_TOK_LPAREN: {
 		/* function call */
-		duk_reg_t reg_cs = DUK__ALLOCTEMPS(comp_ctx, 2);
+		duk_regconst_t reg_cs = DUK__ALLOCTEMPS(comp_ctx, 2);
 		duk_int_t nargs;
-		duk_small_uint_t call_op = DUK_OP_CALL;
+		duk_small_uint_t call_op = DUK_OP_CALL0;
 
 		/* XXX: attempt to get the call result to "next temp" whenever
 		 * possible to avoid unnecessary register shuffles.
@@ -66335,12 +67865,12 @@
 
 		if (left->t == DUK_IVAL_VAR) {
 			duk_hstring *h_varname;
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
 
 			DUK_DDD(DUK_DDDPRINT("function call with identifier base"));
 
-			h_varname = duk_known_hstring(ctx, left->x1.valstack_idx);
+			h_varname = duk_known_hstring(thr, left->x1.valstack_idx);
 			if (h_varname == DUK_HTHREAD_STRING_EVAL(thr)) {
 				/* Potential direct eval call detected, flag the CALL
 				 * so that a run-time "direct eval" check is made and
@@ -66350,16 +67880,16 @@
 				DUK_DDD(DUK_DDDPRINT("function call with identifier 'eval' "
 				                     "-> using EVALCALL, marking function "
 				                     "as may_direct_eval"));
-				call_op = DUK_OP_EVALCALL;
+				call_op |= DUK_BC_CALL_FLAG_CALLED_AS_EVAL;
 				comp_ctx->curr_func.may_direct_eval = 1;
 			}
 
-			duk_dup(ctx, left->x1.valstack_idx);
+			duk_dup(thr, left->x1.valstack_idx);
 			if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 				duk__emit_a_bc(comp_ctx,
 				              DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE,
-				              (duk_regconst_t) reg_varbind,
-				              (duk_regconst_t) (reg_cs + 0));
+				              reg_varbind,
+				              reg_cs + 0);
 			} else {
 				/* XXX: expand target register or constant field to
 				 * reduce shuffling.
@@ -66367,7 +67897,7 @@
 				DUK_ASSERT(DUK__ISCONST(rc_varname));
 				duk__emit_a_b(comp_ctx,
 				              DUK_OP_CSVAR | DUK__EMIT_FLAG_BC_REGCONST,
-				              (duk_regconst_t) (reg_cs + 0),
+				              reg_cs + 0,
 				              rc_varname);
 			}
 		} else if (left->t == DUK_IVAL_PROP) {
@@ -66376,34 +67906,61 @@
 			 * but a typical call setup took 3 opcodes (e.g. LDREG, LDCONST,
 			 * CSPROP) and the same can be achieved with ordinary loads.
 			 */
+#if defined(DUK_USE_VERBOSE_ERRORS)
+			duk_regconst_t reg_key;
+#endif
+
 			DUK_DDD(DUK_DDDPRINT("function call with property base"));
 
+			/* XXX: For Math.sin() this generates: LDCONST + LDREG +
+			 * GETPROPC + call.  The LDREG is unnecessary because LDCONST
+			 * could be loaded directly into reg_cs + 1.  This doesn't
+			 * happen now because a variable cannot be in left->x1 of a
+			 * DUK_IVAL_PROP.  We could notice that left->x1 is a temp
+			 * and reuse, but it would still be in the wrong position
+			 * (reg_cs + 0 rather than reg_cs + 1).
+			 */
 			duk__ispec_toforcedreg(comp_ctx, &left->x1, reg_cs + 1);  /* base */
+#if defined(DUK_USE_VERBOSE_ERRORS)
+			reg_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
+			duk__emit_a_b_c(comp_ctx,
+			                DUK_OP_GETPROPC | DUK__EMIT_FLAG_BC_REGCONST,
+			                reg_cs + 0,
+			                reg_cs + 1,
+			                reg_key);
+#else
 			duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0);  /* base[key] */
+#endif
 		} else {
 			DUK_DDD(DUK_DDDPRINT("function call with register base"));
 
 			duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0);
+#if 0
 			duk__emit_a_bc(comp_ctx,
 			               DUK_OP_CSREG | DUK__EMIT_FLAG_A_IS_SOURCE,
-			               (duk_regconst_t) (reg_cs + 0),
-			               (duk_regconst_t) (reg_cs + 0));  /* in-place setup */
+			               reg_cs + 0,
+			               reg_cs + 0);  /* in-place setup */
+#endif
+			/* Because of in-place setup, REGCS is equivalent to
+			 * just this LDUNDEF.
+			 */
+			duk__emit_bc(comp_ctx, DUK_OP_LDUNDEF, reg_cs + 1);
 		}
 
 		DUK__SETTEMP(comp_ctx, reg_cs + 2);
 		nargs = duk__parse_arguments(comp_ctx, res);  /* parse args starting from "next temp" */
 
-		/* Tailcalls are handled by back-patching the opcode to TAILCALL to the
-		 * already emitted instruction later (in return statement parser).
+		/* Tailcalls are handled by back-patching the already emitted opcode
+		 * later in return statement parser.
 		 */
 
 		duk__emit_a_bc(comp_ctx,
-		               call_op | DUK__EMIT_FLAG_NO_SHUFFLE_A,
+		               call_op,
 		               (duk_regconst_t) nargs /*numargs*/,
-		               (duk_regconst_t) reg_cs /*basereg*/);
+		               reg_cs /*basereg*/);
 		DUK__SETTEMP(comp_ctx, reg_cs + 1);    /* result in csreg */
 
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_cs);
+		duk__ivalue_regconst(res, reg_cs);
 		return;
 	}
 
@@ -66552,7 +68109,7 @@
 		/* XXX: common reg allocation need is to reuse a sub-expression's temp reg,
 		 * but only if it really is a temp.  Nothing fancy here now.
 		 */
-		duk_reg_t reg_temp;
+		duk_regconst_t reg_temp;
 		duk_int_t pc_jump1;
 		duk_int_t pc_jump2;
 
@@ -66568,7 +68125,7 @@
 		duk__patch_jump_here(comp_ctx, pc_jump2);
 
 		DUK__SETTEMP(comp_ctx, reg_temp + 1);
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+		duk__ivalue_regconst(res, reg_temp);
 		return;
 	}
 
@@ -66699,11 +68256,11 @@
 
 		res->x2.t = res->x1.t;
 		res->x2.regconst = res->x1.regconst;
-		duk_copy(ctx, res->x1.valstack_idx, res->x2.valstack_idx);
+		duk_copy(thr, res->x1.valstack_idx, res->x2.valstack_idx);
 
 		res->x1.t = left->x1.t;
 		res->x1.regconst = left->x1.regconst;
-		duk_copy(ctx, left->x1.valstack_idx, res->x1.valstack_idx);
+		duk_copy(thr, left->x1.valstack_idx, res->x1.valstack_idx);
 
 		DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x1.regconst=0x%08lx, x2.t=%ld, x2.regconst=0x%08lx",
 		                     (long) res->t, (long) res->x1.t, (unsigned long) res->x1.regconst, (long) res->x2.t, (unsigned long) res->x2.regconst));
@@ -66731,7 +68288,7 @@
 	 */
 
 	{
-		duk_reg_t reg_temp;
+		duk_regconst_t reg_temp;
 		duk_int_t pc_jump;
 		duk_small_uint_t args_truthval = args >> 8;
 		duk_small_uint_t args_rbp = args & 0xff;
@@ -66744,12 +68301,12 @@
 		DUK_ASSERT(DUK__ISREG(reg_temp));
 		duk__emit_bc(comp_ctx,
 		            (args_truthval ? DUK_OP_IFTRUE_R : DUK_OP_IFFALSE_R),
-		            (duk_regconst_t) reg_temp);  /* skip jump conditionally */
+		            reg_temp);  /* skip jump conditionally */
 		pc_jump = duk__emit_jump_empty(comp_ctx);
 		duk__expr_toforcedreg(comp_ctx, res, args_rbp /*rbp_flags*/, reg_temp /*forced_reg*/);
 		duk__patch_jump_here(comp_ctx, pc_jump);
 
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_temp);
+		duk__ivalue_regconst(res, reg_temp);
 		return;
 	}
 
@@ -66814,17 +68371,17 @@
 
 		if (left->t == DUK_IVAL_VAR) {
 			duk_hstring *h_varname;
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
 
 			DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE);  /* LHS is already side effect free */
 
-			h_varname = duk_known_hstring(ctx, left->x1.valstack_idx);
+			h_varname = duk_known_hstring(thr, left->x1.valstack_idx);
 			if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
 				/* E5 Section 11.13.1 (and others for other assignments), step 4. */
 				goto syntax_error_lvalue;
 			}
-			duk_dup(ctx, left->x1.valstack_idx);
+			duk_dup(thr, left->x1.valstack_idx);
 			(void) duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname);
 
 			if (args_op == DUK_OP_NONE) {
@@ -66836,8 +68393,7 @@
 					/* 'res' must be a plain ivalue, and not register-bound variable. */
 					DUK_DDD(DUK_DDDPRINT("plain assignment, not toplevel assign, ensure not a reg-bound identifier"));
 					if (res->t != DUK_IVAL_PLAIN || (res->x1.t == DUK_ISPEC_REGCONST &&
-					                                 (res->x1.regconst & DUK__CONST_MARKER) == 0 &&
-					                                 !DUK__ISTEMP(comp_ctx, res->x1.regconst))) {
+					                                 DUK__ISREG_NOTTEMP(comp_ctx, res->x1.regconst))) {
 						duk__ivalue_totempconst(comp_ctx, res);
 					}
 				}
@@ -66847,13 +68403,13 @@
 				 * can change X, but when we do <op> we must use
 				 * the pre-op value.
 				 */
-				duk_reg_t reg_temp;
+				duk_regconst_t reg_temp;
 
 				reg_temp = DUK__ALLOCTEMP(comp_ctx);
 
 				if (reg_varbind >= 0) {
-					duk_reg_t reg_res;
-					duk_reg_t reg_src;
+					duk_regconst_t reg_res;
+					duk_regconst_t reg_src;
 					duk_int_t pc_temp_load;
 					duk_int_t pc_before_rhs;
 					duk_int_t pc_after_rhs;
@@ -66884,7 +68440,7 @@
 					pc_temp_load = duk__get_current_pc(comp_ctx);
 					duk__emit_a_bc(comp_ctx,
 					               DUK_OP_LDREG,
-					               (duk_regconst_t) reg_temp,
+					               reg_temp,
 					               reg_varbind);
 
 					pc_before_rhs = duk__get_current_pc(comp_ctx);
@@ -66902,7 +68458,7 @@
 						 * one instruction, so use explicit PC computation.
 						 */
 						DUK_DD(DUK_DDPRINT("rhs is side effect free, rewind and avoid unnecessary temp for reg-based <op>="));
-						DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, (pc_temp_load - pc_before_rhs) * sizeof(duk_compiler_instr));
+						DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, (duk_size_t) (pc_temp_load - pc_before_rhs) * sizeof(duk_compiler_instr));
 						reg_src = reg_varbind;
 					} else {
 						DUK_DD(DUK_DDPRINT("rhs evaluation emitted code, not sure if rhs is side effect free; use temp reg for LHS"));
@@ -66911,14 +68467,14 @@
 
 					duk__emit_a_b_c(comp_ctx,
 					                args_op | DUK__EMIT_FLAG_BC_REGCONST,
-					                (duk_regconst_t) reg_res,
-					                (duk_regconst_t) reg_src,
+					                reg_res,
+					                reg_src,
 					                res->x1.regconst);
 
-					res->x1.regconst = (duk_regconst_t) reg_res;
+					res->x1.regconst = reg_res;
 
 					/* Ensure compact use of temps. */
-					if (DUK__ISTEMP(comp_ctx, reg_res)) {
+					if (DUK__ISREG_TEMP(comp_ctx, reg_res)) {
 						DUK__SETTEMP(comp_ctx, reg_res + 1);
 					}
 				} else {
@@ -66928,7 +68484,7 @@
 
 					duk__emit_a_bc(comp_ctx,
 					               DUK_OP_GETVAR,
-					               (duk_regconst_t) reg_temp,
+					               reg_temp,
 					               rc_varname);
 
 					duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
@@ -66936,10 +68492,10 @@
 
 					duk__emit_a_b_c(comp_ctx,
 					                args_op | DUK__EMIT_FLAG_BC_REGCONST,
-					                (duk_regconst_t) reg_temp,
-					                (duk_regconst_t) reg_temp,
+					                reg_temp,
+					                reg_temp,
 					                res->x1.regconst);
-					res->x1.regconst = (duk_regconst_t) reg_temp;
+					res->x1.regconst = reg_temp;
 				}
 
 				DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST);
@@ -66992,10 +68548,10 @@
 			/* 'res' contains expression value */
 		} else if (left->t == DUK_IVAL_PROP) {
 			/* E5 Section 11.13.1 (and others) step 4 never matches for prop writes -> no check */
-			duk_reg_t reg_obj;
+			duk_regconst_t reg_obj;
 			duk_regconst_t rc_key;
 			duk_regconst_t rc_res;
-			duk_reg_t reg_temp;
+			duk_regconst_t reg_temp;
 
 			/* Property access expressions ('a[b]') are critical to correct
 			 * LHS evaluation ordering, see test-dev-assign-eval-order*.js.
@@ -67028,8 +68584,8 @@
 				reg_temp = DUK__ALLOCTEMP(comp_ctx);
 				duk__emit_a_b_c(comp_ctx,
 				                DUK_OP_GETPROP | DUK__EMIT_FLAG_BC_REGCONST,
-				                (duk_regconst_t) reg_temp,
-				                (duk_regconst_t) reg_obj,
+				                reg_temp,
+				                reg_obj,
 				                rc_key);
 
 				duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/);
@@ -67037,19 +68593,19 @@
 
 				duk__emit_a_b_c(comp_ctx,
 				                args_op | DUK__EMIT_FLAG_BC_REGCONST,
-				                (duk_regconst_t) reg_temp,
-				                (duk_regconst_t) reg_temp,
+				                reg_temp,
+				                reg_temp,
 				                res->x1.regconst);
-				rc_res = (duk_regconst_t) reg_temp;
+				rc_res = reg_temp;
 			}
 
 			duk__emit_a_b_c(comp_ctx,
 			                DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST,
-			                (duk_regconst_t) reg_obj,
+			                reg_obj,
 			                rc_key,
 			                rc_res);
 
-			duk__ivalue_regconst(res, (duk_regconst_t) rc_res);
+			duk__ivalue_regconst(res, rc_res);
 		} else {
 			/* No support for lvalues returned from new or function call expressions.
 			 * However, these must NOT cause compile-time SyntaxErrors, but run-time
@@ -67075,7 +68631,7 @@
 
 			duk__emit_op_only(comp_ctx, DUK_OP_INVLHS);
 
-			duk__ivalue_regconst(res, (duk_regconst_t) rc_res);
+			duk__ivalue_regconst(res, rc_res);
 		}
 
 		return;
@@ -67094,7 +68650,7 @@
 		 *  the previous expression if a LineTerminator occurs before '++'/'--'.
 		 */
 
-		duk_reg_t reg_res;
+		duk_regconst_t reg_res;
 		duk_small_uint_t args_op1 = (args >> 8) & 0xff;  /* DUK_OP_POSTINCR/DUK_OP_POSTDECR */
 		duk_small_uint_t args_op2 = args >> 16;          /* DUK_OP_POSTINCP_RR/DUK_OP_POSTDECP_RR */
 
@@ -67106,40 +68662,40 @@
 
 		if (left->t == DUK_IVAL_VAR) {
 			duk_hstring *h_varname;
-			duk_reg_t reg_varbind;
+			duk_regconst_t reg_varbind;
 			duk_regconst_t rc_varname;
 
-			h_varname = duk_known_hstring(ctx, left->x1.valstack_idx);
+			h_varname = duk_known_hstring(thr, left->x1.valstack_idx);
 
 			if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) {
 				goto syntax_error;
 			}
 
-			duk_dup(ctx, left->x1.valstack_idx);
+			duk_dup(thr, left->x1.valstack_idx);
 			if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 				duk__emit_a_bc(comp_ctx,
 				               args_op1,  /* e.g. DUK_OP_POSTINCR */
-				               (duk_regconst_t) reg_res,
-				               (duk_regconst_t) reg_varbind);
+				               reg_res,
+				               reg_varbind);
 			} else {
 				duk__emit_a_bc(comp_ctx,
 				               args_op1 + 4,  /* e.g. DUK_OP_POSTINCV */
-				               (duk_regconst_t) reg_res,
+				               reg_res,
 				               rc_varname);
 			}
 
 			DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld",
 			                     (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname));
 		} else if (left->t == DUK_IVAL_PROP) {
-			duk_reg_t reg_obj;  /* allocate to reg only (not const) */
+			duk_regconst_t reg_obj;  /* allocate to reg only (not const) */
 			duk_regconst_t rc_key;
 
 			reg_obj = duk__ispec_toregconst_raw(comp_ctx, &left->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
 			rc_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 			duk__emit_a_b_c(comp_ctx,
 			                args_op2 | DUK__EMIT_FLAG_BC_REGCONST,  /* e.g. DUK_OP_POSTINCP */
-			                (duk_regconst_t) reg_res,
-			                (duk_regconst_t) reg_obj,
+			                reg_res,
+			                reg_obj,
 			                rc_key);
 		} else {
 			/* Technically return value is not needed because INVLHS will
@@ -67156,7 +68712,7 @@
 		}
 
 		DUK__SETTEMP(comp_ctx, reg_res + 1);
-		duk__ivalue_regconst(res, (duk_regconst_t) reg_res);
+		duk__ivalue_regconst(res, reg_res);
 		return;
 	}
 
@@ -67170,9 +68726,10 @@
 }
 
 DUK_LOCAL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx) {
-	duk_small_int_t tok = comp_ctx->curr_token.t;
-
-	DUK_ASSERT(tok >= DUK_TOK_MINVAL && tok <= DUK_TOK_MAXVAL);
+	duk_small_uint_t tok = comp_ctx->curr_token.t;
+
+	DUK_ASSERT_DISABLE(tok >= DUK_TOK_MINVAL);  /* unsigned */
+	DUK_ASSERT(tok <= DUK_TOK_MAXVAL);
 	DUK_ASSERT(sizeof(duk__token_lbp) == DUK_TOK_MAXVAL + 1);
 
 	/* XXX: integrate support for this into led() instead?
@@ -67215,14 +68772,13 @@
 /* main expression parser function */
 DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_ivalue tmp_alloc;   /* 'res' is used for "left", and 'tmp' for "right" */
 	duk_ivalue *tmp = &tmp_alloc;
 	duk_small_uint_t rbp;
 
 	DUK__RECURSION_INCREASE(comp_ctx, thr);
 
-	duk_require_stack(ctx, DUK__PARSE_EXPR_SLOTS);
+	duk_require_stack(thr, DUK__PARSE_EXPR_SLOTS);
 
 	/* filter out flags from exprtop rbp_flags here to save space */
 	rbp = rbp_flags & DUK__EXPR_RBP_MASK;
@@ -67232,10 +68788,10 @@
 	                     (long) comp_ctx->curr_func.paren_level));
 
 	DUK_MEMZERO(&tmp_alloc, sizeof(tmp_alloc));
-	tmp->x1.valstack_idx = duk_get_top(ctx);
+	tmp->x1.valstack_idx = duk_get_top(thr);
 	tmp->x2.valstack_idx = tmp->x1.valstack_idx + 1;
-	duk_push_undefined(ctx);
-	duk_push_undefined(ctx);
+	duk_push_undefined(thr);
+	duk_push_undefined(thr);
 
 	/* XXX: where to release temp regs in intermediate expressions?
 	 * e.g. 1+2+3 -> don't inflate temp register count when parsing this.
@@ -67252,7 +68808,7 @@
 		if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY)) {
 			DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED);
 		}
-		duk_push_undefined(ctx);
+		duk_push_undefined(thr);
 		duk__ivalue_plain_fromstack(comp_ctx, res);
 		goto cleanup;
 	}
@@ -67268,7 +68824,7 @@
  cleanup:
 	/* final result is already in 'res' */
 
-	duk_pop_2(ctx);
+	duk_pop_2(thr);
 
 	DUK__RECURSION_DECREASE(comp_ctx, thr);
 }
@@ -67299,20 +68855,20 @@
  */
 
 #if 0  /* unused */
-DUK_LOCAL duk_reg_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
+DUK_LOCAL duk_regconst_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
 	duk__expr(comp_ctx, res, rbp_flags);
 	return duk__ivalue_toreg(comp_ctx, res);
 }
 #endif
 
 #if 0  /* unused */
-DUK_LOCAL duk_reg_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
+DUK_LOCAL duk_regconst_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
 	duk__expr(comp_ctx, res, rbp_flags);
 	return duk__ivalue_totemp(comp_ctx, res);
 }
 #endif
 
-DUK_LOCAL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg) {
+DUK_LOCAL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg) {
 	DUK_ASSERT(forced_reg >= 0);
 	duk__expr(comp_ctx, res, rbp_flags);
 	duk__ivalue_toforcedreg(comp_ctx, res, forced_reg);
@@ -67340,19 +68896,19 @@
 	duk__ivalue_toplain_ignore(comp_ctx, res);
 }
 
-DUK_LOCAL duk_reg_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
+DUK_LOCAL duk_regconst_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
 	duk__exprtop(comp_ctx, res, rbp_flags);
 	return duk__ivalue_toreg(comp_ctx, res);
 }
 
 #if 0  /* unused */
-DUK_LOCAL duk_reg_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
+DUK_LOCAL duk_regconst_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) {
 	duk__exprtop(comp_ctx, res, rbp_flags);
 	return duk__ivalue_totemp(comp_ctx, res);
 }
 #endif
 
-DUK_LOCAL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg) {
+DUK_LOCAL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_regconst_t forced_reg) {
 	DUK_ASSERT(forced_reg >= 0);
 	duk__exprtop(comp_ctx, res, rbp_flags);
 	duk__ivalue_toforcedreg(comp_ctx, res, forced_reg);
@@ -67406,11 +68962,10 @@
  * as is done in 'for-in' parsing.
  */
 
-DUK_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
+DUK_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_regconst_t *out_reg_varbind, duk_regconst_t *out_rc_varname) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *h_varname;
-	duk_reg_t reg_varbind;
+	duk_regconst_t reg_varbind;
 	duk_regconst_t rc_varname;
 
 	/* assume 'var' has been eaten */
@@ -67433,17 +68988,17 @@
 		duk_uarridx_t n;
 		DUK_DDD(DUK_DDDPRINT("register variable declaration %!O in pass 1",
 		                     (duk_heaphdr *) h_varname));
-		n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx);
-		duk_push_hstring(ctx, h_varname);
-		duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n);
-		duk_push_int(ctx, DUK_DECL_TYPE_VAR + (0 << 8));
-		duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n + 1);
-	}
-
-	duk_push_hstring(ctx, h_varname);  /* push before advancing to keep reachable */
+		n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
+		duk_push_hstring(thr, h_varname);
+		duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n);
+		duk_push_int(thr, DUK_DECL_TYPE_VAR + (0 << 8));
+		duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1);
+	}
+
+	duk_push_hstring(thr, h_varname);  /* push before advancing to keep reachable */
 
 	/* register binding lookup is based on varmap (even in first pass) */
-	duk_dup_top(ctx);
+	duk_dup_top(thr);
 	(void) duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname);
 
 	duk__advance(comp_ctx);  /* eat identifier */
@@ -67459,11 +69014,11 @@
 		if (reg_varbind >= 0) {
 			duk__ivalue_toforcedreg(comp_ctx, res, reg_varbind);
 		} else {
-			duk_reg_t reg_val;
+			duk_regconst_t reg_val;
 			reg_val = duk__ivalue_toreg(comp_ctx, res);
 			duk__emit_a_bc(comp_ctx,
 			               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
-			               (duk_regconst_t) reg_val,
+			               reg_val,
 			               rc_varname);
 		}
 	} else {
@@ -67473,7 +69028,7 @@
 		}
 	}
 
-	duk_pop(ctx);  /* pop varname */
+	duk_pop(thr);  /* pop varname */
 
 	*out_rc_varname = rc_varname;
 	*out_reg_varbind = reg_varbind;
@@ -67485,7 +69040,7 @@
 }
 
 DUK_LOCAL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags) {
-	duk_reg_t reg_varbind;
+	duk_regconst_t reg_varbind;
 	duk_regconst_t rc_varname;
 
 	duk__advance(comp_ctx);  /* eat 'var' */
@@ -67503,10 +69058,9 @@
 
 DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
-	duk_int_t pc_v34_lhs;    /* start variant 3/4 left-hand-side code (L1 in doc/compiler.rst example) */
-	duk_reg_t temp_reset;    /* knock back "next temp" to this whenever possible */
-	duk_reg_t reg_temps;     /* preallocated temporaries (2) for variants 3 and 4 */
+	duk_int_t pc_v34_lhs;         /* start variant 3/4 left-hand-side code (L1 in doc/compiler.rst example) */
+	duk_regconst_t temp_reset;    /* knock back "next temp" to this whenever possible */
+	duk_regconst_t reg_temps;     /* preallocated temporaries (2) for variants 3 and 4 */
 
 	DUK_DDD(DUK_DDDPRINT("start parsing a for/for-in statement"));
 
@@ -67549,7 +69103,7 @@
 		 *  Variant 2 or 4
 		 */
 
-		duk_reg_t reg_varbind;       /* variable binding register if register-bound (otherwise < 0) */
+		duk_regconst_t reg_varbind;  /* variable binding register if register-bound (otherwise < 0) */
 		duk_regconst_t rc_varname;   /* variable name reg/const, if variable not register-bound */
 
 		duk__advance(comp_ctx);  /* eat 'var' */
@@ -67566,12 +69120,12 @@
 			if (reg_varbind >= 0) {
 				duk__emit_a_bc(comp_ctx,
 				               DUK_OP_LDREG,
-				               (duk_regconst_t) reg_varbind,
-				               (duk_regconst_t) (reg_temps + 0));
+				               reg_varbind,
+				               reg_temps + 0);
 			} else {
 				duk__emit_a_bc(comp_ctx,
 				               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
-				               (duk_regconst_t) (reg_temps + 0),
+				               reg_temps + 0,
 				               rc_varname);
 			}
 			goto parse_3_or_4;
@@ -67618,34 +69172,34 @@
 			}
 
 			if (res->t == DUK_IVAL_VAR) {
-				duk_reg_t reg_varbind;
+				duk_regconst_t reg_varbind;
 				duk_regconst_t rc_varname;
 
-				duk_dup(ctx, res->x1.valstack_idx);
+				duk_dup(thr, res->x1.valstack_idx);
 				if (duk__lookup_lhs(comp_ctx, &reg_varbind, &rc_varname)) {
 					duk__emit_a_bc(comp_ctx,
 					               DUK_OP_LDREG,
-					               (duk_regconst_t) reg_varbind,
-					               (duk_regconst_t) (reg_temps + 0));
+					               reg_varbind,
+					               reg_temps + 0);
 				} else {
 					duk__emit_a_bc(comp_ctx,
 					               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
-					               (duk_regconst_t) (reg_temps + 0),
+					               reg_temps + 0,
 					               rc_varname);
 				}
 			} else if (res->t == DUK_IVAL_PROP) {
 				/* Don't allow a constant for the object (even for a number etc), as
 				 * it goes into the 'A' field of the opcode.
 				 */
-				duk_reg_t reg_obj;
+				duk_regconst_t reg_obj;
 				duk_regconst_t rc_key;
 				reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/);  /* don't allow const */
 				rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/);
 				duk__emit_a_b_c(comp_ctx,
 				                DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE | DUK__EMIT_FLAG_BC_REGCONST,
-				                (duk_regconst_t) reg_obj,
+				                reg_obj,
 				                rc_key,
-				                (duk_regconst_t) (reg_temps + 0));
+				                reg_temps + 0);
 			} else {
 				duk__ivalue_toplain_ignore(comp_ctx, res);  /* just in case */
 				duk__emit_op_only(comp_ctx,
@@ -67765,7 +69319,7 @@
 	{
 		duk_int_t pc_l1, pc_l2, pc_l3, pc_l4, pc_l5;
 		duk_int_t pc_jumpto_l2, pc_jumpto_l3, pc_jumpto_l4, pc_jumpto_l5;
-		duk_reg_t reg_target;
+		duk_regconst_t reg_target;
 
 		DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 3 and 4, pc_v34_lhs=%ld", (long) pc_v34_lhs));
 
@@ -67800,8 +69354,8 @@
 		reg_target = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);  /* Expression */
 		duk__emit_b_c(comp_ctx,
 		              DUK_OP_INITENUM | DUK__EMIT_FLAG_B_IS_TARGET,
-		              (duk_regconst_t) (reg_temps + 1),
-		              (duk_regconst_t) reg_target);
+		              reg_temps + 1,
+		              reg_target);
 		pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx);
 		DUK__SETTEMP(comp_ctx, temp_reset);
 
@@ -67820,8 +69374,8 @@
 		pc_l4 = duk__get_current_pc(comp_ctx);
 		duk__emit_b_c(comp_ctx,
 		              DUK_OP_NEXTENUM | DUK__EMIT_FLAG_B_IS_TARGET | DUK__EMIT_FLAG_RESERVE_JUMPSLOT,
-		              (duk_regconst_t) (reg_temps + 0),
-		              (duk_regconst_t) (reg_temps + 1));
+		              reg_temps + 0,
+		              reg_temps + 1);
 		pc_jumpto_l5 = comp_ctx->emit_jumpslot_pc;  /* NEXTENUM jump slot: executed when enum finished */
 		duk__emit_jump(comp_ctx, pc_l1);  /* jump to next loop, using reg_v34_iter as iterated value */
 
@@ -67858,10 +69412,10 @@
 
 DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_reg_t temp_at_loop;
+	duk_regconst_t temp_at_loop;
 	duk_regconst_t rc_switch;    /* reg/const for switch value */
 	duk_regconst_t rc_case;      /* reg/const for case value */
-	duk_reg_t reg_temp;          /* general temp register */
+	duk_regconst_t reg_temp;     /* general temp register */
 	duk_int_t pc_prevcase = -1;
 	duk_int_t pc_prevstmt = -1;
 	duk_int_t pc_default = -1;   /* -1 == not set, -2 == pending (next statement list) */
@@ -67904,7 +69458,7 @@
 
 	for (;;) {
 		duk_int_t num_stmts;
-		duk_small_int_t tok;
+		duk_small_uint_t tok;
 
 		/* sufficient for keeping temp reg numbers in check */
 		DUK__SETTEMP(comp_ctx, temp_at_loop);
@@ -67936,10 +69490,10 @@
 			reg_temp = DUK__ALLOCTEMP(comp_ctx);
 			duk__emit_a_b_c(comp_ctx,
 			                DUK_OP_SEQ | DUK__EMIT_FLAG_BC_REGCONST,
-			                (duk_regconst_t) reg_temp,
+			                reg_temp,
 			                rc_switch,
 			                rc_case);
-			duk__emit_if_true_skip(comp_ctx, (duk_regconst_t) reg_temp);
+			duk__emit_if_true_skip(comp_ctx, reg_temp);
 
 			/* jump to next case clause */
 			pc_prevcase = duk__emit_jump_empty(comp_ctx);  /* no match, next case */
@@ -68056,7 +69610,7 @@
 }
 
 DUK_LOCAL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
-	duk_reg_t temp_reset;
+	duk_regconst_t temp_reset;
 	duk_regconst_t rc_cond;
 	duk_int_t pc_jump_false;
 
@@ -68130,7 +69684,7 @@
 }
 
 DUK_LOCAL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) {
-	duk_reg_t temp_reset;
+	duk_regconst_t temp_reset;
 	duk_regconst_t rc_cond;
 	duk_int_t pc_start;
 	duk_int_t pc_jump_false;
@@ -68249,7 +69803,7 @@
 		pc_after_expr = duk__get_current_pc(comp_ctx);
 
 		/* Tail call check: if last opcode emitted was CALL, and
-		 * the context allows it, change the CALL to TAILCALL.
+		 * the context allows it, add a tailcall flag to the CALL.
 		 * This doesn't guarantee that a tail call will be allowed at
 		 * runtime, so the RETURN must still be emitted.  (Duktape
 		 * 0.10.0 avoided this and simulated a RETURN if a tail call
@@ -68295,13 +69849,13 @@
 
 			ins = instr->ins;
 			op = (duk_small_uint_t) DUK_DEC_OP(ins);
-			if (op == DUK_OP_CALL &&
-			    DUK__ISTEMP(comp_ctx, rc_val) /* see above */) {
+			if ((op & ~0x0fU) == DUK_OP_CALL0 &&
+			    DUK__ISREG_TEMP(comp_ctx, rc_val) /* see above */) {
 				DUK_DDD(DUK_DDDPRINT("return statement detected a tail call opportunity: "
 				                     "catch depth is 0, duk__exprtop() emitted >= 1 instructions, "
 				                     "and last instruction is a CALL "
 				                     "-> change to TAILCALL"));
-				ins = (ins & ~DUK_BC_SHIFTED_MASK_OP) | (DUK_OP_TAILCALL << DUK_BC_SHIFT_OP);
+				ins |= DUK_ENC_OP(DUK_BC_CALL_FLAG_TAILCALL);
 				instr->ins = ins;
 			}
 		}
@@ -68321,7 +69875,7 @@
 }
 
 DUK_LOCAL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
-	duk_reg_t reg_val;
+	duk_regconst_t reg_val;
 
 	duk__advance(comp_ctx);  /* eat 'throw' */
 
@@ -68334,13 +69888,12 @@
 	reg_val = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/);
 	duk__emit_bc(comp_ctx,
 	             DUK_OP_THROW,
-	             (duk_regconst_t) reg_val);
+	             reg_val);
 }
 
 DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
-	duk_reg_t reg_catch;      /* reg_catch+0 and reg_catch+1 are reserved for TRYCATCH */
+	duk_regconst_t reg_catch;      /* reg_catch+0 and reg_catch+1 are reserved for TRYCATCH */
 	duk_regconst_t rc_varname = 0;
 	duk_small_uint_t trycatch_flags = 0;
 	duk_int_t pc_ldconst = -1;
@@ -68414,7 +69967,7 @@
 		duk_hstring *h_var;
 		duk_int_t varmap_value;  /* for storing/restoring the varmap binding for catch variable */
 
-		DUK_DDD(DUK_DDDPRINT("stack top at start of catch clause: %ld", (long) duk_get_top(ctx)));
+		DUK_DDD(DUK_DDDPRINT("stack top at start of catch clause: %ld", (long) duk_get_top(thr)));
 
 		trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_CATCH;
 
@@ -68430,7 +69983,7 @@
 		h_var = comp_ctx->curr_token.str1;
 		DUK_ASSERT(h_var != NULL);
 
-		duk_push_hstring(ctx, h_var);  /* keep in on valstack, use borrowed ref below */
+		duk_push_hstring(thr, h_var);  /* keep in on valstack, use borrowed ref below */
 
 		if (comp_ctx->curr_func.is_strict &&
 		    ((h_var == DUK_HTHREAD_STRING_EVAL(thr)) ||
@@ -68439,7 +69992,7 @@
 			goto syntax_error;
 		}
 
-		duk_dup_top(ctx);
+		duk_dup_top(thr);
 		rc_varname = duk__getconst(comp_ctx);
 		DUK_DDD(DUK_DDDPRINT("catch clause, rc_varname=0x%08lx (%ld)",
 		                     (unsigned long) rc_varname, (long) rc_varname));
@@ -68450,60 +70003,60 @@
 		duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);
 
 		DUK_DDD(DUK_DDDPRINT("varmap before modifying for catch clause: %!iT",
-		                     (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx)));
-
-		duk_dup_top(ctx);
-		duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx);
-		if (duk_is_undefined(ctx, -1)) {
+		                     (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));
+
+		duk_dup_top(thr);
+		duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
+		if (duk_is_undefined(thr, -1)) {
 			varmap_value = -2;
-		} else if (duk_is_null(ctx, -1)) {
+		} else if (duk_is_null(thr, -1)) {
 			varmap_value = -1;
 		} else {
-			DUK_ASSERT(duk_is_number(ctx, -1));
-			varmap_value = duk_get_int(ctx, -1);
+			DUK_ASSERT(duk_is_number(thr, -1));
+			varmap_value = duk_get_int(thr, -1);
 			DUK_ASSERT(varmap_value >= 0);
 		}
-		duk_pop(ctx);
+		duk_pop(thr);
 
 #if 0
 		/* It'd be nice to do something like this - but it doesn't
 		 * work for closures created inside the catch clause.
 		 */
-		duk_dup_top(ctx);
-		duk_push_int(ctx, (duk_int_t) (reg_catch + 0));
-		duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx);
-#endif
-		duk_dup_top(ctx);
-		duk_push_null(ctx);
-		duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx);
+		duk_dup_top(thr);
+		duk_push_int(thr, (duk_int_t) (reg_catch + 0));
+		duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);
+#endif
+		duk_dup_top(thr);
+		duk_push_null(thr);
+		duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);
 
 		duk__emit_a_bc(comp_ctx,
 		               DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE,
-		               (duk_regconst_t) (reg_catch + 0) /*value*/,
+		               reg_catch + 0 /*value*/,
 		               rc_varname /*varname*/);
 
 		DUK_DDD(DUK_DDDPRINT("varmap before parsing catch clause: %!iT",
-		                     (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx)));
+		                     (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));
 
 		duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/);
 		/* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */
 
 		if (varmap_value == -2) {
 			/* not present */
-			duk_del_prop(ctx, comp_ctx->curr_func.varmap_idx);
+			duk_del_prop(thr, comp_ctx->curr_func.varmap_idx);
 		} else {
 			if (varmap_value == -1) {
-				duk_push_null(ctx);
+				duk_push_null(thr);
 			} else {
 				DUK_ASSERT(varmap_value >= 0);
-				duk_push_int(ctx, varmap_value);
-			}
-			duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx);
+				duk_push_int(thr, varmap_value);
+			}
+			duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);
 		}
 		/* varname is popped by above code */
 
 		DUK_DDD(DUK_DDDPRINT("varmap after restore catch clause: %!iT",
-		                     (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx)));
+		                     (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx)));
 
 		duk__emit_op_only(comp_ctx,
 		                  DUK_OP_ENDCATCH);
@@ -68516,7 +70069,7 @@
 
 		trycatch_flags |= DUK_BC_TRYCATCH_FLAG_CATCH_BINDING;
 
-		DUK_DDD(DUK_DDDPRINT("stack top at end of catch clause: %ld", (long) duk_get_top(ctx)));
+		DUK_DDD(DUK_DDDPRINT("stack top at end of catch clause: %ld", (long) duk_get_top(thr)));
 	}
 
 	if (comp_ctx->curr_token.t == DUK_TOK_FINALLY) {
@@ -68529,9 +70082,9 @@
 		duk__advance_expect(comp_ctx, DUK_TOK_LCURLY);
 		duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/);
 		/* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */
-		duk__emit_b(comp_ctx,
-		            DUK_OP_ENDFIN,
-		            reg_catch);  /* rethrow */
+		duk__emit_abc(comp_ctx,
+		              DUK_OP_ENDFIN,
+		              reg_catch);  /* rethrow */
 	}
 
 	if (!(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) &&
@@ -68575,7 +70128,7 @@
 DUK_LOCAL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) {
 	duk_int_t pc_trycatch;
 	duk_int_t pc_finished;
-	duk_reg_t reg_catch;
+	duk_regconst_t reg_catch;
 	duk_small_uint_t trycatch_flags;
 
 	if (comp_ctx->curr_func.is_strict) {
@@ -68597,7 +70150,7 @@
 	duk__emit_a_bc(comp_ctx,
 	                DUK_OP_TRYCATCH | DUK__EMIT_FLAG_NO_SHUFFLE_A,
 	                (duk_regconst_t) trycatch_flags /*a*/,
-	                (duk_regconst_t) reg_catch /*bc*/);
+	                reg_catch /*bc*/);
 	duk__emit_invalid(comp_ctx);  /* catch jump */
 	duk__emit_invalid(comp_ctx);  /* finished jump */
 
@@ -68637,10 +70190,9 @@
  */
 DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_bool_t dir_prol_at_entry;    /* directive prologue status at entry */
-	duk_reg_t temp_at_entry;
-	duk_uarridx_t labels_len_at_entry;
+	duk_regconst_t temp_at_entry;
+	duk_size_t labels_len_at_entry;
 	duk_int_t pc_at_entry;           /* assumed to also be PC of "LABEL" */
 	duk_int_t stmt_id;
 	duk_small_uint_t stmt_flags = 0;
@@ -68652,7 +70204,7 @@
 
 	temp_at_entry = DUK__GETTEMP(comp_ctx);
 	pc_at_entry = duk__get_current_pc(comp_ctx);
-	labels_len_at_entry = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.labelnames_idx);
+	labels_len_at_entry = duk_get_length(thr, comp_ctx->curr_func.labelnames_idx);
 	stmt_id = comp_ctx->curr_func.stmt_next++;
 	dir_prol_at_entry = comp_ctx->curr_func.in_directive_prologue;
 
@@ -68737,7 +70289,7 @@
 			DUK_DDD(DUK_DDDPRINT("function declaration statement"));
 
 #if defined(DUK_USE_ASSERTIONS)
-			top_before = duk_get_top(ctx);
+			top_before = duk_get_top(thr);
 #endif
 
 			duk__advance(comp_ctx);  /* eat 'function' */
@@ -68751,18 +70303,18 @@
 				duk_uarridx_t n;
 
 #if defined(DUK_USE_ASSERTIONS)
-				DUK_ASSERT(duk_get_top(ctx) == top_before + 1);
+				DUK_ASSERT(duk_get_top(thr) == top_before + 1);
 #endif
 				DUK_DDD(DUK_DDDPRINT("register function declaration %!T in pass 1, fnum %ld",
-				                     duk_get_tval(ctx, -1), (long) fnum));
-				n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx);
+				                     duk_get_tval(thr, -1), (long) fnum));
+				n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
 				/* funcname is at index -1 */
-				duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n);
-				duk_push_int(ctx, (duk_int_t) (DUK_DECL_TYPE_FUNC + (fnum << 8)));
-				duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n + 1);
-			} else {
-#if defined(DUK_USE_ASSERTIONS)
-				DUK_ASSERT(duk_get_top(ctx) == top_before);
+				duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n);
+				duk_push_int(thr, (duk_int_t) (DUK_DECL_TYPE_FUNC + (fnum << 8)));
+				duk_put_prop_index(thr, comp_ctx->curr_func.decls_idx, n + 1);
+			} else {
+#if defined(DUK_USE_ASSERTIONS)
+				DUK_ASSERT(duk_get_top(thr) == top_before);
 #endif
 			}
 
@@ -68973,7 +70525,7 @@
 			/* expected ival */
 			DUK_ASSERT(res->t == DUK_IVAL_VAR);
 			DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE);
-			DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(ctx, res->x1.valstack_idx)));
+			DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx)));
 			h_lab = comp_ctx->prev_token.str1;
 			DUK_ASSERT(h_lab != NULL);
 
@@ -69011,7 +70563,7 @@
 			/* expected ival */
 			DUK_ASSERT(res->t == DUK_IVAL_PLAIN);
 			DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE);
-			DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(ctx, res->x1.valstack_idx)));
+			DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(thr, res->x1.valstack_idx)));
 			h_dir = comp_ctx->prev_token.str1;
 			DUK_ASSERT(h_dir != NULL);
 
@@ -69079,7 +70631,7 @@
 	 */
 
 	if (stmt_flags & DUK__HAS_VAL) {
-		duk_reg_t reg_stmt_value = comp_ctx->curr_func.reg_stmt_value;
+		duk_regconst_t reg_stmt_value = comp_ctx->curr_func.reg_stmt_value;
 		if (reg_stmt_value >= 0) {
 			duk__ivalue_toforcedreg(comp_ctx, res, reg_stmt_value);
 		} else {
@@ -69158,13 +70710,12 @@
 
 DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_ivalue res_alloc;
 	duk_ivalue *res = &res_alloc;
 
 	/* Setup state.  Initial ivalue is 'undefined'. */
 
-	duk_require_stack(ctx, DUK__PARSE_STATEMENTS_SLOTS);
+	duk_require_stack(thr, DUK__PARSE_STATEMENTS_SLOTS);
 
 	/* XXX: 'res' setup can be moved to function body level; in fact, two 'res'
 	 * intermediate values suffice for parsing of each function.  Nesting is needed
@@ -69174,10 +70725,10 @@
 	DUK_MEMZERO(&res_alloc, sizeof(res_alloc));
 	res->t = DUK_IVAL_PLAIN;
 	res->x1.t = DUK_ISPEC_VALUE;
-	res->x1.valstack_idx = duk_get_top(ctx);
+	res->x1.valstack_idx = duk_get_top(thr);
 	res->x2.valstack_idx = res->x1.valstack_idx + 1;
-	duk_push_undefined(ctx);
-	duk_push_undefined(ctx);
+	duk_push_undefined(thr);
+	duk_push_undefined(thr);
 
 	/* Parse statements until a closing token (EOF or '}') is found. */
 
@@ -69209,7 +70760,7 @@
 
 	/* Tear down state. */
 
-	duk_pop_2(ctx);
+	duk_pop_2(thr);
 }
 
 /*
@@ -69244,9 +70795,8 @@
  * handle cases with a very large number of variables?
  */
 
-DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_reg_t *out_stmt_value_reg) {
+DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_regconst_t *out_stmt_value_reg) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *h_name;
 	duk_bool_t configurable_bindings;
 	duk_uarridx_t num_args;
@@ -69259,7 +70809,7 @@
 #endif
 
 #if defined(DUK_USE_ASSERTIONS)
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 #endif
 
 	/*
@@ -69276,21 +70826,21 @@
 	 *  (there's no support for shuffling them now).
 	 */
 
-	num_args = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.argnames_idx);
+	num_args = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx);
 	DUK_DDD(DUK_DDDPRINT("num_args=%ld", (long) num_args));
 	/* XXX: check num_args */
 
 	for (i = 0; i < num_args; i++) {
-		duk_get_prop_index(ctx, comp_ctx->curr_func.argnames_idx, i);
-		h_name = duk_known_hstring(ctx, -1);
+		duk_get_prop_index(thr, comp_ctx->curr_func.argnames_idx, i);
+		h_name = duk_known_hstring(thr, -1);
 
 		if (comp_ctx->curr_func.is_strict) {
 			if (duk__hstring_is_eval_or_arguments(comp_ctx, h_name)) {
 				DUK_DDD(DUK_DDDPRINT("arg named 'eval' or 'arguments' in strict mode -> SyntaxError"));
 				goto error_argname;
 			}
-			duk_dup_top(ctx);
-			if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) {
+			duk_dup_top(thr);
+			if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
 				DUK_DDD(DUK_DDDPRINT("duplicate arg name in strict mode -> SyntaxError"));
 				goto error_argname;
 			}
@@ -69315,14 +70865,14 @@
 
 		/* only functions can have arguments */
 		DUK_ASSERT(comp_ctx->curr_func.is_function);
-		duk_push_uarridx(ctx, i);  /* -> [ ... name index ] */
-		duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* -> [ ... ] */
+		duk_push_uarridx(thr, i);  /* -> [ ... name index ] */
+		duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* -> [ ... ] */
 
 		/* no code needs to be emitted, the regs already have values */
 	}
 
 	/* use temp_next for tracking register allocations */
-	DUK__SETTEMP_CHECKMAX(comp_ctx, (duk_reg_t) num_args);
+	DUK__SETTEMP_CHECKMAX(comp_ctx, (duk_regconst_t) num_args);
 
 	/*
 	 *  After arguments, allocate special registers (like shuffling temps)
@@ -69332,7 +70882,7 @@
 		*out_stmt_value_reg = DUK__ALLOCTEMP(comp_ctx);
 	}
 	if (comp_ctx->curr_func.needs_shuffle) {
-		duk_reg_t shuffle_base = DUK__ALLOCTEMPS(comp_ctx, 3);
+		duk_regconst_t shuffle_base = DUK__ALLOCTEMPS(comp_ctx, 3);
 		comp_ctx->curr_func.shuffle1 = shuffle_base;
 		comp_ctx->curr_func.shuffle2 = shuffle_base + 1;
 		comp_ctx->curr_func.shuffle3 = shuffle_base + 2;
@@ -69350,47 +70900,47 @@
 	 *  Function declarations
 	 */
 
-	num_decls = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx);
+	num_decls = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.decls_idx);
 	DUK_DDD(DUK_DDDPRINT("num_decls=%ld -> %!T",
 	                     (long) num_decls,
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.decls_idx)));
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.decls_idx)));
 	for (i = 0; i < num_decls; i += 2) {
 		duk_int_t decl_type;
 		duk_int_t fnum;
 
-		duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
-		decl_type = duk_to_int(ctx, -1);
+		duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
+		decl_type = duk_to_int(thr, -1);
 		fnum = decl_type >> 8;  /* XXX: macros */
 		decl_type = decl_type & 0xff;
-		duk_pop(ctx);
+		duk_pop(thr);
 
 		if (decl_type != DUK_DECL_TYPE_FUNC) {
 			continue;
 		}
 
-		duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i);  /* decl name */
+		duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */
 
 		/* XXX: spilling */
 		if (comp_ctx->curr_func.is_function) {
-			duk_reg_t reg_bind;
-			duk_dup_top(ctx);
-			if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) {
+			duk_regconst_t reg_bind;
+			duk_dup_top(thr);
+			if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
 				/* shadowed; update value */
-				duk_dup_top(ctx);
-				duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx);
-				reg_bind = duk_to_int(ctx, -1);  /* [ ... name reg_bind ] */
+				duk_dup_top(thr);
+				duk_get_prop(thr, comp_ctx->curr_func.varmap_idx);
+				reg_bind = duk_to_int(thr, -1);  /* [ ... name reg_bind ] */
 				duk__emit_a_bc(comp_ctx,
 				               DUK_OP_CLOSURE,
-				               (duk_regconst_t) reg_bind,
+				               reg_bind,
 				               (duk_regconst_t) fnum);
 			} else {
 				/* function: always register bound */
 				reg_bind = DUK__ALLOCTEMP(comp_ctx);
 				duk__emit_a_bc(comp_ctx,
 				               DUK_OP_CLOSURE,
-				               (duk_regconst_t) reg_bind,
+				               reg_bind,
 				               (duk_regconst_t) fnum);
-				duk_push_int(ctx, (duk_int_t) reg_bind);
+				duk_push_int(thr, (duk_int_t) reg_bind);
 			}
 		} else {
 			/* Function declaration for global/eval code is emitted even
@@ -69401,14 +70951,14 @@
 			 * update the binding value.
 			 */
 
-			duk_reg_t reg_temp = DUK__ALLOCTEMP(comp_ctx);
-			duk_dup_top(ctx);
+			duk_regconst_t reg_temp = DUK__ALLOCTEMP(comp_ctx);
+			duk_dup_top(thr);
 			rc_name = duk__getconst(comp_ctx);
-			duk_push_null(ctx);
+			duk_push_null(thr);
 
 			duk__emit_a_bc(comp_ctx,
 			               DUK_OP_CLOSURE,
-			               (duk_regconst_t) reg_temp,
+			               reg_temp,
 			               (duk_regconst_t) fnum);
 
 			declvar_flags = DUK_PROPDESC_FLAG_WRITABLE |
@@ -69423,19 +70973,19 @@
 			                DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST,
 			                (duk_regconst_t) declvar_flags /*flags*/,
 			                rc_name /*name*/,
-			                (duk_regconst_t) reg_temp /*value*/);
+			                reg_temp /*value*/);
 
 			DUK__SETTEMP(comp_ctx, reg_temp);  /* forget temp */
 		}
 
 		DUK_DDD(DUK_DDDPRINT("function declaration to varmap: %!T -> %!T",
-		                     (duk_tval *) duk_get_tval(ctx, -2),
-		                     (duk_tval *) duk_get_tval(ctx, -1)));
+		                     (duk_tval *) duk_get_tval(thr, -2),
+		                     (duk_tval *) duk_get_tval(thr, -1)));
 
 #if defined(DUK_USE_FASTINT)
-		DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(ctx, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(ctx, -1)));
-#endif
-		duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
+		DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(thr, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(thr, -1)));
+#endif
+		duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
 	}
 
 	/*
@@ -69445,7 +70995,7 @@
 	 *  'arguments' is referenced inside the function body.
 	 */
 
-	if (duk_has_prop_stridx(ctx, comp_ctx->curr_func.varmap_idx, DUK_STRIDX_LC_ARGUMENTS)) {
+	if (duk_has_prop_stridx(thr, comp_ctx->curr_func.varmap_idx, DUK_STRIDX_LC_ARGUMENTS)) {
 		DUK_DDD(DUK_DDDPRINT("'arguments' is shadowed by argument or function declaration "
 		                     "-> arguments object creation can be skipped"));
 		comp_ctx->curr_func.is_arguments_shadowed = 1;
@@ -69462,22 +71012,22 @@
 	for (i = 0; i < num_decls; i += 2) {
 		duk_int_t decl_type;
 
-		duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
-		decl_type = duk_to_int(ctx, -1);
+		duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i + 1);  /* decl type */
+		decl_type = duk_to_int(thr, -1);
 		decl_type = decl_type & 0xff;
-		duk_pop(ctx);
+		duk_pop(thr);
 
 		if (decl_type != DUK_DECL_TYPE_VAR) {
 			continue;
 		}
 
-		duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i);  /* decl name */
-
-		if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) {
+		duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */
+
+		if (duk_has_prop(thr, comp_ctx->curr_func.varmap_idx)) {
 			/* shadowed, ignore */
 		} else {
-			duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i);  /* decl name */
-			h_name = duk_known_hstring(ctx, -1);
+			duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i);  /* decl name */
+			h_name = duk_known_hstring(thr, -1);
 
 			if (h_name == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr) &&
 			    !comp_ctx->curr_func.is_arguments_shadowed) {
@@ -69485,19 +71035,19 @@
 				DUK_DDD(DUK_DDDPRINT("'arguments' not shadowed by a function declaration, "
 				                     "but appears as a variable declaration -> treat as "
 				                     "a no-op for variable declaration purposes"));
-				duk_pop(ctx);
+				duk_pop(thr);
 				continue;
 			}
 
 			/* XXX: spilling */
 			if (comp_ctx->curr_func.is_function) {
-				duk_reg_t reg_bind = DUK__ALLOCTEMP(comp_ctx);
+				duk_regconst_t reg_bind = DUK__ALLOCTEMP(comp_ctx);
 				/* no need to init reg, it will be undefined on entry */
-				duk_push_int(ctx, (duk_int_t) reg_bind);
-			} else {
-				duk_dup_top(ctx);
+				duk_push_int(thr, (duk_int_t) reg_bind);
+			} else {
+				duk_dup_top(thr);
 				rc_name = duk__getconst(comp_ctx);
-				duk_push_null(ctx);
+				duk_push_null(thr);
 
 				declvar_flags = DUK_PROPDESC_FLAG_WRITABLE |
 			                        DUK_PROPDESC_FLAG_ENUMERABLE;
@@ -69509,10 +71059,10 @@
 				                DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_BC_REGCONST,
 				                (duk_regconst_t) declvar_flags /*flags*/,
 				                rc_name /*name*/,
-				                (duk_regconst_t) 0 /*value*/);
-			}
-
-			duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
+				                0 /*value*/);
+			}
+
+			duk_put_prop(thr, comp_ctx->curr_func.varmap_idx);  /* [ ... name reg/null ] -> [ ... ] */
 		}
 	}
 
@@ -69521,10 +71071,10 @@
 	 */
 
 	DUK_DDD(DUK_DDDPRINT("varmap: %!T, is_arguments_shadowed=%ld",
-	                     (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx),
+	                     (duk_tval *) duk_get_tval(thr, comp_ctx->curr_func.varmap_idx),
 	                     (long) comp_ctx->curr_func.is_arguments_shadowed));
 
-	DUK_ASSERT_TOP(ctx, entry_top);
+	DUK_ASSERT_TOP(thr, entry_top);
 	return;
 
  error_outofregs:
@@ -69575,16 +71125,14 @@
 DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expect_eof, duk_bool_t implicit_return_value, duk_small_int_t expect_token) {
 	duk_compiler_func *func;
 	duk_hthread *thr;
-	duk_context *ctx;
-	duk_reg_t reg_stmt_value = -1;
+	duk_regconst_t reg_stmt_value = -1;
 	duk_lexer_point lex_pt;
-	duk_reg_t temp_first;
+	duk_regconst_t temp_first;
 	duk_small_int_t compile_round = 1;
 
 	DUK_ASSERT(comp_ctx != NULL);
 
 	thr = comp_ctx->thr;
-	ctx = (duk_context *) thr;
 	DUK_ASSERT(thr != NULL);
 
 	func = &comp_ctx->curr_func;
@@ -69592,7 +71140,7 @@
 
 	DUK__RECURSION_INCREASE(comp_ctx, thr);
 
-	duk_require_stack(ctx, DUK__FUNCTION_BODY_REQUIRE_SLOTS);
+	duk_require_stack(thr, DUK__FUNCTION_BODY_REQUIRE_SLOTS);
 
 	/*
 	 *  Store lexer position for a later rewind
@@ -69807,7 +71355,7 @@
 	DUK_ASSERT(comp_ctx->curr_func.catch_depth == 0);
 	if (reg_stmt_value >= 0) {
 		DUK_ASSERT(DUK__ISREG(reg_stmt_value));
-		duk__emit_bc(comp_ctx, DUK_OP_RETREG, (duk_regconst_t) reg_stmt_value /*reg*/);
+		duk__emit_bc(comp_ctx, DUK_OP_RETREG, reg_stmt_value /*reg*/);
 	} else {
 		duk__emit_op_only(comp_ctx, DUK_OP_RETUNDEF);
 	}
@@ -69850,7 +71398,6 @@
 /* Parse formals. */
 DUK_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_bool_t first = 1;
 	duk_uarridx_t n;
 
@@ -69876,7 +71423,7 @@
 		 */
 
 		if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) {
-			DUK_ERROR_SYNTAX(thr, "expected identifier");
+			DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER);
 		}
 		DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER);
 		DUK_ASSERT(comp_ctx->curr_token.str1 != NULL);
@@ -69884,9 +71431,9 @@
 		                     (duk_heaphdr *) comp_ctx->curr_token.str1));
 
 		/* XXX: append primitive */
-		duk_push_hstring(ctx, comp_ctx->curr_token.str1);
-		n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.argnames_idx);
-		duk_put_prop_index(ctx, comp_ctx->curr_func.argnames_idx, n);
+		duk_push_hstring(thr, comp_ctx->curr_token.str1);
+		n = (duk_uarridx_t) duk_get_length(thr, comp_ctx->curr_func.argnames_idx);
+		duk_put_prop_index(thr, comp_ctx->curr_func.argnames_idx, n);
 
 		duk__advance(comp_ctx);  /* eat identifier */
 	}
@@ -69898,7 +71445,6 @@
  */
 DUK_LOCAL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_token *tok;
 	duk_bool_t no_advance;
 
@@ -69937,22 +71483,22 @@
 	if (flags & DUK__FUNC_FLAG_GETSET) {
 		/* PropertyName -> IdentifierName | StringLiteral | NumericLiteral */
 		if (tok->t_nores == DUK_TOK_IDENTIFIER || tok->t == DUK_TOK_STRING) {
-			duk_push_hstring(ctx, tok->str1);       /* keep in valstack */
+			duk_push_hstring(thr, tok->str1);       /* keep in valstack */
 		} else if (tok->t == DUK_TOK_NUMBER) {
-			duk_push_number(ctx, tok->num);
-			duk_to_string(ctx, -1);
+			duk_push_number(thr, tok->num);
+			duk_to_string(thr, -1);
 		} else {
 			DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_GETSET_NAME);
 		}
-		comp_ctx->curr_func.h_name = duk_known_hstring(ctx, -1);  /* borrowed reference */
+		comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1);  /* borrowed reference */
 	} else {
 		/* Function name is an Identifier (not IdentifierName), but we get
 		 * the raw name (not recognizing keywords) here and perform the name
 		 * checks only after pass 1.
 		 */
 		if (tok->t_nores == DUK_TOK_IDENTIFIER) {
-			duk_push_hstring(ctx, tok->str1);       /* keep in valstack */
-			comp_ctx->curr_func.h_name = duk_known_hstring(ctx, -1);  /* borrowed reference */
+			duk_push_hstring(thr, tok->str1);       /* keep in valstack */
+			comp_ctx->curr_func.h_name = duk_known_hstring(thr, -1);  /* borrowed reference */
 		} else {
 			/* valstack will be unbalanced, which is OK */
 			DUK_ASSERT((flags & DUK__FUNC_FLAG_GETSET) == 0);
@@ -70019,7 +71565,6 @@
  */
 DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_small_uint_t flags) {
 	duk_hthread *thr = comp_ctx->thr;
-	duk_context *ctx = (duk_context *) thr;
 	duk_compiler_func old_func;
 	duk_idx_t entry_top;
 	duk_int_t fnum;
@@ -70032,12 +71577,12 @@
 		duk_lexer_point lex_pt;
 
 		fnum = comp_ctx->curr_func.fnum_next++;
-		duk_get_prop_index(ctx, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
-		lex_pt.offset = duk_to_int(ctx, -1);
-		duk_pop(ctx);
-		duk_get_prop_index(ctx, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));
-		lex_pt.line = duk_to_int(ctx, -1);
-		duk_pop(ctx);
+		duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
+		lex_pt.offset = (duk_size_t) duk_to_uint(thr, -1);
+		duk_pop(thr);
+		duk_get_prop_index(thr, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));
+		lex_pt.line = duk_to_int(thr, -1);
+		duk_pop(thr);
 
 		DUK_DDD(DUK_DDDPRINT("second pass of an inner func, skip the function, reparse closing brace; lex offset=%ld, line=%ld",
 		                     (long) lex_pt.offset, (long) lex_pt.line));
@@ -70056,7 +71601,7 @@
 	 *  to restore it later, and switch to using a new function in comp_ctx.
 	 */
 
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 	DUK_DDD(DUK_DDDPRINT("before func: entry_top=%ld, curr_tok.start_offset=%ld",
 	                     (long) entry_top, (long) comp_ctx->curr_token.start_offset));
 
@@ -70099,7 +71644,7 @@
 	DUK_ASSERT(comp_ctx->lex.input[comp_ctx->prev_token.start_offset] == (duk_uint8_t) DUK_ASC_RCURLY);
 
 	/* XXX: append primitive */
-	DUK_ASSERT(duk_get_length(ctx, old_func.funcs_idx) == (duk_size_t) (old_func.fnum_next * 3));
+	DUK_ASSERT(duk_get_length(thr, old_func.funcs_idx) == (duk_size_t) (old_func.fnum_next * 3));
 	fnum = old_func.fnum_next++;
 
 	if (fnum > DUK__MAX_FUNCS) {
@@ -70107,11 +71652,11 @@
 	}
 
 	/* array writes autoincrement length */
-	(void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3));
-	duk_push_size_t(ctx, comp_ctx->prev_token.start_offset);
-	(void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
-	duk_push_int(ctx, comp_ctx->prev_token.start_line);
-	(void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));
+	(void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3));
+	duk_push_size_t(thr, comp_ctx->prev_token.start_offset);
+	(void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1));
+	duk_push_int(thr, comp_ctx->prev_token.start_line);
+	(void) duk_put_prop_index(thr, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2));
 
 	/*
 	 *  Cleanup: restore original function, restore valstack state.
@@ -70122,11 +71667,11 @@
 
 	if (flags & DUK__FUNC_FLAG_PUSHNAME_PASS1) {
 		DUK_ASSERT(comp_ctx->curr_func.h_name != NULL);
-		duk_push_hstring(ctx, comp_ctx->curr_func.h_name);
-		duk_replace(ctx, entry_top);
-		duk_set_top(ctx, entry_top + 1);
-	} else {
-		duk_set_top(ctx, entry_top);
+		duk_push_hstring(thr, comp_ctx->curr_func.h_name);
+		duk_replace(thr, entry_top);
+		duk_set_top(thr, entry_top + 1);
+	} else {
+		duk_set_top(thr, entry_top);
 	}
 	DUK_MEMCPY((void *) &comp_ctx->curr_func, (void *) &old_func, sizeof(duk_compiler_func));
 
@@ -70147,8 +71692,7 @@
 
 /* XXX: source code property */
 
-DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_hthread *thr, void *udata) {
 	duk_hstring *h_filename;
 	duk__compiler_stkstate *comp_stk;
 	duk_compiler_ctx *comp_ctx;
@@ -70167,7 +71711,7 @@
 	 *  Arguments check
 	 */
 
-	entry_top = duk_get_top(ctx);
+	entry_top = duk_get_top(thr);
 	DUK_ASSERT(entry_top >= 1);
 
 	comp_stk = (duk__compiler_stkstate *) udata;
@@ -70181,7 +71725,7 @@
 	is_strict = (flags & DUK_COMPILE_STRICT ? 1 : 0);
 	is_funcexpr = (flags & DUK_COMPILE_FUNCEXPR ? 1 : 0);
 
-	h_filename = duk_get_hstring(ctx, -1);  /* may be undefined */
+	h_filename = duk_get_hstring(thr, -1);  /* may be undefined */
 
 	/*
 	 *  Init compiler and lexer contexts
@@ -70197,13 +71741,13 @@
 	comp_ctx->curr_token.str2 = NULL;
 #endif
 
-	duk_require_stack(ctx, DUK__COMPILE_ENTRY_SLOTS);
-
-	duk_push_dynamic_buffer(ctx, 0);       /* entry_top + 0 */
-	duk_push_undefined(ctx);               /* entry_top + 1 */
-	duk_push_undefined(ctx);               /* entry_top + 2 */
-	duk_push_undefined(ctx);               /* entry_top + 3 */
-	duk_push_undefined(ctx);               /* entry_top + 4 */
+	duk_require_stack(thr, DUK__COMPILE_ENTRY_SLOTS);
+
+	duk_push_dynamic_buffer(thr, 0);       /* entry_top + 0 */
+	duk_push_undefined(thr);               /* entry_top + 1 */
+	duk_push_undefined(thr);               /* entry_top + 2 */
+	duk_push_undefined(thr);               /* entry_top + 3 */
+	duk_push_undefined(thr);               /* entry_top + 4 */
 
 	comp_ctx->thr = thr;
 	comp_ctx->h_filename = h_filename;
@@ -70221,7 +71765,7 @@
 	comp_ctx->lex.slot1_idx = comp_ctx->tok11_idx;
 	comp_ctx->lex.slot2_idx = comp_ctx->tok12_idx;
 	comp_ctx->lex.buf_idx = entry_top + 0;
-	comp_ctx->lex.buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(ctx, entry_top + 0);
+	comp_ctx->lex.buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, entry_top + 0);
 	DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(comp_ctx->lex.buf) && !DUK_HBUFFER_HAS_EXTERNAL(comp_ctx->lex.buf));
 	comp_ctx->lex.token_limit = DUK_COMPILER_TOKEN_LIMIT;
 
@@ -70244,9 +71788,9 @@
 		 */
 		DUK_ASSERT(func->h_name == NULL);
 	} else {
-		duk_push_hstring_stridx(ctx, (is_eval ? DUK_STRIDX_EVAL :
+		duk_push_hstring_stridx(thr, (is_eval ? DUK_STRIDX_EVAL :
 		                                        DUK_STRIDX_GLOBAL));
-		func->h_name = duk_get_hstring(ctx, -1);
+		func->h_name = duk_get_hstring(thr, -1);
 	}
 
 	/*
@@ -70298,7 +71842,6 @@
 }
 
 DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags) {
-	duk_context *ctx = (duk_context *) thr;
 	duk__compiler_stkstate comp_stk;
 	duk_compiler_ctx *prev_ctx;
 	duk_ret_t safe_rc;
@@ -70318,12 +71861,12 @@
 
 	prev_ctx = thr->compile_ctx;
 	thr->compile_ctx = &comp_stk.comp_ctx_alloc;  /* for duk_error_augment.c */
-	safe_rc = duk_safe_call(ctx, duk__js_compile_raw, (void *) &comp_stk /*udata*/, 1 /*nargs*/, 1 /*nret*/);
+	safe_rc = duk_safe_call(thr, duk__js_compile_raw, (void *) &comp_stk /*udata*/, 1 /*nargs*/, 1 /*nrets*/);
 	thr->compile_ctx = prev_ctx;  /* must restore reliably before returning */
 
 	if (safe_rc != DUK_EXEC_SUCCESS) {
-		DUK_D(DUK_DPRINT("compilation failed: %!T", duk_get_tval(ctx, -1)));
-		(void) duk_throw(ctx);
+		DUK_D(DUK_DPRINT("compilation failed: %!T", duk_get_tval(thr, -1)));
+		(void) duk_throw(thr);
 	}
 
 	/* [ ... template ] */
@@ -70384,7 +71927,8 @@
 #undef DUK__HAS_VAL
 #undef DUK__ISCONST
 #undef DUK__ISREG
-#undef DUK__ISTEMP
+#undef DUK__ISREG_NOTTEMP
+#undef DUK__ISREG_TEMP
 #undef DUK__IS_TERMINAL
 #undef DUK__IVAL_FLAG_ALLOW_CONST
 #undef DUK__IVAL_FLAG_REQUIRE_SHORT
@@ -70423,7 +71967,7 @@
  *  Local declarations.
  */
 
-DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_size_t entry_callstack_top);
+DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act);
 
 /*
  *  Misc helpers.
@@ -70432,8 +71976,10 @@
 /* Forced inline declaration, only applied for performance oriented build. */
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
 #define DUK__INLINE_PERF
+#define DUK__NOINLINE_PERF
 #else
 #define DUK__INLINE_PERF DUK_ALWAYS_INLINE
+#define DUK__NOINLINE_PERF DUK_NOINLINE
 #endif
 
 /* Replace value stack top to value at 'tv_ptr'.  Optimize for
@@ -70445,7 +71991,7 @@
 		duk_tval *duk__tvdst; \
 		duk_tval duk__tvtmp; \
 		duk__thr = (thr); \
-		duk__tvsrc = DUK_GET_TVAL_NEGIDX((duk_context *) duk__thr, -1); \
+		duk__tvsrc = DUK_GET_TVAL_NEGIDX(duk__thr, -1); \
 		duk__tvdst = (tv_ptr); \
 		DUK_TVAL_SET_TVAL(&duk__tvtmp, duk__tvdst); \
 		DUK_TVAL_SET_TVAL(duk__tvdst, duk__tvsrc); \
@@ -70455,7 +72001,7 @@
 	} while (0)
 
 /* XXX: candidate of being an internal shared API call */
-#if !defined(DUK_USE_EXEC_PREFER_SIZE)
+#if 0  /* unused */
 DUK_LOCAL void duk__push_tvals_incref_only(duk_hthread *thr, duk_tval *tv_src, duk_small_uint_fast_t count) {
 	duk_tval *tv_dst;
 	duk_size_t copy_size;
@@ -70514,15 +72060,13 @@
 	 *  Custom types also have special behavior implemented here.
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_double_union du;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
 	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
 	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
-	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(ctx));
+	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));
 
 	/*
 	 *  Fast paths
@@ -70542,7 +72086,7 @@
 		v2 = DUK_TVAL_GET_FASTINT(tv_y);
 		v3 = v1 + v2;
 		v3_hi = (duk_int32_t) (v3 >> 32);
-		if (DUK_LIKELY(v3_hi >= -0x8000LL && v3_hi <= 0x7fffLL)) {
+		if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) {
 			tv_z = thr->valstack_bottom + idx_z;
 			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3);  /* side effects */
 			return;
@@ -70560,8 +72104,8 @@
 
 		du.d = DUK_TVAL_GET_NUMBER(tv_x) + DUK_TVAL_GET_NUMBER(tv_y);
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-		duk_push_number(ctx, du.d);  /* will NaN normalize result */
-		duk_replace(ctx, idx_z);
+		duk_push_number(thr, du.d);  /* will NaN normalize result */
+		duk_replace(thr, (duk_idx_t) idx_z);
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
 		DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
 		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
@@ -70575,40 +72119,38 @@
 	 *  Slow path: potentially requires function calls for coercion
 	 */
 
-	duk_push_tval(ctx, tv_x);
-	duk_push_tval(ctx, tv_y);
-	duk_to_primitive(ctx, -2, DUK_HINT_NONE);  /* side effects -> don't use tv_x, tv_y after */
-	duk_to_primitive(ctx, -1, DUK_HINT_NONE);
+	duk_push_tval(thr, tv_x);
+	duk_push_tval(thr, tv_y);
+	duk_to_primitive(thr, -2, DUK_HINT_NONE);  /* side effects -> don't use tv_x, tv_y after */
+	duk_to_primitive(thr, -1, DUK_HINT_NONE);
 
 	/* Since Duktape 2.x plain buffers are treated like ArrayBuffer. */
-	if (duk_is_string(ctx, -2) || duk_is_string(ctx, -1)) {
+	if (duk_is_string(thr, -2) || duk_is_string(thr, -1)) {
 		/* Symbols shouldn't technically be handled here, but should
 		 * go into the default ToNumber() coercion path instead and
 		 * fail there with a TypeError.  However, there's a ToString()
-		 * here which also fails with TypeError so no explicit check
-		 * is needed.
-		 */
-		duk_to_string(ctx, -2);
-		duk_to_string(ctx, -1);
-		duk_concat(ctx, 2);  /* [... s1 s2] -> [... s1+s2] */
+		 * in duk_concat_2() which also fails with TypeError so no
+		 * explicit check is needed.
+		 */
+		duk_concat_2(thr);  /* [... s1 s2] -> [... s1+s2] */
 	} else {
 		duk_double_t d1, d2;
 
-		d1 = duk_to_number_m2(ctx);
-		d2 = duk_to_number_m1(ctx);
-		DUK_ASSERT(duk_is_number(ctx, -2));
-		DUK_ASSERT(duk_is_number(ctx, -1));
+		d1 = duk_to_number_m2(thr);
+		d2 = duk_to_number_m1(thr);
+		DUK_ASSERT(duk_is_number(thr, -2));
+		DUK_ASSERT(duk_is_number(thr, -1));
 		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1);
 		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2);
 
 		du.d = d1 + d2;
-		duk_pop_2(ctx);
-		duk_push_number(ctx, du.d);  /* will NaN normalize result */
-	}
-	duk_replace(ctx, (duk_idx_t) idx_z);  /* side effects */
-}
-
-DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_idx_t idx_z, duk_small_uint_fast_t opcode) {
+		duk_pop_2_unsafe(thr);
+		duk_push_number(thr, du.d);  /* will NaN normalize result */
+	}
+	duk_replace(thr, (duk_idx_t) idx_z);  /* side effects */
+}
+
+DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_uint_fast_t idx_z, duk_small_uint_fast_t opcode) {
 	/*
 	 *  Arithmetic operations other than '+' have number-only semantics
 	 *  and are implemented here.  The separate switch-case here means a
@@ -70617,7 +72159,6 @@
 	 *  E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3.
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_double_t d1, d2;
 	duk_double_union du;
 	duk_small_uint_fast_t opcode_shifted;
@@ -70626,11 +72167,10 @@
 #endif
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
 	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
 	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
-	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(ctx));
+	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));
 
 	opcode_shifted = opcode >> 2;  /* Get base opcode without reg/const modifiers. */
 
@@ -70653,8 +72193,8 @@
 			 * 32-bit inputs.  Avoid zero inputs to avoid
 			 * negative zero issues (-1 * 0 = -0, for instance).
 			 */
-			if (v1 >= -0x80000000LL && v1 <= 0x7fffffffLL && v1 != 0 &&
-			    v2 >= -0x80000000LL && v2 <= 0x7fffffffLL && v2 != 0) {
+			if (v1 >= DUK_I64_CONSTANT(-0x80000000) && v1 <= DUK_I64_CONSTANT(0x7fffffff) && v1 != 0 &&
+			    v2 >= DUK_I64_CONSTANT(-0x80000000) && v2 <= DUK_I64_CONSTANT(0x7fffffff) && v2 != 0) {
 				v3 = v1 * v2;
 			} else {
 				goto skip_fastint;
@@ -70697,7 +72237,7 @@
 		}
 
 		v3_hi = (duk_int32_t) (v3 >> 32);
-		if (DUK_LIKELY(v3_hi >= -0x8000LL && v3_hi <= 0x7fffLL)) {
+		if (DUK_LIKELY(v3_hi >= DUK_I64_CONSTANT(-0x8000) && v3_hi <= DUK_I64_CONSTANT(0x7fff))) {
 			tv_z = thr->valstack_bottom + idx_z;
 			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv_z, v3);  /* side effects */
 			return;
@@ -70712,15 +72252,15 @@
 		d1 = DUK_TVAL_GET_NUMBER(tv_x);
 		d2 = DUK_TVAL_GET_NUMBER(tv_y);
 	} else {
-		duk_push_tval(ctx, tv_x);
-		duk_push_tval(ctx, tv_y);
-		d1 = duk_to_number_m2(ctx);  /* side effects */
-		d2 = duk_to_number_m1(ctx);
-		DUK_ASSERT(duk_is_number(ctx, -2));
-		DUK_ASSERT(duk_is_number(ctx, -1));
+		duk_push_tval(thr, tv_x);
+		duk_push_tval(thr, tv_y);
+		d1 = duk_to_number_m2(thr);  /* side effects */
+		d2 = duk_to_number_m1(thr);
+		DUK_ASSERT(duk_is_number(thr, -2));
+		DUK_ASSERT(duk_is_number(thr, -1));
 		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d1);
 		DUK_ASSERT_DOUBLE_IS_NORMALIZED(d2);
-		duk_pop_2(ctx);
+		duk_pop_2_unsafe(thr);
 	}
 
 	switch (opcode_shifted) {
@@ -70754,8 +72294,8 @@
 	}
 
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-	duk_push_number(ctx, du.d);  /* will NaN normalize result */
-	duk_replace(ctx, idx_z);
+	duk_push_number(thr, du.d);  /* will NaN normalize result */
+	duk_replace(thr, (duk_idx_t) idx_z);
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
 	/* important to use normalized NaN with 8-byte tagged types */
 	DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
@@ -70776,7 +72316,6 @@
 	 *  E5 Sections 11.10, 11.7.1, 11.7.2, 11.7.3
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_int32_t i1, i2, i3;
 	duk_uint32_t u1, u2, u3;
 #if defined(DUK_USE_FASTINT)
@@ -70790,11 +72329,10 @@
 #endif
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(tv_x != NULL);  /* may be reg or const */
 	DUK_ASSERT(tv_y != NULL);  /* may be reg or const */
 	DUK_ASSERT_DISABLE(idx_z >= 0);  /* unsigned */
-	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(ctx));
+	DUK_ASSERT((duk_uint_t) idx_z < (duk_uint_t) duk_get_top(thr));
 
 	opcode_shifted = opcode >> 2;  /* Get base opcode without reg/const modifiers. */
 
@@ -70806,11 +72344,11 @@
 	else
 #endif  /* DUK_USE_FASTINT */
 	{
-		duk_push_tval(ctx, tv_x);
-		duk_push_tval(ctx, tv_y);
-		i1 = duk_to_int32(ctx, -2);
-		i2 = duk_to_int32(ctx, -1);
-		duk_pop_2(ctx);
+		duk_push_tval(thr, tv_x);
+		duk_push_tval(thr, tv_y);
+		i1 = duk_to_int32(thr, -2);
+		i2 = duk_to_int32(thr, -1);
+		duk_pop_2_unsafe(thr);
 	}
 
 	switch (opcode_shifted) {
@@ -70885,8 +72423,8 @@
 	DUK_ASSERT_DOUBLE_IS_NORMALIZED(d3);   /* always normalized */
 
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-	duk_push_number(ctx, d3);  /* would NaN normalize result, but unnecessary */
-	duk_replace(ctx, idx_z);
+	duk_push_number(thr, d3);  /* would NaN normalize result, but unnecessary */
+	duk_replace(thr, (duk_idx_t) idx_z);
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
 	tv_z = thr->valstack_bottom + idx_z;
 	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_z, d3);  /* side effects */
@@ -70895,7 +72433,7 @@
 }
 
 /* In-place unary operation. */
-DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx_t idx_src, duk_idx_t idx_dst, duk_small_uint_fast_t opcode) {
+DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst, duk_small_uint_fast_t opcode) {
 	/*
 	 *  Arithmetic operations other than '+' have number-only semantics
 	 *  and are implemented here.  The separate switch-case here means a
@@ -70904,18 +72442,16 @@
 	 *  E5 Sections 11.5, 11.5.1, 11.5.2, 11.5.3, 11.6, 11.6.1, 11.6.2, 11.6.3.
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval *tv;
 	duk_double_t d1;
 	duk_double_union du;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(opcode == DUK_OP_UNM || opcode == DUK_OP_UNP);
-	DUK_ASSERT(idx_src >= 0);
-	DUK_ASSERT(idx_dst >= 0);
-
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_src);
+	DUK_ASSERT_DISABLE(idx_src >= 0);
+	DUK_ASSERT_DISABLE(idx_dst >= 0);
+
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);
 
 #if defined(DUK_USE_FASTINT)
 	if (DUK_TVAL_IS_FASTINT(tv)) {
@@ -70929,7 +72465,7 @@
 			 */
 			if (DUK_LIKELY(v1 != DUK_FASTINT_MIN && v1 != 0)) {
 				v2 = -v1;
-				tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+				tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 				DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2);
 				return;
 			}
@@ -70937,7 +72473,7 @@
 			/* ToNumber() for a fastint is a no-op. */
 			DUK_ASSERT(opcode == DUK_OP_UNP);
 			v2 = v1;
-			tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+			tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 			DUK_TVAL_SET_FASTINT_UPDREF(thr, tv, v2);
 			return;
 		}
@@ -70948,8 +72484,7 @@
 	if (DUK_TVAL_IS_NUMBER(tv)) {
 		d1 = DUK_TVAL_GET_NUMBER(tv);
 	} else {
-		d1 = duk_to_number(ctx, idx_src);  /* side effects, perform in-place */
-		DUK_ASSERT(DUK_TVAL_IS_NUMBER(DUK_GET_TVAL_POSIDX(ctx, idx_src)));
+		d1 = duk_to_number_tval(thr, tv);  /* side effects */
 	}
 
 	if (opcode == DUK_OP_UNP) {
@@ -70959,7 +72494,7 @@
 		du.d = d1;
 		DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
 #if defined(DUK_USE_FASTINT)
-		tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+		tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 		DUK_TVAL_SET_NUMBER_CHKFAST_UPDREF(thr, tv, du.d);  /* always 'fast', i.e. inlined */
 		return;
 #endif
@@ -70971,7 +72506,7 @@
 	}
 
 	/* XXX: size optimize: push+replace? */
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, du.d);
 }
 
@@ -70980,19 +72515,16 @@
 	 *  E5 Section 11.4.8
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval *tv;
 	duk_int32_t i1, i2;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT_DISABLE(idx_src >= 0);
 	DUK_ASSERT_DISABLE(idx_dst >= 0);
-	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(ctx));
-	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(ctx));
-	DUK_UNREF(thr);  /* w/o refcounts */
-
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_src);
+	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr));
+	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr));
+
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);
 
 #if defined(DUK_USE_FASTINT)
 	if (DUK_TVAL_IS_FASTINT(tv)) {
@@ -71001,48 +72533,46 @@
 	else
 #endif  /* DUK_USE_FASTINT */
 	{
-		i1 = duk_to_int32(ctx, idx_src);  /* side effects */
+		duk_push_tval(thr, tv);
+		i1 = duk_to_int32(thr, -1);  /* side effects */
+		duk_pop_unsafe(thr);
 	}
 
 	/* Result is always fastint compatible. */
 	i2 = ~i1;
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 	DUK_TVAL_SET_I32_UPDREF(thr, tv, i2);  /* side effects */
 }
 
-DUK_LOCAL DUK__INLINE_PERF void duk__vm_logical_not(duk_hthread *thr, duk_idx_t idx_src, duk_idx_t idx_dst) {
+DUK_LOCAL DUK__INLINE_PERF void duk__vm_logical_not(duk_hthread *thr, duk_uint_fast_t idx_src, duk_uint_fast_t idx_dst) {
 	/*
 	 *  E5 Section 11.4.9
 	 */
 
-	duk_context *ctx = (duk_context *) thr;
 	duk_tval *tv;
 	duk_bool_t res;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT_DISABLE(idx_src >= 0);
 	DUK_ASSERT_DISABLE(idx_dst >= 0);
-	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(ctx));
-	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(ctx));
-	DUK_UNREF(thr);  /* w/o refcounts */
+	DUK_ASSERT((duk_uint_t) idx_src < (duk_uint_t) duk_get_top(thr));
+	DUK_ASSERT((duk_uint_t) idx_dst < (duk_uint_t) duk_get_top(thr));
 
 	/* ToBoolean() does not require any operations with side effects so
 	 * we can do it efficiently.  For footprint it would be better to use
 	 * duk_js_toboolean() and then push+replace to the result slot.
 	 */
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_src);
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_src);
 	res = duk_js_toboolean(tv);  /* does not modify 'tv' */
 	DUK_ASSERT(res == 0 || res == 1);
 	res ^= 1;
-	tv = DUK_GET_TVAL_POSIDX(ctx, idx_dst);
+	tv = DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst);
 	/* XXX: size optimize: push+replace? */
 	DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, res);  /* side effects */
 }
 
 /* XXX: size optimized variant */
 DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_reg_helper(duk_hthread *thr, duk_tval *tv_dst, duk_tval *tv_src, duk_small_uint_t op) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_double_t x, y, z;
 
 	/* Two lowest bits of opcode are used to distinguish
@@ -71101,15 +72631,15 @@
 		bc = (duk_idx_t) (tv_src - thr->valstack_bottom);  /* XXX: pass index explicitly? */
 		tv_src = NULL;  /* no longer referenced */
 
-		x = duk_to_number(ctx, bc);
+		x = duk_to_number(thr, bc);
 		if (op & 0x01) {
 			y = x - 1.0;
 		} else {
 			y = x + 1.0;
 		}
 
-		duk_push_number(ctx, y);
-		duk_replace(ctx, bc);
+		duk_push_number(thr, y);
+		duk_replace(thr, bc);
 
 		tv_dst = (duk_tval *) (void *) (((duk_uint8_t *) thr->valstack_bottom) + off_dst);
 	}
@@ -71118,8 +72648,7 @@
 	DUK_TVAL_SET_NUMBER_UPDREF(thr, tv_dst, z);  /* side effects */
 }
 
-DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, duk_small_uint_t idx_dst, duk_tval *tv_id, duk_small_uint_t op, duk_uint_t is_strict) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, duk_small_uint_t idx_dst, duk_tval *tv_id, duk_small_uint_t op, duk_small_uint_t is_strict) {
 	duk_activation *act;
 	duk_double_t x, y;
 	duk_hstring *name;
@@ -71152,7 +72681,7 @@
 	 * not intuitive.
 	 */
 
-	x = duk_to_number_m2(ctx);
+	x = duk_to_number_m2(thr);
 	if (op & 0x01) {
 		y = x - 1.0;
 	} else {
@@ -71162,21 +72691,21 @@
 	/* [... x this] */
 
 	if (op & 0x02) {
-		duk_push_number(ctx, y);  /* -> [ ... x this y ] */
-		act = thr->callstack_curr;
-		duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict);
-		duk_pop_2(ctx);  /* -> [ ... x ] */
-	} else {
-		duk_pop_2(ctx);  /* -> [ ... ] */
-		duk_push_number(ctx, y);  /* -> [ ... y ] */
-		act = thr->callstack_curr;
-		duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict);
+		duk_push_number(thr, y);  /* -> [ ... x this y ] */
+		DUK_ASSERT(act == thr->callstack_curr);
+		duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict);
+		duk_pop_2_unsafe(thr);  /* -> [ ... x ] */
+	} else {
+		duk_pop_2_unsafe(thr);  /* -> [ ... ] */
+		duk_push_number(thr, y);  /* -> [ ... y ] */
+		DUK_ASSERT(act == thr->callstack_curr);
+		duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(thr, -1), is_strict);
 	}
 
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-	duk_replace(ctx, (duk_idx_t) idx_dst);
+	duk_replace(thr, (duk_idx_t) idx_dst);
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
-	DUK__REPLACE_TO_TVPTR(thr, DUK_GET_TVAL_POSIDX(ctx, idx_dst));
+	DUK__REPLACE_TO_TVPTR(thr, DUK_GET_TVAL_POSIDX(thr, (duk_idx_t) idx_dst));
 #endif  /* DUK_USE_EXEC_PREFER_SIZE */
 }
 
@@ -71201,119 +72730,118 @@
  * top are combined into one pass.
  */
 
-/* Reconfigure value stack for return to an Ecmascript function at 'act_idx'. */
-DUK_LOCAL void duk__reconfig_valstack_ecma_return(duk_hthread *thr, duk_size_t act_idx) {
+/* Reconfigure value stack for return to an Ecmascript function at
+ * callstack top (caller unwinds).
+ */
+DUK_LOCAL void duk__reconfig_valstack_ecma_return(duk_hthread *thr) {
 	duk_activation *act;
 	duk_hcompfunc *h_func;
 	duk_idx_t clamp_top;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(act_idx >= 0);  /* unsigned */
-	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + act_idx) != NULL);
-	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + act_idx)));
-	DUK_ASSERT_DISABLE(thr->callstack[act_idx].idx_retval >= 0);  /* unsigned */
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
+	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
 
 	/* Clamp so that values at 'clamp_top' and above are wiped and won't
 	 * retain reachable garbage.  Then extend to 'nregs' because we're
 	 * returning to an Ecmascript function.
 	 */
 
-	act = thr->callstack + act_idx;
 	h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
 
-	thr->valstack_bottom = thr->valstack + act->idx_bottom;
-	DUK_ASSERT(act->idx_retval >= act->idx_bottom);
-	clamp_top = (duk_idx_t) (act->idx_retval - act->idx_bottom + 1);  /* +1 = one retval */
-	duk_set_top((duk_context *) thr, clamp_top);
-	act = NULL;
-
-	(void) duk_valstack_resize_raw((duk_context *) thr,
-	                               (thr->valstack_bottom - thr->valstack) +  /* bottom of current func */
-	                                   h_func->nregs +                       /* reg count */
-	                                   DUK_VALSTACK_INTERNAL_EXTRA,          /* + spare */
-	                               DUK_VSRESIZE_FLAG_SHRINK |                /* flags */
-	                               0 /* no compact */ |
-	                               DUK_VSRESIZE_FLAG_THROW);
-
-	duk_set_top((duk_context *) thr, h_func->nregs);
-}
-
-DUK_LOCAL void duk__reconfig_valstack_ecma_catcher(duk_hthread *thr, duk_size_t act_idx, duk_size_t cat_idx) {
-	duk_activation *act;
+	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
+	DUK_ASSERT(act->retval_byteoff >= act->bottom_byteoff);
+	clamp_top = (duk_idx_t) ((act->retval_byteoff - act->bottom_byteoff + sizeof(duk_tval)) / sizeof(duk_tval));  /* +1 = one retval */
+	duk_set_top_and_wipe(thr, h_func->nregs, clamp_top);
+
+	DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff);
+	thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff);
+
+	/* XXX: a best effort shrink check would be OK here */
+}
+
+/* Reconfigure value stack for an Ecmascript catcher.  Use topmost catcher
+ * in 'act'.
+ */
+DUK_LOCAL void duk__reconfig_valstack_ecma_catcher(duk_hthread *thr, duk_activation *act) {
 	duk_catcher *cat;
 	duk_hcompfunc *h_func;
+	duk_size_t idx_bottom;
 	duk_idx_t clamp_top;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT_DISABLE(act_idx >= 0);  /* unsigned */
-	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + act_idx) != NULL);
-	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + act_idx)));
-	DUK_ASSERT_DISABLE(thr->callstack[act_idx].idx_retval >= 0);  /* unsigned */
-
-	act = thr->callstack + act_idx;
-	cat = thr->catchstack + cat_idx;
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
+	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+
 	h_func = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
 
-	thr->valstack_bottom = thr->valstack + act->idx_bottom;
-	DUK_ASSERT(cat->idx_base >= act->idx_bottom);
-	clamp_top = (duk_idx_t) (cat->idx_base - act->idx_bottom + 2);  /* +2 = catcher value, catcher lj_type */
-	duk_set_top((duk_context *) thr, clamp_top);
-	act = NULL;
-	cat = NULL;
-
-	(void) duk_valstack_resize_raw((duk_context *) thr,
-	                               (thr->valstack_bottom - thr->valstack) +  /* bottom of current func */
-	                                   h_func->nregs +                       /* reg count */
-	                                   DUK_VALSTACK_INTERNAL_EXTRA,          /* + spare */
-	                               DUK_VSRESIZE_FLAG_SHRINK |                /* flags */
-	                               0 /* no compact */ |
-	                               DUK_VSRESIZE_FLAG_THROW);
-
-	duk_set_top((duk_context *) thr, h_func->nregs);
-}
-
-/* Set catcher regs: idx_base+0 = value, idx_base+1 = lj_type. */
-DUK_LOCAL void duk__set_catcher_regs(duk_hthread *thr, duk_size_t cat_idx, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
+	thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
+	idx_bottom = (duk_size_t) (thr->valstack_bottom - thr->valstack);
+	DUK_ASSERT(cat->idx_base >= idx_bottom);
+	clamp_top = (duk_idx_t) (cat->idx_base - idx_bottom + 2);  /* +2 = catcher value, catcher lj_type */
+	duk_set_top_and_wipe(thr, h_func->nregs, clamp_top);
+
+	DUK_ASSERT((duk_uint8_t *) thr->valstack_end >= (duk_uint8_t *) thr->valstack + act->reserve_byteoff);
+	thr->valstack_end = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->reserve_byteoff);
+
+	/* XXX: a best effort shrink check would be OK here */
+}
+
+/* Set catcher regs: idx_base+0 = value, idx_base+1 = lj_type.
+ * No side effects.
+ */
+DUK_LOCAL void duk__set_catcher_regs_norz(duk_hthread *thr, duk_catcher *cat, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
 	duk_tval *tv1;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(tv_val_unstable != NULL);
 
-	tv1 = thr->valstack + thr->catchstack[cat_idx].idx_base;
+	tv1 = thr->valstack + cat->idx_base;
 	DUK_ASSERT(tv1 < thr->valstack_top);
-	DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable);  /* side effects */
-
-	tv1 = thr->valstack + thr->catchstack[cat_idx].idx_base + 1;
+	DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, tv1, tv_val_unstable);
+
+	tv1++;
+	DUK_ASSERT(tv1 == thr->valstack + cat->idx_base + 1);
 	DUK_ASSERT(tv1 < thr->valstack_top);
-
-	DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) lj_type);  /* side effects */
-}
-
-DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
-	duk_context *ctx;
+	DUK_TVAL_SET_U32_UPDREF_NORZ(thr, tv1, (duk_uint32_t) lj_type);
+}
+
+DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
 	duk_activation *act;
+	duk_catcher *cat;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(tv_val_unstable != NULL);
-	ctx = (duk_context *) thr;
-
-	duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type);
-
-	duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1);
-	duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1);
+
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(act->cat != NULL);
+	DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);
+
+	duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type);
 
 	DUK_ASSERT(thr->callstack_top >= 1);
 	DUK_ASSERT(thr->callstack_curr != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
 
-	duk__reconfig_valstack_ecma_catcher(thr, thr->callstack_top - 1, cat_idx);
+	DUK_ASSERT(thr->callstack_top >= 1);
+	DUK_ASSERT(act == thr->callstack_curr);
+	DUK_ASSERT(act != NULL);
+	duk__reconfig_valstack_ecma_catcher(thr, act);
 
 	DUK_ASSERT(thr->callstack_top >= 1);
-	act = thr->callstack_curr;
+	DUK_ASSERT(act == thr->callstack_curr);
 	DUK_ASSERT(act != NULL);
-	act->curr_pc = thr->catchstack[cat_idx].pc_base + 0;  /* +0 = catch */
-	act = NULL;
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+
+	act->curr_pc = cat->pc_base + 0;  /* +0 = catch */
 
 	/*
 	 *  If entering a 'catch' block which requires an automatic
@@ -71325,31 +72853,26 @@
 	 *  which implies the binding is not deletable.
 	 */
 
-	if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(&thr->catchstack[cat_idx])) {
+	if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat)) {
 		duk_hdecenv *new_env;
 
 		DUK_DDD(DUK_DDDPRINT("catcher has an automatic catch binding"));
 
-		/* Note: 'act' is dangerous here because it may get invalidate at many
-		 * points, so we re-lookup it multiple times.
-		 */
 		DUK_ASSERT(thr->callstack_top >= 1);
-		act = thr->callstack_curr;
+		DUK_ASSERT(act == thr->callstack_curr);
 		DUK_ASSERT(act != NULL);
 
 		if (act->lex_env == NULL) {
 			DUK_ASSERT(act->var_env == NULL);
 			DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
 
-			/* this may have side effects, so re-lookup act */
 			duk_js_init_activation_environment_records_delayed(thr, act);
-			act = thr->callstack_curr;
+			DUK_ASSERT(act == thr->callstack_curr);
 			DUK_ASSERT(act != NULL);
 		}
 		DUK_ASSERT(act->lex_env != NULL);
 		DUK_ASSERT(act->var_env != NULL);
 		DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
-		DUK_UNREF(act);  /* unreferenced without assertions */
 
 		/* XXX: If an out-of-memory happens here, longjmp state asserts
 		 * will be triggered at present and a try-catch fails to catch.
@@ -71361,7 +72884,7 @@
 		                            DUK_HOBJECT_FLAG_EXTENSIBLE |
 		                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
 		DUK_ASSERT(new_env != NULL);
-		duk_push_hobject(ctx, (duk_hobject *) new_env);
+		duk_push_hobject(thr, (duk_hobject *) new_env);
 		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
 		DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env));
 
@@ -71372,12 +72895,12 @@
 		 */
 
 		/* XXX: duk_xdef_prop() may cause an out-of-memory, see above. */
-		DUK_ASSERT(thr->catchstack[cat_idx].h_varname != NULL);
-		duk_push_hstring(ctx, thr->catchstack[cat_idx].h_varname);
-		duk_push_tval(ctx, thr->valstack + thr->catchstack[cat_idx].idx_base);
-		duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_W);  /* writable, not configurable */
-
-		act = thr->callstack_curr;
+		DUK_ASSERT(cat->h_varname != NULL);
+		duk_push_hstring(thr, cat->h_varname);
+		duk_push_tval(thr, thr->valstack + cat->idx_base);
+		duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_W);  /* writable, not configurable */
+
+		DUK_ASSERT(act == thr->callstack_curr);
 		DUK_ASSERT(act != NULL);
 		DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act->lex_env);
 		act->lex_env = (duk_hobject *) new_env;
@@ -71386,66 +72909,74 @@
 		 * prototype, decref for act->lex_env overwrite.
 		 */
 
-		DUK_CAT_SET_LEXENV_ACTIVE(&thr->catchstack[cat_idx]);
-
-		duk_pop(ctx);
+		DUK_CAT_SET_LEXENV_ACTIVE(cat);
+
+		duk_pop_unsafe(thr);
 
 		DUK_DDD(DUK_DDDPRINT("new_env finished: %!iO", (duk_heaphdr *) new_env));
 	}
 
-	DUK_CAT_CLEAR_CATCH_ENABLED(&thr->catchstack[cat_idx]);
-}
-
-DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_size_t cat_idx, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
+	DUK_CAT_CLEAR_CATCH_ENABLED(cat);
+}
+
+DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_tval *tv_val_unstable, duk_small_uint_t lj_type) {
 	duk_activation *act;
+	duk_catcher *cat;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(tv_val_unstable != NULL);
 
-	duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type);
-
-	duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1);  /* cat_idx catcher is kept, even for finally */
-	duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1);
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	DUK_ASSERT(act->cat != NULL);
+	DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);
+
+	duk__set_catcher_regs_norz(thr, act->cat, tv_val_unstable, lj_type);
 
 	DUK_ASSERT(thr->callstack_top >= 1);
 	DUK_ASSERT(thr->callstack_curr != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
 
-	duk__reconfig_valstack_ecma_catcher(thr, thr->callstack_top - 1, cat_idx);
+	DUK_ASSERT(thr->callstack_top >= 1);
+	DUK_ASSERT(act == thr->callstack_curr);
+	DUK_ASSERT(act != NULL);
+	duk__reconfig_valstack_ecma_catcher(thr, act);
 
 	DUK_ASSERT(thr->callstack_top >= 1);
-	act = thr->callstack_curr;
+	DUK_ASSERT(act == thr->callstack_curr);
 	DUK_ASSERT(act != NULL);
-	act->curr_pc = thr->catchstack[cat_idx].pc_base + 1;  /* +1 = finally */
-	act = NULL;
-
-	DUK_CAT_CLEAR_FINALLY_ENABLED(&thr->catchstack[cat_idx]);
-}
-
-DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small_uint_t lj_type) {
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+
+	act->curr_pc = cat->pc_base + 1;  /* +1 = finally */
+
+	DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
+}
+
+DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_small_uint_t lj_type) {
 	duk_activation *act;
+	duk_catcher *cat;
 
 	DUK_ASSERT(thr != NULL);
 
 	DUK_ASSERT(thr->callstack_top >= 1);
 	act = thr->callstack_curr;
 	DUK_ASSERT(act != NULL);
-
 	DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
 	DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
 
 	/* +0 = break, +1 = continue */
-	act->curr_pc = thr->catchstack[cat_idx].pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
-	act = NULL;  /* invalidated */
-
-	duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1);  /* keep label catcher */
-	/* no need to unwind callstack */
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+	DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL);
+
+	act->curr_pc = cat->pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
 
 	/* valstack should not need changes */
 #if defined(DUK_USE_ASSERTIONS)
 	DUK_ASSERT(thr->callstack_top >= 1);
-	act = thr->callstack_curr;
+	DUK_ASSERT(act == thr->callstack_curr);
 	DUK_ASSERT(act != NULL);
 	DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) ==
 	           (duk_size_t) ((duk_hcompfunc *) DUK_ACT_GET_FUNC(act))->nregs);
@@ -71454,41 +72985,35 @@
 
 /* Called for handling both a longjmp() with type DUK_LJ_TYPE_YIELD and
  * when a RETURN opcode terminates a thread and yields to the resumer.
+ * Caller unwinds so that top of callstack is the activation we return to.
  */
 #if defined(DUK_USE_COROUTINE_SUPPORT)
-DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_size_t act_idx, duk_tval *tv_val_unstable) {
+DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_tval *tv_val_unstable) {
+	duk_activation *act_resumer;
 	duk_tval *tv1;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(resumer != NULL);
 	DUK_ASSERT(tv_val_unstable != NULL);
-	DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + act_idx) != NULL);
-	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack + act_idx)));  /* resume caller must be an ecmascript func */
-
-	tv1 = resumer->valstack + resumer->callstack[act_idx].idx_retval;  /* return value from Duktape.Thread.resume() */
-	DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable);  /* side effects */
-
-	duk_hthread_callstack_unwind_norz(resumer, act_idx + 1);  /* unwind to 'resume' caller */
-
-	/* no need to unwind catchstack */
-	duk__reconfig_valstack_ecma_return(resumer, act_idx);
+	act_resumer = resumer->callstack_curr;
+	DUK_ASSERT(act_resumer != NULL);
+	DUK_ASSERT(DUK_ACT_GET_FUNC(act_resumer) != NULL);
+	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act_resumer)));  /* resume caller must be an ecmascript func */
+
+	tv1 = (duk_tval *) (void *) ((duk_uint8_t *) resumer->valstack + act_resumer->retval_byteoff);  /* return value from Duktape.Thread.resume() */
+	DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable);  /* side effects */  /* XXX: avoid side effects */
+
+	duk__reconfig_valstack_ecma_return(resumer);
 
 	/* caller must change active thread, and set thr->resumer to NULL */
 }
 #endif  /* DUK_USE_COROUTINE_SUPPORT */
 
-DUK_LOCAL
-duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
-                                     duk_hthread *entry_thread,
-                                     duk_size_t entry_callstack_top) {
-	duk_size_t entry_callstack_index;
+DUK_LOCAL duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, duk_activation *entry_act) {
 	duk_small_uint_t retval = DUK__LONGJMP_RESTART;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(entry_thread != NULL);
-	DUK_ASSERT(entry_callstack_top > 0);  /* guarantees entry_callstack_top - 1 >= 0 */
-
-	entry_callstack_index = entry_callstack_top - 1;
+	DUK_ASSERT(entry_act != NULL);
 
 	/* 'thr' is the current thread, as no-one resumes except us and we
 	 * switch 'thr' in that case.
@@ -71524,7 +73049,6 @@
 
 		duk_tval *tv;
 		duk_tval *tv2;
-		duk_size_t act_idx;
 		duk_hthread *resumee;
 
 		/* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */
@@ -71532,10 +73056,10 @@
 		DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);                                                         /* unchanged by Duktape.Thread.resume() */
 		DUK_ASSERT(thr->callstack_top >= 2);                                                                         /* Ecmascript activation + Duktape.Thread.resume() activation */
 		DUK_ASSERT(thr->callstack_curr != NULL);
+		DUK_ASSERT(thr->callstack_curr->parent != NULL);
 		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
 		           DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
 		           ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_resume);
-		DUK_ASSERT_DISABLE((thr->callstack_curr - 1)->idx_retval >= 0);                                              /* unsigned */
 
 		tv = &thr->heap->lj.value2;  /* resumee */
 		DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
@@ -71553,8 +73077,6 @@
 		           (DUK_ACT_GET_FUNC(resumee->callstack_curr) != NULL &&
 		            DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack_curr)) &&
 		            ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack_curr))->func == duk_bi_thread_yield));
-		DUK_ASSERT_DISABLE(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
-		           (resumee->callstack_curr - 1)->idx_retval >= 0);                                                  /* idx_retval unsigned */
 		DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_INACTIVE ||
 		           resumee->callstack_top == 0);                                                                     /* INACTIVE: no activation, single function value on valstack */
 
@@ -71587,19 +73109,28 @@
 			DUK_DD(DUK_DDPRINT("-> resume with an error, converted to a throw in the resumee, propagate"));
 			goto check_longjmp;
 		} else if (resumee->state == DUK_HTHREAD_STATE_YIELDED) {
-			act_idx = resumee->callstack_top - 2;  /* Ecmascript function */
-			DUK_ASSERT_DISABLE(resumee->callstack[act_idx].idx_retval >= 0);  /* unsigned */
-
-			tv = resumee->valstack + resumee->callstack[act_idx].idx_retval;  /* return value from Duktape.Thread.yield() */
+			/* Unwind previous Duktape.Thread.yield() call.  The
+			 * activation remaining must always be an Ecmascript
+			 * call now (yield() accepts calls from Ecmascript
+			 * only).
+			 */
+			duk_activation *act_resumee;
+
+			DUK_ASSERT(resumee->callstack_top >= 2);
+			act_resumee = resumee->callstack_curr;  /* Duktape.Thread.yield() */
+			DUK_ASSERT(act_resumee != NULL);
+			act_resumee = act_resumee->parent;      /* Ecmascript call site for yield() */
+			DUK_ASSERT(act_resumee != NULL);
+
+			tv = (duk_tval *) (void *) ((duk_uint8_t *) resumee->valstack + act_resumee->retval_byteoff);  /* return value from Duktape.Thread.yield() */
 			DUK_ASSERT(tv >= resumee->valstack && tv < resumee->valstack_top);
 			tv2 = &thr->heap->lj.value1;
-			DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2);  /* side effects */
-
-			duk_hthread_callstack_unwind_norz(resumee, act_idx + 1);  /* unwind to 'yield' caller */
-
-			/* no need to unwind catchstack */
-
-			duk__reconfig_valstack_ecma_return(resumee, act_idx);
+			DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2);  /* side effects */  /* XXX: avoid side effects */
+
+			duk_hthread_activation_unwind_norz(resumee);  /* unwind to 'yield' caller */
+			/* no need to unwind catch stack */
+
+			duk__reconfig_valstack_ecma_return(resumee);
 
 			DUK_ASSERT(resumee->resumer == NULL);
 			resumee->resumer = thr;
@@ -71614,22 +73145,21 @@
 			retval = DUK__LONGJMP_RESTART;
 			goto wipe_and_return;
 		} else {
+			/* Initial resume call. */
 			duk_small_uint_t call_flags;
-			duk_bool_t setup_rc;
+			duk_int_t setup_rc;
 
 			/* resumee: [... initial_func]  (currently actually: [initial_func]) */
 
-			duk_push_undefined((duk_context *) resumee);
+			duk_push_undefined(resumee);
 			tv = &thr->heap->lj.value1;
-			duk_push_tval((duk_context *) resumee, tv);
+			duk_push_tval(resumee, tv);
 
 			/* resumee: [... initial_func undefined(= this) resume_value ] */
 
-			call_flags = DUK_CALL_FLAG_IS_RESUME;  /* is resume, not a tail call */
-
-			setup_rc = duk_handle_ecma_call_setup(resumee,
-			                                      1,              /* num_stack_args */
-			                                      call_flags);    /* call_flags */
+			call_flags = DUK_CALL_FLAG_ALLOW_ECMATOECMA;  /* not tailcall, ecma-to-ecma (assumed to succeed) */
+
+			setup_rc = duk_handle_call_unprotected_nargs(resumee, 1 /*nargs*/, call_flags);
 			if (setup_rc == 0) {
 				/* This shouldn't happen; Duktape.Thread.resume()
 				 * should make sure of that.  If it does happen
@@ -71671,16 +73201,18 @@
 
 		/* duk_bi_duk_object_yield() and duk_bi_duk_object_resume() ensure all of these are met */
 
+#if 0  /* entry_thread not available for assert */
 		DUK_ASSERT(thr != entry_thread);                                                                             /* Duktape.Thread.yield() should prevent */
+#endif
 		DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);                                                         /* unchanged from Duktape.Thread.yield() */
 		DUK_ASSERT(thr->callstack_top >= 2);                                                                         /* Ecmascript activation + Duktape.Thread.yield() activation */
 		DUK_ASSERT(thr->callstack_curr != NULL);
+		DUK_ASSERT(thr->callstack_curr->parent != NULL);
 		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
 		           DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
 		           ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_yield);
-		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL &&
-		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr - 1)));                              /* an Ecmascript function */
-		DUK_ASSERT_DISABLE((thr->callstack_curr - 1)->idx_retval >= 0);                                              /* unsigned */
+		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL &&
+		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent)));                              /* an Ecmascript function */
 
 		resumer = thr->resumer;
 
@@ -71688,12 +73220,12 @@
 		DUK_ASSERT(resumer->state == DUK_HTHREAD_STATE_RESUMED);                                                     /* written by a previous RESUME handling */
 		DUK_ASSERT(resumer->callstack_top >= 2);                                                                     /* Ecmascript activation + Duktape.Thread.resume() activation */
 		DUK_ASSERT(resumer->callstack_curr != NULL);
+		DUK_ASSERT(resumer->callstack_curr->parent != NULL);
 		DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr) != NULL &&
 		           DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr)) &&
 		           ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack_curr))->func == duk_bi_thread_resume);
-		DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr - 1) != NULL &&
-		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr - 1)));                            /* an Ecmascript function */
-		DUK_ASSERT_DISABLE((resumer->callstack_curr - 1)->idx_retval >= 0);                                            /* unsigned */
+		DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent) != NULL &&
+		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr->parent)));                            /* an Ecmascript function */
 
 		if (thr->heap->lj.iserror) {
 			thr->state = DUK_HTHREAD_STATE_YIELDED;
@@ -71710,7 +73242,8 @@
 			DUK_DD(DUK_DDPRINT("-> yield an error, converted to a throw in the resumer, propagate"));
 			goto check_longjmp;
 		} else {
-			duk__handle_yield(thr, resumer, resumer->callstack_top - 2, &thr->heap->lj.value1);
+			duk_hthread_activation_unwind_norz(resumer);
+			duk__handle_yield(thr, resumer, &thr->heap->lj.value1);
 
 			thr->state = DUK_HTHREAD_STATE_YIELDED;
 			thr->resumer = NULL;
@@ -71749,77 +73282,75 @@
 		 *  Ecmascript activations.
 		 */
 
+		duk_activation *act;
 		duk_catcher *cat;
 		duk_hthread *resumer;
 
-		cat = thr->catchstack + thr->catchstack_top - 1;
-		while (cat >= thr->catchstack) {
-			if (thr == entry_thread &&
-			    cat->callstack_index < entry_callstack_index) {
-				/* entry level reached */
+		for (;;) {
+			act = thr->callstack_curr;
+			if (act == NULL) {
 				break;
 			}
 
-			if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
-				DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
-
-				duk__handle_catch(thr,
-				                  cat - thr->catchstack,
-				                  &thr->heap->lj.value1,
-				                  DUK_LJ_TYPE_THROW);
-
-				DUK_DD(DUK_DDPRINT("-> throw caught by a 'catch' clause, restart execution"));
-				retval = DUK__LONGJMP_RESTART;
-				goto wipe_and_return;
-			}
-
-			if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
-				DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
-				DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));
-
-				duk__handle_finally(thr,
-				                    cat - thr->catchstack,
-				                    &thr->heap->lj.value1,
-				                    DUK_LJ_TYPE_THROW);
-
-				DUK_DD(DUK_DDPRINT("-> throw caught by a 'finally' clause, restart execution"));
-				retval = DUK__LONGJMP_RESTART;
-				goto wipe_and_return;
-			}
-
-			cat--;
-		}
-
-		if (thr == entry_thread) {
-			/* not caught by anything before entry level; rethrow and let the
-			 * final catcher unwind everything
-			 */
-#if 0
-			duk_hthread_catchstack_unwind_norz(thr, (cat - thr->catchstack) + 1);  /* leave 'cat' as top catcher (also works if catchstack exhausted) */
-			duk_hthread_callstack_unwind_norz(thr, entry_callstack_index + 1);
-			DUK_REFZERO_CHECK_SLOW(thr);
-#endif
-			DUK_D(DUK_DPRINT("-> throw propagated up to entry level, rethrow and exit bytecode executor"));
-			retval = DUK__LONGJMP_RETHROW;
-			goto just_return;
-			/* Note: MUST NOT wipe_and_return here, as heap->lj must remain intact */
+			for (;;) {
+				cat = act->cat;
+				if (cat == NULL) {
+					break;
+				}
+
+				if (DUK_CAT_HAS_CATCH_ENABLED(cat)) {
+					DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
+
+					duk__handle_catch(thr,
+					                  &thr->heap->lj.value1,
+					                  DUK_LJ_TYPE_THROW);
+
+					DUK_DD(DUK_DDPRINT("-> throw caught by a 'catch' clause, restart execution"));
+					retval = DUK__LONGJMP_RESTART;
+					goto wipe_and_return;
+				}
+
+				if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
+					DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF);
+					DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));
+
+					duk__handle_finally(thr,
+					                    &thr->heap->lj.value1,
+					                    DUK_LJ_TYPE_THROW);
+
+					DUK_DD(DUK_DDPRINT("-> throw caught by a 'finally' clause, restart execution"));
+					retval = DUK__LONGJMP_RESTART;
+					goto wipe_and_return;
+				}
+
+				duk_hthread_catcher_unwind_norz(thr, act);
+			}
+
+			if (act == entry_act) {
+				/* Not caught by anything before entry level; rethrow and let the
+				 * final catcher finish unwinding (esp. value stack).
+				 */
+				DUK_D(DUK_DPRINT("-> throw propagated up to entry level, rethrow and exit bytecode executor"));
+				retval = DUK__LONGJMP_RETHROW;
+				goto just_return;
+			}
+
+			duk_hthread_activation_unwind_norz(thr);
 		}
 
 		DUK_DD(DUK_DDPRINT("-> throw not caught by current thread, yield error to resumer and recheck longjmp"));
 
-		/* not caught by current thread, thread terminates (yield error to resumer);
+		/* Not caught by current thread, thread terminates (yield error to resumer);
 		 * note that this may cause a cascade if the resumer terminates with an uncaught
-		 * exception etc (this is OK, but needs careful testing)
+		 * exception etc (this is OK, but needs careful testing).
 		 */
 
 		DUK_ASSERT(thr->resumer != NULL);
 		DUK_ASSERT(thr->resumer->callstack_top >= 2);  /* Ecmascript activation + Duktape.Thread.resume() activation */
 		DUK_ASSERT(thr->resumer->callstack_curr != NULL);
-		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
-		           DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
-		           ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume);  /* Duktape.Thread.resume() */
-		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1) != NULL &&
-		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1)));  /* an Ecmascript function */
+		DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL);
+		DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL &&
+		           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent)));  /* an Ecmascript function */
 
 		resumer = thr->resumer;
 
@@ -71880,70 +73411,61 @@
  * handling because it has a measurable performance impact in ordinary
  * environments and an extreme impact in Emscripten (GH-342).
  */
-DUK_LOCAL void duk__handle_break_or_continue(duk_hthread *thr,
-                                             duk_uint_t label_id,
-                                             duk_small_uint_t lj_type) {
+DUK_LOCAL DUK__NOINLINE_PERF void duk__handle_break_or_continue(duk_hthread *thr,
+                                                                duk_uint_t label_id,
+                                                                duk_small_uint_t lj_type) {
+	duk_activation *act;
 	duk_catcher *cat;
-	duk_size_t orig_callstack_index;
-
-	DUK_ASSERT(thr != NULL);
-
-	/*
-	 *  Find a matching label catcher or 'finally' catcher in
-	 *  the same function.
-	 *
-	 *  A label catcher must always exist and will match unless
-	 *  a 'finally' captures the break/continue first.  It is the
-	 *  compiler's responsibility to ensure that labels are used
-	 *  correctly.
-	 */
-
-	/* Note: thr->catchstack_top may be 0, so that cat < thr->catchstack
-	 * initially.  This is OK and intended.
-	 */
-	cat = thr->catchstack + thr->catchstack_top - 1;
-	DUK_ASSERT(thr->callstack_top > 0);
-	orig_callstack_index = thr->callstack_top - 1;
-
-	DUK_DDD(DUK_DDDPRINT("handling break/continue with label=%ld, callstack index=%ld",
-	                     (long) label_id, (long) cat->callstack_index));
-
-	while (cat >= thr->catchstack) {
-		if (cat->callstack_index != orig_callstack_index) {
-			break;
-		}
-		DUK_DDD(DUK_DDDPRINT("considering catcher %ld: type=%ld label=%ld",
-		                     (long) (cat - thr->catchstack),
+
+	DUK_ASSERT(thr != NULL);
+
+	/* Find a matching label catcher or 'finally' catcher in
+	 * the same function, unwinding catchers as we go.
+	 *
+	 * A label catcher must always exist and will match unless
+	 * a 'finally' captures the break/continue first.  It is the
+	 * compiler's responsibility to ensure that labels are used
+	 * correctly.
+	 */
+
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+
+	for (;;) {
+		cat = act->cat;
+		if (cat == NULL) {
+			break;
+		}
+
+		DUK_DDD(DUK_DDDPRINT("considering catcher %p: type=%ld label=%ld",
+		                     (void *) cat,
 		                     (long) DUK_CAT_GET_TYPE(cat),
 		                     (long) DUK_CAT_GET_LABEL(cat)));
 
+		/* XXX: bit mask test; FINALLY <-> TCF, single bit mask would suffice? */
+
 		if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF &&
 		    DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
-			duk_size_t cat_idx;
 			duk_tval tv_tmp;
 
-			cat_idx = (duk_size_t) (cat - thr->catchstack);  /* get before side effects */
-
 			DUK_TVAL_SET_U32(&tv_tmp, (duk_uint32_t) label_id);
-			duk__handle_finally(thr, cat_idx, &tv_tmp, lj_type);
+			duk__handle_finally(thr, &tv_tmp, lj_type);
 
 			DUK_DD(DUK_DDPRINT("-> break/continue caught by 'finally', restart execution"));
 			return;
 		}
 		if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL &&
 		    (duk_uint_t) DUK_CAT_GET_LABEL(cat) == label_id) {
-			duk_size_t cat_idx;
-
-			cat_idx = (duk_size_t) (cat - thr->catchstack);
-			duk__handle_label(thr, cat_idx, lj_type);
+			duk__handle_label(thr, lj_type);
 
 			DUK_DD(DUK_DDPRINT("-> break/continue caught by a label catcher (in the same function), restart execution"));
 			return;
 		}
-		cat--;
-	}
-
-	/* should never happen, but be robust */
+
+		duk_hthread_catcher_unwind_norz(thr, act);
+	}
+
+	/* Should never happen, but be robust. */
 	DUK_D(DUK_DPRINT("-> break/continue not caught by anything in the current function (should never happen), throw internal error"));
 	DUK_ERROR_INTERNAL(thr);
 	return;
@@ -71953,22 +73475,19 @@
  * it has a measurable performance impact in ordinary environments and an extreme
  * impact in Emscripten (GH-342).  Return value is on value stack top.
  */
-DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr,
-                                              duk_hthread *entry_thread,
-                                              duk_size_t entry_callstack_top) {
+DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, duk_activation *entry_act) {
 	duk_tval *tv1;
 	duk_tval *tv2;
 #if defined(DUK_USE_COROUTINE_SUPPORT)
 	duk_hthread *resumer;
 #endif
+	duk_activation *act;
 	duk_catcher *cat;
-	duk_size_t new_cat_top;
-	duk_size_t orig_callstack_index;
 
 	/* We can directly access value stack here. */
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(entry_thread != NULL);
+	DUK_ASSERT(entry_act != NULL);
 	DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
 	tv1 = thr->valstack_top - 1;
 	DUK_TVAL_CHKFAST_INPLACE_FAST(tv1);  /* fastint downgrade check for return values */
@@ -71996,78 +73515,69 @@
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->callstack_top >= 1);
-	DUK_ASSERT(thr->catchstack != NULL);
-
-	/* XXX: does not work if thr->catchstack is NULL */
-	/* XXX: does not work if thr->catchstack is allocated but lowest pointer */
-
-	cat = thr->catchstack + thr->catchstack_top - 1;  /* may be < thr->catchstack initially */
-	DUK_ASSERT(thr->callstack_top > 0);  /* ensures callstack_top - 1 >= 0 */
-	DUK_ASSERT(thr->callstack_curr != NULL);
-	orig_callstack_index = thr->callstack_top - 1;
-
-	while (cat >= thr->catchstack) {
-		if (cat->callstack_index != orig_callstack_index) {
-			break;
-		}
+
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+
+	for (;;) {
+		cat = act->cat;
+		if (cat == NULL) {
+			break;
+		}
+
 		if (DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF &&
 		    DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
-			duk_size_t cat_idx;
-
-			cat_idx = (duk_size_t) (cat - thr->catchstack);  /* get before side effects */
-
 			DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
-			duk__handle_finally(thr, cat_idx, thr->valstack_top - 1, DUK_LJ_TYPE_RETURN);
+			duk__handle_finally(thr, thr->valstack_top - 1, DUK_LJ_TYPE_RETURN);
 
 			DUK_DD(DUK_DDPRINT("-> return caught by 'finally', restart execution"));
 			return DUK__RETHAND_RESTART;
 		}
-		cat--;
-	}
-	/* If out of catchstack, cat = thr->catchstack - 1;
-	 * new_cat_top will be 0 in that case.
-	 */
-	new_cat_top = (duk_size_t) ((cat + 1) - thr->catchstack);
-	cat = NULL;  /* avoid referencing, invalidated */
-
-	DUK_DDD(DUK_DDDPRINT("no catcher in catch stack, return to calling activation / yield"));
-
-	if (thr == entry_thread &&
-	    thr->callstack_top == entry_callstack_top) {
-		/* Return to the bytecode executor caller which will unwind stacks.
+
+		duk_hthread_catcher_unwind_norz(thr, act);
+	}
+
+	if (act == entry_act) {
+		/* Return to the bytecode executor caller who will unwind stacks
+		 * and handle constructor post-processing.
 		 * Return value is already on the stack top: [ ... retval ].
 		 */
 
-		/* XXX: could unwind catchstack here, so that call handling
-		 * didn't need to do that?
-		 */
 		DUK_DDD(DUK_DDDPRINT("-> return propagated up to entry level, exit bytecode executor"));
 		return DUK__RETHAND_FINISHED;
 	}
 
 	if (thr->callstack_top >= 2) {
 		/* There is a caller; it MUST be an Ecmascript caller (otherwise it would
-		 * match entry level check)
-		 */
-
-		DUK_DDD(DUK_DDDPRINT("return to Ecmascript caller, idx_retval=%ld, lj_value1=%!T",
-		                     (long) (thr->callstack_curr - 1)->idx_retval,
+		 * match entry_act check).
+		 */
+		DUK_DDD(DUK_DDDPRINT("return to Ecmascript caller, retval_byteoff=%ld, lj_value1=%!T",
+		                     (long) (thr->callstack_curr->parent->retval_byteoff),
 		                     (duk_tval *) &thr->heap->lj.value1));
 
-		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr - 1)));   /* must be ecmascript */
-
-		tv1 = thr->valstack + (thr->callstack_curr - 1)->idx_retval;
+		DUK_ASSERT(thr->callstack_curr != NULL);
+		DUK_ASSERT(thr->callstack_curr->parent != NULL);
+		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr->parent)));   /* must be ecmascript */
+
+#if defined(DUK_USE_ES6_PROXY)
+		if (thr->callstack_curr->flags & (DUK_ACT_FLAG_CONSTRUCT | DUK_ACT_FLAG_CONSTRUCT_PROXY)) {
+			duk_call_construct_postprocess(thr, thr->callstack_curr->flags & DUK_ACT_FLAG_CONSTRUCT_PROXY);  /* side effects */
+		}
+#else
+		if (thr->callstack_curr->flags & DUK_ACT_FLAG_CONSTRUCT) {
+			duk_call_construct_postprocess(thr, 0);  /* side effects */
+		}
+#endif
+
+		tv1 = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + thr->callstack_curr->parent->retval_byteoff);
 		DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
 		tv2 = thr->valstack_top - 1;
 		DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2);  /* side effects */
 
-		DUK_DDD(DUK_DDDPRINT("return value at idx_retval=%ld is %!T",
-		                     (long) (thr->callstack_curr - 1)->idx_retval,
-		                     (duk_tval *) (thr->valstack + (thr->callstack_curr - 1)->idx_retval)));
-
-		duk_hthread_catchstack_unwind_norz(thr, new_cat_top);  /* leave 'cat' as top catcher (also works if catchstack exhausted) */
-		duk_hthread_callstack_unwind_norz(thr, thr->callstack_top - 1);
-		duk__reconfig_valstack_ecma_return(thr, thr->callstack_top - 1);
+		/* Catch stack unwind happens inline in callstack unwind. */
+		duk_hthread_activation_unwind_norz(thr);
+
+		duk__reconfig_valstack_ecma_return(thr);
 
 		DUK_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller"));
 		return DUK__RETHAND_RESTART;
@@ -72079,28 +73589,49 @@
 	DUK_ASSERT(thr->resumer != NULL);
 	DUK_ASSERT(thr->resumer->callstack_top >= 2);  /* Ecmascript activation + Duktape.Thread.resume() activation */
 	DUK_ASSERT(thr->resumer->callstack_curr != NULL);
+	DUK_ASSERT(thr->resumer->callstack_curr->parent != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
-	           DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
-	           ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume);  /* Duktape.Thread.resume() */
-	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1) != NULL &&
-	           DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1)));  /* an Ecmascript function */
-	DUK_ASSERT_DISABLE((thr->resumer->callstack_curr - 1)->idx_retval >= 0);                  /* unsigned */
+			DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
+			((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume);  /* Duktape.Thread.resume() */
+	DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent) != NULL &&
+			DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr->parent)));  /* an Ecmascript function */
 	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
 	DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
 
 	resumer = thr->resumer;
 
-	/* Share yield longjmp handler. */
+	/* Share yield longjmp handler.
+	 *
+	 * This sequence of steps is a bit fragile (see GH-1845):
+	 * - We need the return value from 'thr' (resumed thread) value stack.
+	 *   The termination unwinds its value stack, losing the value.
+	 * - We need a refcounted reference for 'thr', which may only exist
+	 *   in the caller value stack.  We can't unwind or reconfigure the
+	 *   caller's value stack without potentially freeing 'thr'.
+	 *
+	 * Current approach is to capture the 'thr' return value and store
+	 * a reference to 'thr' in the caller value stack temporarily.  This
+	 * keeps 'thr' reachable until final yield/return handling which
+	 * removes the references atomatically.
+	 */
+
 	DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
-	duk__handle_yield(thr, resumer, resumer->callstack_top - 2, thr->valstack_top - 1);
-
-	duk_hthread_terminate(thr);  /* updates thread state, minimizes its allocations */
-	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED);
-
+	duk_hthread_activation_unwind_norz(resumer);  /* May remove last reference to 'thr', but is NORZ. */
+	duk_push_tval(resumer, thr->valstack_top - 1);  /* Capture return value, side effect free. */
+	duk_push_hthread(resumer, thr);  /* Make 'thr' reachable again, before side effects. */
+
+	duk_hthread_terminate(thr);  /* Updates thread state, minimizes its allocations. */
 	thr->resumer = NULL;
 	DUK_HTHREAD_DECREF(thr, resumer);
+	DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED);
+
 	resumer->state = DUK_HTHREAD_STATE_RUNNING;
 	DUK_HEAP_SWITCH_THREAD(thr->heap, resumer);
+
+	DUK_ASSERT(resumer->valstack_top - 2 >= resumer->valstack_bottom);
+	duk__handle_yield(thr, resumer, resumer->valstack_top - 2);
+	thr = NULL;  /* 'thr' invalidated by call */
+
 #if 0
 	thr = resumer;  /* not needed */
 #endif
@@ -72146,7 +73677,6 @@
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_immediate, duk_small_uint_t *out_interrupt_retval) {
-	duk_context *ctx;
 	duk_activation *act;
 	duk_breakpoint *bp;
 	duk_breakpoint **bp_active;
@@ -72156,7 +73686,6 @@
 
 	DUK_ASSERT(thr->heap->dbg_processing == 0);  /* don't re-enter e.g. during Eval */
 
-	ctx = (duk_context *) thr;
 	act = thr->callstack_curr;
 	DUK_ASSERT(act != NULL);
 
@@ -72166,22 +73695,28 @@
 	 */
 
 	/*
+	 *  Single opcode step check
+	 */
+
+	if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE) {
+		DUK_D(DUK_DPRINT("PAUSE TRIGGERED by one opcode step"));
+		duk_debug_set_paused(thr->heap);
+	}
+
+	/*
 	 *  Breakpoint and step state checks
 	 */
 
 	if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE ||
-	    (thr->heap->dbg_step_thread == thr &&
-	     thr->heap->dbg_step_csindex == thr->callstack_top - 1)) {
+	    (thr->heap->dbg_pause_act == thr->callstack_curr)) {
 		line = duk_debug_curr_line(thr);
 
 		if (act->prev_line != line) {
 			/* Stepped?  Step out is handled by callstack unwind. */
-			if ((thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO ||
-			     thr->heap->dbg_step_type == DUK_STEP_TYPE_OVER) &&
-			    (thr->heap->dbg_step_thread == thr) &&
-			    (thr->heap->dbg_step_csindex == thr->callstack_top - 1) &&
-			    (line != thr->heap->dbg_step_startline)) {
-				DUK_D(DUK_DPRINT("STEP STATE TRIGGERED PAUSE at line %ld",
+			if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
+			    (thr->heap->dbg_pause_act == thr->callstack_curr) &&
+			    (line != thr->heap->dbg_pause_startline)) {
+				DUK_D(DUK_DPRINT("PAUSE TRIGGERED by line change, at line %ld",
 				                 (long) line));
 				duk_debug_set_paused(thr->heap);
 			}
@@ -72207,7 +73742,7 @@
 
 				DUK_ASSERT(bp->filename != NULL);
 				if (act->prev_line != bp->line && line == bp->line) {
-					DUK_D(DUK_DPRINT("BREAKPOINT TRIGGERED at %!O:%ld",
+					DUK_D(DUK_DPRINT("PAUSE TRIGGERED by breakpoint at %!O:%ld",
 					                 (duk_heaphdr *) bp->filename, (long) bp->line));
 					duk_debug_set_paused(thr->heap);
 				}
@@ -72216,7 +73751,7 @@
 			;
 		}
 
-		act->prev_line = line;
+		act->prev_line = (duk_uint32_t) line;
 	}
 
 	/*
@@ -72240,7 +73775,8 @@
 	}
 
 	/* XXX: remove heap->dbg_exec_counter, use heap->inst_count_interrupt instead? */
-	thr->heap->dbg_exec_counter += thr->interrupt_init;
+	DUK_ASSERT(thr->interrupt_init >= 0);
+	thr->heap->dbg_exec_counter += (duk_uint_t) thr->interrupt_init;
 	if (thr->heap->dbg_exec_counter - thr->heap->dbg_last_counter >= DUK_HEAP_DBG_RATELIMIT_OPCODES) {
 		/* Overflow of the execution counter is fine and doesn't break
 		 * anything here.
@@ -72249,12 +73785,14 @@
 		duk_double_t now, diff_last;
 
 		thr->heap->dbg_last_counter = thr->heap->dbg_exec_counter;
-		now = DUK_USE_DATE_GET_NOW(ctx);
+		now = duk_time_get_monotonic_time(thr);
 
 		diff_last = now - thr->heap->dbg_last_time;
 		if (diff_last < 0.0 || diff_last >= (duk_double_t) DUK_HEAP_DBG_RATELIMIT_MILLISECS) {
-			/* Negative value checked so that a "time jump" works
-			 * reasonably.
+			/* Monotonic time should not experience time jumps,
+			 * but the provider may be missing and we're actually
+			 * using Ecmascript time.  So, tolerate negative values
+			 * so that a time jump works reasonably.
 			 *
 			 * Same interval is now used for status sending and
 			 * peeking.
@@ -72281,7 +73819,6 @@
 	 *  detaching.
 	 */
 
-	act = NULL;  /* may be changed */
 	if (process_messages) {
 		DUK_ASSERT(thr->heap->dbg_processing == 0);
 		processed_messages = duk_debug_process_messages(thr, 0 /*no_block*/);
@@ -72297,13 +73834,12 @@
 	 */
 
 	if (duk_debug_is_attached(thr->heap)) {
-		act = thr->callstack_curr;  /* relookup, may have changed */
+		DUK_ASSERT(act == thr->callstack_curr);
 		DUK_ASSERT(act != NULL);
 		if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE ||
-		    ((thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO ||
-		      thr->heap->dbg_step_type == DUK_STEP_TYPE_OVER) &&
-		     thr->heap->dbg_step_thread == thr &&
-		     thr->heap->dbg_step_csindex == thr->callstack_top - 1) ||
+		    (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) ||
+		    ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
+		     thr->heap->dbg_pause_act == thr->callstack_curr) ||
 		     DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) {
 			*out_immediate = 1;
 		}
@@ -72314,6 +73850,13 @@
 		if (processed_messages) {
 			DUK_D(DUK_DPRINT("processed debug messages, restart execution to recheck possibly changed breakpoints"));
 			*out_interrupt_retval = DUK__INT_RESTART;
+		} else {
+			if (thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_ONE_OPCODE) {
+				/* Set 'pause after one opcode' active only when we're
+				 * actually just about to execute code.
+				 */
+				thr->heap->dbg_pause_flags |= DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE;
+			}
 		}
 	} else {
 		DUK_D(DUK_DPRINT("debugger became detached, resume normal execution"));
@@ -72321,7 +73864,7 @@
 }
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
-DUK_LOCAL DUK_NOINLINE DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
+DUK_LOCAL DUK__NOINLINE_PERF DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
 	duk_int_t ctr;
 	duk_activation *act;
 	duk_hcompfunc *fun;
@@ -72330,7 +73873,6 @@
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(thr->heap != NULL);
-	DUK_ASSERT(thr->callstack != NULL);
 	DUK_ASSERT(thr->callstack_top > 0);
 
 #if defined(DUK_USE_DEBUG)
@@ -72408,8 +73950,7 @@
 		 * detaching (to finish off the pending detach).
 		 */
 		duk__interrupt_handle_debugger(thr, &immediate, &retval);
-		act = thr->callstack_curr;  /* relookup if changed */
-		DUK_UNREF(act);  /* 'act' is no longer accessed, scanbuild fix */
+		DUK_ASSERT(act == thr->callstack_curr);
 	}
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
@@ -72545,21 +74086,19 @@
 	DUK_DD(DUK_DDPRINT("ACTIVE BREAKPOINTS: %ld", (long) (bp_active - thr->heap->dbg_breakpoints_active)));
 
 	/* Force pause if we were doing "step into" in another activation. */
-	if (thr->heap->dbg_step_thread != NULL &&
-	    thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO &&
-	    (thr->heap->dbg_step_thread != thr ||
-	     thr->heap->dbg_step_csindex != thr->callstack_top - 1)) {
-		DUK_D(DUK_DPRINT("STEP INTO ACTIVE, FORCE PAUSED"));
+	if ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_FUNC_ENTRY) &&
+	    thr->heap->dbg_pause_act != thr->callstack_curr) {
+		DUK_D(DUK_DPRINT("PAUSE TRIGGERED by function entry"));
 		duk_debug_set_paused(thr->heap);
 	}
 
 	/* Force interrupt right away if we're paused or in "checked mode".
 	 * Step out is handled by callstack unwind.
 	 */
-	if (act->flags & (DUK_ACT_FLAG_BREAKPOINT_ACTIVE) ||
+	if ((act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE) ||
 	    DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ||
-	    (thr->heap->dbg_step_type != DUK_STEP_TYPE_OUT &&
-	     thr->heap->dbg_step_csindex == thr->callstack_top - 1)) {
+	    ((thr->heap->dbg_pause_flags & DUK_PAUSE_FLAG_LINE_CHANGE) &&
+	     thr->heap->dbg_pause_act == thr->callstack_curr)) {
 		/* We'll need to interrupt early so recompute the init
 		 * counter to reflect the number of bytecode instructions
 		 * executed so that step counts for e.g. debugger rate
@@ -72573,6 +74112,519 @@
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
 
 /*
+ *  Opcode handlers for opcodes with a lot of code and which are relatively
+ *  rare; NOINLINE to reduce amount of code in main bytecode dispatcher.
+ */
+
+DUK_LOCAL DUK__NOINLINE_PERF void duk__handle_op_initset_initget(duk_hthread *thr, duk_uint_fast32_t ins) {
+	duk_bool_t is_set = (DUK_DEC_OP(ins) == DUK_OP_INITSET);
+	duk_uint_fast_t idx;
+	duk_uint_t defprop_flags;
+
+	/* A -> object register (acts as a source)
+	 * BC -> BC+0 contains key, BC+1 closure (value)
+	 */
+
+	/* INITSET/INITGET are only used to initialize object literal keys.
+	 * There may be a previous propery in ES2015 because duplicate property
+	 * names are allowed.
+	 */
+
+	/* This could be made more optimal by accessing internals directly. */
+
+	idx = (duk_uint_fast_t) DUK_DEC_BC(ins);
+	duk_dup(thr, (duk_idx_t) (idx + 0));  /* key */
+	duk_dup(thr, (duk_idx_t) (idx + 1));  /* getter/setter */
+	if (is_set) {
+	        defprop_flags = DUK_DEFPROP_HAVE_SETTER |
+	                        DUK_DEFPROP_FORCE |
+	                        DUK_DEFPROP_SET_ENUMERABLE |
+	                        DUK_DEFPROP_SET_CONFIGURABLE;
+	} else {
+	        defprop_flags = DUK_DEFPROP_HAVE_GETTER |
+	                        DUK_DEFPROP_FORCE |
+	                        DUK_DEFPROP_SET_ENUMERABLE |
+	                        DUK_DEFPROP_SET_CONFIGURABLE;
+	}
+	duk_def_prop(thr, (duk_idx_t) DUK_DEC_A(ins), defprop_flags);
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF void duk__handle_op_trycatch(duk_hthread *thr, duk_uint_fast32_t ins, duk_instr_t *curr_pc) {
+	duk_activation *act;
+	duk_catcher *cat;
+	duk_tval *tv1;
+	duk_small_uint_fast_t a;
+	duk_small_uint_fast_t bc;
+
+	/* A -> flags
+	 * BC -> reg_catch; base register for two registers used both during
+	 *       trycatch setup and when catch is triggered
+	 *
+	 *      If DUK_BC_TRYCATCH_FLAG_CATCH_BINDING set:
+	 *          reg_catch + 0: catch binding variable name (string).
+	 *          Automatic declarative environment is established for
+	 *          the duration of the 'catch' clause.
+	 *
+	 *      If DUK_BC_TRYCATCH_FLAG_WITH_BINDING set:
+	 *          reg_catch + 0: with 'target value', which is coerced to
+	 *          an object and then used as a bindind object for an
+	 *          environment record.  The binding is initialized here, for
+	 *          the 'try' clause.
+	 *
+	 * Note that a TRYCATCH generated for a 'with' statement has no
+	 * catch or finally parts.
+	 */
+
+	/* XXX: TRYCATCH handling should be reworked to avoid creating
+	 * an explicit scope unless it is actually needed (e.g. function
+	 * instances or eval is executed inside the catch block).  This
+	 * rework is not trivial because the compiler doesn't have an
+	 * intermediate representation.  When the rework is done, the
+	 * opcode format can also be made more straightforward.
+	 */
+
+	/* XXX: side effect handling is quite awkward here */
+
+	DUK_DDD(DUK_DDDPRINT("TRYCATCH: reg_catch=%ld, have_catch=%ld, "
+	                     "have_finally=%ld, catch_binding=%ld, with_binding=%ld (flags=0x%02lx)",
+	                     (long) DUK_DEC_BC(ins),
+	                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH ? 1 : 0),
+	                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY ? 1 : 0),
+	                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING ? 1 : 0),
+	                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_WITH_BINDING ? 1 : 0),
+	                     (unsigned long) DUK_DEC_A(ins)));
+
+	a = DUK_DEC_A(ins);
+	bc = DUK_DEC_BC(ins);
+
+	/* Registers 'bc' and 'bc + 1' are written in longjmp handling
+	 * and if their previous values (which are temporaries) become
+	 * unreachable -and- have a finalizer, there'll be a function
+	 * call during error handling which is not supported now (GH-287).
+	 * Ensure that both 'bc' and 'bc + 1' have primitive values to
+	 * guarantee no finalizer calls in error handling.  Scrubbing also
+	 * ensures finalizers for the previous values run here rather than
+	 * later.  Error handling related values are also written to 'bc'
+	 * and 'bc + 1' but those values never become unreachable during
+	 * error handling, so there's no side effect problem even if the
+	 * error value has a finalizer.
+	 */
+	duk_dup(thr, (duk_idx_t) bc);  /* Stabilize value. */
+	duk_to_undefined(thr, (duk_idx_t) bc);
+	duk_to_undefined(thr, (duk_idx_t) (bc + 1));
+
+	/* Allocate catcher and populate it.  Doesn't have to
+	 * be fully atomic, but the catcher must be in a
+	 * consistent state if side effects (such as finalizer
+	 * calls) occur.
+	 */
+
+	cat = duk_hthread_catcher_alloc(thr);
+	DUK_ASSERT(cat != NULL);
+
+	cat->flags = DUK_CAT_TYPE_TCF;
+	cat->h_varname = NULL;
+	cat->pc_base = (duk_instr_t *) curr_pc;  /* pre-incremented, points to first jump slot */
+	cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;
+
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	cat->parent = act->cat;
+	act->cat = cat;
+
+	if (a & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) {
+		cat->flags |= DUK_CAT_FLAG_CATCH_ENABLED;
+	}
+	if (a & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) {
+		cat->flags |= DUK_CAT_FLAG_FINALLY_ENABLED;
+	}
+	if (a & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING) {
+		DUK_DDD(DUK_DDDPRINT("catch binding flag set to catcher"));
+		cat->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED;
+		tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
+		DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
+
+		/* borrowed reference; although 'tv1' comes from a register,
+		 * its value was loaded using LDCONST so the constant will
+		 * also exist and be reachable.
+		 */
+		cat->h_varname = DUK_TVAL_GET_STRING(tv1);
+	} else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) {
+		duk_hobjenv *env;
+		duk_hobject *target;
+
+		/* Delayed env initialization for activation (if needed). */
+		DUK_ASSERT(thr->callstack_top >= 1);
+		DUK_ASSERT(act == thr->callstack_curr);
+		DUK_ASSERT(act != NULL);
+		if (act->lex_env == NULL) {
+			DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
+			DUK_ASSERT(act->var_env == NULL);
+
+			duk_js_init_activation_environment_records_delayed(thr, act);
+			DUK_ASSERT(act == thr->callstack_curr);
+			DUK_UNREF(act);  /* 'act' is no longer accessed, scanbuild fix */
+		}
+		DUK_ASSERT(act->lex_env != NULL);
+		DUK_ASSERT(act->var_env != NULL);
+
+		/* Coerce 'with' target. */
+		target = duk_to_hobject(thr, -1);
+		DUK_ASSERT(target != NULL);
+
+		/* Create an object environment; it is not pushed
+		 * so avoid side effects very carefully until it is
+		 * referenced.
+		 */
+		env = duk_hobjenv_alloc(thr,
+		                        DUK_HOBJECT_FLAG_EXTENSIBLE |
+		                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
+		DUK_ASSERT(env != NULL);
+		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
+		env->target = target;  /* always provideThis=true */
+		DUK_HOBJECT_INCREF(thr, target);
+		env->has_this = 1;
+		DUK_ASSERT_HOBJENV_VALID(env);
+		DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iO", env));
+
+		DUK_ASSERT(act == thr->callstack_curr);
+		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
+		DUK_ASSERT(act->lex_env != NULL);
+		DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, act->lex_env);
+		act->lex_env = (duk_hobject *) env;  /* Now reachable. */
+		DUK_HOBJECT_INCREF(thr, (duk_hobject *) env);
+		/* Net refcount change to act->lex_env is 0: incref for env's
+		 * prototype, decref for act->lex_env overwrite.
+		 */
+
+		/* Set catcher lex_env active (affects unwind)
+		 * only when the whole setup is complete.
+		 */
+		cat = act->cat;  /* XXX: better to relookup? not mandatory because 'cat' is stable */
+		cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE;
+	} else {
+		;
+	}
+
+	DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, pc_base=%ld, "
+	                     "idx_base=%ld, h_varname=%!O",
+	                     (unsigned long) cat->flags,
+	                     (long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname));
+
+	duk_pop_unsafe(thr);
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF duk_instr_t *duk__handle_op_endtry(duk_hthread *thr, duk_uint_fast32_t ins) {
+	duk_activation *act;
+	duk_catcher *cat;
+	duk_tval *tv1;
+	duk_instr_t *pc_base;
+
+	DUK_UNREF(ins);
+
+	DUK_ASSERT(thr->callstack_top >= 1);
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+	DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_TCF);
+
+	DUK_DDD(DUK_DDDPRINT("ENDTRY: clearing catch active flag (regardless of whether it was set or not)"));
+	DUK_CAT_CLEAR_CATCH_ENABLED(cat);
+
+	pc_base = cat->pc_base;
+
+	if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
+		DUK_DDD(DUK_DDDPRINT("ENDTRY: finally part is active, jump through 2nd jump slot with 'normal continuation'"));
+
+		tv1 = thr->valstack + cat->idx_base;
+		DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
+		DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
+		tv1 = NULL;
+
+		tv1 = thr->valstack + cat->idx_base + 1;
+		DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
+		DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
+		tv1 = NULL;
+
+		DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
+	} else {
+		DUK_DDD(DUK_DDDPRINT("ENDTRY: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));
+
+		duk_hthread_catcher_unwind_norz(thr, act);  /* lexenv may be set for 'with' binding */
+		/* no need to unwind callstack */
+	}
+
+	return pc_base + 1;  /* new curr_pc value */
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF duk_instr_t *duk__handle_op_endcatch(duk_hthread *thr, duk_uint_fast32_t ins) {
+	duk_activation *act;
+	duk_catcher *cat;
+	duk_tval *tv1;
+	duk_instr_t *pc_base;
+
+	DUK_UNREF(ins);
+
+	DUK_ASSERT(thr->callstack_top >= 1);
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	cat = act->cat;
+	DUK_ASSERT(cat != NULL);
+	DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));  /* cleared before entering catch part */
+
+	if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
+		duk_hobject *prev_env;
+
+		/* 'with' binding has no catch clause, so can't be here unless a normal try-catch */
+		DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat));
+		DUK_ASSERT(act->lex_env != NULL);
+
+		DUK_DDD(DUK_DDDPRINT("ENDCATCH: popping catcher part lexical environment"));
+
+		prev_env = act->lex_env;
+		DUK_ASSERT(prev_env != NULL);
+		act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, prev_env);
+		DUK_CAT_CLEAR_LEXENV_ACTIVE(cat);
+		DUK_HOBJECT_INCREF(thr, act->lex_env);
+		DUK_HOBJECT_DECREF(thr, prev_env);  /* side effects */
+
+		DUK_ASSERT(act == thr->callstack_curr);
+		DUK_ASSERT(act != NULL);
+	}
+
+	pc_base = cat->pc_base;
+
+	if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
+		DUK_DDD(DUK_DDDPRINT("ENDCATCH: finally part is active, jump through 2nd jump slot with 'normal continuation'"));
+
+		tv1 = thr->valstack + cat->idx_base;
+		DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
+		DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
+		tv1 = NULL;
+
+		tv1 = thr->valstack + cat->idx_base + 1;
+		DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
+		DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
+		tv1 = NULL;
+
+		DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
+	} else {
+		DUK_DDD(DUK_DDDPRINT("ENDCATCH: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));
+
+		duk_hthread_catcher_unwind_norz(thr, act);
+		/* no need to unwind callstack */
+	}
+
+	return pc_base + 1;  /* new curr_pc value */
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF duk_small_uint_t duk__handle_op_endfin(duk_hthread *thr, duk_uint_fast32_t ins, duk_activation *entry_act) {
+	duk_activation *act;
+	duk_tval *tv1;
+	duk_uint_t reg_catch;
+	duk_small_uint_t cont_type;
+	duk_small_uint_t ret_result;
+
+	DUK_ASSERT(thr->ptr_curr_pc == NULL);
+	DUK_ASSERT(thr->callstack_top >= 1);
+	act = thr->callstack_curr;
+	DUK_ASSERT(act != NULL);
+	reg_catch = DUK_DEC_ABC(ins);
+
+	/* CATCH flag may be enabled or disabled here; it may be enabled if
+	 * the statement has a catch block but the try block does not throw
+	 * an error.
+	 */
+
+	DUK_DDD(DUK_DDDPRINT("ENDFIN: completion value=%!T, type=%!T",
+	                     (duk_tval *) (thr->valstack_bottom + reg_catch + 0),
+	                     (duk_tval *) (thr->valstack_bottom + reg_catch + 1)));
+
+	tv1 = thr->valstack_bottom + reg_catch + 1;  /* type */
+	DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
+#if defined(DUK_USE_FASTINT)
+	DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
+	cont_type = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1);
+#else
+	cont_type = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
+#endif
+
+	tv1--;  /* value */
+
+	switch (cont_type) {
+	case DUK_LJ_TYPE_NORMAL: {
+		DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'normal' (non-abrupt) completion -> "
+		                     "dismantle catcher, resume execution after ENDFIN"));
+
+		duk_hthread_catcher_unwind_norz(thr, act);
+		/* no need to unwind callstack */
+		return 0;  /* restart execution */
+	}
+	case DUK_LJ_TYPE_RETURN: {
+		DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'return' complation -> dismantle "
+		                     "catcher, handle return, lj.value1=%!T", tv1));
+
+		/* Not necessary to unwind catch stack: return handling will
+		 * do it.  The finally flag of 'cat' is no longer set.  The
+		 * catch flag may be set, but it's not checked by return handling.
+		 */
+
+		duk_push_tval(thr, tv1);
+		ret_result = duk__handle_return(thr, entry_act);
+		if (ret_result == DUK__RETHAND_RESTART) {
+			return 0;  /* restart execution */
+		}
+		DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED);
+
+		DUK_DDD(DUK_DDDPRINT("exiting executor after ENDFIN and RETURN (pseudo) longjmp type"));
+		return 1;  /* exit executor */
+	}
+	case DUK_LJ_TYPE_BREAK:
+	case DUK_LJ_TYPE_CONTINUE: {
+		duk_uint_t label_id;
+		duk_small_uint_t lj_type;
+
+		/* Not necessary to unwind catch stack: break/continue
+		 * handling will do it.  The finally flag of 'cat' is
+		 * no longer set.  The catch flag may be set, but it's
+		 * not checked by break/continue handling.
+		 */
+
+		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
+#if defined(DUK_USE_FASTINT)
+		DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
+		label_id = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1);
+#else
+		label_id = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
+#endif
+		lj_type = cont_type;
+		duk__handle_break_or_continue(thr, label_id, lj_type);
+		return 0;  /* restart execution */
+	}
+	default: {
+		DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with abrupt completion, lj_type=%ld -> "
+		                     "dismantle catcher, re-throw error",
+		                     (long) cont_type));
+
+		duk_err_setup_ljstate1(thr, (duk_small_uint_t) cont_type, tv1);
+		/* No debugger Throw notify check on purpose (rethrow). */
+
+		DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* always in executor */
+		duk_err_longjmp(thr);
+		DUK_UNREACHABLE();
+	}
+	}
+
+	DUK_UNREACHABLE();
+	return 0;
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF void duk__handle_op_initenum(duk_hthread *thr, duk_uint_fast32_t ins) {
+	duk_small_uint_t b;
+	duk_small_uint_t c;
+
+	/*
+	 *  Enumeration semantics come from for-in statement, E5 Section 12.6.4.
+	 *  If called with 'null' or 'undefined', this opcode returns 'null' as
+	 *  the enumerator, which is special cased in NEXTENUM.  This simplifies
+	 *  the compiler part
+	 */
+
+	/* B -> register for writing enumerator object
+	 * C -> value to be enumerated (register)
+	 */
+	b = DUK_DEC_B(ins);
+	c = DUK_DEC_C(ins);
+
+	if (duk_is_null_or_undefined(thr, (duk_idx_t) c)) {
+		duk_push_null(thr);
+		duk_replace(thr, (duk_idx_t) b);
+	} else {
+		duk_dup(thr, (duk_idx_t) c);
+		duk_to_object(thr, -1);
+		duk_hobject_enumerator_create(thr, 0 /*enum_flags*/);  /* [ ... val ] --> [ ... enum ] */
+		duk_replace(thr, (duk_idx_t) b);
+	}
+}
+
+DUK_LOCAL DUK__NOINLINE_PERF duk_small_uint_t duk__handle_op_nextenum(duk_hthread *thr, duk_uint_fast32_t ins) {
+	duk_small_uint_t b;
+	duk_small_uint_t c;
+	duk_small_uint_t pc_skip = 0;
+
+	/*
+	 *  NEXTENUM checks whether the enumerator still has unenumerated
+	 *  keys.  If so, the next key is loaded to the target register
+	 *  and the next instruction is skipped.  Otherwise the next instruction
+	 *  will be executed, jumping out of the enumeration loop.
+	 */
+
+	/* B -> target register for next key
+	 * C -> enum register
+	 */
+	b = DUK_DEC_B(ins);
+	c = DUK_DEC_C(ins);
+
+	DUK_DDD(DUK_DDDPRINT("NEXTENUM: b->%!T, c->%!T",
+	                     (duk_tval *) duk_get_tval(thr, (duk_idx_t) b),
+	                     (duk_tval *) duk_get_tval(thr, (duk_idx_t) c)));
+
+	if (duk_is_object(thr, (duk_idx_t) c)) {
+		/* XXX: assert 'c' is an enumerator */
+		duk_dup(thr, (duk_idx_t) c);
+		if (duk_hobject_enumerator_next(thr, 0 /*get_value*/)) {
+			/* [ ... enum ] -> [ ... next_key ] */
+			DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ",
+			                     (duk_tval *) duk_get_tval(thr, -1)));
+			pc_skip = 1;
+		} else {
+			/* [ ... enum ] -> [ ... ] */
+			DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
+			DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
+			thr->valstack_top++;
+		}
+		duk_replace(thr, (duk_idx_t) b);
+	} else {
+		/* 'null' enumerator case -> behave as with an empty enumerator */
+		DUK_ASSERT(duk_is_null(thr, (duk_idx_t) c));
+		DUK_DDD(DUK_DDDPRINT("enum is null, execute jump slot"));
+	}
+
+	return pc_skip;
+}
+
+/*
+ *  Call handling helpers.
+ */
+
+DUK_LOCAL duk_bool_t duk__executor_handle_call(duk_hthread *thr, duk_idx_t idx, duk_idx_t nargs, duk_small_uint_t call_flags) {
+	duk_bool_t rc;
+
+	duk_set_top_unsafe(thr, (duk_idx_t) (idx + nargs + 2));   /* [ ... func this arg1 ... argN ] */
+
+	/* Attempt an Ecma-to-Ecma call setup.  If the call
+	 * target is (directly or indirectly) Reflect.construct(),
+	 * the call may change into a constructor call on the fly.
+	 */
+	rc = (duk_bool_t) duk_handle_call_unprotected(thr, idx, call_flags);
+	if (rc != 0) {
+		/* Ecma-to-ecma call possible, may or may not
+		 * be a tail call.  Avoid C recursion by
+		 * reusing current executor instance.
+		 */
+		DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
+		/* curr_pc synced by duk_handle_call_unprotected() */
+		DUK_ASSERT(thr->ptr_curr_pc == NULL);
+		return rc;
+	} else {
+		/* Call was handled inline. */
+	}
+	DUK_ASSERT(thr->ptr_curr_pc != NULL);
+	return rc;
+}
+
+/*
  *  Ecmascript bytecode executor.
  *
  *  Resume execution for the current thread from its current activation.
@@ -72607,7 +74659,9 @@
 #else
 #define DUK__FUN()          ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr))
 #endif
-#define DUK__STRICT()       (DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN()))
+
+/* Strict flag. */
+#define DUK__STRICT()       ((duk_small_uint_t) DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN()))
 
 /* Reg/const access macros: these are very footprint and performance sensitive
  * so modify with care.  Arguments are sometimes evaluated multiple times which
@@ -72681,23 +74735,23 @@
 #endif
 
 #define DUK__SYNC_CURR_PC()  do { \
-		duk_activation *act; \
-		act = thr->callstack_curr; \
-		act->curr_pc = curr_pc; \
+		duk_activation *duk__act; \
+		duk__act = thr->callstack_curr; \
+		duk__act->curr_pc = curr_pc; \
 	} while (0)
 #define DUK__SYNC_AND_NULL_CURR_PC()  do { \
-		duk_activation *act; \
-		act = thr->callstack_curr; \
-		act->curr_pc = curr_pc; \
+		duk_activation *duk__act; \
+		duk__act = thr->callstack_curr; \
+		duk__act->curr_pc = curr_pc; \
 		thr->ptr_curr_pc = NULL; \
 	} while (0)
 
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-#define DUK__LOOKUP_INDIRECT_INDEX(idx) do { \
-		(idx) = (duk_uint_fast_t) duk_get_uint(ctx, (idx)); \
+#define DUK__LOOKUP_INDIRECT(idx) do { \
+		(idx) = (duk_uint_fast_t) duk_get_uint(thr, (duk_idx_t) (idx)); \
 	} while (0)
 #elif defined(DUK_USE_FASTINT)
-#define DUK__LOOKUP_INDIRECT_INDEX(idx) do { \
+#define DUK__LOOKUP_INDIRECT(idx) do { \
 		duk_tval *tv_ind; \
 		tv_ind = DUK__REGP((idx)); \
 		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \
@@ -72705,7 +74759,7 @@
 		(idx) = (duk_uint_fast_t) DUK_TVAL_GET_FASTINT_U32(tv_ind); \
 	} while (0)
 #else
-#define DUK__LOOKUP_INDIRECT_INDEX(idx) do { \
+#define DUK__LOOKUP_INDIRECT(idx) do { \
 		duk_tval *tv_ind; \
 		tv_ind = DUK__REGP(idx); \
 		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_ind)); \
@@ -72714,8 +74768,7 @@
 #endif
 
 DUK_LOCAL void duk__handle_executor_error(duk_heap *heap,
-                                          duk_hthread *entry_thread,
-                                          duk_size_t entry_callstack_top,
+                                          duk_activation *entry_act,
                                           duk_int_t entry_call_recursion_depth,
                                           duk_jmpbuf *entry_jmpbuf_ptr) {
 	duk_small_uint_t lj_ret;
@@ -72737,7 +74790,7 @@
 	 */
 	heap->lj.jmpbuf_ptr = (duk_jmpbuf *) entry_jmpbuf_ptr;
 
-	lj_ret = duk__handle_longjmp(heap->curr_thread, entry_thread, entry_callstack_top);
+	lj_ret = duk__handle_longjmp(heap->curr_thread, entry_act);
 
 	/* Error handling complete, remove side effect protections.
 	 */
@@ -72771,7 +74824,7 @@
 DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
 	/* Entry level info. */
 	duk_hthread *entry_thread;
-	duk_size_t entry_callstack_top;
+	duk_activation *entry_act;
 	duk_int_t entry_call_recursion_depth;
 	duk_jmpbuf *entry_jmpbuf_ptr;
 	duk_jmpbuf our_jmpbuf;
@@ -72782,6 +74835,7 @@
 	DUK_ASSERT(exec_thr->heap->curr_thread != NULL);
 	DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR((duk_heaphdr *) exec_thr);
 	DUK_ASSERT(exec_thr->callstack_top >= 1);  /* at least one activation, ours */
+	DUK_ASSERT(exec_thr->callstack_curr != NULL);
 	DUK_ASSERT(DUK_ACT_GET_FUNC(exec_thr->callstack_curr) != NULL);
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack_curr)));
 
@@ -72789,7 +74843,8 @@
 
 	entry_thread = exec_thr;
 	heap = entry_thread->heap;
-	entry_callstack_top = entry_thread->callstack_top;
+	entry_act = entry_thread->callstack_curr;
+	DUK_ASSERT(entry_act != NULL);
 	entry_call_recursion_depth = entry_thread->heap->call_recursion_depth;
 	entry_jmpbuf_ptr = entry_thread->heap->lj.jmpbuf_ptr;
 
@@ -72813,7 +74868,7 @@
 		if (DUK_SETJMP(our_jmpbuf.jb) == 0) {
 #endif
 			/* Execute bytecode until returned or longjmp(). */
-			duk__js_execute_bytecode_inner(entry_thread, entry_callstack_top);
+			duk__js_execute_bytecode_inner(entry_thread, entry_act);
 
 			/* Successful return: restore jmpbuf and return to caller. */
 			heap->lj.jmpbuf_ptr = entry_jmpbuf_ptr;
@@ -72828,10 +74883,10 @@
 			DUK_UNREF(exc);
 #endif
 			DUK_DDD(DUK_DDDPRINT("longjmp caught by bytecode executor"));
+			DUK_STATS_INC(exec_thr->heap, stats_exec_throw);
 
 			duk__handle_executor_error(heap,
-			                           entry_thread,
-			                           entry_callstack_top,
+			                           entry_act,
 			                           entry_call_recursion_depth,
 			                           entry_jmpbuf_ptr);
 		}
@@ -72842,6 +74897,7 @@
 				what = "unknown";
 			}
 			DUK_D(DUK_DPRINT("unexpected c++ std::exception (perhaps thrown by user code)"));
+			DUK_STATS_INC(exec_thr->heap, stats_exec_throw);
 			try {
 				DUK_ASSERT(heap->curr_thread != NULL);
 				DUK_ERROR_FMT1(heap->curr_thread, DUK_ERR_TYPE_ERROR, "caught invalid c++ std::exception '%s' (perhaps thrown by user code)", what);
@@ -72849,13 +74905,13 @@
 				DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ std::exception"));
 				DUK_UNREF(exc);
 				duk__handle_executor_error(heap,
-				                           entry_thread,
-				                           entry_callstack_top,
+				                           entry_act,
 				                           entry_call_recursion_depth,
 				                           entry_jmpbuf_ptr);
 			}
 		} catch (...) {
 			DUK_D(DUK_DPRINT("unexpected c++ exception (perhaps thrown by user code)"));
+			DUK_STATS_INC(exec_thr->heap, stats_exec_throw);
 			try {
 				DUK_ASSERT(heap->curr_thread != NULL);
 				DUK_ERROR_TYPE(heap->curr_thread, "caught invalid c++ exception (perhaps thrown by user code)");
@@ -72863,8 +74919,7 @@
 				DUK_D(DUK_DPRINT("caught api error thrown from unexpected c++ exception"));
 				DUK_UNREF(exc);
 				duk__handle_executor_error(heap,
-				                           entry_thread,
-				                           entry_callstack_top,
+				                           entry_act,
 				                           entry_call_recursion_depth,
 				                           entry_jmpbuf_ptr);
 			}
@@ -72876,7 +74931,7 @@
 }
 
 /* Inner executor, performance critical. */
-DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_size_t entry_callstack_top) {
+DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_activation *entry_act) {
 	/* Current PC, accessed by other functions through thr->ptr_to_curr_pc.
 	 * Critical for performance.  It would be safest to make this volatile,
 	 * but that eliminates performance benefits; aliasing guarantees
@@ -72954,7 +75009,6 @@
 	 *
 	 *  The following are not assumed to have stable pointers at all:
 	 *    - the value stack (registers) of the current thread
-	 *    - the catch stack of the current thread
 	 *
 	 *  See execution.rst for discussion.
 	 */
@@ -72995,9 +75049,9 @@
 		DUK_ASSERT(consts != NULL);
 
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
-		if (duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing) {
+		if (DUK_UNLIKELY(duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing)) {
 			duk__executor_recheck_debugger(thr, act, fun);
-			act = thr->callstack_curr;  /* relookup after side effects (no side effects currently however) */
+			DUK_ASSERT(act == thr->callstack_curr);
 			DUK_ASSERT(act != NULL);
 		}
 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
@@ -73011,7 +75065,7 @@
 	}
 
 	DUK_DD(DUK_DDPRINT("restarting execution, thr %p, act idx %ld, fun %p,"
-	                   "consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, catchstack_top=%ld, "
+	                   "consts %p, funcs %p, lev %ld, regbot %ld, regtop %ld, "
 	                   "preventcount=%ld",
 	                   (void *) thr,
 	                   (long) (thr->callstack_top - 1),
@@ -73021,7 +75075,6 @@
 	                   (long) (thr->callstack_top - 1),
 	                   (long) (thr->valstack_bottom - thr->valstack),
 	                   (long) (thr->valstack_top - thr->valstack),
-	                   (long) thr->catchstack_top,
 	                   (long) thr->callstack_preventcount));
 
 	/* Dispatch loop. */
@@ -73047,6 +75100,8 @@
 			/* Trigger at zero or below */
 			duk_small_uint_t exec_int_ret;
 
+			DUK_STATS_INC(thr->heap, stats_exec_interrupt);
+
 			/* Write curr_pc back for the debugger. */
 			{
 				duk_activation *act;
@@ -73056,7 +75111,7 @@
 				act->curr_pc = (duk_instr_t *) curr_pc;
 			}
 
-			/* Force restart caused by a function return; must recheck
+			/* Forced restart caused by a function return; must recheck
 			 * debugger breakpoints before checking line transitions,
 			 * see GH-303.  Restart and then handle interrupt_counter
 			 * zero again.
@@ -73118,6 +75173,7 @@
 #endif
 
 		ins = *curr_pc++;
+		DUK_STATS_INC(thr->heap, stats_exec_opcodes);
 
 		/* Typing: use duk_small_(u)int_fast_t when decoding small
 		 * opcode fields (op, A, B, C, BC) which fit into 16 bits
@@ -73145,7 +75201,7 @@
 		duk_bool_t duk__bval; \
 		duk__bval = (bval); \
 		DUK_ASSERT(duk__bval == 0 || duk__bval == 1); \
-		duk_push_boolean((duk_context *) thr, duk__bval); \
+		duk_push_boolean(thr, duk__bval); \
 		DUK__REPLACE_TOP_A_BREAK(); \
 	}
 #else
@@ -73206,15 +75262,15 @@
 			duk_int32_t val;
 
 			val = (duk_int32_t) DUK_DEC_BC(ins) - (duk_int32_t) DUK_BC_LDINT_BIAS;
-			duk_push_int((duk_context *) thr, val);
+			duk_push_int(thr, val);
 			DUK__REPLACE_TOP_A_BREAK();
 		}
 		case DUK_OP_LDINTX: {
 			duk_int32_t val;
 
-			val = (duk_int32_t) duk_get_int((duk_context *) thr, DUK_DEC_A(ins));
+			val = (duk_int32_t) duk_get_int(thr, DUK_DEC_A(ins));
 			val = (val << DUK_BC_LDINTX_SHIFT) + (duk_int32_t) DUK_DEC_BC(ins);  /* no bias */
-			duk_push_int((duk_context *) thr, val);
+			duk_push_int(thr, val);
 			DUK__REPLACE_TOP_A_BREAK();
 		}
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
@@ -73248,23 +75304,23 @@
 
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
 		case DUK_OP_LDTHIS: {
-			duk_push_this((duk_context *) thr);
+			duk_push_this(thr);
 			DUK__REPLACE_TOP_BC_BREAK();
 		}
 		case DUK_OP_LDUNDEF: {
-			duk_to_undefined((duk_context *) thr, (duk_idx_t) DUK_DEC_BC(ins));
+			duk_to_undefined(thr, (duk_idx_t) DUK_DEC_BC(ins));
 			break;
 		}
 		case DUK_OP_LDNULL: {
-			duk_to_null((duk_context *) thr, (duk_idx_t) DUK_DEC_BC(ins));
+			duk_to_null(thr, (duk_idx_t) DUK_DEC_BC(ins));
 			break;
 		}
 		case DUK_OP_LDTRUE: {
-			duk_push_true((duk_context *) thr);
+			duk_push_true(thr);
 			DUK__REPLACE_TOP_BC_BREAK();
 		}
 		case DUK_OP_LDFALSE: {
-			duk_push_false((duk_context *) thr);
+			duk_push_false(thr);
 			DUK__REPLACE_TOP_BC_BREAK();
 		}
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
@@ -73340,8 +75396,8 @@
 			duk_small_uint_t stridx;
 
 			stridx = duk_js_typeof_stridx(DUK__REGP_BC(ins));
-			DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS);
-			duk_push_hstring_stridx((duk_context *) thr, stridx);
+			DUK_ASSERT_STRIDX_VALID(stridx);
+			duk_push_hstring_stridx(thr, stridx);
 			DUK__REPLACE_TOP_A_BREAK();
 		}
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
@@ -73361,7 +75417,6 @@
 #endif  /* DUK_USE_EXEC_PREFER_SIZE */
 
 		case DUK_OP_TYPEOFID: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_small_uint_t stridx;
 #if !defined(DUK_USE_EXEC_PREFER_SIZE)
 			duk_hstring *h_str;
@@ -73381,17 +75436,17 @@
 			act = thr->callstack_curr;
 			if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) {
 				/* -> [... val this] */
-				tv = DUK_GET_TVAL_NEGIDX(ctx, -2);
+				tv = DUK_GET_TVAL_NEGIDX(thr, -2);
 				stridx = duk_js_typeof_stridx(tv);
 				tv = NULL;  /* no longer needed */
-				duk_pop_2(ctx);
+				duk_pop_2_unsafe(thr);
 			} else {
 				/* unresolvable, no stack changes */
 				stridx = DUK_STRIDX_LC_UNDEFINED;
 			}
 			DUK_ASSERT_STRIDX_VALID(stridx);
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-			duk_push_hstring_stridx(ctx, stridx);
+			duk_push_hstring_stridx(thr, stridx);
 			DUK__REPLACE_TOP_A_BREAK();
 #else  /* DUK_USE_EXEC_PREFER_SIZE */
 			h_str = DUK_HTHREAD_GET_STRING(thr, stridx);
@@ -73958,7 +76013,6 @@
 		case DUK_OP_POSTDECP_CR:
 		case DUK_OP_POSTDECP_RC:
 		case DUK_OP_POSTDECP_CC: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_tval *tv_obj;
 			duk_tval *tv_key;
 			duk_tval *tv_val;
@@ -73994,16 +76048,16 @@
 			 * not intuitive.
 			 */
 
-			x = duk_to_number_m1(ctx);
-			duk_pop(ctx);
+			x = duk_to_number_m1(thr);
+			duk_pop_unsafe(thr);
 			if (ins & DUK_BC_INCDECP_FLAG_DEC) {
 				y = x - 1.0;
 			} else {
 				y = x + 1.0;
 			}
 
-			duk_push_number(ctx, y);
-			tv_val = DUK_GET_TVAL_NEGIDX(ctx, -1);
+			duk_push_number(thr, y);
+			tv_val = DUK_GET_TVAL_NEGIDX(thr, -1);
 			DUK_ASSERT(tv_val != NULL);
 			tv_obj = DUK__REGCONSTP_B(ins);
 			tv_key = DUK__REGCONSTP_C(ins);
@@ -74011,11 +76065,11 @@
 			DUK_UNREF(rc);  /* ignore */
 			tv_obj = NULL;  /* invalidated */
 			tv_key = NULL;  /* invalidated */
-			duk_pop(ctx);
+			duk_pop_unsafe(thr);
 
 			z = (ins & DUK_BC_INCDECP_FLAG_POST) ? x : y;
 #if defined(DUK_USE_EXEC_PREFER_SIZE)
-			duk_push_number(ctx, z);
+			duk_push_number(thr, z);
 			DUK__REPLACE_TOP_A_BREAK();
 #else
 			tv_dst = DUK__REGP_A(ins);
@@ -74036,6 +76090,21 @@
 		(void) duk_hobject_getprop(thr, (barg), (carg)); \
 		DUK__REPLACE_TOP_A_BREAK(); \
 	}
+#define DUK__GETPROPC_BODY(barg,carg) { \
+		/* Same as GETPROP but callability check for property-based calls. */ \
+		duk_tval *tv__targ; \
+		(void) duk_hobject_getprop(thr, (barg), (carg)); \
+		DUK_GC_TORTURE(thr->heap); \
+		tv__targ = DUK_GET_TVAL_NEGIDX(thr, -1); \
+		if (DUK_UNLIKELY(!duk_is_callable_tval(thr, tv__targ))) { \
+			/* Here we intentionally re-evaluate the macro \
+			 * arguments to deal with potentially changed \
+			 * valstack base pointer! \
+			 */ \
+			duk_call_setup_propcall_error(thr, tv__targ, (barg), (carg)); \
+		} \
+		DUK__REPLACE_TOP_A_BREAK(); \
+	}
 #define DUK__PUTPROP_BODY(aarg,barg,carg) { \
 		/* A -> object reg \
 		 * B -> key reg/const \
@@ -74063,6 +76132,13 @@
 		case DUK_OP_GETPROP_RC:
 		case DUK_OP_GETPROP_CC:
 			DUK__GETPROP_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
+#if defined(DUK_USE_VERBOSE_ERRORS)
+		case DUK_OP_GETPROPC_RR:
+		case DUK_OP_GETPROPC_CR:
+		case DUK_OP_GETPROPC_RC:
+		case DUK_OP_GETPROPC_CC:
+			DUK__GETPROPC_BODY(DUK__REGCONSTP_B(ins), DUK__REGCONSTP_C(ins));
+#endif
 		case DUK_OP_PUTPROP_RR:
 		case DUK_OP_PUTPROP_CR:
 		case DUK_OP_PUTPROP_RC:
@@ -74080,6 +76156,16 @@
 			DUK__GETPROP_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
 		case DUK_OP_GETPROP_CC:
 			DUK__GETPROP_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
+#if defined(DUK_USE_VERBOSE_ERRORS)
+		case DUK_OP_GETPROPC_RR:
+			DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__REGP_C(ins));
+		case DUK_OP_GETPROPC_CR:
+			DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__REGP_C(ins));
+		case DUK_OP_GETPROPC_RC:
+			DUK__GETPROPC_BODY(DUK__REGP_B(ins), DUK__CONSTP_C(ins));
+		case DUK_OP_GETPROPC_CC:
+			DUK__GETPROPC_BODY(DUK__CONSTP_B(ins), DUK__CONSTP_C(ins));
+#endif
 		case DUK_OP_PUTPROP_RR:
 			DUK__PUTPROP_BODY(DUK__REGP_A(ins), DUK__REGP_B(ins), DUK__REGP_C(ins));
 		case DUK_OP_PUTPROP_CR:
@@ -74100,7 +76186,6 @@
 		case DUK_OP_DECLVAR_RC:
 		case DUK_OP_DECLVAR_CC: {
 			duk_activation *act;
-			duk_context *ctx = (duk_context *) thr;
 			duk_small_uint_fast_t a = DUK_DEC_A(ins);
 			duk_tval *tv1;
 			duk_hstring *name;
@@ -74124,18 +76209,18 @@
 			prop_flags = a & DUK_PROPDESC_FLAGS_MASK;
 
 			if (is_func_decl) {
-				duk_push_tval(ctx, DUK__REGCONSTP_C(ins));
+				duk_push_tval(thr, DUK__REGCONSTP_C(ins));
 			} else {
 				DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
 				thr->valstack_top++;
 			}
-			tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1);
+			tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
 
 			act = thr->callstack_curr;
 			if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) {
 				if (is_func_decl) {
 					/* Already declared, update value. */
-					tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1);
+					tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
 					duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
 				} else {
 					/* Already declared but no initializer value
@@ -74144,7 +76229,7 @@
 				}
 			}
 
-			duk_pop(ctx);
+			duk_pop_unsafe(thr);
 			break;
 		}
 
@@ -74161,8 +76246,8 @@
 			 * C -> escaped source
 			 */
 
-			duk_push_tval((duk_context *) thr, DUK__REGCONSTP_C(ins));
-			duk_push_tval((duk_context *) thr, DUK__REGCONSTP_B(ins));  /* -> [ ... escaped_source bytecode ] */
+			duk_push_tval(thr, DUK__REGCONSTP_C(ins));
+			duk_push_tval(thr, DUK__REGCONSTP_B(ins));  /* -> [ ... escaped_source bytecode ] */
 			duk_regexp_create_instance(thr);   /* -> [ ... regexp_instance ] */
 			DUK__REPLACE_TOP_A_BREAK();
 		}
@@ -74181,7 +76266,6 @@
 			 *   (2) that object environment record is a 'with' block.
 			 */
 
-			duk_context *ctx = (duk_context *) thr;
 			duk_activation *act;
 			duk_uint_fast_t idx;
 			duk_tval *tv1;
@@ -74201,8 +76285,8 @@
 			idx = (duk_uint_fast_t) DUK_DEC_A(ins);
 
 			/* Could add direct value stack handling. */
-			duk_replace(ctx, (duk_idx_t) (idx + 1));  /* 'this' binding */
-			duk_replace(ctx, (duk_idx_t) idx);        /* variable value (function, we hope, not checked here) */
+			duk_replace(thr, (duk_idx_t) (idx + 1));  /* 'this' binding */
+			duk_replace(thr, (duk_idx_t) idx);        /* variable value (function, we hope, not checked here) */
 			break;
 		}
 
@@ -74252,7 +76336,6 @@
 		}
 
 		case DUK_OP_GETVAR: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_activation *act;
 			duk_tval *tv1;
 			duk_hstring *name;
@@ -74262,8 +76345,9 @@
 			name = DUK_TVAL_GET_STRING(tv1);
 			DUK_ASSERT(name != NULL);
 			act = thr->callstack_curr;
+			DUK_ASSERT(act != NULL);
 			(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/);  /* -> [... val this] */
-			duk_pop(ctx);  /* 'this' binding is not needed here */
+			duk_pop_unsafe(thr);  /* 'this' binding is not needed here */
 			DUK__REPLACE_TOP_A_BREAK();
 		}
 
@@ -74317,9 +76401,8 @@
 		 * for potential out-of-memory situations which will then \
 		 * propagate out of the executor longjmp handler. \
 		 */ \
-		ret_result = duk__handle_return(thr, \
-			                        entry_thread, \
-			                        entry_callstack_top); \
+		DUK_ASSERT(thr->ptr_curr_pc == NULL); \
+		ret_result = duk__handle_return(thr, entry_act); \
 		if (ret_result == DUK__RETHAND_RESTART) { \
 			goto restart_execution; \
 		} \
@@ -74336,13 +76419,13 @@
 			DUK__SYNC_AND_NULL_CURR_PC();
 
 			if (op == DUK_OP_RETREG) {
-				duk_push_tval((duk_context *) thr, DUK__REGP_BC(ins));
+				duk_push_tval(thr, DUK__REGP_BC(ins));
 			} else if (op == DUK_OP_RETUNDEF) {
 				DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
 				thr->valstack_top++;
 			} else {
 				DUK_ASSERT(op == DUK_OP_RETCONST || op == DUK_OP_RETCONSTN);
-				duk_push_tval((duk_context *) thr, DUK__CONSTP_BC(ins));
+				duk_push_tval(thr, DUK__CONSTP_BC(ins));
 			}
 
 			DUK__RETURN_SHARED();
@@ -74391,24 +76474,28 @@
 #endif  /* DUK_USE_EXEC_PREFER_SIZE */
 
 		case DUK_OP_LABEL: {
+			duk_activation *act;
 			duk_catcher *cat;
 			duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
 
-			/* allocate catcher and populate it (should be atomic) */
-
-			duk_hthread_catchstack_grow(thr);
-			cat = thr->catchstack + thr->catchstack_top;
-			thr->catchstack_top++;
-
-			cat->flags = DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT);
-			cat->callstack_index = thr->callstack_top - 1;
+			/* Allocate catcher and populate it (must be atomic). */
+
+			cat = duk_hthread_catcher_alloc(thr);
+			DUK_ASSERT(cat != NULL);
+
+			cat->flags = (duk_uint32_t) (DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT));
 			cat->pc_base = (duk_instr_t *) curr_pc;  /* pre-incremented, points to first jump slot */
 			cat->idx_base = 0;  /* unused for label */
 			cat->h_varname = NULL;
 
-			DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
+			act = thr->callstack_curr;
+			DUK_ASSERT(act != NULL);
+			cat->parent = act->cat;
+			act->cat = cat;
+
+			DUK_DDD(DUK_DDDPRINT("LABEL catcher: flags=0x%08lx, pc_base=%ld, "
 			                     "idx_base=%ld, h_varname=%!O, label_id=%ld",
-			                     (long) cat->flags, (long) cat->callstack_index, (long) cat->pc_base,
+			                     (long) cat->flags, (long) cat->pc_base,
 			                     (long) cat->idx_base, (duk_heaphdr *) cat->h_varname, (long) DUK_CAT_GET_LABEL(cat)));
 
 			curr_pc += 2;  /* skip jump slots */
@@ -74416,7 +76503,7 @@
 		}
 
 		case DUK_OP_ENDLABEL: {
-			duk_catcher *cat;
+			duk_activation *act;
 #if (defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)) || defined(DUK_USE_ASSERTIONS)
 			duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
 #endif
@@ -74424,14 +76511,12 @@
 			DUK_DDD(DUK_DDDPRINT("ENDLABEL %ld", (long) bc));
 #endif
 
-			DUK_ASSERT(thr->catchstack_top >= 1);
-
-			cat = thr->catchstack + thr->catchstack_top - 1;
-			DUK_UNREF(cat);
-			DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL);
-			DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(cat) == bc);
-
-			duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
+			act = thr->callstack_curr;
+			DUK_ASSERT(act->cat != NULL);
+			DUK_ASSERT(DUK_CAT_GET_TYPE(act->cat) == DUK_CAT_TYPE_LABEL);
+			DUK_ASSERT((duk_uint_fast_t) DUK_CAT_GET_LABEL(act->cat) == bc);
+			duk_hthread_catcher_unwind_nolexenv_norz(thr, act);
+
 			/* no need to unwind callstack */
 			break;
 		}
@@ -74454,384 +76539,34 @@
 
 		/* XXX: move to helper, too large to be inline here */
 		case DUK_OP_TRYCATCH: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_activation *act;
-			duk_catcher *cat;
-			duk_tval *tv1;
-			duk_small_uint_fast_t a;
-			duk_small_uint_fast_t bc;
-
-			/* A -> flags
-			 * BC -> reg_catch; base register for two registers used both during
-			 *       trycatch setup and when catch is triggered
-			 *
-			 *      If DUK_BC_TRYCATCH_FLAG_CATCH_BINDING set:
-			 *          reg_catch + 0: catch binding variable name (string).
-			 *          Automatic declarative environment is established for
-			 *          the duration of the 'catch' clause.
-			 *
-			 *      If DUK_BC_TRYCATCH_FLAG_WITH_BINDING set:
-			 *          reg_catch + 0: with 'target value', which is coerced to
-			 *          an object and then used as a bindind object for an
-			 *          environment record.  The binding is initialized here, for
-			 *          the 'try' clause.
-			 *
-			 * Note that a TRYCATCH generated for a 'with' statement has no
-			 * catch or finally parts.
-			 */
-
-			/* XXX: TRYCATCH handling should be reworked to avoid creating
-			 * an explicit scope unless it is actually needed (e.g. function
-			 * instances or eval is executed inside the catch block).  This
-			 * rework is not trivial because the compiler doesn't have an
-			 * intermediate representation.  When the rework is done, the
-			 * opcode format can also be made more straightforward.
-			 */
-
-			/* XXX: side effect handling is quite awkward here */
-
-			DUK_DDD(DUK_DDDPRINT("TRYCATCH: reg_catch=%ld, have_catch=%ld, "
-			                     "have_finally=%ld, catch_binding=%ld, with_binding=%ld (flags=0x%02lx)",
-			                     (long) DUK_DEC_BC(ins),
-			                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH ? 1 : 0),
-			                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY ? 1 : 0),
-			                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING ? 1 : 0),
-			                     (long) (DUK_DEC_A(ins) & DUK_BC_TRYCATCH_FLAG_WITH_BINDING ? 1 : 0),
-			                     (unsigned long) DUK_DEC_A(ins)));
-
-			a = DUK_DEC_A(ins);
-			bc = DUK_DEC_BC(ins);
-
-			/* Registers 'bc' and 'bc + 1' are written in longjmp handling
-			 * and if their previous values (which are temporaries) become
-			 * unreachable -and- have a finalizer, there'll be a function
-			 * call during error handling which is not supported now (GH-287).
-			 * Ensure that both 'bc' and 'bc + 1' have primitive values to
-			 * guarantee no finalizer calls in error handling.  Scrubbing also
-			 * ensures finalizers for the previous values run here rather than
-			 * later.  Error handling related values are also written to 'bc'
-			 * and 'bc + 1' but those values never become unreachable during
-			 * error handling, so there's no side effect problem even if the
-			 * error value has a finalizer.
-			 */
-			duk_dup(ctx, bc);  /* Stabilize value. */
-			duk_to_undefined(ctx, bc);
-			duk_to_undefined(ctx, bc + 1);
-
-			/* Ensure a catchstack entry is available.  One entry
-			 * is guaranteed even if side effects cause function
-			 * calls and the catchstack is shrunk because some
-			 * spare room is left behind by a shrink operation.
-			 */
-			duk_hthread_catchstack_grow(thr);
-
-			/* Allocate catcher and populate it.  Doesn't have to
-			 * be fully atomic, but the catcher must be in a
-			 * consistent state if side effects (such as finalizer
-			 * calls) occur.
-			 */
-
-			DUK_ASSERT(thr->catchstack_top + 1 <= thr->catchstack_size);
-			cat = thr->catchstack + thr->catchstack_top;
-			thr->catchstack_top++;
-
-			cat->flags = DUK_CAT_TYPE_TCF;
-			cat->h_varname = NULL;
-			cat->callstack_index = thr->callstack_top - 1;
-			cat->pc_base = (duk_instr_t *) curr_pc;  /* pre-incremented, points to first jump slot */
-			cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;
-
-			if (a & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) {
-				cat->flags |= DUK_CAT_FLAG_CATCH_ENABLED;
-			}
-			if (a & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) {
-				cat->flags |= DUK_CAT_FLAG_FINALLY_ENABLED;
-			}
-			if (a & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING) {
-				DUK_DDD(DUK_DDDPRINT("catch binding flag set to catcher"));
-				cat->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED;
-				tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
-				DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
-
-				/* borrowed reference; although 'tv1' comes from a register,
-				 * its value was loaded using LDCONST so the constant will
-				 * also exist and be reachable.
-				 */
-				cat->h_varname = DUK_TVAL_GET_STRING(tv1);
-			} else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) {
-				duk_hobjenv *env;
-				duk_hobject *target;
-
-				/* Delayed env initialization for activation (if needed). */
-				DUK_ASSERT(thr->callstack_top >= 1);
-				act = thr->callstack_curr;
-				DUK_ASSERT(act != NULL);
-				if (act->lex_env == NULL) {
-					DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
-					DUK_ASSERT(act->var_env == NULL);
-
-					duk_js_init_activation_environment_records_delayed(thr, act);
-					act = thr->callstack_curr;  /* relookup, side effects */
-					DUK_UNREF(act);  /* 'act' is no longer accessed, scanbuild fix */
-				}
-				DUK_ASSERT(act->lex_env != NULL);
-				DUK_ASSERT(act->var_env != NULL);
-
-				/* Coerce 'with' target. */
-				target = duk_to_hobject(ctx, -1);
-				DUK_ASSERT(target != NULL);
-
-				/* Create an object environment; it is not pushed
-				 * so avoid side effects very carefully until it is
-				 * referenced.
-				 */
-				env = duk_hobjenv_alloc(thr,
-				                        DUK_HOBJECT_FLAG_EXTENSIBLE |
-				                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
-				DUK_ASSERT(env != NULL);
-				DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
-				env->target = target;  /* always provideThis=true */
-				DUK_HOBJECT_INCREF(thr, target);
-				env->has_this = 1;
-				DUK_ASSERT_HOBJENV_VALID(env);
-				DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iO", env));
-
-				act = thr->callstack_curr;  /* relookup (side effects) */
-				DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
-				DUK_ASSERT(act->lex_env != NULL);
-				DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, act->lex_env);
-				act->lex_env = (duk_hobject *) env;  /* Now reachable. */
-				DUK_HOBJECT_INCREF(thr, (duk_hobject *) env);
-				/* Net refcount change to act->lex_env is 0: incref for env's
-				 * prototype, decref for act->lex_env overwrite.
-				 */
-
-				/* Set catcher lex_env active (affects unwind)
-				 * only when the whole setup is complete.
-				 */
-				cat = thr->catchstack + thr->catchstack_top - 1;
-				cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE;
-			} else {
-				;
-			}
-
-			DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
-			                     "idx_base=%ld, h_varname=%!O",
-			                     (unsigned long) cat->flags, (long) cat->callstack_index,
-			                     (long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname));
-
-			duk_pop(ctx);
-
+			duk__handle_op_trycatch(thr, ins, curr_pc);
 			curr_pc += 2;  /* skip jump slots */
 			break;
 		}
 
 		case DUK_OP_ENDTRY: {
-			duk_catcher *cat;
-			duk_tval *tv1;
-
-			DUK_ASSERT(thr->catchstack_top >= 1);
-			DUK_ASSERT(thr->callstack_top >= 1);
-			DUK_ASSERT(thr->catchstack[thr->catchstack_top - 1].callstack_index == thr->callstack_top - 1);
-
-			cat = thr->catchstack + thr->catchstack_top - 1;
-
-			DUK_DDD(DUK_DDDPRINT("ENDTRY: clearing catch active flag (regardless of whether it was set or not)"));
-			DUK_CAT_CLEAR_CATCH_ENABLED(cat);
-
-			if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
-				DUK_DDD(DUK_DDDPRINT("ENDTRY: finally part is active, jump through 2nd jump slot with 'normal continuation'"));
-
-				tv1 = thr->valstack + cat->idx_base;
-				DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
-				DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
-				tv1 = NULL;
-
-				tv1 = thr->valstack + cat->idx_base + 1;
-				DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
-				DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
-				tv1 = NULL;
-
-				DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
-			} else {
-				DUK_DDD(DUK_DDDPRINT("ENDTRY: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));
-				duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
-				/* no need to unwind callstack */
-			}
-
-			curr_pc = cat->pc_base + 1;
+			curr_pc = duk__handle_op_endtry(thr, ins);
 			break;
 		}
 
 		case DUK_OP_ENDCATCH: {
-			duk_activation *act;
-			duk_catcher *cat;
-			duk_tval *tv1;
-
-			DUK_ASSERT(thr->catchstack_top >= 1);
-			DUK_ASSERT(thr->callstack_top >= 1);
-			DUK_ASSERT(thr->catchstack[thr->catchstack_top - 1].callstack_index == thr->callstack_top - 1);
-
-			cat = thr->catchstack + thr->catchstack_top - 1;
-			DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat));  /* cleared before entering catch part */
-
-			act = thr->callstack_curr;
-			DUK_ASSERT(act != NULL);
-
-			if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
-				duk_hobject *prev_env;
-
-				/* 'with' binding has no catch clause, so can't be here unless a normal try-catch */
-				DUK_ASSERT(DUK_CAT_HAS_CATCH_BINDING_ENABLED(cat));
-				DUK_ASSERT(act->lex_env != NULL);
-
-				DUK_DDD(DUK_DDDPRINT("ENDCATCH: popping catcher part lexical environment"));
-
-				prev_env = act->lex_env;
-				DUK_ASSERT(prev_env != NULL);
-				act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, prev_env);
-				DUK_CAT_CLEAR_LEXENV_ACTIVE(cat);
-				DUK_HOBJECT_INCREF(thr, act->lex_env);
-				DUK_HOBJECT_DECREF(thr, prev_env);  /* side effects */
-			}
-
-			if (DUK_CAT_HAS_FINALLY_ENABLED(cat)) {
-				DUK_DDD(DUK_DDDPRINT("ENDCATCH: finally part is active, jump through 2nd jump slot with 'normal continuation'"));
-
-				tv1 = thr->valstack + cat->idx_base;
-				DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
-				DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv1);  /* side effects */
-				tv1 = NULL;
-
-				tv1 = thr->valstack + cat->idx_base + 1;
-				DUK_ASSERT(tv1 >= thr->valstack && tv1 < thr->valstack_top);
-				DUK_TVAL_SET_U32_UPDREF(thr, tv1, (duk_uint32_t) DUK_LJ_TYPE_NORMAL);  /* side effects */
-				tv1 = NULL;
-
-				DUK_CAT_CLEAR_FINALLY_ENABLED(cat);
-			} else {
-				DUK_DDD(DUK_DDDPRINT("ENDCATCH: no finally part, dismantle catcher, jump through 2nd jump slot (to end of statement)"));
-				duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
-				/* no need to unwind callstack */
-			}
-
-			curr_pc = cat->pc_base + 1;
+			duk__handle_op_endcatch(thr, ins);
 			break;
 		}
 
 		case DUK_OP_ENDFIN: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_catcher *cat;
-			duk_tval *tv1;
-			duk_small_uint_t cont_type;
-			duk_small_uint_t ret_result;
-
 			/* Sync and NULL early. */
 			DUK__SYNC_AND_NULL_CURR_PC();
 
-			DUK_ASSERT(thr->catchstack_top >= 1);
-			DUK_ASSERT(thr->callstack_top >= 1);
-			DUK_ASSERT(thr->catchstack[thr->catchstack_top - 1].callstack_index == thr->callstack_top - 1);
-
-			cat = thr->catchstack + thr->catchstack_top - 1;
-
-			/* CATCH flag may be enabled or disabled here; it may be enabled if
-			 * the statement has a catch block but the try block does not throw
-			 * an error.
-			 */
-			DUK_ASSERT(!DUK_CAT_HAS_FINALLY_ENABLED(cat));  /* cleared before entering finally */
-			/* XXX: assert idx_base */
-
-			DUK_DDD(DUK_DDDPRINT("ENDFIN: completion value=%!T, type=%!T",
-			                     (duk_tval *) (thr->valstack + cat->idx_base + 0),
-			                     (duk_tval *) (thr->valstack + cat->idx_base + 1)));
-
-			tv1 = thr->valstack + cat->idx_base + 1;  /* type */
-			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
-			cont_type = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
-
-			switch (cont_type) {
-			case DUK_LJ_TYPE_NORMAL: {
-				DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'normal' (non-abrupt) completion -> "
-				                     "dismantle catcher, resume execution after ENDFIN"));
-				duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
-				/* no need to unwind callstack */
-				goto restart_execution;
-			}
-			case DUK_LJ_TYPE_RETURN: {
-				DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with 'return' complation -> dismantle "
-				                     "catcher, handle return, lj.value1=%!T", thr->valstack + cat->idx_base));
-
-				/* Not necessary to unwind catchstack: return handling will
-				 * do it.  The finally flag of 'cat' is no longer set.  The
-				 * catch flag may be set, but it's not checked by return handling.
-				 */
-				DUK_ASSERT(!DUK_CAT_HAS_FINALLY_ENABLED(cat));  /* cleared before entering finally */
-#if 0
-				duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
-#endif
-
-				duk_push_tval(ctx, thr->valstack + cat->idx_base);
-				ret_result = duk__handle_return(thr,
-					                        entry_thread,
-					                        entry_callstack_top);
-				if (ret_result == DUK__RETHAND_RESTART) {
-					goto restart_execution;
-				}
-				DUK_ASSERT(ret_result == DUK__RETHAND_FINISHED);
-
-				DUK_DDD(DUK_DDDPRINT("exiting executor after ENDFIN and RETURN (pseudo) longjmp type"));
+			if (duk__handle_op_endfin(thr, ins, entry_act) != 0) {
 				return;
 			}
-			case DUK_LJ_TYPE_BREAK:
-			case DUK_LJ_TYPE_CONTINUE: {
-				duk_uint_t label_id;
-				duk_small_uint_t lj_type;
-
-				/* Not necessary to unwind catchstack: break/continue
-				 * handling will do it.  The finally flag of 'cat' is
-				 * no longer set.  The catch flag may be set, but it's
-				 * not checked by break/continue handling.
-				 */
-#if 0
-				duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1);
-#endif
-
-				tv1 = thr->valstack + cat->idx_base;
-				DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv1));
-#if defined(DUK_USE_FASTINT)
-				DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv1));
-				label_id = (duk_small_uint_t) DUK_TVAL_GET_FASTINT_U32(tv1);
-#else
-				label_id = (duk_small_uint_t) DUK_TVAL_GET_NUMBER(tv1);
-#endif
-				lj_type = cont_type;
-				duk__handle_break_or_continue(thr, label_id, lj_type);
-				goto restart_execution;
-			}
-			default: {
-				DUK_DDD(DUK_DDDPRINT("ENDFIN: finally part finishing with abrupt completion, lj_type=%ld -> "
-				                     "dismantle catcher, re-throw error",
-				                     (long) cont_type));
-
-				duk_push_tval(ctx, thr->valstack + cat->idx_base);
-
-				duk_err_setup_ljstate1(thr, (duk_small_int_t) cont_type, thr->valstack + cat->idx_base);
-				/* No debugger Throw notify check on purpose (rethrow). */
-
-				DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL);  /* always in executor */
-				duk_err_longjmp(thr);
-				DUK_UNREACHABLE();
-			}
-			}
-
-			/* Must restart in all cases because we NULLed thr->ptr_curr_pc. */
-			DUK_UNREACHABLE();
-			break;
+
+			/* Must restart because we NULLed out curr_pc. */
+			goto restart_execution;
 		}
 
 		case DUK_OP_THROW: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
 
 			/* Note: errors are augmented when they are created, not
@@ -74845,16 +76580,16 @@
 			 */
 			DUK__SYNC_AND_NULL_CURR_PC();
 
-			duk_dup(ctx, (duk_idx_t) bc);
+			duk_dup(thr, (duk_idx_t) bc);
 			DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (before throw augment)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
 			duk_err_augment_error_throw(thr);
 			DUK_DDD(DUK_DDDPRINT("THROW ERROR (BYTECODE): %!dT (after throw augment)",
-			                     (duk_tval *) duk_get_tval(ctx, -1)));
-#endif
-
-			duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1));
+			                     (duk_tval *) duk_get_tval(thr, -1)));
+#endif
+
+			duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(thr, -1));
 #if defined(DUK_USE_DEBUGGER_SUPPORT)
 			duk_err_check_debugger_integration(thr);
 #endif
@@ -74880,9 +76615,9 @@
 			 */
 
 #if defined(DUK_USE_PREFER_SIZE)
-			duk_dup((duk_context *) thr, a);
-			duk_replace((duk_context *) thr, bc);
-			duk_to_undefined((duk_context *) thr, bc + 1);
+			duk_dup(thr, (duk_idx_t) a);
+			duk_replace(thr, (duk_idx_t) bc);
+			duk_to_undefined(thr, (duk_idx_t) (bc + 1));
 #else
 			duk_tval *tv1;
 			duk_tval *tv2;
@@ -74904,129 +76639,48 @@
 			break;
 		}
 
-		case DUK_OP_EVALCALL: {
-			/* Eval call or a normal call made using the identifier 'eval'.
-			 * Eval calls are never handled as tail calls for simplicity.
-			 */
-			duk_context *ctx = (duk_context *) thr;
-			duk_small_uint_fast_t nargs;
-			duk_uint_fast_t idx;
-			duk_idx_t num_stack_args;
-			duk_small_uint_t call_flags;
-			duk_tval *tv_func;
-			duk_hobject *obj_func;
-#if !defined(DUK_USE_EXEC_FUN_LOCAL)
-			duk_hcompfunc *fun;
-#endif
-
-			/* Technically we should also check for the possibility of
-			 * a pure Ecmascript-to-Ecmascript call: while built-in eval()
-			 * is native, it's possible for the 'eval' identifier to be
-			 * shadowed.  In practice that would be rare and optimizing the
-			 * C call stack for that case is a bit pointless.
-			 */
-
-			nargs = (duk_small_uint_fast_t) DUK_DEC_A(ins);
-			idx = (duk_uint_fast_t) DUK_DEC_BC(ins);
-			duk_set_top(ctx, (duk_idx_t) (idx + nargs + 2));   /* [ ... func this arg1 ... argN ] */
-
-			call_flags = 0;
-			tv_func = DUK_GET_TVAL_POSIDX(ctx, idx);
-			if (DUK_TVAL_IS_OBJECT(tv_func)) {
-				obj_func = DUK_TVAL_GET_OBJECT(tv_func);
-				DUK_ASSERT(obj_func != NULL);
-				if (DUK_HOBJECT_IS_NATFUNC(obj_func) &&
-				    ((duk_hnatfunc *) obj_func)->func == duk_bi_global_object_eval) {
-					DUK_DDD(DUK_DDDPRINT("call target is eval, call identifier was 'eval' -> direct eval"));
-					call_flags |= DUK_CALL_FLAG_DIRECT_EVAL;
-				}
-			}
-			num_stack_args = nargs;
-			duk_handle_call_unprotected(thr, num_stack_args, call_flags);
-
-#if !defined(DUK_USE_EXEC_FUN_LOCAL)
-			fun = DUK__FUN();
-#endif
-			duk_set_top(ctx, (duk_idx_t) fun->nregs);
-			break;
-		}
-
-		case DUK_OP_CALL:
-		case DUK_OP_TAILCALL: {
-			/* DUK_OP_CALL: plain call, not tailcall compatible.
+
+		/* XXX: in some cases it's faster NOT to reuse the value
+		 * stack but rather copy the arguments on top of the stack
+		 * (mainly when the calling value stack is large and the value
+		 * stack resize would be large).
+		 */
+
+		case DUK_OP_CALL0:
+		case DUK_OP_CALL1:
+		case DUK_OP_CALL2:
+		case DUK_OP_CALL3:
+		case DUK_OP_CALL4:
+		case DUK_OP_CALL5:
+		case DUK_OP_CALL6:
+		case DUK_OP_CALL7: {
+			/* Opcode packs 4 flag bits: 1 for indirect, 3 map
+			 * 1:1 to three lowest call handling flags.
 			 *
-			 * DUK_OP_TAILCALL: plain call which is tailcall
-			 * compatible.  Tail call may not be possible due
-			 * to e.g. target not being an Ecmascript function.
-			 *
-			 * Not a direct eval call.  Indirect eval calls don't
-			 * need special handling here.
-			 */
-
-			/* To determine whether to use an optimized Ecmascript-to-Ecmascript
-			 * call, we need to know whether the final, non-bound function is an
-			 * Ecmascript function.  Current implementation is to first try an
-			 * Ecma-to-Ecma call setup which also resolves the bound function
-			 * chain.  The setup attempt overwrites call target at DUK__REGP(idx)
-			 * and may also fudge the argument list.  However, it won't resolve
-			 * the effective 'this' binding if the setup fails.  This is somewhat
-			 * awkward, and the two call setup code paths should be merged.
-			 *
-			 * If an Ecma-to-Ecma call is not possible, the actual call handling
-			 * will do another (unnecessary) attempt to resolve the bound function.
-			 */
-
-			duk_context *ctx = (duk_context *) thr;
-			duk_small_uint_fast_t nargs;
-			duk_uint_fast_t idx;
-			duk_idx_t num_stack_args;
+			 * A -> nargs or register with nargs (indirect)
+			 * BC -> base register for call (base -> func, base+1 -> this, base+2 -> arg1 ... base+2+N-1 -> argN)
+			 */
+
+			duk_idx_t nargs;
+			duk_idx_t idx;
 			duk_small_uint_t call_flags;
 #if !defined(DUK_USE_EXEC_FUN_LOCAL)
 			duk_hcompfunc *fun;
 #endif
 
-			/* A -> nargs
-			 * BC -> base register for call (base -> func, base+1 -> this, base+2 -> arg1 ... base+2+N-1 -> argN)
-			 */
-
-			/* XXX: in some cases it's faster NOT to reuse the value
-			 * stack but rather copy the arguments on top of the stack
-			 * (mainly when the calling value stack is large and the value
-			 * stack resize would be large).  See DUK_OP_NEW.
-			 */
-
-			nargs = (duk_small_uint_fast_t) DUK_DEC_A(ins);
-			idx = (duk_uint_fast_t) DUK_DEC_BC(ins);
-			duk_set_top(ctx, (duk_idx_t) (idx + nargs + 2));   /* [ ... func this arg1 ... argN ] */
-
-			/* DUK_OP_CALL and DUK_OP_TAILCALL are consecutive
-			 * which allows a simple bit test.
-			 */
-			DUK_ASSERT((DUK_OP_CALL & 0x01) == 0);
-			DUK_ASSERT((DUK_OP_TAILCALL & 0x01) == 1);
-			call_flags = (ins & (1UL << DUK_BC_SHIFT_OP)) ? DUK_CALL_FLAG_IS_TAILCALL : 0;
-
-			num_stack_args = nargs;
-			if (duk_handle_ecma_call_setup(thr, num_stack_args, call_flags)) {
-				/* Ecma-to-ecma call possible, may or may not be a tail call.
-				 * Avoid C recursion by being clever.
-				 */
-				DUK_DDD(DUK_DDDPRINT("ecma-to-ecma call setup possible, restart execution"));
-				/* curr_pc synced by duk_handle_ecma_call_setup() */
+			DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0);
+			DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) == 0);
+
+			nargs = (duk_idx_t) DUK_DEC_A(ins);
+			call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA;
+			idx = (duk_idx_t) DUK_DEC_BC(ins);
+
+			if (duk__executor_handle_call(thr, idx, nargs, call_flags)) {
+				/* curr_pc synced by duk_handle_call_unprotected() */
+				DUK_ASSERT(thr->ptr_curr_pc == NULL);
 				goto restart_execution;
 			}
-
-			/* Recompute argument count: bound function handling may have shifted. */
-			num_stack_args = duk_get_top(ctx) - (idx + 2);
-			DUK_DDD(DUK_DDDPRINT("recomputed arg count: %ld\n", (long) num_stack_args));
-
-			/* Target is either a lightfunc or a function object.
-			 * We don't need to check for eval handling here: the
-			 * call may be an indirect eval ('myEval("something")')
-			 * but that requires no special handling.
-			 */
-
-			duk_handle_call_unprotected(thr, num_stack_args, 0 /*call_flags*/);
+			DUK_ASSERT(thr->ptr_curr_pc != NULL);
 
 			/* duk_js_call.c is required to restore the stack reserve
 			 * so we only need to reset the top.
@@ -75034,97 +76688,103 @@
 #if !defined(DUK_USE_EXEC_FUN_LOCAL)
 			fun = DUK__FUN();
 #endif
-			duk_set_top(ctx, (duk_idx_t) fun->nregs);
+			duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs);
 
 			/* No need to reinit setjmp() catchpoint, as call handling
 			 * will store and restore our state.
-			 */
-
-			/* When debugger is enabled, we need to recheck the activation
+			 *
+			 * When debugger is enabled, we need to recheck the activation
 			 * status after returning.  This is now handled by call handling
 			 * and heap->dbg_force_restart.
 			 */
 			break;
 		}
 
-		case DUK_OP_NEW: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_small_uint_fast_t a = DUK_DEC_A(ins);
-			duk_small_uint_fast_t bc = DUK_DEC_BC(ins);
-#if defined(DUK_USE_EXEC_PREFER_SIZE)
+		case DUK_OP_CALL8:
+		case DUK_OP_CALL9:
+		case DUK_OP_CALL10:
+		case DUK_OP_CALL11:
+		case DUK_OP_CALL12:
+		case DUK_OP_CALL13:
+		case DUK_OP_CALL14:
+		case DUK_OP_CALL15: {
+			/* Indirect variant. */
+			duk_uint_fast_t nargs;
+			duk_idx_t idx;
+			duk_small_uint_t call_flags;
 #if !defined(DUK_USE_EXEC_FUN_LOCAL)
 			duk_hcompfunc *fun;
 #endif
-#else
-			duk_small_uint_fast_t count;
-			duk_tval *tv_src;
-#endif
-
-			/* A -> num args (N)
-			 * BC -> target register and start reg: constructor, arg1, ..., argN
-			 */
-
-			/* duk_new() will call the constuctor using duk_handle_call().
-			 * A constructor call prevents a yield from inside the constructor,
-			 * even if the constructor is an Ecmascript function.
-			 */
-
-			/* Don't need to sync curr_pc here; duk_new() will do that
-			 * when it augments the created error.
-			 */
-
-#if defined(DUK_USE_EXEC_PREFER_SIZE)
-			/* This alternative relies on our being allowed to trash anything
-			 * above 'bc' so we can just reuse the argument registers which
-			 * means smaller value stack use.  Footprint is a bit smaller.
-			 */
-			duk_set_top(ctx, (duk_idx_t) (bc + a + 1));
-			duk_new(ctx, (duk_idx_t) a);  /* [... constructor arg1 ... argN] -> [retval] */
-
-			/* The return value is already in its correct place at the stack,
-			 * i.e. it has replaced the 'constructor' at index bc.  Just reset
-			 * top and we're done.
-			 */
+
+			DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0);
+			DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) != 0);
+
+			nargs = (duk_uint_fast_t) DUK_DEC_A(ins);
+			DUK__LOOKUP_INDIRECT(nargs);
+			call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA;
+			idx = (duk_idx_t) DUK_DEC_BC(ins);
+
+			if (duk__executor_handle_call(thr, idx, (duk_idx_t) nargs, call_flags)) {
+				DUK_ASSERT(thr->ptr_curr_pc == NULL);
+				goto restart_execution;
+			}
+			DUK_ASSERT(thr->ptr_curr_pc != NULL);
 
 #if !defined(DUK_USE_EXEC_FUN_LOCAL)
 			fun = DUK__FUN();
 #endif
-			duk_set_top(ctx, (duk_idx_t) fun->nregs);
-#else  /* DUK_USE_EXEC_PREFER_SIZE */
-			/* Faster alternative is to duplicate the values to avoid a resize.
-			 * This depends on the relative size between the value stack and
-			 * the argument count, though.
-			 */
-			count = a + 1;
-			duk_require_stack(ctx, count);
-			tv_src = DUK_GET_TVAL_POSIDX(ctx, bc);
-			duk__push_tvals_incref_only(thr, tv_src, count);
-			duk_new(ctx, (duk_idx_t) a);  /* [... constructor arg1 ... argN] -> [retval] */
-			duk_replace(ctx, bc);
-#endif  /* DUK_USE_EXEC_PREFER_SIZE */
-
-			/* When debugger is enabled, we need to recheck the activation
-			 * status after returning.  This is now handled by call handling
-			 * and heap->dbg_force_restart.
-			 */
+			duk_set_top_unsafe(thr, (duk_idx_t) fun->nregs);
 			break;
 		}
 
 		case DUK_OP_NEWOBJ: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_push_object(ctx);
+			duk_push_object(thr);
+#if defined(DUK_USE_ASSERTIONS)
+			{
+				duk_hobject *h;
+				h = duk_require_hobject(thr, -1);
+				DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
+			}
+#endif
+#if !defined(DUK_USE_PREFER_SIZE)
+			/* XXX: could do a direct props realloc, but need hash size */
+			duk_hobject_resize_entrypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins));
+#endif
 			DUK__REPLACE_TOP_BC_BREAK();
 		}
 
 		case DUK_OP_NEWARR: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_push_array(ctx);
+			duk_push_array(thr);
+#if defined(DUK_USE_ASSERTIONS)
+			{
+				duk_hobject *h;
+				h = duk_require_hobject(thr, -1);
+				DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
+				DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(h));
+			}
+#endif
+#if !defined(DUK_USE_PREFER_SIZE)
+			duk_hobject_realloc_props(thr,
+			                          duk_known_hobject(thr, -1),
+			                          0 /*new_e_size*/,
+			                          DUK_DEC_A(ins) /*new_a_size*/,
+			                          0 /*new_h_size*/,
+			                          0 /*abandon_array*/);
+#if 0
+			duk_hobject_resize_arraypart(thr, duk_known_hobject(thr, -1), DUK_DEC_A(ins));
+#endif
+#endif
 			DUK__REPLACE_TOP_BC_BREAK();
 		}
 
 		case DUK_OP_MPUTOBJ:
 		case DUK_OP_MPUTOBJI: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_idx_t obj_idx;
 			duk_uint_fast_t idx, idx_end;
 			duk_small_uint_fast_t count;
@@ -75137,11 +76797,11 @@
 			 */
 
 			obj_idx = DUK_DEC_A(ins);
-			DUK_ASSERT(duk_is_object(ctx, obj_idx));
+			DUK_ASSERT(duk_is_object(thr, obj_idx));
 
 			idx = (duk_uint_fast_t) DUK_DEC_B(ins);
 			if (DUK_DEC_OP(ins) == DUK_OP_MPUTOBJI) {
-				DUK__LOOKUP_INDIRECT_INDEX(idx);
+				DUK__LOOKUP_INDIRECT(idx);
 			}
 
 			count = (duk_small_uint_fast_t) DUK_DEC_C(ins);
@@ -75149,7 +76809,7 @@
 			idx_end = idx + count;
 
 #if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK)
-			if (DUK_UNLIKELY(idx_end > (duk_uint_fast_t) duk_get_top(ctx))) {
+			if (DUK_UNLIKELY(idx_end > (duk_uint_fast_t) duk_get_top(thr))) {
 				/* XXX: use duk_is_valid_index() instead? */
 				/* XXX: improve check; check against nregs, not against top */
 				DUK__INTERNAL_ERROR("MPUTOBJ out of bounds");
@@ -75167,9 +76827,9 @@
 			 */
 			do {
 				/* XXX: faster initialization (direct access or better primitives) */
-				duk_dup(ctx, idx);
-				duk_dup(ctx, idx + 1);
-				duk_def_prop(ctx, obj_idx, DUK_DEFPROP_HAVE_VALUE |
+				duk_dup(thr, (duk_idx_t) idx);
+				duk_dup(thr, (duk_idx_t) (idx + 1));
+				duk_def_prop(thr, obj_idx, DUK_DEFPROP_HAVE_VALUE |
 				                           DUK_DEFPROP_FORCE |
 				                           DUK_DEFPROP_SET_WRITABLE |
 				                           DUK_DEFPROP_SET_ENUMERABLE |
@@ -75181,43 +76841,12 @@
 
 		case DUK_OP_INITSET:
 		case DUK_OP_INITGET: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_bool_t is_set = (op == DUK_OP_INITSET);
-			duk_uint_fast_t idx;
-			duk_uint_t defprop_flags;
-
-			/* A -> object register (acts as a source)
-			 * BC -> BC+0 contains key, BC+1 closure (value)
-			 */
-
-			/* INITSET/INITGET are only used to initialize object literal keys.
-			 * There may be a previous propery in ES2015 because duplicate property
-			 * names are allowed.
-			 */
-
-			/* This could be made more optimal by accessing internals directly. */
-
-			idx = (duk_uint_fast_t) DUK_DEC_BC(ins);
-			duk_dup(ctx, (duk_idx_t) (idx + 0));  /* key */
-			duk_dup(ctx, (duk_idx_t) (idx + 1));  /* getter/setter */
-			if (is_set) {
-				defprop_flags = DUK_DEFPROP_HAVE_SETTER |
-				                DUK_DEFPROP_FORCE |
-				                DUK_DEFPROP_SET_ENUMERABLE |
-				                DUK_DEFPROP_SET_CONFIGURABLE;
-			} else {
-				defprop_flags = DUK_DEFPROP_HAVE_GETTER |
-				                DUK_DEFPROP_FORCE |
-				                DUK_DEFPROP_SET_ENUMERABLE |
-				                DUK_DEFPROP_SET_CONFIGURABLE;
-			}
-			duk_def_prop(ctx, (duk_idx_t) DUK_DEC_A(ins), defprop_flags);
+			duk__handle_op_initset_initget(thr, ins);
 			break;
 		}
 
 		case DUK_OP_MPUTARR:
 		case DUK_OP_MPUTARRI: {
-			duk_context *ctx = (duk_context *) thr;
 			duk_idx_t obj_idx;
 			duk_uint_fast_t idx, idx_end;
 			duk_small_uint_fast_t count;
@@ -75231,11 +76860,11 @@
 			 */
 
 			obj_idx = DUK_DEC_A(ins);
-			DUK_ASSERT(duk_is_object(ctx, obj_idx));
+			DUK_ASSERT(duk_is_object(thr, obj_idx));
 
 			idx = (duk_uint_fast_t) DUK_DEC_B(ins);
 			if (DUK_DEC_OP(ins) == DUK_OP_MPUTARRI) {
-				DUK__LOOKUP_INDIRECT_INDEX(idx);
+				DUK__LOOKUP_INDIRECT(idx);
 			}
 
 			count = (duk_small_uint_fast_t) DUK_DEC_C(ins);
@@ -75243,7 +76872,7 @@
 			idx_end = idx + count;
 
 #if defined(DUK_USE_EXEC_INDIRECT_BOUND_CHECK)
-			if (idx_end > (duk_uint_fast_t) duk_get_top(ctx)) {
+			if (idx_end > (duk_uint_fast_t) duk_get_top(thr)) {
 				/* XXX: use duk_is_valid_index() instead? */
 				/* XXX: improve check; check against nregs, not against top */
 				DUK__INTERNAL_ERROR("MPUTARR out of bounds");
@@ -75273,8 +76902,8 @@
 				 * and finally set 'length' manually in the end (as already happens now).
 				 */
 
-				duk_dup(ctx, idx);
-				duk_xdef_prop_index_wec(ctx, obj_idx, arr_idx);
+				duk_dup(thr, (duk_idx_t) idx);
+				duk_xdef_prop_index_wec(thr, obj_idx, arr_idx);
 
 				idx++;
 				arr_idx++;
@@ -75311,73 +76940,12 @@
 		}
 
 		case DUK_OP_INITENUM: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_small_uint_fast_t b = DUK_DEC_B(ins);
-			duk_small_uint_fast_t c = DUK_DEC_C(ins);
-
-			/*
-			 *  Enumeration semantics come from for-in statement, E5 Section 12.6.4.
-			 *  If called with 'null' or 'undefined', this opcode returns 'null' as
-			 *  the enumerator, which is special cased in NEXTENUM.  This simplifies
-			 *  the compiler part
-			 */
-
-			/* B -> register for writing enumerator object
-			 * C -> value to be enumerated (register)
-			 */
-
-			if (duk_is_null_or_undefined(ctx, (duk_idx_t) c)) {
-				duk_push_null(ctx);
-				duk_replace(ctx, (duk_idx_t) b);
-			} else {
-				duk_dup(ctx, (duk_idx_t) c);
-				duk_to_object(ctx, -1);
-				duk_hobject_enumerator_create(ctx, 0 /*enum_flags*/);  /* [ ... val ] --> [ ... enum ] */
-				duk_replace(ctx, (duk_idx_t) b);
-			}
+			duk__handle_op_initenum(thr, ins);
 			break;
 		}
 
 		case DUK_OP_NEXTENUM: {
-			duk_context *ctx = (duk_context *) thr;
-			duk_small_uint_fast_t b = DUK_DEC_B(ins);
-			duk_small_uint_fast_t c = DUK_DEC_C(ins);
-
-			/*
-			 *  NEXTENUM checks whether the enumerator still has unenumerated
-			 *  keys.  If so, the next key is loaded to the target register
-			 *  and the next instruction is skipped.  Otherwise the next instruction
-			 *  will be executed, jumping out of the enumeration loop.
-			 */
-
-			/* B -> target register for next key
-			 * C -> enum register
-			 */
-
-			DUK_DDD(DUK_DDDPRINT("NEXTENUM: b->%!T, c->%!T",
-			                     (duk_tval *) duk_get_tval(ctx, (duk_idx_t) b),
-			                     (duk_tval *) duk_get_tval(ctx, (duk_idx_t) c)));
-
-			if (duk_is_object(ctx, (duk_idx_t) c)) {
-				/* XXX: assert 'c' is an enumerator */
-				duk_dup(ctx, (duk_idx_t) c);
-				if (duk_hobject_enumerator_next(ctx, 0 /*get_value*/)) {
-					/* [ ... enum ] -> [ ... next_key ] */
-					DUK_DDD(DUK_DDDPRINT("enum active, next key is %!T, skip jump slot ",
-					                     (duk_tval *) duk_get_tval(ctx, -1)));
-					curr_pc++;
-				} else {
-					/* [ ... enum ] -> [ ... ] */
-					DUK_DDD(DUK_DDDPRINT("enum finished, execute jump slot"));
-					DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top));  /* valstack policy */
-					thr->valstack_top++;
-				}
-				duk_replace(ctx, (duk_idx_t) b);
-			} else {
-				/* 'null' enumerator case -> behave as with an empty enumerator */
-				DUK_ASSERT(duk_is_null(ctx, (duk_idx_t) c));
-				DUK_DDD(DUK_DDDPRINT("enum is null, execute jump slot"));
-			}
+			curr_pc += duk__handle_op_nextenum(thr, ins);
 			break;
 		}
 
@@ -75410,7 +76978,9 @@
 		}
 
 		case DUK_OP_NOP: {
-			/* nop */
+			/* Nop, ignored, but ABC fields may carry a value e.g.
+			 * for indirect opcode handling.
+			 */
 			break;
 		}
 
@@ -75419,6 +76989,46 @@
 			break;
 		}
 
+#if defined(DUK_USE_ES6)
+		case DUK_OP_NEWTARGET: {
+			/* https://www.ecma-international.org/ecma-262/6.0/#sec-meta-properties-runtime-semantics-evaluation
+			 * https://www.ecma-international.org/ecma-262/6.0/#sec-getnewtarget
+			 *
+			 * No newTarget support now, so as a first approximation
+			 * use the resolved (non-bound) target function.
+			 */
+			/* XXX: C API: push_new_target()? */
+			duk_activation *act;
+
+			act = thr->callstack_curr;
+			DUK_ASSERT(act != NULL);
+
+			/* Check CONSTRUCT flag from current function, or if running
+			 * direct eval, from a non-direct-eval parent (with possibly
+			 * more than one nested direct eval).  An alternative to this
+			 * would be to store [[NewTarget]] as a hidden symbol of the
+			 * lexical scope, and then just look up that variable.
+			 */
+			for (;;) {
+				if (act == NULL) {
+					duk_push_undefined(thr);
+					break;
+				}
+				if (act->flags & DUK_ACT_FLAG_CONSTRUCT) {
+					duk_push_tval(thr, &act->tv_func);
+					break;
+				} else if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) {
+					act = act->parent;
+				} else {
+					duk_push_undefined(thr);
+					break;
+				}
+			}
+
+			DUK__REPLACE_TOP_BC_BREAK();
+		}
+#endif  /* DUK_USE_ES6 */
+
 #if !defined(DUK_USE_EXEC_PREFER_SIZE)
 #if !defined(DUK_USE_ES7_EXP_OPERATOR)
 		case DUK_OP_EXP_RR:
@@ -75426,24 +77036,16 @@
 		case DUK_OP_EXP_RC:
 		case DUK_OP_EXP_CC:
 #endif
-		case DUK_OP_UNUSED194:
-		case DUK_OP_UNUSED195:
-		case DUK_OP_UNUSED196:
-		case DUK_OP_UNUSED197:
-		case DUK_OP_UNUSED198:
-		case DUK_OP_UNUSED199:
-		case DUK_OP_UNUSED200:
-		case DUK_OP_UNUSED201:
-		case DUK_OP_UNUSED202:
-		case DUK_OP_UNUSED203:
-		case DUK_OP_UNUSED204:
-		case DUK_OP_UNUSED205:
-		case DUK_OP_UNUSED206:
+#if !defined(DUK_USE_ES6)
+		case DUK_OP_NEWTARGET:
+#endif
+#if !defined(DUK_USE_VERBOSE_ERRORS)
+		case DUK_OP_GETPROPC_RR:
+		case DUK_OP_GETPROPC_CR:
+		case DUK_OP_GETPROPC_RC:
+		case DUK_OP_GETPROPC_CC:
+#endif
 		case DUK_OP_UNUSED207:
-		case DUK_OP_UNUSED208:
-		case DUK_OP_UNUSED209:
-		case DUK_OP_UNUSED210:
-		case DUK_OP_UNUSED211:
 		case DUK_OP_UNUSED212:
 		case DUK_OP_UNUSED213:
 		case DUK_OP_UNUSED214:
@@ -75495,7 +77097,7 @@
 		 * a small detail and obviously compiler dependent.
 		 */
 		/* default: clause omitted on purpose */
-#else
+#else  /* DUK_USE_EXEC_PREFER_SIZE */
 		default:
 #endif  /* DUK_USE_EXEC_PREFER_SIZE */
 		{
@@ -75547,6 +77149,7 @@
 #undef DUK__DELPROP_BODY
 #undef DUK__EQ_BODY
 #undef DUK__FUN
+#undef DUK__GETPROPC_BODY
 #undef DUK__GETPROP_BODY
 #undef DUK__GE_BODY
 #undef DUK__GT_BODY
@@ -75559,13 +77162,14 @@
 #undef DUK__LE_BODY
 #undef DUK__LONGJMP_RESTART
 #undef DUK__LONGJMP_RETHROW
-#undef DUK__LOOKUP_INDIRECT_INDEX
+#undef DUK__LOOKUP_INDIRECT
 #undef DUK__LT_BODY
 #undef DUK__MASK_A
 #undef DUK__MASK_B
 #undef DUK__MASK_BC
 #undef DUK__MASK_C
 #undef DUK__NEQ_BODY
+#undef DUK__NOINLINE_PERF
 #undef DUK__PUTPROP_BODY
 #undef DUK__RCBIT_B
 #undef DUK__RCBIT_C
@@ -75748,11 +77352,10 @@
 
 /* E5 Section 9.3.1 */
 DUK_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_small_uint_t s2n_flags;
 	duk_double_t d;
 
-	DUK_ASSERT(duk_is_string(ctx, -1));
+	DUK_ASSERT(duk_is_string(thr, -1));
 
 	/* Quite lenient, e.g. allow empty as zero, but don't allow trailing
 	 * garbage.
@@ -75771,11 +77374,11 @@
 	            DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT |
 	            DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT;
 
-	duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags);
+	duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
 
 #if defined(DUK_USE_PREFER_SIZE)
-	d = duk_get_number(ctx, -1);
-	duk_pop(ctx);
+	d = duk_get_number(thr, -1);
+	duk_pop_unsafe(thr);
 #else
 	thr->valstack_top--;
 	DUK_ASSERT(DUK_TVAL_IS_NUMBER(thr->valstack_top));
@@ -75789,8 +77392,6 @@
 }
 
 DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
-	duk_context *ctx = (duk_hthread *) thr;
-
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(tv != NULL);
 
@@ -75818,22 +77419,22 @@
 		if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
 			DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL);
 		}
-		duk_push_hstring(ctx, h);
+		duk_push_hstring(thr, h);
 		return duk__tonumber_string_raw(thr);
 	}
 	case DUK_TAG_BUFFER:  /* plain buffer treated like object */
 	case DUK_TAG_OBJECT: {
 		duk_double_t d;
-		duk_push_tval(ctx, tv);
-		duk_to_primitive(ctx, -1, DUK_HINT_NUMBER);  /* 'tv' becomes invalid */
+		duk_push_tval(thr, tv);
+		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);  /* 'tv' becomes invalid */
 
 		/* recursive call for a primitive value (guaranteed not to cause second
 		 * recursion).
 		 */
-		DUK_ASSERT(duk_get_tval(ctx, -1) != NULL);
-		d = duk_js_tonumber(thr, duk_get_tval(ctx, -1));
-
-		duk_pop(ctx);
+		DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
+		d = duk_js_tonumber(thr, duk_get_tval(thr, -1));
+
+		duk_pop_unsafe(thr);
 		return d;
 	}
 	case DUK_TAG_POINTER: {
@@ -75883,7 +77484,7 @@
 	/* NaN and Infinity have the same exponent so it's a cheap
 	 * initial check for the rare path.
 	 */
-	if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x))) {
+	if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x) != 0U)) {
 		if (duk_double_is_nan(x)) {
 			return 0.0;
 		} else {
@@ -76118,8 +77719,7 @@
 #endif  /* DUK_USE_PARANOID_MATH */
 }
 
-DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
 	duk_uint_t type_mask_x;
 	duk_uint_t type_mask_y;
 
@@ -76242,7 +77842,7 @@
 		if (!DUK_TVAL_STRING_IS_SYMBOL(tv_y)) {
 			duk_double_t d1, d2;
 			d1 = DUK_TVAL_GET_NUMBER(tv_x);
-			d2 = duk_to_number_tval(ctx, tv_y);
+			d2 = duk_to_number_tval(thr, tv_y);
 			return duk__js_equals_number(d1, d2);
 		}
 	}
@@ -76250,7 +77850,7 @@
 		if (!DUK_TVAL_STRING_IS_SYMBOL(tv_x)) {
 			duk_double_t d1, d2;
 			d1 = DUK_TVAL_GET_NUMBER(tv_y);
-			d2 = duk_to_number_tval(ctx, tv_x);
+			d2 = duk_to_number_tval(thr, tv_x);
 			return duk__js_equals_number(d1, d2);
 		}
 	}
@@ -76264,14 +77864,14 @@
 	 */
 	if (type_mask_x & DUK_TYPE_MASK_BOOLEAN) {
 		DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_x) == 0 || DUK_TVAL_GET_BOOLEAN(tv_x) == 1);
-		duk_push_int(ctx, DUK_TVAL_GET_BOOLEAN(tv_x));
-		duk_push_tval(ctx, tv_y);
+		duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_x));
+		duk_push_tval(thr, tv_y);
 		goto recursive_call;
 	}
 	if (type_mask_y & DUK_TYPE_MASK_BOOLEAN) {
 		DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1);
-		duk_push_tval(ctx, tv_x);
-		duk_push_int(ctx, DUK_TVAL_GET_BOOLEAN(tv_y));
+		duk_push_tval(thr, tv_x);
+		duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_y));
 		goto recursive_call;
 	}
 
@@ -76279,17 +77879,17 @@
 	if ((type_mask_x & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER)) &&
 	    (type_mask_y & DUK_TYPE_MASK_OBJECT)) {
 		/* No symbol check needed because symbols and strings are accepted. */
-		duk_push_tval(ctx, tv_x);
-		duk_push_tval(ctx, tv_y);
-		duk_to_primitive(ctx, -1, DUK_HINT_NONE);  /* apparently no hint? */
+		duk_push_tval(thr, tv_x);
+		duk_push_tval(thr, tv_y);
+		duk_to_primitive(thr, -1, DUK_HINT_NONE);  /* apparently no hint? */
 		goto recursive_call;
 	}
 	if ((type_mask_x & DUK_TYPE_MASK_OBJECT) &&
 	    (type_mask_y & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER))) {
 		/* No symbol check needed because symbols and strings are accepted. */
-		duk_push_tval(ctx, tv_x);
-		duk_push_tval(ctx, tv_y);
-		duk_to_primitive(ctx, -2, DUK_HINT_NONE);  /* apparently no hint? */
+		duk_push_tval(thr, tv_x);
+		duk_push_tval(thr, tv_y);
+		duk_to_primitive(thr, -2, DUK_HINT_NONE);  /* apparently no hint? */
 		goto recursive_call;
 	}
 
@@ -76301,10 +77901,10 @@
 	{
 		duk_bool_t rc;
 		rc = duk_js_equals_helper(thr,
-		                          DUK_GET_TVAL_NEGIDX(ctx, -2),
-		                          DUK_GET_TVAL_NEGIDX(ctx, -1),
+		                          DUK_GET_TVAL_NEGIDX(thr, -2),
+		                          DUK_GET_TVAL_NEGIDX(thr, -1),
 		                          0 /*flags:nonstrict*/);
-		duk_pop_2(ctx);
+		duk_pop_2_unsafe(thr);
 		return rc;
 	}
 }
@@ -76496,8 +78096,7 @@
 }
 #endif  /* DUK_USE_PARANOID_MATH */
 
-DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_int_t flags) {
-	duk_context *ctx = (duk_context *) thr;
+DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
 	duk_double_t d1, d2;
 	duk_small_int_t rc;
 	duk_bool_t retval;
@@ -76526,20 +78125,20 @@
 
 	/* Slow path */
 
-	duk_push_tval(ctx, tv_x);
-	duk_push_tval(ctx, tv_y);
+	duk_push_tval(thr, tv_x);
+	duk_push_tval(thr, tv_y);
 
 	if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
-		duk_to_primitive(ctx, -2, DUK_HINT_NUMBER);
-		duk_to_primitive(ctx, -1, DUK_HINT_NUMBER);
-	} else {
-		duk_to_primitive(ctx, -1, DUK_HINT_NUMBER);
-		duk_to_primitive(ctx, -2, DUK_HINT_NUMBER);
+		duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
+		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
+	} else {
+		duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
+		duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
 	}
 
 	/* Note: reuse variables */
-	tv_x = DUK_GET_TVAL_NEGIDX(ctx, -2);
-	tv_y = DUK_GET_TVAL_NEGIDX(ctx, -1);
+	tv_x = DUK_GET_TVAL_NEGIDX(thr, -2);
+	tv_y = DUK_GET_TVAL_NEGIDX(thr, -1);
 
 	if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) {
 		duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x);
@@ -76549,7 +78148,7 @@
 
 		if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) {
 			rc = duk_js_string_compare(h1, h2);
-			duk_pop_2(ctx);
+			duk_pop_2_unsafe(thr);
 			if (rc < 0) {
 				return retval ^ 1;
 			} else {
@@ -76565,27 +78164,27 @@
 	/* Ordering should not matter (E5 Section 11.8.5, step 3.a). */
 #if 0
 	if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
-		d1 = duk_to_number_m2(ctx);
-		d2 = duk_to_number_m1(ctx);
-	} else {
-		d2 = duk_to_number_m1(ctx);
-		d1 = duk_to_number_m2(ctx);
-	}
-#endif
-	d1 = duk_to_number_m2(ctx);
-	d2 = duk_to_number_m1(ctx);
-
-	/* We want to duk_pop_2(ctx); because the values are numbers
+		d1 = duk_to_number_m2(thr);
+		d2 = duk_to_number_m1(thr);
+	} else {
+		d2 = duk_to_number_m1(thr);
+		d1 = duk_to_number_m2(thr);
+	}
+#endif
+	d1 = duk_to_number_m2(thr);
+	d2 = duk_to_number_m1(thr);
+
+	/* We want to duk_pop_2_unsafe(thr); because the values are numbers
 	 * no decref check is needed.
 	 */
 #if defined(DUK_USE_PREFER_SIZE)
-	duk_pop_2(ctx);
-#else
-	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(ctx, -2)));
-	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(ctx, -1)));
-	DUK_ASSERT(duk_get_top(ctx) >= 2);
-	((duk_hthread *) ctx)->valstack_top -= 2;
-	tv_x = ((duk_hthread *) ctx)->valstack_top;
+	duk_pop_2_nodecref_unsafe(thr);
+#else
+	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -2)));
+	DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -1)));
+	DUK_ASSERT(duk_get_top(thr) >= 2);
+	thr->valstack_top -= 2;
+	tv_x = thr->valstack_top;
 	tv_y = tv_x + 1;
 	DUK_TVAL_SET_UNDEFINED(tv_x);  /* Value stack policy */
 	DUK_TVAL_SET_UNDEFINED(tv_y);
@@ -76615,13 +78214,12 @@
  */
 
 DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hobject *func;
 	duk_hobject *val;
 	duk_hobject *proto;
 	duk_tval *tv;
+	duk_bool_t skip_first;
 	duk_uint_t sanity;
-	duk_bool_t skip_first;
 
 	/*
 	 *  Get the values onto the stack first.  It would be possible to cover
@@ -76633,52 +78231,41 @@
 	 *  Using duk_require_hobject() is thus correct (except for error msg).
 	 */
 
-	duk_push_tval(ctx, tv_x);
-	duk_push_tval(ctx, tv_y);
-	func = duk_require_hobject(ctx, -1);
+	duk_push_tval(thr, tv_x);
+	duk_push_tval(thr, tv_y);
+	func = duk_require_hobject(thr, -1);
+	DUK_ASSERT(func != NULL);
 
 	/*
 	 *  For bound objects, [[HasInstance]] just calls the target function
 	 *  [[HasInstance]].  If that is again a bound object, repeat until
 	 *  we find a non-bound Function object.
-	 */
-
-	/* XXX: this bound function resolution also happens elsewhere,
-	 * move into a shared helper.
-	 */
-
-	sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
-	do {
-		/* check func supports [[HasInstance]] (this is checked for every function
-		 * in the bound chain, including the final one)
-		 */
-
-		if (!DUK_HOBJECT_IS_CALLABLE(func)) {
-			/*
-			 *  Note: of native Ecmascript objects, only Function instances
-			 *  have a [[HasInstance]] internal property.  Custom objects might
-			 *  also have it, but not in current implementation.
-			 *
-			 *  XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
-			 */
-			DUK_ERROR_TYPE(thr, "invalid instanceof rval");
-		}
-
-		if (!DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
-			break;
-		}
-
-		/* [ ... lval rval ] */
-
-		duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TARGET);         /* -> [ ... lval rval new_rval ] */
-		duk_replace(ctx, -1);                                        /* -> [ ... lval new_rval ] */
-		func = duk_require_hobject(ctx, -1);
-
-		/* func support for [[HasInstance]] checked in the beginning of the loop */
-	} while (--sanity > 0);
-
-	if (DUK_UNLIKELY(sanity == 0)) {
-		DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
+	 *
+	 *  The bound function chain is now "collapsed" so there can be only
+	 *  one bound function in the chain.
+	 */
+
+	if (!DUK_HOBJECT_IS_CALLABLE(func)) {
+		/*
+		 *  Note: of native Ecmascript objects, only Function instances
+		 *  have a [[HasInstance]] internal property.  Custom objects might
+		 *  also have it, but not in current implementation.
+		 *
+		 *  XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
+		 */
+		goto error_invalid_rval;
+	}
+
+	if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
+		duk_push_tval(thr, &((duk_hboundfunc *) func)->target);
+		duk_replace(thr, -2);
+		func = duk_require_hobject(thr, -1);  /* lightfunc throws */
+
+		/* Rely on Function.prototype.bind() never creating bound
+		 * functions whose target is not proper.
+		 */
+		DUK_ASSERT(func != NULL);
+		DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
 	}
 
 	/*
@@ -76687,6 +78274,7 @@
 	 *  to execute E5 Section 15.3.5.3.
 	 */
 
+	DUK_ASSERT(func != NULL);
 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
 	DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
 
@@ -76696,7 +78284,7 @@
 	 * from the virtual prototype object.
 	 */
 	skip_first = 0;
-	tv = DUK_GET_TVAL_NEGIDX(ctx, -2);
+	tv = DUK_GET_TVAL_NEGIDX(thr, -2);
 	switch (DUK_TVAL_GET_TAG(tv)) {
 	case DUK_TAG_LIGHTFUNC:
 		val = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
@@ -76716,13 +78304,22 @@
 		DUK_ASSERT(val != NULL);
 		break;
 	default:
-		goto pop_and_false;
+		goto pop2_and_false;
 	}
 	DUK_ASSERT(val != NULL);  /* Loop doesn't actually rely on this. */
 
-	duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_PROTOTYPE);  /* -> [ ... lval rval rval.prototype ] */
-	proto = duk_require_hobject(ctx, -1);
-	duk_pop(ctx);  /* -> [ ... lval rval ] */
+	/* Look up .prototype of rval.  Leave it on the value stack in case it
+	 * has been virtualized (e.g. getter, Proxy trap).
+	 */
+	duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_PROTOTYPE);  /* -> [ ... lval rval rval.prototype ] */
+#if defined(DUK_USE_VERBOSE_ERRORS)
+	proto = duk_get_hobject(thr, -1);
+	if (proto == NULL) {
+		goto error_invalid_rval_noproto;
+	}
+#else
+	proto = duk_require_hobject(thr, -1);
+#endif
 
 	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
 	do {
@@ -76745,18 +78342,18 @@
 		 */
 
 		if (!val) {
-			goto pop_and_false;
+			goto pop3_and_false;
 		}
 
 		DUK_ASSERT(val != NULL);
 #if defined(DUK_USE_ES6_PROXY)
-		val = duk_hobject_resolve_proxy_target(thr, val);
+		val = duk_hobject_resolve_proxy_target(val);
 #endif
 
 		if (skip_first) {
 			skip_first = 0;
 		} else if (val == proto) {
-			goto pop_and_true;
+			goto pop3_and_true;
 		}
 
 		DUK_ASSERT(val != NULL);
@@ -76768,13 +78365,27 @@
 	}
 	DUK_UNREACHABLE();
 
- pop_and_false:
-	duk_pop_2(ctx);
-	return 0;
-
- pop_and_true:
-	duk_pop_2(ctx);
-	return 1;
+ pop2_and_false:
+	duk_pop_2_unsafe(thr);
+	return 0;
+
+ pop3_and_false:
+	duk_pop_3_unsafe(thr);
+	return 0;
+
+ pop3_and_true:
+	duk_pop_3_unsafe(thr);
+	return 1;
+
+ error_invalid_rval:
+	DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL);
+	return 0;
+
+#if defined(DUK_USE_VERBOSE_ERRORS)
+ error_invalid_rval_noproto:
+	DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO);
+	return 0;
+#endif
 }
 
 /*
@@ -76788,7 +78399,6 @@
  */
 
 DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_bool_t retval;
 
 	/*
@@ -76808,17 +78418,17 @@
 	/* TypeError if rval is not an object or object like (e.g. lightfunc
 	 * or plain buffer).
 	 */
-	duk_push_tval(ctx, tv_x);
-	duk_push_tval(ctx, tv_y);
-	duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
-
-	(void) duk_to_property_key_hstring(ctx, -2);
+	duk_push_tval(thr, tv_x);
+	duk_push_tval(thr, tv_y);
+	duk_require_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
+
+	(void) duk_to_property_key_hstring(thr, -2);
 
 	retval = duk_hobject_hasprop(thr,
-	                             DUK_GET_TVAL_NEGIDX(ctx, -1),
-	                             DUK_GET_TVAL_NEGIDX(ctx, -2));
-
-	duk_pop_2(ctx);
+	                             DUK_GET_TVAL_NEGIDX(thr, -1),
+	                             DUK_GET_TVAL_NEGIDX(thr, -2));
+
+	duk_pop_2_unsafe(thr);
 	return retval;
 }
 
@@ -77006,8 +78616,8 @@
 			/* Scanning to NUL is always safe for interned strings. */
 			break;
 		}
-		DUK_ASSERT(t >= DUK_ASC_0 && t <= DUK_ASC_9);
-		res = res * 10U + (t - DUK_ASC_0);
+		DUK_ASSERT(t >= (duk_uint8_t) DUK_ASC_0 && t <= (duk_uint8_t) DUK_ASC_9);
+		res = res * 10U + (duk_uarridx_t) t - (duk_uarridx_t) DUK_ASC_0;
 	}
 	return res;
 }
@@ -77063,7 +78673,7 @@
 	duk_hobject *env;
 	duk_hobject *holder;      /* for object-bound identifiers */
 	duk_tval *value;          /* for register-bound and declarative env identifiers */
-	duk_int_t attrs;          /* property attributes for identifier (relevant if value != NULL) */
+	duk_uint_t attrs;         /* property attributes for identifier (relevant if value != NULL) */
 	duk_bool_t has_this;      /* for object-bound identifiers: provide 'this' binding */
 } duk__id_lookup_result;
 
@@ -77149,7 +78759,6 @@
                          duk_hobject *outer_var_env,
                          duk_hobject *outer_lex_env,
                          duk_bool_t add_auto_proto) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hcompfunc *fun_clos;
 	duk_small_uint_t i;
 	duk_uint_t len_value;
@@ -77162,11 +78771,11 @@
 	DUK_ASSERT(outer_lex_env != NULL);
 	DUK_UNREF(len_value);
 
-	fun_clos = duk_push_hcompfunc(ctx);
+	fun_clos = duk_push_hcompfunc(thr);
 	DUK_ASSERT(fun_clos != NULL);
 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) fun_clos) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
 
-	duk_push_hobject(ctx, &fun_temp->obj);  /* -> [ ... closure template ] */
+	duk_push_hobject(thr, &fun_temp->obj);  /* -> [ ... closure template ] */
 
 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun_clos));
 	DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) == NULL);
@@ -77196,10 +78805,14 @@
 	DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) != NULL);
 	DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) != NULL);
 
-	/* XXX: could also copy from template, but there's no way to have any
+	/* XXX: Could also copy from template, but there's no way to have any
 	 * other value here now (used code has no access to the template).
-	 */
+	 * Prototype is set by duk_push_hcompfunc().
+	 */
+	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+#if 0
 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &fun_clos->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+#endif
 
 	/* Copy duk_hobject flags as is from the template using a mask.
 	 * Leave out duk_heaphdr owned flags just in case (e.g. if there's
@@ -77274,7 +78887,7 @@
 			                            DUK_HOBJECT_FLAG_EXTENSIBLE |
 			                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
 			DUK_ASSERT(new_env != NULL);
-			duk_push_hobject(ctx, (duk_hobject *) new_env);
+			duk_push_hobject(thr, (duk_hobject *) new_env);
 
 			DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
 			DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto);
@@ -77292,10 +78905,10 @@
 			 * the name 'undefined' gets bound and maps to the closure (which is
 			 * a bit odd, but safe).
 			 */
-			(void) duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME);
+			(void) duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
 			/* -> [ ... closure template env funcname ] */
-			duk_dup_m4(ctx);                                           /* -> [ ... closure template env funcname closure ] */
-			duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE);           /* -> [ ... closure template env ] */
+			duk_dup_m4(thr);                                           /* -> [ ... closure template env funcname closure ] */
+			duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE);           /* -> [ ... closure template env ] */
 			/* env[funcname] = closure */
 
 			/* [ ... closure template env ] */
@@ -77304,7 +78917,7 @@
 			DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, (duk_hobject *) new_env);
 			DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
 			DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
-			duk_pop(ctx);
+			duk_pop_unsafe(thr);
 
 			/* [ ... closure template ] */
 		}
@@ -77358,18 +78971,18 @@
 	/* [ ... closure template ] */
 
 	DUK_DDD(DUK_DDDPRINT("copying properties: closure=%!iT, template=%!iT",
-	                     (duk_tval *) duk_get_tval(ctx, -2),
-	                     (duk_tval *) duk_get_tval(ctx, -1)));
+	                     (duk_tval *) duk_get_tval(thr, -2),
+	                     (duk_tval *) duk_get_tval(thr, -1)));
 
 	for (i = 0; i < (duk_small_uint_t) (sizeof(duk__closure_copy_proplist) / sizeof(duk_uint16_t)); i++) {
 		duk_small_int_t stridx = (duk_small_int_t) duk__closure_copy_proplist[i];
-		if (duk_get_prop_stridx_short(ctx, -1, stridx)) {
+		if (duk_get_prop_stridx_short(thr, -1, stridx)) {
 			/* [ ... closure template val ] */
 			DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> found", (long) stridx));
-			duk_xdef_prop_stridx_short(ctx, -3, stridx, DUK_PROPDESC_FLAGS_C);
+			duk_xdef_prop_stridx_short(thr, -3, stridx, DUK_PROPDESC_FLAGS_C);
 		} else {
 			DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx));
-			duk_pop(ctx);
+			duk_pop_unsafe(thr);
 		}
 	}
 
@@ -77385,18 +78998,18 @@
 	/* XXX: these lookups should be just own property lookups instead of
 	 * looking up the inheritance chain.
 	 */
-	if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_FORMALS)) {
+	if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_INT_FORMALS)) {
 		/* [ ... closure template formals ] */
-		len_value = (duk_uint_t) duk_get_length(ctx, -1);  /* could access duk_harray directly, not important */
+		len_value = (duk_uint_t) duk_get_length(thr, -1);  /* could access duk_harray directly, not important */
 		DUK_DD(DUK_DDPRINT("closure length from _Formals -> %ld", (long) len_value));
 	} else {
 		len_value = fun_temp->nargs;
 		DUK_DD(DUK_DDPRINT("closure length defaulted from nargs -> %ld", (long) len_value));
 	}
-	duk_pop(ctx);
-
-	duk_push_uint(ctx, len_value);  /* [ ... closure template len_value ] */
-	duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
+	duk_pop_unsafe(thr);
+
+	duk_push_uint(thr, len_value);  /* [ ... closure template len_value ] */
+	duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
 
 	/*
 	 *  "prototype" is, by default, a fresh object with the "constructor"
@@ -77415,11 +79028,11 @@
 	/* [ ... closure template ] */
 
 	if (add_auto_proto) {
-		duk_push_object(ctx);  /* -> [ ... closure template newobj ] */
-		duk_dup_m3(ctx);       /* -> [ ... closure template newobj closure ] */
-		duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* -> [ ... closure template newobj ] */
-		duk_compact(ctx, -1);  /* compact the prototype */
-		duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);     /* -> [ ... closure template ] */
+		duk_push_object(thr);  /* -> [ ... closure template newobj ] */
+		duk_dup_m3(thr);       /* -> [ ... closure template newobj closure ] */
+		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* -> [ ... closure template newobj ] */
+		duk_compact(thr, -1);  /* compact the prototype */
+		duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);     /* -> [ ... closure template ] */
 	}
 
 	/*
@@ -77433,13 +79046,13 @@
 	/* [ ... closure template ] */
 
 	if (DUK_HOBJECT_HAS_STRICT(&fun_clos->obj)) {
-		duk_xdef_prop_stridx_thrower(ctx, -2, DUK_STRIDX_CALLER);
-		duk_xdef_prop_stridx_thrower(ctx, -2, DUK_STRIDX_LC_ARGUMENTS);
+		duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_CALLER);
+		duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_LC_ARGUMENTS);
 	} else {
 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
 		DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property in use, add initial 'null' value"));
-		duk_push_null(ctx);
-		duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
+		duk_push_null(thr);
+		duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
 #else
 		DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used"));
 #endif
@@ -77456,18 +79069,18 @@
 	/* XXX: Look for own property only; doesn't matter much because
 	 * templates are bare objects.
 	 */
-	if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_NAME)) {
+	if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME)) {
 		/* [ ... closure template name ] */
-		DUK_ASSERT(duk_is_string(ctx, -1));
-		DUK_DD(DUK_DDPRINT("setting function instance name to %!T", duk_get_tval(ctx, -1)));
-		duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);  /* -> [ ... closure template ] */
+		DUK_ASSERT(duk_is_string(thr, -1));
+		DUK_DD(DUK_DDPRINT("setting function instance name to %!T", duk_get_tval(thr, -1)));
+		duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);  /* -> [ ... closure template ] */
 	} else {
 		/* Anonymous functions don't have a .name in ES2015, so don't set
 		 * it on the instance either.  The instance will then inherit
 		 * it from Function.prototype.name.
 		 */
 		DUK_DD(DUK_DDPRINT("not setting function instance .name"));
-		duk_pop(ctx);
+		duk_pop_unsafe(thr);
 	}
 #endif
 
@@ -77480,7 +79093,7 @@
 	 *  through the API).
 	 */
 
-	duk_compact(ctx, -2);
+	duk_compact(thr, -2);
 
 	/*
 	 *  Some assertions (E5 Section 13.2).
@@ -77489,13 +79102,13 @@
 	DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(&fun_clos->obj) == DUK_HOBJECT_CLASS_FUNCTION);
 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
 	DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
-	DUK_ASSERT(duk_has_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH) != 0);
-	DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE) != 0);
+	DUK_ASSERT(duk_has_prop_stridx(thr, -2, DUK_STRIDX_LENGTH) != 0);
+	DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(thr, -2, DUK_STRIDX_PROTOTYPE) != 0);
 	/* May be missing .name */
 	DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
-	           duk_has_prop_stridx(ctx, -2, DUK_STRIDX_CALLER) != 0);
+	           duk_has_prop_stridx(thr, -2, DUK_STRIDX_CALLER) != 0);
 	DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
-	           duk_has_prop_stridx(ctx, -2, DUK_STRIDX_LC_ARGUMENTS) != 0);
+	           duk_has_prop_stridx(thr, -2, DUK_STRIDX_LC_ARGUMENTS) != 0);
 
 	/*
 	 *  Finish
@@ -77504,10 +79117,10 @@
 	/* [ ... closure template ] */
 
 	DUK_DDD(DUK_DDDPRINT("created function instance: template=%!iT -> closure=%!iT",
-	                     (duk_tval *) duk_get_tval(ctx, -1),
-	                     (duk_tval *) duk_get_tval(ctx, -2)));
-
-	duk_pop(ctx);
+	                     (duk_tval *) duk_get_tval(thr, -1),
+	                     (duk_tval *) duk_get_tval(thr, -2)));
+
+	duk_pop_unsafe(thr);
 
 	/* [ ... closure ] */
 }
@@ -77523,13 +79136,11 @@
 DUK_INTERNAL
 duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
                                                       duk_hobject *func,
-                                                      duk_size_t idx_bottom) {
-	duk_context *ctx = (duk_context *) thr;
+                                                      duk_size_t bottom_byteoff) {
 	duk_hdecenv *env;
 	duk_hobject *parent;
 	duk_hcompfunc *f;
 
-	DUK_ASSERT(ctx != NULL);
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(func != NULL);
 
@@ -77543,7 +79154,7 @@
 	                        DUK_HOBJECT_FLAG_EXTENSIBLE |
 	                        DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
 	DUK_ASSERT(env != NULL);
-	duk_push_hobject(ctx, (duk_hobject *) env);
+	duk_push_hobject(thr, (duk_hobject *) env);
 
 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
 	DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent);
@@ -77553,7 +79164,7 @@
 
 	DUK_ASSERT(env->thread == NULL);
 	DUK_ASSERT(env->varmap == NULL);
-	DUK_ASSERT(env->regbase == 0);
+	DUK_ASSERT(env->regbase_byteoff == 0);
 	if (DUK_HOBJECT_IS_COMPFUNC(func)) {
 		duk_hobject *varmap;
 		duk_tval *tv;
@@ -77567,12 +79178,12 @@
 			DUK_HOBJECT_INCREF(thr, varmap);
 			env->thread = thr;
 			DUK_HTHREAD_INCREF(thr, thr);
-			env->regbase = idx_bottom;
+			env->regbase_byteoff = bottom_byteoff;
 		} else {
 			/* If function has no _Varmap, leave the environment closed. */
 			DUK_ASSERT(env->thread == NULL);
 			DUK_ASSERT(env->varmap == NULL);
-			DUK_ASSERT(env->regbase == 0);
+			DUK_ASSERT(env->regbase_byteoff == 0);
 		}
 	}
 
@@ -77582,13 +79193,10 @@
 DUK_INTERNAL
 void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
                                                         duk_activation *act) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hobject *func;
 	duk_hobject *env;
-	duk_size_t act_off;
-
-	DUK_ASSERT(act != NULL);
-	act_off = (duk_size_t) ((duk_uint8_t *) act - (duk_uint8_t *) thr->callstack);
+
+	DUK_ASSERT(thr != NULL);
 	func = DUK_ACT_GET_FUNC(act);
 	DUK_ASSERT(func != NULL);
 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));  /* bound functions are never in act 'func' */
@@ -77601,9 +79209,9 @@
 	DUK_ASSERT(act->lex_env == NULL);
 	DUK_ASSERT(act->var_env == NULL);
 
-	env = duk_create_activation_environment_record(thr, func, act->idx_bottom);
+	env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff);
 	DUK_ASSERT(env != NULL);
-	act = (duk_activation *) (void *) ((duk_uint8_t *) thr->callstack + act_off);
+	/* 'act' is a stable pointer, so still OK. */
 
 	DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr *) env));
 #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
@@ -77621,7 +79229,7 @@
 	DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (here 2 times) */
 	DUK_HOBJECT_INCREF(thr, env);
 
-	duk_pop(ctx);
+	duk_pop_unsafe(thr);
 }
 
 /*
@@ -77633,7 +79241,6 @@
  */
 
 DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_uint_fast32_t i;
 	duk_hobject *varmap;
 	duk_hstring *key;
@@ -77686,7 +79293,7 @@
 	 * then realloc with hash part if large enough).
 	 */
 	for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
-		duk_size_t regbase;
+		duk_size_t regbase_byteoff;
 
 		key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
 		DUK_ASSERT(key != NULL);   /* assume keys are compact in _Varmap */
@@ -77702,19 +79309,19 @@
 		regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
 #endif
 
-		regbase = ((duk_hdecenv *) env)->regbase;
-		DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack);
-		DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top);
+		regbase_byteoff = ((duk_hdecenv *) env)->regbase_byteoff;
+		DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum >= (duk_uint8_t *) thr->valstack);
+		DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum < (duk_uint8_t *) thr->valstack_top);
 
 		/* If property already exists, overwrites silently.
 		 * Property is writable, but not deletable (not configurable
 		 * in terms of property attributes).
 		 */
-		duk_push_tval(ctx, thr->valstack + regbase + regnum);
+		duk_push_tval(thr, (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum));
 		DUK_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T",
 		                     (duk_heaphdr *) key,
 		                     (long) regnum,
-		                     (duk_tval *) duk_get_tval(ctx, -1)));
+		                     (duk_tval *) duk_get_tval(thr, -1)));
 		duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
 	}
 
@@ -77758,7 +79365,6 @@
                                          duk__id_lookup_result *out) {
 	duk_tval *tv;
 	duk_size_t reg_rel;
-	duk_size_t idx;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(name != NULL);
@@ -77789,8 +79395,7 @@
 #endif
 	DUK_ASSERT_DISABLE(reg_rel >= 0);  /* unsigned */
 
-	idx = env->regbase + reg_rel;
-	tv = env->thread->valstack + idx;
+	tv = (duk_tval *) (void *) ((duk_uint8_t *) env->thread->valstack + env->regbase_byteoff + sizeof(duk_tval) * reg_rel);
 	DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end);  /* XXX: more accurate? */
 
 	out->value = tv;
@@ -77811,7 +79416,6 @@
 	duk_hobject *func;
 	duk_hobject *varmap;
 	duk_size_t reg_rel;
-	duk_size_t idx;
 
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(name != NULL);
@@ -77844,9 +79448,8 @@
 	DUK_ASSERT_DISABLE(reg_rel >= 0);
 	DUK_ASSERT(reg_rel < ((duk_hcompfunc *) func)->nregs);
 
-	idx = act->idx_bottom + reg_rel;
-	DUK_ASSERT(idx >= act->idx_bottom);
-	tv = thr->valstack + idx;
+	tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
+	tv += reg_rel;
 
 	out->value = tv;
 	out->attrs = DUK_PROPDESC_FLAGS_W;  /* registers are mutable, non-deletable */
@@ -77864,7 +79467,6 @@
                                          duk_bool_t parents,
                                          duk__id_lookup_result *out) {
 	duk_tval *tv;
-	duk_tval tv_name;
 	duk_uint_t sanity;
 
 	DUK_ASSERT(thr != NULL);
@@ -77957,8 +79559,8 @@
 
 	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
 	while (env != NULL) {
-		duk_small_int_t cl;
-		duk_int_t attrs;
+		duk_small_uint_t cl;
+		duk_uint_t attrs;
 
 		DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO",
 		                     (duk_heaphdr *) name,
@@ -78042,7 +79644,9 @@
 			 * property is found, but rather the object binding target object.
 			 */
 
-			if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)) {
+#if defined(DUK_USE_ES6_PROXY)
+			if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(target))) {
+				duk_tval tv_name;
 				duk_tval tv_target_tmp;
 
 				DUK_ASSERT(name != NULL);
@@ -78050,7 +79654,9 @@
 				DUK_TVAL_SET_OBJECT(&tv_target_tmp, target);
 
 				found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name);
-			} else {
+			} else
+#endif  /* DUK_USE_ES6_PROXY */
+			{
 				/* XXX: duk_hobject_hasprop() would be correct for
 				 * non-Proxy objects too, but it is about ~20-25%
 				 * slower at present so separate code paths for
@@ -78172,7 +79778,6 @@
                               duk_activation *act,
                               duk_hstring *name,
                               duk_bool_t throw_flag) {
-	duk_context *ctx = (duk_context *) thr;
 	duk__id_lookup_result ref;
 	duk_tval tv_tmp_obj;
 	duk_tval tv_tmp_key;
@@ -78187,14 +79792,16 @@
 	DUK_ASSERT(name != NULL);
 	/* env and act may be NULL */
 
+	DUK_STATS_INC(thr->heap, stats_getvar_all);
+
         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
 
 	parents = 1;     /* follow parent chain */
 	if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
 		if (ref.value) {
-			duk_push_tval(ctx, ref.value);
-			duk_push_undefined(ctx);
+			duk_push_tval(thr, ref.value);
+			duk_push_undefined(thr);
 		} else {
 			DUK_ASSERT(ref.holder != NULL);
 
@@ -78208,9 +79815,9 @@
 			(void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key);  /* [value] */
 
 			if (ref.has_this) {
-				duk_push_hobject(ctx, ref.holder);
-			} else {
-				duk_push_undefined(ctx);
+				duk_push_hobject(thr, ref.holder);
+			} else {
+				duk_push_undefined(thr);
 			}
 
 			/* [value this] */
@@ -78272,6 +79879,8 @@
 	duk_tval tv_tmp_key;
 	duk_bool_t parents;
 
+	DUK_STATS_INC(thr->heap, stats_putvar_all);
+
 	DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld "
 	                     "(env -> %!dO, val -> %!T)",
 	                     (void *) thr, (void *) env, (void *) act,
@@ -78514,9 +80123,8 @@
                                duk_hobject *env,
                                duk_hstring *name,
                                duk_tval *val,
-                               duk_small_int_t prop_flags,
+                               duk_small_uint_t prop_flags,
                                duk_bool_t is_func_decl) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hobject *holder;
 	duk_bool_t parents;
 	duk__id_lookup_result ref;
@@ -78557,7 +80165,7 @@
 	if (duk__get_identifier_reference(thr, env, name, NULL, parents, &ref)) {
 		duk_int_t e_idx;
 		duk_int_t h_idx;
-		duk_small_int_t flags;
+		duk_small_uint_t flags;
 
 		/*
 		 *  Variable already declared, ignore re-declaration.
@@ -78604,8 +80212,8 @@
 		/* must be found: was found earlier, and cannot be inherited */
 		for (;;) {
 			DUK_ASSERT(holder != NULL);
-			duk_hobject_find_existing_entry(thr->heap, holder, name, &e_idx, &h_idx);
-			if (e_idx >= 0) {
+			if (duk_hobject_find_existing_entry(thr->heap, holder, name, &e_idx, &h_idx)) {
+				DUK_ASSERT(e_idx >= 0);
 				break;
 			}
 			/* SCANBUILD: NULL pointer dereference, doesn't actually trigger,
@@ -78684,7 +80292,7 @@
 			DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor"));
 
 			DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]);
-			duk_push_tval(ctx, val);
+			duk_push_tval(thr, val);
 			duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags);
 		}
 
@@ -78724,11 +80332,11 @@
 		goto fail_not_extensible;
 	}
 
-	duk_push_hobject(ctx, holder);
-	duk_push_hstring(ctx, name);
-	duk_push_tval(ctx, val);
-	duk_xdef_prop(ctx, -3, prop_flags);  /* [holder name val] -> [holder] */
-	duk_pop(ctx);
+	duk_push_hobject(thr, holder);
+	duk_push_hstring(thr, name);
+	duk_push_tval(thr, val);
+	duk_xdef_prop(thr, -3, prop_flags);  /* [holder name val] -> [holder] */
+	duk_pop_unsafe(thr);
 
 	return 0;
 
@@ -78743,14 +80351,12 @@
                                      duk_activation *act,
                                      duk_hstring *name,
                                      duk_tval *val,
-                                     duk_small_int_t prop_flags,
+                                     duk_small_uint_t prop_flags,
                                      duk_bool_t is_func_decl) {
 	duk_hobject *env;
 	duk_tval tv_val_copy;
-	duk_size_t act_off;
 
 	DUK_ASSERT(act != NULL);
-	act_off = (duk_size_t) ((duk_uint8_t *) act - (duk_uint8_t *) thr->callstack);
 
 	/*
 	 *  Make a value copy of the input val.  This ensures that
@@ -78767,7 +80373,7 @@
 	if (!act->var_env) {
 		DUK_ASSERT(act->lex_env == NULL);
 		duk_js_init_activation_environment_records_delayed(thr, act);
-		act = (duk_activation *) (void *) ((duk_uint8_t *) thr->callstack + act_off);
+		/* 'act' is a stable pointer, so still OK. */
 	}
 	DUK_ASSERT(act->lex_env != NULL);
 	DUK_ASSERT(act->var_env != NULL);
@@ -79093,7 +80699,7 @@
 	lex_ctx->input_offset = (duk_size_t) (p - lex_ctx->input);
 	lex_ctx->input_line = input_line;
 
-	DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_DECODE_FAILED);
+	DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED);
 }
 
 DUK_LOCAL void duk__advance_bytes(duk_lexer_ctx *lex_ctx, duk_small_uint_t count_bytes) {
@@ -79253,7 +80859,7 @@
 
  error_clipped:   /* clipped codepoint */
  error_encoding:  /* invalid codepoint encoding or codepoint */
-	DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_DECODE_FAILED);
+	DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_SOURCE_DECODE_FAILED);
 	return 0;
 }
 
@@ -79342,13 +80948,11 @@
  */
 
 DUK_LOCAL duk_hstring *duk__internbuffer(duk_lexer_ctx *lex_ctx, duk_idx_t valstack_idx) {
-	duk_context *ctx = (duk_context *) lex_ctx->thr;
-
 	DUK_ASSERT(valstack_idx == lex_ctx->slot1_idx || valstack_idx == lex_ctx->slot2_idx);
 
 	DUK_BW_PUSH_AS_STRING(lex_ctx->thr, &lex_ctx->bw);
-	duk_replace(ctx, valstack_idx);
-	return duk_known_hstring(ctx, valstack_idx);
+	duk_replace(lex_ctx->thr, valstack_idx);
+	return duk_known_hstring(lex_ctx->thr, valstack_idx);
 }
 
 /*
@@ -79436,7 +81040,7 @@
 	duk_small_int_t digits;  /* Initial value 2 or 4 for fixed length escapes, 0 for ES2015 \u{H+}. */
 	duk_codepoint_t escval;
 	duk_codepoint_t x;
-	duk_small_int_t adv;
+	duk_small_uint_t adv;
 
 	DUK_ASSERT(DUK__L0() == DUK_ASC_BACKSLASH);  /* caller responsibilities */
 	DUK_ASSERT(DUK__L1() == DUK_ASC_LC_X || DUK__L1() == DUK_ASC_LC_U);
@@ -79530,10 +81134,10 @@
  * RegExp octal escape parsing.  Window[0] must be the slash '\' and the first
  * digit must already be validated to be in [0-9] by the caller.
  */
-DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, duk_small_int_t *out_adv, duk_bool_t reject_annex_b) {
+DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, duk_small_uint_t *out_adv, duk_bool_t reject_annex_b) {
 	duk_codepoint_t cp;
 	duk_small_uint_t lookup_idx;
-	duk_small_int_t adv;
+	duk_small_uint_t adv;
 	duk_codepoint_t tmp;
 
 	DUK_ASSERT(out_adv != NULL);
@@ -79541,6 +81145,7 @@
 	DUK_ASSERT(DUK__LOOKUP(lex_ctx, 1) >= DUK_ASC_0 && DUK__LOOKUP(lex_ctx, 1) <= DUK_ASC_9);
 
 	cp = 0;
+	tmp = 0;
 	for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) {
 		DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp));
 		tmp = DUK__LOOKUP(lex_ctx, lookup_idx);
@@ -79589,7 +81194,7 @@
 
 /* XXX: move strict mode to lex_ctx? */
 DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token *out_token, duk_small_int_t quote, duk_bool_t strict_mode) {
-	duk_small_int_t adv;
+	duk_small_uint_t adv;
 
 	for (adv = 1 /* initial quote */ ;;) {
 		duk_codepoint_t x;
@@ -79818,7 +81423,7 @@
 	}
 
 	out_token->t = DUK_TOK_EOF;
-	out_token->t_nores = -1;  /* marker: copy t if not changed */
+	out_token->t_nores = DUK_TOK_INVALID;  /* marker: copy t if not changed */
 #if 0  /* not necessary to init, disabled for faster parsing */
 	out_token->num = DUK_DOUBLE_NAN;
 	out_token->str1 = NULL;
@@ -79833,8 +81438,8 @@
 	 * freed normally.
 	 */
 #if 0
-	duk_to_undefined((duk_context *) lex_ctx->thr, lex_ctx->slot1_idx);
-	duk_to_undefined((duk_context *) lex_ctx->thr, lex_ctx->slot2_idx);
+	duk_to_undefined(lex_ctx->thr, lex_ctx->slot1_idx);
+	duk_to_undefined(lex_ctx->thr, lex_ctx->slot2_idx);
 #endif
 
 	/* 'advtok' indicates how much to advance and which token id to assign
@@ -80083,7 +81688,7 @@
 #if defined(DUK_USE_HTML_COMMENTS)
 		if (DUK__L1() == DUK_ASC_EXCLAMATION && DUK__L2() == DUK_ASC_MINUS && DUK__L3() == DUK_ASC_MINUS) {
 			/*
-			 *  ES6: B.1.3, handle "<!--" SingleLineHTMLOpenComment
+			 *  ES2015: B.1.3, handle "<!--" SingleLineHTMLOpenComment
 			 */
 
 			/* DUK__ADVANCECHARS(lex_ctx, 4) would be correct here, but not necessary */
@@ -80148,7 +81753,7 @@
 #if defined(DUK_USE_HTML_COMMENTS)
 		if (got_lineterm && DUK__L1() == DUK_ASC_MINUS && DUK__L2() == DUK_ASC_RANGLE) {
 			/*
-			 *  ES6: B.1.3, handle "-->" SingleLineHTMLCloseComment
+			 *  ES2015: B.1.3, handle "-->" SingleLineHTMLCloseComment
 			 *  Only allowed:
 			 *  - on new line
 			 *  - preceded only by whitespace
@@ -80232,7 +81837,7 @@
 		DUK__INITBUFFER(lex_ctx);
 		duk__lexer_parse_string_literal(lex_ctx, out_token, x /*quote*/, strict_mode);
 		duk__internbuffer(lex_ctx, lex_ctx->slot1_idx);
-		out_token->str1 = duk_known_hstring((duk_context *) lex_ctx->thr, lex_ctx->slot1_idx);
+		out_token->str1 = duk_known_hstring(lex_ctx->thr, lex_ctx->slot1_idx);
 
 		DUK__INITBUFFER(lex_ctx);  /* free some memory */
 
@@ -80291,7 +81896,7 @@
 		 *  parsing the identifier.  This has little practical impact.
 		 */
 
-		duk_small_int_t i, i_end;
+		duk_small_uint_t i, i_end;
 		duk_bool_t first = 1;
 		duk_hstring *str;
 
@@ -80363,7 +81968,8 @@
 		advtok = DUK__ADVTOK(0, DUK_TOK_IDENTIFIER);
 		if (out_token->num_escapes == 0) {
 			for (i = DUK_STRIDX_START_RESERVED; i < i_end; i++) {
-				DUK_ASSERT(i >= 0 && i < DUK_HEAP_NUM_STRINGS);
+				DUK_ASSERT_DISABLE(i >= 0);  /* unsigned */
+				DUK_ASSERT(i < DUK_HEAP_NUM_STRINGS);
 				if (DUK_HTHREAD_GET_STRING(lex_ctx->thr, i) == str) {
 					advtok = DUK__ADVTOK(0, DUK_STRIDX_TO_TOK(i));
 					break;
@@ -80404,7 +82010,7 @@
 		                         */
 		duk_small_uint_t s2n_flags;
 		duk_codepoint_t y, z;
-		duk_small_uint_t s2n_radix = 10;
+		duk_small_int_t s2n_radix = 10;
 		duk_small_uint_t pre_adv = 0;
 
 		DUK__INITBUFFER(lex_ctx);
@@ -80510,13 +82116,13 @@
 			            DUK_S2N_FLAG_ALLOW_LEADING_ZERO;
 		}
 
-		duk_dup((duk_context *) lex_ctx->thr, lex_ctx->slot1_idx);
-		duk_numconv_parse((duk_context *) lex_ctx->thr, s2n_radix, s2n_flags);
-		val = duk_to_number_m1((duk_context *) lex_ctx->thr);
+		duk_dup(lex_ctx->thr, lex_ctx->slot1_idx);
+		duk_numconv_parse(lex_ctx->thr, s2n_radix, s2n_flags);
+		val = duk_to_number_m1(lex_ctx->thr);
 		if (DUK_ISNAN(val)) {
 			goto fail_number_literal;
 		}
-		duk_replace((duk_context *) lex_ctx->thr, lex_ctx->slot1_idx);  /* could also just pop? */
+		duk_replace(lex_ctx->thr, lex_ctx->slot1_idx);  /* could also just pop? */
 
 		DUK__INITBUFFER(lex_ctx);  /* free some memory */
 
@@ -80546,7 +82152,7 @@
 
 	DUK__ADVANCEBYTES(lex_ctx, advtok >> 8);
 	out_token->t = advtok & 0xff;
-	if (out_token->t_nores < 0) {
+	if (out_token->t_nores == DUK_TOK_INVALID) {
 		out_token->t_nores = out_token->t;
 	}
 	out_token->lineterm = got_lineterm;
@@ -80606,7 +82212,7 @@
  */
 
 DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token *out_token) {
-	duk_small_int_t advtok = 0;  /* init is unnecessary but suppresses "may be used uninitialized" warnings */
+	duk_small_uint_t advtok = 0;  /* init is unnecessary but suppresses "may be used uninitialized" warnings */
 	duk_codepoint_t x, y;
 
 	if (++lex_ctx->token_count >= lex_ctx->token_limit) {
@@ -80671,8 +82277,8 @@
 	}
 	case DUK_ASC_LCURLY: {
 		/* Production allows 'DecimalDigits', including leading zeroes */
-		duk_uint_fast32_t val1 = 0;
-		duk_uint_fast32_t val2 = DUK_RE_QUANTIFIER_INFINITE;
+		duk_uint32_t val1 = 0;
+		duk_uint32_t val2 = DUK_RE_QUANTIFIER_INFINITE;
 		duk_small_int_t digits = 0;
 #if defined(DUK_USE_ES6_REGEXP_SYNTAX)
 		duk_lexer_point lex_pt;
@@ -80688,7 +82294,7 @@
 			x = DUK__L0();
 			if (DUK__ISDIGIT(x)) {
 				digits++;
-				val1 = val1 * 10 + (duk_uint_fast32_t) duk__hexval(x);
+				val1 = val1 * 10 + (duk_uint32_t) duk__hexval(x);
 			} else if (x == DUK_ASC_COMMA) {
 				if (digits > DUK__MAX_RE_QUANT_DIGITS) {
 					goto invalid_quantifier;
@@ -80784,7 +82390,7 @@
 			x = DUK__L2();
 			if ((x >= DUK_ASC_LC_A && x <= DUK_ASC_LC_Z) ||
 			    (x >= DUK_ASC_UC_A && x <= DUK_ASC_UC_Z)) {
-				out_token->num = (x % 32);
+				out_token->num = (duk_uint32_t) (x % 32);
 				advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_CHAR);
 			} else {
 				goto fail_escape;
@@ -80795,7 +82401,7 @@
 			 * here.  The \u{H+} is only allowed in Unicode mode
 			 * which we don't support yet.
 			 */
-			out_token->num = duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/);
+			out_token->num = (duk_uint32_t) duk__lexer_parse_escape(lex_ctx, 0 /*allow_es6*/);
 			advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_CHAR);
 		} else if (y == DUK_ASC_LC_D) {
 			advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_DIGIT);
@@ -80819,7 +82425,7 @@
 				advtok = DUK__ADVTOK(2, DUK_RETOK_ATOM_CHAR);
 			} else {
 				/* XXX: shared parsing? */
-				duk_uint_fast32_t val = 0;
+				duk_uint32_t val = 0;
 				duk_small_int_t i;
 				for (i = 0; ; i++) {
 					if (i >= DUK__MAX_RE_DECESC_DIGITS) {
@@ -80830,7 +82436,7 @@
 					if (!DUK__ISDIGIT(x)) {
 						break;
 					}
-					val = val * 10 + (duk_uint_fast32_t) duk__hexval(x);
+					val = val * 10 + (duk_uint32_t) duk__hexval(x);
 				}
 				/* DUK__L0() cannot be a digit, because the loop doesn't terminate if it is */
 				advtok = DUK__ADVTOK(0, DUK_RETOK_ATOM_BACKREFERENCE);
@@ -80856,7 +82462,7 @@
 			 * test-regexp-identity-escape-dollar.js.
 			 */
 #endif  /* DUK_USE_ES6_REGEXP_SYNTAX */
-			out_token->num = y;
+			out_token->num = (duk_uint32_t) y;
 		} else {
 			goto fail_escape;
 		}
@@ -80923,7 +82529,7 @@
 	default: {
 		/* PatternCharacter, all excluded characters are matched by cases above */
 		advtok = DUK__ADVTOK(1, DUK_RETOK_ATOM_CHAR);
-		out_token->num = x;
+		out_token->num = (duk_uint32_t) x;
 		break;
 	}
 	}
@@ -81002,7 +82608,7 @@
 	duk_codepoint_t ch;
 	duk_codepoint_t x;
 	duk_bool_t dash = 0;
-	duk_small_int_t adv = 0;
+	duk_small_uint_t adv = 0;
 
 	DUK_DD(DUK_DDPRINT("parsing regexp ranges"));
 
@@ -81024,7 +82630,7 @@
 			DUK__ADVANCECHARS(lex_ctx, 1);  /* eat ']' before finishing */
 			break;
 		} else if (x == DUK_ASC_MINUS) {
-			if (start >= 0 && !dash && DUK__L0() != DUK_ASC_RBRACKET) {
+			if (start >= 0 && !dash && DUK__L1() != DUK_ASC_RBRACKET) {
 				/* '-' as a range indicator */
 				dash = 1;
 				continue;
@@ -81377,7 +82983,7 @@
 	if (n == 0) {
 		return;
 	}
-	DUK_MEMCPY((void *) x->v, (const void *) y->v, (size_t) (sizeof(duk_uint32_t) * n));
+	DUK_MEMCPY((void *) x->v, (const void *) y->v, (size_t) (sizeof(duk_uint32_t) * (size_t) n));
 }
 
 DUK_LOCAL void duk__bi_set_small(duk__bigint *x, duk_uint32_t v) {
@@ -81562,7 +83168,7 @@
 			tz = 0;
 		}
 		tmp = (duk_int64_t) ty - (duk_int64_t) tz + tmp;
-		x->v[i] = (duk_uint32_t) (tmp & 0xffffffffUL);
+		x->v[i] = (duk_uint32_t) ((duk_uint64_t) tmp & 0xffffffffUL);
 		tmp = tmp >> 32;  /* 0 or -1 */
 	}
 	DUK_ASSERT(tmp == 0);
@@ -81653,7 +83259,7 @@
 		return;
 	}
 
-	DUK_MEMZERO((void *) x->v, (size_t) (sizeof(duk_uint32_t) * nx));
+	DUK_MEMZERO((void *) x->v, (size_t) (sizeof(duk_uint32_t) * (size_t) nx));
 	x->n = nx;
 
 	nz = z->n;
@@ -81803,7 +83409,7 @@
 	n = (y / 32) + 1;
 	DUK_ASSERT(n > 0);
 	r = y % 32;
-	DUK_MEMZERO((void *) x->v, sizeof(duk_uint32_t) * n);
+	DUK_MEMZERO((void *) x->v, sizeof(duk_uint32_t) * (size_t) n);
 	x->n = n;
 	x->v[n - 1] = (((duk_uint32_t) 1) << r);
 }
@@ -81826,7 +83432,7 @@
 	DUK_DDD(DUK_DDDPRINT("exp_small: b=%ld, y=%ld", (long) b, (long) y));
 
 	duk__bi_set_small(x, 1);
-	duk__bi_set_small(t1, b);
+	duk__bi_set_small(t1, (duk_uint32_t) b);
 	for (;;) {
 		/* Loop structure ensures that we don't compute t1^2 unnecessarily
 		 * on the final round, as that might create a bignum exceeding the
@@ -81867,10 +83473,10 @@
  */
 
 /* Maximum number of digits generated. */
-#define DUK__MAX_OUTPUT_DIGITS          1040  /* (Number.MAX_VALUE).toString(2).length == 1024, + spare */
+#define DUK__MAX_OUTPUT_DIGITS          1040  /* (Number.MAX_VALUE).toString(2).length == 1024, + slack */
 
 /* Maximum number of characters in formatted value. */
-#define DUK__MAX_FORMATTED_LENGTH       1040  /* (-Number.MAX_VALUE).toString(2).length == 1025, + spare */
+#define DUK__MAX_FORMATTED_LENGTH       1040  /* (-Number.MAX_VALUE).toString(2).length == 1025, + slack */
 
 /* Number and (minimum) size of bigints in the nc_ctx structure. */
 #define DUK__NUMCONV_CTX_NUM_BIGINTS    7
@@ -81915,7 +83521,7 @@
 	duk_uint8_t *p;
 	duk_size_t len;
 	duk_small_int_t dig;
-	duk_small_int_t t;
+	duk_uint32_t t;
 
 	DUK_ASSERT(radix >= 2 && radix <= 36);
 
@@ -81926,8 +83532,8 @@
 
 	p = buf + 32;
 	for (;;) {
-		t = x / radix;
-		dig = x - t * radix;
+		t = x / (duk_uint32_t) radix;
+		dig = (duk_small_int_t) (x - t * (duk_uint32_t) radix);
 		x = t;
 
 		DUK_ASSERT(dig >= 0 && dig < 36);
@@ -82007,10 +83613,10 @@
 			                     "unequal gaps"));
 
 			duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2);  /* mm <- b^e */
-			duk__bi_mul_small(&nc_ctx->mp, &nc_ctx->mm, nc_ctx->b);  /* mp <- b^(e+1) */
+			duk__bi_mul_small(&nc_ctx->mp, &nc_ctx->mm, (duk_uint32_t) nc_ctx->b);           /* mp <- b^(e+1) */
 			duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2);
-			duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp);       /* r <- (2 * f) * b^(e+1) */
-			duk__bi_set_small(&nc_ctx->s, nc_ctx->b * 2);            /* s <- 2 * b */
+			duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp);              /* r <- (2 * f) * b^(e+1) */
+			duk__bi_set_small(&nc_ctx->s, (duk_uint32_t) (nc_ctx->b * 2));  /* s <- 2 * b */
 			nc_ctx->unequal_gaps = 1;
 		} else {
 			/* (>= e 0) AND (not (= f (expt b (- p 1))))
@@ -82056,7 +83662,7 @@
 			                     "lowest mantissa for this exponent -> "
 			                     "unequal gaps"));
 
-			duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, nc_ctx->b * 2);  /* r <- (2 * b) * f */
+			duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, (duk_uint32_t) (nc_ctx->b * 2));  /* r <- (2 * b) * f */
 			duk__bi_exp_small(&nc_ctx->t1, nc_ctx->b, 1 - nc_ctx->e, &nc_ctx->s, &nc_ctx->t2);  /* NB: use 's' as temp on purpose */
 			duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2);             /* s <- b^(1-e) * 2 */
 			duk__bi_set_small(&nc_ctx->mp, 2);
@@ -82135,7 +83741,7 @@
 			 * k <- (+ k 1)
 			 */
 
-			duk__bi_mul_small_copy(&nc_ctx->s, nc_ctx->B, &nc_ctx->t1);
+			duk__bi_mul_small_copy(&nc_ctx->s, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
 			k++;
 		} else {
 			break;
@@ -82155,7 +83761,7 @@
 		DUK__BI_PRINT("m-", &nc_ctx->mm);
 
 		duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 = (+ r m+) */
-		duk__bi_mul_small(&nc_ctx->t2, &nc_ctx->t1, nc_ctx->B);   /* t2 = (* (+ r m+) B) */
+		duk__bi_mul_small(&nc_ctx->t2, &nc_ctx->t1, (duk_uint32_t) nc_ctx->B);   /* t2 = (* (+ r m+) B) */
 		if (duk__bi_compare(&nc_ctx->t2, &nc_ctx->s) <= (nc_ctx->high_ok ? -1 : 0)) {
 			DUK_DDD(DUK_DDDPRINT("k is too high"));
 			/* r <- (* r B)
@@ -82164,11 +83770,11 @@
 			 * m- <- (* m- B)
 			 * k <- (- k 1)
 			 */
-			duk__bi_mul_small_copy(&nc_ctx->r, nc_ctx->B, &nc_ctx->t1);
-			duk__bi_mul_small_copy(&nc_ctx->mp, nc_ctx->B, &nc_ctx->t1);
+			duk__bi_mul_small_copy(&nc_ctx->r, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
+			duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
 			if (nc_ctx->unequal_gaps) {
 				DUK_DDD(DUK_DDDPRINT("m+ != m- -> need to update m- too"));
-				duk__bi_mul_small_copy(&nc_ctx->mm, nc_ctx->B, &nc_ctx->t1);
+				duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
 			}
 			k--;
 		} else {
@@ -82224,7 +83830,7 @@
 		DUK__BI_PRINT("m-", &nc_ctx->mm);
 
 		/* (quotient-remainder (* r B) s) using a dummy subtraction loop */
-		duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, nc_ctx->B);       /* t1 <- (* r B) */
+		duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, (duk_uint32_t) nc_ctx->B);       /* t1 <- (* r B) */
 		d = 0;
 		for (;;) {
 			if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) {
@@ -82238,8 +83844,8 @@
 		DUK_DDD(DUK_DDDPRINT("-> d(quot)=%ld", (long) d));
 		DUK__BI_PRINT("r(rem)", &nc_ctx->r);
 
-		duk__bi_mul_small_copy(&nc_ctx->mp, nc_ctx->B, &nc_ctx->t2); /* m+ <- (* m+ B) */
-		duk__bi_mul_small_copy(&nc_ctx->mm, nc_ctx->B, &nc_ctx->t2); /* m- <- (* m- B) */
+		duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m+ <- (* m+ B) */
+		duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m- <- (* m- B) */
 		DUK__BI_PRINT("mp(upd)", &nc_ctx->mp);
 		DUK__BI_PRINT("mm(upd)", &nc_ctx->mm);
 
@@ -82409,7 +84015,7 @@
 				DUK_DDD(DUK_DDDPRINT("carry propagated to first digit -> special case handling"));
 				DUK_MEMMOVE((void *) (&nc_ctx->digits[1]),
 				            (const void *) (&nc_ctx->digits[0]),
-				            (size_t) (sizeof(char) * nc_ctx->count));
+				            (size_t) (sizeof(char) * (size_t) nc_ctx->count));
 				nc_ctx->digits[0] = 1;  /* don't increase 'count' */
 				nc_ctx->k++;  /* position of highest digit changed */
 				nc_ctx->count++;  /* number of digits changed */
@@ -82438,11 +84044,11 @@
 #define DUK__NO_EXP  (65536)  /* arbitrary marker, outside valid exp range */
 
 DUK_LOCAL void duk__dragon4_convert_and_push(duk__numconv_stringify_ctx *nc_ctx,
-                                          duk_context *ctx,
-                                          duk_small_int_t radix,
-                                          duk_small_int_t digits,
-                                          duk_small_uint_t flags,
-                                          duk_small_int_t neg) {
+                                             duk_hthread *thr,
+                                             duk_small_int_t radix,
+                                             duk_small_int_t digits,
+                                             duk_small_uint_t flags,
+                                             duk_small_int_t neg) {
 	duk_small_int_t k;
 	duk_small_int_t pos, pos_end;
 	duk_small_int_t expt;
@@ -82578,7 +84184,7 @@
 		q += len;
 	}
 
-	duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf));
+	duk_push_lstring(thr, (const char *) buf, (size_t) (q - buf));
 }
 
 /*
@@ -82757,7 +84363,7 @@
 	                     (unsigned long) DUK_DBLUNION_GET_LOW32(&u)));
 
 	DUK_ASSERT(expt >= 0 && expt <= 0x7ffL);
-	t += expt << 20;
+	t += ((duk_uint32_t) expt) << 20;
 #if 0  /* caller handles sign change */
 	if (negative) {
 		t |= 0x80000000U;
@@ -82779,7 +84385,7 @@
  *  Output: [ string ]
  */
 
-DUK_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) {
+DUK_INTERNAL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) {
 	duk_double_t x;
 	duk_small_int_t c;
 	duk_small_int_t neg;
@@ -82787,8 +84393,8 @@
 	duk__numconv_stringify_ctx nc_ctx_alloc;  /* large context; around 2kB now */
 	duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;
 
-	x = (duk_double_t) duk_require_number(ctx, -1);
-	duk_pop(ctx);
+	x = (duk_double_t) duk_require_number(thr, -1);
+	duk_pop(thr);
 
 	/*
 	 *  Handle special cases (NaN, infinity, zero).
@@ -82806,15 +84412,15 @@
 	DUK_ASSERT(c == DUK_FP_NAN || DUK_SIGNBIT((double) x) == 0);
 
 	if (c == DUK_FP_NAN) {
-		duk_push_hstring_stridx(ctx, DUK_STRIDX_NAN);
+		duk_push_hstring_stridx(thr, DUK_STRIDX_NAN);
 		return;
 	} else if (c == DUK_FP_INFINITE) {
 		if (neg) {
 			/* -Infinity */
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_MINUS_INFINITY);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_INFINITY);
 		} else {
 			/* Infinity */
-			duk_push_hstring_stridx(ctx, DUK_STRIDX_INFINITY);
+			duk_push_hstring_stridx(thr, DUK_STRIDX_INFINITY);
 		}
 		return;
 	} else if (c == DUK_FP_ZERO) {
@@ -82848,7 +84454,7 @@
 			*p++ = (duk_uint8_t) '-';
 		}
 		p += duk__dragon4_format_uint32(p, uval, radix);
-		duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (p - buf));
+		duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
 		return;
 	}
 
@@ -82907,7 +84513,7 @@
 		}
 		DUK_DDD(DUK_DDDPRINT("count=%ld", (long) count));
 		DUK_ASSERT(count >= 1);
-		DUK_MEMZERO((void *) nc_ctx->digits, count);
+		DUK_MEMZERO((void *) nc_ctx->digits, (size_t) count);
 		nc_ctx->count = count;
 		nc_ctx->k = 1;  /* 0.000... */
 		neg = 0;
@@ -82969,7 +84575,7 @@
 		 */
 	}
 
-	duk__dragon4_convert_and_push(nc_ctx, ctx, radix, digits, flags, neg);
+	duk__dragon4_convert_and_push(nc_ctx, thr, radix, digits, flags, neg);
 }
 
 /*
@@ -82982,8 +84588,7 @@
  *  fails due to an internal error, an InternalError is thrown.
  */
 
-DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk_small_uint_t flags) {
-	duk_hthread *thr = (duk_hthread *) ctx;
+DUK_INTERNAL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) {
 	duk__numconv_stringify_ctx nc_ctx_alloc;  /* large context; around 2kB now */
 	duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;
 	duk_double_t res;
@@ -83003,7 +84608,7 @@
 	duk_small_int_t ch;
 
 	DUK_DDD(DUK_DDDPRINT("parse number: %!T, radix=%ld, flags=0x%08lx",
-	                     (duk_tval *) duk_get_tval(ctx, -1),
+	                     (duk_tval *) duk_get_tval(thr, -1),
 	                     (long) radix, (unsigned long) flags));
 
 	DUK_ASSERT(radix >= 2 && radix <= 36);
@@ -83035,9 +84640,9 @@
 		 * sometimes not.  After white space trimming, all valid input
 		 * characters are pure ASCII.
 		 */
-		duk_trim(ctx, -1);
-	}
-	h_str = duk_require_hstring(ctx, -1);
+		duk_trim(thr, -1);
+	}
+	h_str = duk_require_hstring(thr, -1);
 	DUK_ASSERT(h_str != NULL);
 	p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_str);
 
@@ -83276,8 +84881,8 @@
 					/* XXX: join these ops (multiply-accumulate), but only if
 					 * code footprint decreases.
 					 */
-					duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, radix);
-					duk__bi_add_small(&nc_ctx->f, &nc_ctx->t1, dig);
+					duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, (duk_uint32_t) radix);
+					duk__bi_add_small(&nc_ctx->f, &nc_ctx->t1, (duk_uint32_t) dig);
 					dig_prec++;
 				}
 			} else {
@@ -83399,7 +85004,7 @@
 		 * have enough (apparent) precision to work with.
 		 */
 		DUK_DDD(DUK_DDDPRINT("dig_prec=%ld, pad significand with zero", (long) dig_prec));
-		duk__bi_mul_small_copy(&nc_ctx->f, radix, &nc_ctx->t1);
+		duk__bi_mul_small_copy(&nc_ctx->f, (duk_uint32_t) radix, &nc_ctx->t1);
 		DUK__BI_PRINT("f", &nc_ctx->f);
 		expt--;
 		dig_prec++;
@@ -83487,15 +85092,15 @@
 	if (neg) {
 		res = -res;
 	}
-	duk_pop(ctx);
-	duk_push_number(ctx, (double) res);
-	DUK_DDD(DUK_DDDPRINT("result: %!T", (duk_tval *) duk_get_tval(ctx, -1)));
+	duk_pop(thr);
+	duk_push_number(thr, (double) res);
+	DUK_DDD(DUK_DDDPRINT("result: %!T", (duk_tval *) duk_get_tval(thr, -1)));
 	return;
 
  parse_fail:
 	DUK_DDD(DUK_DDDPRINT("parse failed"));
-	duk_pop(ctx);
-	duk_push_nan(ctx);
+	duk_pop(thr);
+	duk_push_nan(thr);
 	return;
 
  parse_explimit_error:
@@ -83605,7 +85210,8 @@
 	duk_small_int_t len;
 
 	len = duk_unicode_encode_xutf8((duk_ucodepoint_t) x, buf);
-	DUK_BW_INSERT_ENSURE_BYTES(re_ctx->thr, &re_ctx->bw, offset, buf, len);
+	DUK_ASSERT(len >= 0);
+	DUK_BW_INSERT_ENSURE_BYTES(re_ctx->thr, &re_ctx->bw, offset, buf, (duk_size_t) len);
 	return (duk_uint32_t) len;
 }
 
@@ -83747,71 +85353,213 @@
 /*
  *  duk_re_range_callback for generating character class ranges.
  *
- *  When ignoreCase is false, the range is simply emitted as is.
- *  We don't, for instance, eliminate duplicates or overlapping
- *  ranges in a character class.
- *
- *  When ignoreCase is true, the range needs to be normalized through
- *  canonicalization.  Unfortunately a canonicalized version of a
- *  continuous range is not necessarily continuous (e.g. [x-{] is
- *  continuous but [X-{] is not).  The current algorithm creates the
- *  canonicalized range(s) space efficiently at the cost of compile
- *  time execution time (see doc/regexp.rst for discussion).
- *
- *  Note that the ctx->nranges is a context-wide temporary value
- *  (this is OK because there cannot be multiple character classes
- *  being parsed simultaneously).
- */
-
-DUK_LOCAL void duk__generate_ranges(void *userdata, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct) {
+ *  When ignoreCase is false, the range is simply emitted as is.  We don't,
+ *  for instance, eliminate duplicates or overlapping ranges in a character
+ *  class.
+ *
+ *  When ignoreCase is true but the 'direct' flag is set, the caller knows
+ *  that the range canonicalizes to itself for case insensitive matching,
+ *  so the range is emitted as is.  This is mainly useful for built-in ranges
+ *  like \W.
+ *
+ *  Otherwise, when ignoreCase is true, the range needs to be normalized
+ *  through canonicalization.  Unfortunately a canonicalized version of a
+ *  continuous range is not necessarily continuous (e.g. [x-{] is continuous
+ *  but [X-{] is not).  As a result, a single input range may expand to a lot
+ *  of output ranges.  The current algorithm creates the canonicalized ranges
+ *  footprint efficiently at the cost of compile time execution time; see
+ *  doc/regexp.rst for discussion, and some more details below.
+ *
+ *  Note that the ctx->nranges is a context-wide temporary value.  This is OK
+ *  because there cannot be multiple character classes being parsed
+ *  simultaneously.
+ *
+ *  More detail on canonicalization:
+ *
+ *  Conceptually, a range is canonicalized by scanning the entire range,
+ *  normalizing each codepoint by converting it to uppercase, and generating
+ *  a set of result ranges.
+ *
+ *  Ideally a minimal set of output ranges would be emitted by merging all
+ *  possible ranges even if they're emitted out of sequence.  Because the
+ *  input string is also case normalized during matching, some codepoints
+ *  never occur at runtime; these "don't care" codepoints can be included or
+ *  excluded from ranges when merging/optimizing ranges.
+ *
+ *  The current algorithm does not do optimal range merging.  Rather, output
+ *  codepoints are generated in sequence, and when the output codepoints are
+ *  continuous (CP, CP+1, CP+2, ...), they are merged locally into as large a
+ *  range as possible.  A small canonicalization bitmap is used to reduce
+ *  actual codepoint canonicalizations which are quite slow at present.  The
+ *  bitmap provides a "codepoint block is continuous with respect to
+ *  canonicalization" for N-codepoint blocks.  This allows blocks to be
+ *  skipped quickly.
+ *
+ *  There are a number of shortcomings and future work here:
+ *
+ *    - Individual codepoint normalizations are slow because they involve
+ *      walking bit-packed rules without a lookup index.
+ *
+ *    - The conceptual algorithm needs to canonicalize every codepoint in the
+ *      input range to figure out the output range(s).  Even with the small
+ *      canonicalization bitmap the algorithm runs quite slowly for worst case
+ *      inputs.  There are many data structure alternatives to improve this.
+ *
+ *    - While the current algorithm generates maximal output ranges when the
+ *      output codepoints are emitted linearly, output ranges are not sorted or
+ *      merged otherwise.  In the worst case a lot of ranges are emitted when
+ *      most of the ranges could be merged.  In this process one could take
+ *      advantage of "don't care" codepoints, which are never matched against at
+ *      runtime due to canonicalization of input codepoints before comparison,
+ *      to merge otherwise discontinuous output ranges.
+ *
+ *    - The runtime data structure is just a linear list of ranges to match
+ *      against.  This can be quite slow if there are a lot of output ranges.
+ *      There are various ways to make matching against the ranges faster,
+ *      e.g. sorting the ranges and using a binary search; skip lists; tree
+ *      based representations; full or approximate codepoint bitmaps, etc.
+ *
+ *    - Only BMP is supported, codepoints above BMP are assumed to canonicalize
+ *      to themselves.  For now this is one place where we don't want to
+ *      support chars outside the BMP, because the exhaustive search would be
+ *      massively larger.  It would be possible to support non-BMP with a
+ *      different algorithm, or perhaps doing case normalization only at match
+ *      time.
+ */
+
+DUK_LOCAL void duk__regexp_emit_range(duk_re_compiler_ctx *re_ctx, duk_codepoint_t r1, duk_codepoint_t r2) {
+	DUK_ASSERT(r2 >= r1);
+	duk__append_u32(re_ctx, (duk_uint32_t) r1);
+	duk__append_u32(re_ctx, (duk_uint32_t) r2);
+	re_ctx->nranges++;
+}
+
+#if defined(DUK_USE_REGEXP_CANON_BITMAP)
+/* Find next canonicalization discontinuity (conservative estimate) starting
+ * from 'start', not exceeding 'end'.  If continuity is fine up to 'end'
+ * inclusive, returns end.  Minimum possible return value is start.
+ */
+DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) {
+	duk_uint_t start_blk;
+	duk_uint_t end_blk;
+	duk_uint_t blk;
+	duk_uint_t offset;
+	duk_uint8_t mask;
+
+	/* Inclusive block range. */
+	DUK_ASSERT(start >= 0);
+	DUK_ASSERT(end >= 0);
+	DUK_ASSERT(end >= start);
+	start_blk = (duk_uint_t) (start >> DUK_CANON_BITMAP_BLKSHIFT);
+	end_blk = (duk_uint_t) (end >> DUK_CANON_BITMAP_BLKSHIFT);
+
+	for (blk = start_blk; blk <= end_blk; blk++) {
+		offset = blk >> 3;
+		mask = 1U << (blk & 0x07);
+		if (offset >= sizeof(duk_unicode_re_canon_bitmap)) {
+			/* Reached non-BMP range which is assumed continuous. */
+			return end;
+		}
+		DUK_ASSERT(offset < sizeof(duk_unicode_re_canon_bitmap));
+		if ((duk_unicode_re_canon_bitmap[offset] & mask) == 0) {
+			/* Block is discontinuous, continuity is guaranteed
+			 * only up to end of previous block (+1 for exclusive
+			 * return value => start of current block).  Start
+			 * block requires special handling.
+			 */
+			if (blk > start_blk) {
+				return (duk_codepoint_t) (blk << DUK_CANON_BITMAP_BLKSHIFT);
+			} else {
+				return start;
+			}
+		}
+	}
+	DUK_ASSERT(blk == end_blk + 1);  /* Reached end block which is continuous. */
+	return end;
+}
+#else  /* DUK_USE_REGEXP_CANON_BITMAP */
+DUK_LOCAL duk_codepoint_t duk__re_canon_next_discontinuity(duk_codepoint_t start, duk_codepoint_t end) {
+	DUK_ASSERT(start >= 0);
+	DUK_ASSERT(end >= 0);
+	DUK_ASSERT(end >= start);
+	if (start >= 0x10000) {
+		/* Even without the bitmap, treat non-BMP as continuous. */
+		return end;
+	}
+	return start;
+}
+#endif  /* DUK_USE_REGEXP_CANON_BITMAP */
+
+DUK_LOCAL void duk__regexp_generate_ranges(void *userdata, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct) {
 	duk_re_compiler_ctx *re_ctx = (duk_re_compiler_ctx *) userdata;
-
-	DUK_DD(DUK_DDPRINT("duk__generate_ranges(): re_ctx=%p, range=[%ld,%ld] direct=%ld",
+	duk_codepoint_t r_start;
+	duk_codepoint_t r_end;
+	duk_codepoint_t i;
+	duk_codepoint_t t;
+	duk_codepoint_t r_disc;
+
+	DUK_DD(DUK_DDPRINT("duk__regexp_generate_ranges(): re_ctx=%p, range=[%ld,%ld] direct=%ld",
 	                   (void *) re_ctx, (long) r1, (long) r2, (long) direct));
 
-	if (!direct && (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE)) {
-		/*
-		 *  Canonicalize a range, generating result ranges as necessary.
-		 *  Needs to exhaustively scan the entire range (at most 65536
-		 *  code points).  If 'direct' is set, caller (lexer) has ensured
-		 *  that the range is already canonicalization compatible (this
-		 *  is used to avoid unnecessary canonicalization of built-in
-		 *  ranges like \W, which are not affected by canonicalization).
-		 *
-		 *  NOTE: here is one place where we don't want to support chars
-		 *  outside the BMP, because the exhaustive search would be
-		 *  massively larger.
-		 */
-
-		duk_codepoint_t i;
-		duk_codepoint_t t;
-		duk_codepoint_t r_start, r_end;
-
-		r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1);
-		r_end = r_start;
-		for (i = r1 + 1; i <= r2; i++) {
-			t = duk_unicode_re_canonicalize_char(re_ctx->thr, i);
-			if (t == r_end + 1) {
-				r_end = t;
-			} else {
-				DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
-				duk__append_u32(re_ctx, (duk_uint32_t) r_start);
-				duk__append_u32(re_ctx, (duk_uint32_t) r_end);
-				re_ctx->nranges++;
-				r_start = t;
-				r_end = t;
-			}
-		}
-		DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
-		duk__append_u32(re_ctx, (duk_uint32_t) r_start);
-		duk__append_u32(re_ctx, (duk_uint32_t) r_end);
-		re_ctx->nranges++;
-	} else {
-		DUK_DD(DUK_DDPRINT("direct, emit range: [%ld,%ld]", (long) r1, (long) r2));
-		duk__append_u32(re_ctx, (duk_uint32_t) r1);
-		duk__append_u32(re_ctx, (duk_uint32_t) r2);
-		re_ctx->nranges++;
-	}
+	DUK_ASSERT(r2 >= r1);  /* SyntaxError for out of order range. */
+
+	if (direct || (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) == 0) {
+		DUK_DD(DUK_DDPRINT("direct or not case sensitive, emit range: [%ld,%ld]", (long) r1, (long) r2));
+		duk__regexp_emit_range(re_ctx, r1, r2);
+		return;
+	}
+
+	DUK_DD(DUK_DDPRINT("case sensitive, process range: [%ld,%ld]", (long) r1, (long) r2));
+
+	r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1);
+	r_end = r_start;
+
+	for (i = r1 + 1; i <= r2;) {
+		/* Input codepoint space processed up to i-1, and
+		 * current range in r_{start,end} is up-to-date
+		 * (inclusive) and may either break or continue.
+		 */
+		r_disc = duk__re_canon_next_discontinuity(i, r2);
+		DUK_ASSERT(r_disc >= i);
+		DUK_ASSERT(r_disc <= r2);
+
+		r_end += r_disc - i;  /* May be zero. */
+		t = duk_unicode_re_canonicalize_char(re_ctx->thr, r_disc);
+		if (t == r_end + 1) {
+			/* Not actually a discontinuity, continue range
+			 * to r_disc and recheck.
+			 */
+			r_end = t;
+		} else {
+			duk__regexp_emit_range(re_ctx, r_start, r_end);
+			r_start = t;
+			r_end = t;
+		}
+		i = r_disc + 1;  /* Guarantees progress. */
+	}
+	duk__regexp_emit_range(re_ctx, r_start, r_end);
+
+#if 0  /* Exhaustive search, very slow. */
+	r_start = duk_unicode_re_canonicalize_char(re_ctx->thr, r1);
+	r_end = r_start;
+	for (i = r1 + 1; i <= r2; i++) {
+		t = duk_unicode_re_canonicalize_char(re_ctx->thr, i);
+		if (t == r_end + 1) {
+			r_end = t;
+		} else {
+			DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
+			duk__append_u32(re_ctx, (duk_uint32_t) r_start);
+			duk__append_u32(re_ctx, (duk_uint32_t) r_end);
+			re_ctx->nranges++;
+			r_start = t;
+			r_end = t;
+		}
+	}
+	DUK_DD(DUK_DDPRINT("canonicalized, emit range: [%ld,%ld]", (long) r_start, (long) r_end));
+	duk__append_u32(re_ctx, (duk_uint32_t) r_start);
+	duk__append_u32(re_ctx, (duk_uint32_t) r_end);
+	re_ctx->nranges++;
+#endif
 }
 
 /*
@@ -83946,21 +85694,21 @@
 				duk_uint32_t offset;
 
 				DUK_ASSERT(unpatched_disjunction_split >= 0);
-				offset = unpatched_disjunction_jump;
+				offset = (duk_uint32_t) unpatched_disjunction_jump;
 				offset += duk__insert_jump_offset(re_ctx,
 				                                  offset,
 				                                  (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
 				/* offset is now target of the pending split (right after jump) */
 				duk__insert_jump_offset(re_ctx,
-				                        unpatched_disjunction_split,
-				                        offset - unpatched_disjunction_split);
+				                        (duk_uint32_t) unpatched_disjunction_split,
+				                        (duk_int32_t) offset - unpatched_disjunction_split);
 			}
 
 			/* add a new pending split to the beginning of the entire disjunction */
 			(void) duk__insert_u32(re_ctx,
 			                       entry_offset,
 			                       DUK_REOP_SPLIT1);   /* prefer direct execution */
-			unpatched_disjunction_split = entry_offset + 1;   /* +1 for opcode */
+			unpatched_disjunction_split = (duk_int32_t) (entry_offset + 1);   /* +1 for opcode */
 
 			/* add a new pending match jump for latest finished alternative */
 			duk__append_reop(re_ctx, DUK_REOP_JUMP);
@@ -84007,14 +85755,14 @@
 				}
 
 				duk__append_reop(re_ctx, DUK_REOP_MATCH);   /* complete 'sub atom' */
-				atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - atom_start_offset);
-
-				offset = atom_start_offset;
+				atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (duk_size_t) atom_start_offset);
+
+				offset = (duk_uint32_t) atom_start_offset;
 				if (re_ctx->curr_token.greedy) {
 					offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQGREEDY);
 					offset += duk__insert_u32(re_ctx, offset, qmin);
 					offset += duk__insert_u32(re_ctx, offset, qmax);
-					offset += duk__insert_u32(re_ctx, offset, atom_char_length);
+					offset += duk__insert_u32(re_ctx, offset, (duk_uint32_t) atom_char_length);
 					offset += duk__insert_jump_offset(re_ctx, offset, atom_code_length);
 				} else {
 					offset += duk__insert_u32(re_ctx, offset, DUK_REOP_SQMINIMAL);
@@ -84054,9 +85802,9 @@
 					                     (long) atom_start_captures, (long) re_ctx->captures));
 
 					/* insert (DUK_REOP_WIPERANGE, start, count) in reverse order so the order ends up right */
-					duk__insert_u32(re_ctx, atom_start_offset, (re_ctx->captures - atom_start_captures) * 2);
-					duk__insert_u32(re_ctx, atom_start_offset, (atom_start_captures + 1) * 2);
-					duk__insert_u32(re_ctx, atom_start_offset, DUK_REOP_WIPERANGE);
+					duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, (re_ctx->captures - atom_start_captures) * 2U);
+					duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, (atom_start_captures + 1) * 2);
+					duk__insert_u32(re_ctx, (duk_uint32_t) atom_start_offset, DUK_REOP_WIPERANGE);
 				} else {
 					DUK_DDD(DUK_DDDPRINT("no need to wipe captures: atom_start_captures == re_ctx->captures == %ld",
 					                     (long) atom_start_captures));
@@ -84068,7 +85816,7 @@
 				tmp_qmin = re_ctx->curr_token.qmin;
 				tmp_qmax = re_ctx->curr_token.qmax;
 				while (tmp_qmin > 0) {
-					duk__append_slice(re_ctx, atom_start_offset, atom_code_length);
+					duk__append_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
 					tmp_qmin--;
 					if (tmp_qmax != DUK_RE_QUANTIFIER_INFINITE) {
 						tmp_qmax--;
@@ -84086,7 +85834,7 @@
 						 */
 						duk__append_reop(re_ctx, DUK_REOP_JUMP);
 						duk__append_jump_offset(re_ctx, atom_code_length);
-						duk__append_slice(re_ctx, atom_start_offset, atom_code_length);
+						duk__append_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
 					}
 					if (re_ctx->curr_token.greedy) {
 						duk__append_reop(re_ctx, DUK_REOP_SPLIT2);   /* prefer jump */
@@ -84113,7 +85861,7 @@
 					 */
 					duk_uint32_t offset = (duk_uint32_t) DUK__RE_BUFLEN(re_ctx);
 					while (tmp_qmax > 0) {
-						duk__insert_slice(re_ctx, offset, atom_start_offset, atom_code_length);
+						duk__insert_slice(re_ctx, offset, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
 						if (re_ctx->curr_token.greedy) {
 							duk__insert_u32(re_ctx, offset, DUK_REOP_SPLIT1);   /* prefer direct */
 						} else {
@@ -84127,7 +85875,7 @@
 				}
 
 				/* remove the original 'template' atom */
-				duk__remove_slice(re_ctx, atom_start_offset, atom_code_length);
+				duk__remove_slice(re_ctx, (duk_uint32_t) atom_start_offset, (duk_uint32_t) atom_code_length);
 			}
 
 			/* 'taint' result as complex */
@@ -84195,7 +85943,7 @@
 			duk__append_reop(re_ctx, DUK_REOP_CHAR);
 			ch = re_ctx->curr_token.num;
 			if (re_ctx->re_flags & DUK_RE_FLAG_IGNORE_CASE) {
-				ch = duk_unicode_re_canonicalize_char(re_ctx->thr, ch);
+				ch = (duk_uint32_t) duk_unicode_re_canonicalize_char(re_ctx->thr, (duk_codepoint_t) ch);
 			}
 			duk__append_u32(re_ctx, ch);
 			break;
@@ -84222,8 +85970,8 @@
 
 			DUK_ASSERT(DUK_RETOK_ATOM_WHITE == DUK_RETOK_ATOM_DIGIT + 2);
 			DUK_ASSERT(DUK_RETOK_ATOM_WORD_CHAR == DUK_RETOK_ATOM_DIGIT + 4);
-			idx = (re_ctx->curr_token.t - DUK_RETOK_ATOM_DIGIT) >> 1;
-			DUK_ASSERT(idx <= 2);  /* Assume continuous token numbers; also checks negative underflow. */
+			idx = (duk_small_uint_t) ((re_ctx->curr_token.t - DUK_RETOK_ATOM_DIGIT) >> 1U);
+			DUK_ASSERT(idx <= 2U);  /* Assume continuous token numbers; also checks negative underflow. */
 
 			duk__append_range_atom_matcher(re_ctx, re_op, duk__re_range_lookup1[idx], duk__re_range_lookup2[idx]);
 			break;
@@ -84298,7 +86046,7 @@
 
 			/* parse ranges until character class ends */
 			re_ctx->nranges = 0;    /* note: ctx-wide temporary */
-			duk_lexer_parse_re_ranges(&re_ctx->lex, duk__generate_ranges, (void *) re_ctx);
+			duk_lexer_parse_re_ranges(&re_ctx->lex, duk__regexp_generate_ranges, (void *) re_ctx);
 
 			/* insert range count */
 			duk__insert_u32(re_ctx, offset, re_ctx->nranges);
@@ -84344,14 +86092,14 @@
 		duk_uint32_t offset;
 
 		DUK_ASSERT(unpatched_disjunction_split >= 0);
-		offset = unpatched_disjunction_jump;
+		offset = (duk_uint32_t) unpatched_disjunction_jump;
 		offset += duk__insert_jump_offset(re_ctx,
 		                                  offset,
 		                                  (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - offset));
 		/* offset is now target of the pending split (right after jump) */
 		duk__insert_jump_offset(re_ctx,
-		                        unpatched_disjunction_split,
-		                        offset - unpatched_disjunction_split);
+		                        (duk_uint32_t) unpatched_disjunction_split,
+		                        (duk_int32_t) offset - unpatched_disjunction_split);
 	}
 
 #if 0
@@ -84436,7 +86184,6 @@
  */
 
 DUK_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hstring *h;
 	const duk_uint8_t *p;
 	duk_bufwriter_ctx bw_alloc;
@@ -84445,12 +86192,12 @@
 	duk_size_t i, n;
 	duk_uint_fast8_t c_prev, c;
 
-	h = duk_known_hstring(ctx, idx_pattern);
+	h = duk_known_hstring(thr, idx_pattern);
 	p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h);
 	n = (duk_size_t) DUK_HSTRING_GET_BYTELEN(h);
 
 	if (n == 0) {
-		duk_push_string(ctx, "(?:)");
+		duk_push_string(thr, "(?:)");
 		return;
 	}
 
@@ -84477,7 +86224,7 @@
 	}
 
 	DUK_BW_SETPTR_AND_COMPACT(thr, bw, q);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe if input is safe. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe if input is safe. */
 
 	/* [ ... escaped_source ] */
 }
@@ -84498,7 +86245,6 @@
  */
 
 DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_re_compiler_ctx re_ctx;
 	duk_lexer_point lex_point;
 	duk_hstring *h_pattern;
@@ -84506,15 +86252,14 @@
 	duk__re_disjunction_info ign_disj;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 
 	/*
 	 *  Args validation
 	 */
 
 	/* TypeError if fails */
-	h_pattern = duk_require_hstring_notsymbol(ctx, -2);
-	h_flags = duk_require_hstring_notsymbol(ctx, -1);
+	h_pattern = duk_require_hstring_notsymbol(thr, -2);
+	h_flags = duk_require_hstring_notsymbol(thr, -1);
 
 	/*
 	 *  Create normalized 'source' property (E5 Section 15.10.3).
@@ -84592,7 +86337,7 @@
 	/* [ ... pattern flags escaped_source buffer ] */
 
 	DUK_BW_COMPACT(thr, &re_ctx.bw);
-	(void) duk_buffer_to_string(ctx, -1);  /* Safe because flags is at most 7 bit. */
+	(void) duk_buffer_to_string(thr, -1);  /* Safe because flags is at most 7 bit. */
 
 	/* [ ... pattern flags escaped_source bytecode ] */
 
@@ -84600,11 +86345,11 @@
 	 *  Finalize stack
 	 */
 
-	duk_remove(ctx, -4);     /* -> [ ... flags escaped_source bytecode ] */
-	duk_remove(ctx, -3);     /* -> [ ... escaped_source bytecode ] */
+	duk_remove(thr, -4);     /* -> [ ... flags escaped_source bytecode ] */
+	duk_remove(thr, -3);     /* -> [ ... escaped_source bytecode ] */
 
 	DUK_DD(DUK_DDPRINT("regexp compilation successful, bytecode: %!T, escaped source: %!T",
-	                   (duk_tval *) duk_get_tval(ctx, -1), (duk_tval *) duk_get_tval(ctx, -2)));
+	                   (duk_tval *) duk_get_tval(thr, -1), (duk_tval *) duk_get_tval(thr, -2)));
 }
 
 /*
@@ -84618,21 +86363,20 @@
  */
 
 DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_hobject *h;
 
 	/* [ ... escaped_source bytecode ] */
 
-	duk_push_object(ctx);
-	h = duk_known_hobject(ctx, -1);
-	duk_insert(ctx, -3);
+	duk_push_object(thr);
+	h = duk_known_hobject(thr, -1);
+	duk_insert(thr, -3);
 
 	/* [ ... regexp_object escaped_source bytecode ] */
 
 	DUK_HOBJECT_SET_CLASS_NUMBER(h, DUK_HOBJECT_CLASS_REGEXP);
 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[DUK_BIDX_REGEXP_PROTOTYPE]);
 
-	duk_xdef_prop_stridx_short(ctx, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE);
+	duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_BYTECODE, DUK_PROPDESC_FLAGS_NONE);
 
 	/* [ ... regexp_object escaped_source ] */
 
@@ -84641,12 +86385,12 @@
 	 * property for the getter.
 	 */
 
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE);
 
 	/* [ ... regexp_object ] */
 
-	duk_push_int(ctx, 0);
-	duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W);
+	duk_push_int(thr, 0);
+	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LAST_INDEX, DUK_PROPDESC_FLAGS_W);
 
 	/* [ ... regexp_object ] */
 }
@@ -85160,8 +86904,8 @@
 			}
 			DUK_ASSERT(idx_count > 0);
 
-			duk_require_stack((duk_context *) re_ctx->thr, 1);
-			range_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero((duk_context *) re_ctx->thr,
+			duk_require_stack(re_ctx->thr, 1);
+			range_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr,
 			                                                           sizeof(duk_uint8_t *) * idx_count);
 			DUK_ASSERT(range_save != NULL);
 			DUK_MEMCPY(range_save, re_ctx->saved + idx_start, sizeof(duk_uint8_t *) * idx_count);
@@ -85180,7 +86924,7 @@
 				DUK_DDD(DUK_DDDPRINT("match: keep wiped/resaved values [%ld,%ld] (captures [%ld,%ld])",
 				                     (long) idx_start, (long) (idx_start + idx_count - 1),
 			                             (long) (idx_start / 2), (long) ((idx_start + idx_count - 1) / 2)));
-				duk_pop((duk_context *) re_ctx->thr);
+				duk_pop_unsafe(re_ctx->thr);
 				sp = sub_sp;
 				goto match;
 			}
@@ -85192,7 +86936,7 @@
 			DUK_MEMCPY((void *) (re_ctx->saved + idx_start),
 			           (const void *) range_save,
 			           sizeof(duk_uint8_t *) * idx_count);
-			duk_pop((duk_context *) re_ctx->thr);
+			duk_pop_unsafe(re_ctx->thr);
 			goto fail;
 		}
 		case DUK_REOP_LOOKPOS:
@@ -85207,7 +86951,7 @@
 			 *  The temporary save buffer is pushed on to the valstack to handle
 			 *  errors correctly.  Each lookahead causes a C recursion and pushes
 			 *  more stuff on the value stack.  If the C recursion limit is less
-			 *  than the value stack spare, there is no need to check the stack.
+			 *  than the value stack slack, there is no need to check the stack.
 			 *  We do so regardless, just in case.
 			 */
 
@@ -85217,8 +86961,8 @@
 
 			DUK_ASSERT(re_ctx->nsaved > 0);
 
-			duk_require_stack((duk_context *) re_ctx->thr, 1);
-			full_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero((duk_context *) re_ctx->thr,
+			duk_require_stack(re_ctx->thr, 1);
+			full_save = (duk_uint8_t **) duk_push_fixed_buffer_nozero(re_ctx->thr,
 			                                                          sizeof(duk_uint8_t *) * re_ctx->nsaved);
 			DUK_ASSERT(full_save != NULL);
 			DUK_MEMCPY(full_save, re_ctx->saved, sizeof(duk_uint8_t *) * re_ctx->nsaved);
@@ -85237,7 +86981,7 @@
 			sub_sp = duk__match_regexp(re_ctx, pc + skip, sp);
 			if (sub_sp) {
 				/* match: keep saves */
-				duk_pop((duk_context *) re_ctx->thr);
+				duk_pop_unsafe(re_ctx->thr);
 				sp = sub_sp;
 				goto match;
 			}
@@ -85249,7 +86993,7 @@
 			DUK_MEMCPY((void *) re_ctx->saved,
 			           (const void *) full_save,
 			           sizeof(duk_uint8_t *) * re_ctx->nsaved);
-			duk_pop((duk_context *) re_ctx->thr);
+			duk_pop_unsafe(re_ctx->thr);
 			goto fail;
 		}
 		case DUK_REOP_BACKREFERENCE: {
@@ -85344,7 +87088,6 @@
  */
 
 DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_global) {
-	duk_context *ctx = (duk_context *) thr;
 	duk_re_matcher_ctx re_ctx;
 	duk_hobject *h_regexp;
 	duk_hstring *h_bytecode;
@@ -85359,11 +87102,10 @@
 	duk_uint32_t char_offset;
 
 	DUK_ASSERT(thr != NULL);
-	DUK_ASSERT(ctx != NULL);
 
 	DUK_DD(DUK_DDPRINT("regexp match: regexp=%!T, input=%!T",
-	                   (duk_tval *) duk_get_tval(ctx, -2),
-	                   (duk_tval *) duk_get_tval(ctx, -1)));
+	                   (duk_tval *) duk_get_tval(thr, -2),
+	                   (duk_tval *) duk_get_tval(thr, -1)));
 
 	/*
 	 *  Regexp instance check, bytecode check, input coercion.
@@ -85372,16 +87114,16 @@
 	 */
 
 	/* TypeError if wrong; class check, see E5 Section 15.10.6 */
-	h_regexp = duk_require_hobject_with_class(ctx, -2, DUK_HOBJECT_CLASS_REGEXP);
+	h_regexp = duk_require_hobject_with_class(thr, -2, DUK_HOBJECT_CLASS_REGEXP);
 	DUK_ASSERT(h_regexp != NULL);
 	DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_regexp) == DUK_HOBJECT_CLASS_REGEXP);
 	DUK_UNREF(h_regexp);
 
-	h_input = duk_to_hstring(ctx, -1);
+	h_input = duk_to_hstring(thr, -1);
 	DUK_ASSERT(h_input != NULL);
 
-	duk_get_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_BYTECODE);  /* [ ... re_obj input ] -> [ ... re_obj input bc ] */
-	h_bytecode = duk_require_hstring(ctx, -1);  /* no regexp instance should exist without a non-configurable bytecode property */
+	duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_INT_BYTECODE);  /* [ ... re_obj input ] -> [ ... re_obj input bc ] */
+	h_bytecode = duk_require_hstring(thr, -1);  /* no regexp instance should exist without a non-configurable bytecode property */
 	DUK_ASSERT(h_bytecode != NULL);
 
 	/*
@@ -85414,14 +87156,14 @@
 	re_ctx.bytecode = pc;
 
 	DUK_ASSERT(DUK_RE_FLAG_GLOBAL < 0x10000UL);  /* must fit into duk_small_int_t */
-	global = (duk_small_int_t) (force_global | (re_ctx.re_flags & DUK_RE_FLAG_GLOBAL));
+	global = (duk_small_int_t) (force_global | (duk_small_int_t) (re_ctx.re_flags & DUK_RE_FLAG_GLOBAL));
 
 	DUK_ASSERT(re_ctx.nsaved >= 2);
 	DUK_ASSERT((re_ctx.nsaved % 2) == 0);
 
-	p_buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, sizeof(duk_uint8_t *) * re_ctx.nsaved);  /* rely on zeroing */
+	p_buf = (duk_uint8_t *) duk_push_fixed_buffer(thr, sizeof(duk_uint8_t *) * re_ctx.nsaved);  /* rely on zeroing */
 	DUK_UNREF(p_buf);
-	re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(ctx, -1, NULL);
+	re_ctx.saved = (const duk_uint8_t **) duk_get_buffer(thr, -1, NULL);
 	DUK_ASSERT(re_ctx.saved != NULL);
 
 	/* [ ... re_obj input bc saved_buf ] */
@@ -85459,10 +87201,10 @@
 
 	/* [ ... re_obj input bc saved_buf ] */
 
-	duk_get_prop_stridx_short(ctx, -4, DUK_STRIDX_LAST_INDEX);  /* -> [ ... re_obj input bc saved_buf lastIndex ] */
-	(void) duk_to_int(ctx, -1);  /* ToInteger(lastIndex) */
-	d = duk_get_number(ctx, -1);  /* integer, but may be +/- Infinite, +/- zero (not NaN, though) */
-	duk_pop(ctx);
+	duk_get_prop_stridx_short(thr, -4, DUK_STRIDX_LAST_INDEX);  /* -> [ ... re_obj input bc saved_buf lastIndex ] */
+	(void) duk_to_int(thr, -1);  /* ToInteger(lastIndex) */
+	d = duk_get_number(thr, -1);  /* integer, but may be +/- Infinite, +/- zero (not NaN, though) */
+	duk_pop_nodecref_unsafe(thr);
 
 	if (global) {
 		if (d < 0.0 || d > (double) DUK_HSTRING_GET_CHARLEN(h_input)) {
@@ -85499,7 +87241,7 @@
 		DUK_ASSERT_DISABLE(char_offset >= 0);
 		DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input));
 
-		/* Note: ctx.steps is intentionally not reset, it applies to the entire unanchored match */
+		/* Note: re_ctx.steps is intentionally not reset, it applies to the entire unanchored match */
 		DUK_ASSERT(re_ctx.recursion_depth == 0);
 
 		DUK_DDD(DUK_DDDPRINT("attempt match at char offset %ld; %p [%p,%p]",
@@ -85514,7 +87256,7 @@
 		 *
 		 *    - Clearing saved[] is not necessary because backtracking does it
 		 *
-		 *    - Backtracking also rewinds ctx.recursion back to zero, unless an
+		 *    - Backtracking also rewinds re_ctx.recursion back to zero, unless an
 		 *      internal/limit error occurs (which causes a longjmp())
 		 *
 		 *    - If we supported anchored matches, we would break out here
@@ -85585,10 +87327,10 @@
 		 * objects are usually short lived.
 		 */
 
-		duk_push_array(ctx);
-
-#if defined(DUK_USE_ASSERTIONS)
-		h_res = duk_require_hobject(ctx, -1);
+		duk_push_array(thr);
+
+#if defined(DUK_USE_ASSERTIONS)
+		h_res = duk_require_hobject(thr, -1);
 		DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(h_res));
 		DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_ARRAY(h_res));
 		DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(h_res) == DUK_HOBJECT_CLASS_ARRAY);
@@ -85596,11 +87338,11 @@
 
 		/* [ ... re_obj input bc saved_buf res_obj ] */
 
-		duk_push_u32(ctx, char_offset);
-		duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INDEX);
-
-		duk_dup_m4(ctx);
-		duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INPUT);
+		duk_push_u32(thr, char_offset);
+		duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INDEX);
+
+		duk_dup_m4(thr);
+		duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INPUT);
 
 		for (i = 0; i < re_ctx.nsaved; i += 2) {
 			/* Captures which are undefined have NULL pointers and are returned
@@ -85608,7 +87350,7 @@
 			 * (this should, of course, never happen in practice).
 			 */
 			if (re_ctx.saved[i] && re_ctx.saved[i + 1] && re_ctx.saved[i + 1] >= re_ctx.saved[i]) {
-				duk_push_lstring(ctx,
+				duk_push_lstring(thr,
 				                 (const char *) re_ctx.saved[i],
 				                 (duk_size_t) (re_ctx.saved[i+1] - re_ctx.saved[i]));
 				if (i == 0) {
@@ -85617,14 +87359,14 @@
 					 * will be zero).  Also assumes clen reflects the
 					 * correct char length.
 					 */
-					char_end_offset = char_offset + (duk_uint32_t) duk_get_length(ctx, -1);  /* add charlen */
-				}
-			} else {
-				duk_push_undefined(ctx);
+					char_end_offset = char_offset + (duk_uint32_t) duk_get_length(thr, -1);  /* add charlen */
+				}
+			} else {
+				duk_push_undefined(thr);
 			}
 
 			/* [ ... re_obj input bc saved_buf res_obj val ] */
-			duk_put_prop_index(ctx, -2, i / 2);
+			duk_put_prop_index(thr, -2, (duk_uarridx_t) (i / 2));
 		}
 
 		/* [ ... re_obj input bc saved_buf res_obj ] */
@@ -85633,8 +87375,8 @@
 
 		if (global) {
 			/* global regexp: lastIndex updated on match */
-			duk_push_u32(ctx, char_end_offset);
-			duk_put_prop_stridx_short(ctx, -6, DUK_STRIDX_LAST_INDEX);
+			duk_push_u32(thr, char_end_offset);
+			duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX);
 		} else {
 			/* non-global regexp: lastIndex never updated on match */
 			;
@@ -85648,21 +87390,21 @@
 
 		DUK_DDD(DUK_DDDPRINT("regexp does not match"));
 
-		duk_push_null(ctx);
+		duk_push_null(thr);
 
 		/* [ ... re_obj input bc saved_buf res_obj ] */
 
-		duk_push_int(ctx, 0);
-		duk_put_prop_stridx_short(ctx, -6, DUK_STRIDX_LAST_INDEX);
+		duk_push_int(thr, 0);
+		duk_put_prop_stridx_short(thr, -6, DUK_STRIDX_LAST_INDEX);
 	}
 
 	/* [ ... re_obj input bc saved_buf res_obj ] */
 
-	duk_insert(ctx, -5);
+	duk_insert(thr, -5);
 
 	/* [ ... res_obj re_obj input bc saved_buf ] */
 
-	duk_pop_n(ctx, 4);
+	duk_pop_n_unsafe(thr, 4);
 
 	/* [ ... res_obj ] */
 
@@ -86074,6 +87816,7 @@
 DUK_LOCAL duk_uint_t duk__selftest_fmod(void) {
 	duk_uint_t error_count = 0;
 	duk__test_double_union u1, u2;
+	volatile duk_double_t t1, t2, t3;
 
 	/* fmod() with integer argument and exponent 2^32 is used by e.g.
 	 * ToUint32() and some Duktape internals.
@@ -86090,6 +87833,22 @@
 	u2.d = 10.0;
 	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
 
+	/* 52-bit integer split into two parts:
+	 * >>> 0x1fedcba9876543
+	 * 8987183256397123
+	 * >>> float(0x1fedcba9876543) / float(2**53)
+	 * 0.9977777777777778
+	 */
+	u1.d = DUK_FMOD(8987183256397123.0, 4294967296.0);
+	u2.d = (duk_double_t) 0xa9876543UL;
+	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
+	t1 = 8987183256397123.0;
+	t2 = 4294967296.0;
+	t3 = t1 / t2;
+	u1.d = DUK_FLOOR(t3);
+	u2.d = (duk_double_t) 0x1fedcbUL;
+	DUK__DBLUNION_CMP_TRUE(&u1, &u2);
+
 	/* C99 behavior is for fmod() result sign to mathc argument sign. */
 	u1.d = DUK_FMOD(-10.0, 4294967296.0);
 	u2.d = -10.0;
@@ -86148,7 +87907,7 @@
 	/* Catch a double-to-int64 cast issue encountered in practice. */
 	d = 2147483648.0;
 	i = (duk_int64_t) d;
-	if (i != 0x80000000LL) {
+	if (i != DUK_I64_CONSTANT(0x80000000)) {
 		DUK__FAILED("casting 2147483648.0 to duk_int64_t failed");
 	}
 #else
@@ -86243,7 +88002,7 @@
 	}
 
 	for (i = 1; i <= 256; i++) {
-		ptr = alloc_func(udata, i);
+		ptr = alloc_func(udata, (duk_size_t) i);
 		if (ptr == NULL) {
 			DUK_D(DUK_DPRINT("alloc failed, ignore"));
 			continue;  /* alloc failed, ignore */
@@ -86353,9 +88112,9 @@
 	if (shift >= 0 && shift <= 46) {  /* exponents 1023 to 1069 */
 		duk_int64_t t;
 
-		if (((0x000fffffffffffffLL >> shift) & i) == 0) {
-			t = i | 0x0010000000000000LL;  /* implicit leading one */
-			t = t & 0x001fffffffffffffLL;
+		if (((DUK_I64_CONSTANT(0x000fffffffffffff) >> shift) & i) == 0) {
+			t = i | DUK_I64_CONSTANT(0x0010000000000000);  /* implicit leading one */
+			t = t & DUK_I64_CONSTANT(0x001fffffffffffff);
 			t = t >> (52 - shift);
 			if (i < 0) {
 				t = -t;
@@ -86364,13 +88123,13 @@
 			return;
 		}
 	} else if (shift == -1023) {  /* exponent 0 */
-		if (i >= 0 && (i & 0x000fffffffffffffLL) == 0) {
+		if (i >= 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) {
 			/* Note: reject negative zero. */
 			DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) 0);
 			return;
 		}
 	} else if (shift == 47) {  /* exponent 1070 */
-		if (i < 0 && (i & 0x000fffffffffffffLL) == 0) {
+		if (i < 0 && (i & DUK_I64_CONSTANT(0x000fffffffffffff)) == 0) {
 			DUK_TVAL_SET_FASTINT(tv, (duk_int64_t) DUK_FASTINT_MIN);
 			return;
 		}
@@ -86396,15 +88155,15 @@
 	t = (duk_uint64_t) DUK_DBLUNION_GET_UINT64(tv);
 	if ((t >> 48) != DUK_TAG_FASTINT) {
 		return tv->d;
-	} else if (t & 0x0000800000000000ULL) {
+	} else if (t & DUK_U64_CONSTANT(0x0000800000000000)) {
 		t = (duk_uint64_t) (-((duk_int64_t) t));  /* avoid unary minus on unsigned */
-		t = t & 0x0000ffffffffffffULL;  /* negative */
-		t |= 0xc330000000000000ULL;
+		t = t & DUK_U64_CONSTANT(0x0000ffffffffffff);  /* negative */
+		t |= DUK_U64_CONSTANT(0xc330000000000000);
 		DUK_DBLUNION_SET_UINT64(&du, t);
 		return du.d + 4503599627370496.0;  /* 1 << 52 */
 	} else if (t != 0) {
-		t &= 0x0000ffffffffffffULL;  /* positive */
-		t |= 0x4330000000000000ULL;
+		t &= DUK_U64_CONSTANT(0x0000ffffffffffff);  /* positive */
+		t |= DUK_U64_CONSTANT(0x4330000000000000);
 		DUK_DBLUNION_SET_UINT64(&du, t);
 		return du.d - 4503599627370496.0;  /* 1 << 52 */
 	} else {
@@ -86423,11 +88182,11 @@
 
 	if (tv->t == DUK_TAG_FASTINT) {
 		if (tv->v.fi >= 0) {
-			t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
+			t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi;
 			DUK_DBLUNION_SET_UINT64(&du, t);
 			return du.d - 4503599627370496.0;  /* 1 << 52 */
 		} else {
-			t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
+			t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi);
 			DUK_DBLUNION_SET_UINT64(&du, t);
 			return du.d + 4503599627370496.0;  /* 1 << 52 */
 		}
@@ -86446,11 +88205,11 @@
 	DUK_ASSERT(tv->t == DUK_TAG_FASTINT);
 
 	if (tv->v.fi >= 0) {
-		t = 0x4330000000000000ULL | (duk_uint64_t) tv->v.fi;
+		t = DUK_U64_CONSTANT(0x4330000000000000) | (duk_uint64_t) tv->v.fi;
 		DUK_DBLUNION_SET_UINT64(&du, t);
 		return du.d - 4503599627370496.0;  /* 1 << 52 */
 	} else {
-		t = 0xc330000000000000ULL | (duk_uint64_t) (-tv->v.fi);
+		t = DUK_U64_CONSTANT(0xc330000000000000) | (duk_uint64_t) (-tv->v.fi);
 		DUK_DBLUNION_SET_UINT64(&du, t);
 		return du.d + 4503599627370496.0;  /* 1 << 52 */
 	}
@@ -92600,6 +94359,29 @@
 65528L,65529L,65530L,65531L,65532L,65533L,65534L,65535L,
 };
 #endif
+
+#if defined(DUK_USE_REGEXP_CANON_BITMAP)
+/*
+ *  Automatically generated by extract_caseconv.py, do not edit!
+ */
+
+const duk_uint8_t duk_unicode_re_canon_bitmap[256] = {
+23,0,224,19,1,228,255,255,255,255,255,255,255,255,255,255,255,255,255,127,
+255,255,255,255,255,255,255,255,231,247,0,16,255,227,255,255,63,255,255,
+255,255,255,255,255,1,252,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+227,193,255,255,255,147,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,251,
+};
+#endif
 #line 1 "duk_util_bitdecoder.c"
 /*
  *  Bitstream decoder.
@@ -92644,7 +94426,7 @@
 	 * to be cleared, we just ignore them on next round.
 	 */
 	shift = ctx->currbits - bits;
-	mask = (1 << bits) - 1;
+	mask = (((duk_uint32_t) 1U) << bits) - 1U;
 	tmp = (ctx->currval >> shift) & mask;
 	ctx->currbits = shift;  /* remaining */
 
@@ -92716,7 +94498,7 @@
 	DUK_ASC_0, DUK_ASC_1, DUK_ASC_2, DUK_ASC_3,
 	DUK_ASC_4, DUK_ASC_5, DUK_ASC_6, DUK_ASC_7,
 	DUK_ASC_8, DUK_ASC_9, DUK_ASC_UNDERSCORE, DUK_ASC_SPACE,
-	0xff, 0x80, DUK_ASC_DOUBLEQUOTE, DUK_ASC_LCURLY
+	0x82, 0x80, DUK_ASC_DOUBLEQUOTE, DUK_ASC_LCURLY
 };
 
 DUK_INTERNAL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out) {
@@ -92813,7 +94595,7 @@
 }
 #line 1 "duk_util_bufwriter.c"
 /*
- *  Fast buffer writer with spare management.
+ *  Fast buffer writer with slack management.
  */
 
 /* #include duk_internal.h -> already included */
@@ -92841,21 +94623,17 @@
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(bw_ctx != NULL);
 	DUK_ASSERT(h_buf != NULL);
-	DUK_UNREF(thr);
 
 	bw_ctx->buf = h_buf;
 	duk__bw_update_ptrs(thr, bw_ctx, 0, DUK_HBUFFER_DYNAMIC_GET_SIZE(h_buf));
 }
 
 DUK_INTERNAL void duk_bw_init_pushbuf(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_size_t buf_size) {
-	duk_context *ctx;
-
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(bw_ctx != NULL);
-	ctx = (duk_context *) thr;
-
-	(void) duk_push_dynamic_buffer(ctx, buf_size);
-	bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(ctx, -1);
+
+	(void) duk_push_dynamic_buffer(thr, buf_size);
+	bw_ctx->buf = (duk_hbuffer_dynamic *) duk_known_hbuffer(thr, -1);
 	duk__bw_update_ptrs(thr, bw_ctx, 0, buf_size);
 }
 
@@ -92875,7 +94653,7 @@
 	 */
 
 	curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
-	add_sz = (curr_off >> DUK_BW_SPARE_SHIFT) + DUK_BW_SPARE_ADD;
+	add_sz = (curr_off >> DUK_BW_SLACK_SHIFT) + DUK_BW_SLACK_ADD;
 	new_sz = curr_off + sz + add_sz;
 	if (DUK_UNLIKELY(new_sz < curr_off)) {
 		/* overflow */
@@ -92935,7 +94713,6 @@
 	DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
 	DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
 	DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
-	DUK_UNREF(thr);
 
 	DUK_BW_ENSURE(thr, bw, len);
 	duk_bw_write_raw_slice(thr, bw, src_off, len);
@@ -92952,7 +94729,7 @@
 	DUK_UNREF(thr);
 
 	p_base = bw->p_base;
-	buf_sz = bw->p - p_base;
+	buf_sz = (duk_size_t) (bw->p - p_base);  /* constrained by maximum buffer size */
 	move_sz = buf_sz - dst_off;
 
 	DUK_ASSERT(p_base != NULL);  /* buffer size is >= 1 */
@@ -92970,7 +94747,6 @@
 	DUK_ASSERT(bw != NULL);
 	DUK_ASSERT(dst_off <= DUK_BW_GET_SIZE(thr, bw));
 	DUK_ASSERT(buf != NULL);
-	DUK_UNREF(thr);
 
 	DUK_BW_ENSURE(thr, bw, len);
 	duk_bw_insert_raw_bytes(thr, bw, dst_off, buf, len);
@@ -93000,7 +94776,7 @@
 		src_off += len;
 	}
 
-	buf_sz = bw->p - p_base;
+	buf_sz = (duk_size_t) (bw->p - p_base);
 	move_sz = buf_sz - dst_off;
 
 	DUK_ASSERT(p_base != NULL);  /* buffer size is >= 1 */
@@ -93020,7 +94796,6 @@
 	DUK_ASSERT(src_off <= DUK_BW_GET_SIZE(thr, bw));
 	DUK_ASSERT(len <= DUK_BW_GET_SIZE(thr, bw));
 	DUK_ASSERT(src_off + len <= DUK_BW_GET_SIZE(thr, bw));
-	DUK_UNREF(thr);
 
 	/* Don't support "straddled" source now. */
 	DUK_ASSERT(dst_off <= src_off || dst_off >= src_off + len);
@@ -93039,7 +94814,7 @@
 	DUK_UNREF(thr);
 
 	p_base = bw->p_base;
-	buf_sz = bw->p - p_base;
+	buf_sz = (duk_size_t) (bw->p - p_base);
 	move_sz = buf_sz - off;
 	p_dst = p_base + off + len;
 	p_src = p_base + off;
@@ -93051,7 +94826,6 @@
 	DUK_ASSERT(thr != NULL);
 	DUK_ASSERT(bw != NULL);
 	DUK_ASSERT(off <= DUK_BW_GET_SIZE(thr, bw));
-	DUK_UNREF(thr);
 
 	DUK_BW_ENSURE(thr, bw, len);
 	return duk_bw_insert_raw_area(thr, bw, off, len);
@@ -93297,9 +95071,9 @@
 #if defined(DUK__RANDOM_XOROSHIRO128PLUS)
 DUK_LOCAL DUK_ALWAYS_INLINE duk_uint64_t duk__rnd_splitmix64(duk_uint64_t *x) {
 	duk_uint64_t z;
-	z = (*x += 0x9E3779B97F4A7C15ULL);
-	z = (z ^ (z >> 30U)) * 0xBF58476D1CE4E5B9ULL;
-	z = (z ^ (z >> 27U)) * 0x94D049BB133111EBULL;
+	z = (*x += DUK_U64_CONSTANT(0x9E3779B97F4A7C15));
+	z = (z ^ (z >> 30U)) * DUK_U64_CONSTANT(0xBF58476D1CE4E5B9);
+	z = (z ^ (z >> 27U)) * DUK_U64_CONSTANT(0x94D049BB133111EB);
 	return z ^ (z >> 31U);
 }
 
@@ -93345,7 +95119,7 @@
 	 * is the same so a direct assignment works.  For mixed endian the
 	 * 32-bit parts must be swapped.
 	 */
-	v = (0x3ffULL << 52U) | (duk__xoroshiro128plus((duk_uint64_t *) thr->heap->rnd_state) >> 12U);
+	v = (DUK_U64_CONSTANT(0x3ff) << 52U) | (duk__xoroshiro128plus((duk_uint64_t *) thr->heap->rnd_state) >> 12U);
 	du.ull[0] = v;
 #if defined(DUK_USE_DOUBLE_ME)
 	do {
--- a/duktape/duktape.h	Fri Jul 13 12:53:06 2018 +0200
+++ b/duktape/duktape.h	Fri Jul 13 12:57:10 2018 +0200
@@ -1,13 +1,13 @@
 /*
- *  Duktape public API for Duktape 2.1.0.
+ *  Duktape public API for Duktape 2.2.1.
  *
  *  See the API reference for documentation on call semantics.  The exposed,
  *  supported API is between the "BEGIN PUBLIC API" and "END PUBLIC API"
  *  comments.  Other parts of the header are Duktape internal and related to
  *  e.g. platform/compiler/feature detection.
  *
- *  Git commit 1f1f51a4f9595ffe8def0e9ba45b20f14679393a (v2.1.0).
- *  Git branch master.
+ *  Git commit 25420e773c5fbc50d5b46bf487fc45717e35b94f (v2.2.1).
+ *  Git branch v2.2-maintenance.
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  licensing information.
@@ -89,6 +89,9 @@
  *  * Remko Tron\u00e7on (https://el-tramo.be)
  *  * Romero Malaquias (rbsm@ic.ufal.br)
  *  * Michael Drake <michael.drake@codethink.co.uk>
+ *  * Steven Don (https://github.com/shdon)
+ *  * Simon Stone (https://github.com/sstone1)
+ *  * \J. McC. (https://github.com/jmhmccr)
  *  
  *  Other contributions
  *  ===================
@@ -151,16 +154,16 @@
  * development snapshots have 99 for patch level (e.g. 0.10.99 would be a
  * development version after 0.10.0 but before the next official release).
  */
-#define DUK_VERSION                       20100L
+#define DUK_VERSION                       20201L
 
 /* Git commit, describe, and branch for Duktape build.  Useful for
  * non-official snapshot builds so that application code can easily log
  * which Duktape snapshot was used.  Not available in the Ecmascript
  * environment.
  */
-#define DUK_GIT_COMMIT                    "1f1f51a4f9595ffe8def0e9ba45b20f14679393a"
-#define DUK_GIT_DESCRIBE                  "v2.1.0"
-#define DUK_GIT_BRANCH                    "master"
+#define DUK_GIT_COMMIT                    "25420e773c5fbc50d5b46bf487fc45717e35b94f"
+#define DUK_GIT_DESCRIBE                  "v2.2.1"
+#define DUK_GIT_BRANCH                    "v2.2-maintenance"
 
 /* External duk_config.h provides platform/compiler/OS dependent
  * typedefs and macros, and DUK_USE_xxx config options so that
@@ -286,35 +289,35 @@
 /* Number of value stack entries (in addition to actual call arguments)
  * guaranteed to be allocated on entry to a Duktape/C function.
  */
-#define DUK_API_ENTRY_STACK               64
+#define DUK_API_ENTRY_STACK               64U
 
 /* Value types, used by e.g. duk_get_type() */
-#define DUK_TYPE_MIN                      0
-#define DUK_TYPE_NONE                     0    /* no value, e.g. invalid index */
-#define DUK_TYPE_UNDEFINED                1    /* Ecmascript undefined */
-#define DUK_TYPE_NULL                     2    /* Ecmascript null */
-#define DUK_TYPE_BOOLEAN                  3    /* Ecmascript boolean: 0 or 1 */
-#define DUK_TYPE_NUMBER                   4    /* Ecmascript number: double */
-#define DUK_TYPE_STRING                   5    /* Ecmascript string: CESU-8 / extended UTF-8 encoded */
-#define DUK_TYPE_OBJECT                   6    /* Ecmascript object: includes objects, arrays, functions, threads */
-#define DUK_TYPE_BUFFER                   7    /* fixed or dynamic, garbage collected byte buffer */
-#define DUK_TYPE_POINTER                  8    /* raw void pointer */
-#define DUK_TYPE_LIGHTFUNC                9    /* lightweight function pointer */
-#define DUK_TYPE_MAX                      9
+#define DUK_TYPE_MIN                      0U
+#define DUK_TYPE_NONE                     0U    /* no value, e.g. invalid index */
+#define DUK_TYPE_UNDEFINED                1U    /* Ecmascript undefined */
+#define DUK_TYPE_NULL                     2U    /* Ecmascript null */
+#define DUK_TYPE_BOOLEAN                  3U    /* Ecmascript boolean: 0 or 1 */
+#define DUK_TYPE_NUMBER                   4U    /* Ecmascript number: double */
+#define DUK_TYPE_STRING                   5U    /* Ecmascript string: CESU-8 / extended UTF-8 encoded */
+#define DUK_TYPE_OBJECT                   6U    /* Ecmascript object: includes objects, arrays, functions, threads */
+#define DUK_TYPE_BUFFER                   7U    /* fixed or dynamic, garbage collected byte buffer */
+#define DUK_TYPE_POINTER                  8U    /* raw void pointer */
+#define DUK_TYPE_LIGHTFUNC                9U    /* lightweight function pointer */
+#define DUK_TYPE_MAX                      9U
 
 /* Value mask types, used by e.g. duk_get_type_mask() */
-#define DUK_TYPE_MASK_NONE                (1 << DUK_TYPE_NONE)
-#define DUK_TYPE_MASK_UNDEFINED           (1 << DUK_TYPE_UNDEFINED)
-#define DUK_TYPE_MASK_NULL                (1 << DUK_TYPE_NULL)
-#define DUK_TYPE_MASK_BOOLEAN             (1 << DUK_TYPE_BOOLEAN)
-#define DUK_TYPE_MASK_NUMBER              (1 << DUK_TYPE_NUMBER)
-#define DUK_TYPE_MASK_STRING              (1 << DUK_TYPE_STRING)
-#define DUK_TYPE_MASK_OBJECT              (1 << DUK_TYPE_OBJECT)
-#define DUK_TYPE_MASK_BUFFER              (1 << DUK_TYPE_BUFFER)
-#define DUK_TYPE_MASK_POINTER             (1 << DUK_TYPE_POINTER)
-#define DUK_TYPE_MASK_LIGHTFUNC           (1 << DUK_TYPE_LIGHTFUNC)
-#define DUK_TYPE_MASK_THROW               (1 << 10)  /* internal flag value: throw if mask doesn't match */
-#define DUK_TYPE_MASK_PROMOTE             (1 << 11)  /* internal flag value: promote to object if mask matches */
+#define DUK_TYPE_MASK_NONE                (1U << DUK_TYPE_NONE)
+#define DUK_TYPE_MASK_UNDEFINED           (1U << DUK_TYPE_UNDEFINED)
+#define DUK_TYPE_MASK_NULL                (1U << DUK_TYPE_NULL)
+#define DUK_TYPE_MASK_BOOLEAN             (1U << DUK_TYPE_BOOLEAN)
+#define DUK_TYPE_MASK_NUMBER              (1U << DUK_TYPE_NUMBER)
+#define DUK_TYPE_MASK_STRING              (1U << DUK_TYPE_STRING)
+#define DUK_TYPE_MASK_OBJECT              (1U << DUK_TYPE_OBJECT)
+#define DUK_TYPE_MASK_BUFFER              (1U << DUK_TYPE_BUFFER)
+#define DUK_TYPE_MASK_POINTER             (1U << DUK_TYPE_POINTER)
+#define DUK_TYPE_MASK_LIGHTFUNC           (1U << DUK_TYPE_LIGHTFUNC)
+#define DUK_TYPE_MASK_THROW               (1U << 10)  /* internal flag value: throw if mask doesn't match */
+#define DUK_TYPE_MASK_PROMOTE             (1U << 11)  /* internal flag value: promote to object if mask matches */
 
 /* Coercion hints */
 #define DUK_HINT_NONE                     0    /* prefer number, unless input is a Date, in which
@@ -324,52 +327,83 @@
 #define DUK_HINT_NUMBER                   2    /* prefer number */
 
 /* Enumeration flags for duk_enum() */
-#define DUK_ENUM_INCLUDE_NONENUMERABLE    (1 << 0)    /* enumerate non-numerable properties in addition to enumerable */
-#define DUK_ENUM_INCLUDE_HIDDEN           (1 << 1)    /* enumerate hidden symbols too (in Duktape 1.x called internal properties) */
-#define DUK_ENUM_INCLUDE_SYMBOLS          (1 << 2)    /* enumerate symbols */
-#define DUK_ENUM_EXCLUDE_STRINGS          (1 << 3)    /* exclude strings */
-#define DUK_ENUM_OWN_PROPERTIES_ONLY      (1 << 4)    /* don't walk prototype chain, only check own properties */
-#define DUK_ENUM_ARRAY_INDICES_ONLY       (1 << 5)    /* only enumerate array indices */
-#define DUK_ENUM_SORT_ARRAY_INDICES       (1 << 6)    /* sort array indices (applied to full enumeration result, including inherited array indices) */
-#define DUK_ENUM_NO_PROXY_BEHAVIOR        (1 << 7)    /* enumerate a proxy object itself without invoking proxy behavior */
+#define DUK_ENUM_INCLUDE_NONENUMERABLE    (1U << 0)    /* enumerate non-numerable properties in addition to enumerable */
+#define DUK_ENUM_INCLUDE_HIDDEN           (1U << 1)    /* enumerate hidden symbols too (in Duktape 1.x called internal properties) */
+#define DUK_ENUM_INCLUDE_SYMBOLS          (1U << 2)    /* enumerate symbols */
+#define DUK_ENUM_EXCLUDE_STRINGS          (1U << 3)    /* exclude strings */
+#define DUK_ENUM_OWN_PROPERTIES_ONLY      (1U << 4)    /* don't walk prototype chain, only check own properties */
+#define DUK_ENUM_ARRAY_INDICES_ONLY       (1U << 5)    /* only enumerate array indices */
+/* XXX: misleading name */
+#define DUK_ENUM_SORT_ARRAY_INDICES       (1U << 6)    /* sort array indices (applied to full enumeration result, including inherited array indices); XXX: misleading name */
+#define DUK_ENUM_NO_PROXY_BEHAVIOR        (1U << 7)    /* enumerate a proxy object itself without invoking proxy behavior */
 
 /* Compilation flags for duk_compile() and duk_eval() */
 /* DUK_COMPILE_xxx bits 0-2 are reserved for an internal 'nargs' argument.
  */
-#define DUK_COMPILE_EVAL                  (1 << 3)    /* compile eval code (instead of global code) */
-#define DUK_COMPILE_FUNCTION              (1 << 4)    /* compile function code (instead of global code) */
-#define DUK_COMPILE_STRICT                (1 << 5)    /* use strict (outer) context for global, eval, or function code */
-#define DUK_COMPILE_SHEBANG               (1 << 6)    /* allow shebang ('#! ...') comment on first line of source */
-#define DUK_COMPILE_SAFE                  (1 << 7)    /* (internal) catch compilation errors */
-#define DUK_COMPILE_NORESULT              (1 << 8)    /* (internal) omit eval result */
-#define DUK_COMPILE_NOSOURCE              (1 << 9)    /* (internal) no source string on stack */
-#define DUK_COMPILE_STRLEN                (1 << 10)   /* (internal) take strlen() of src_buffer (avoids double evaluation in macro) */
-#define DUK_COMPILE_NOFILENAME            (1 << 11)   /* (internal) no filename on stack */
-#define DUK_COMPILE_FUNCEXPR              (1 << 12)   /* (internal) source is a function expression (used for Function constructor) */
+#define DUK_COMPILE_EVAL                  (1U << 3)    /* compile eval code (instead of global code) */
+#define DUK_COMPILE_FUNCTION              (1U << 4)    /* compile function code (instead of global code) */
+#define DUK_COMPILE_STRICT                (1U << 5)    /* use strict (outer) context for global, eval, or function code */
+#define DUK_COMPILE_SHEBANG               (1U << 6)    /* allow shebang ('#! ...') comment on first line of source */
+#define DUK_COMPILE_SAFE                  (1U << 7)    /* (internal) catch compilation errors */
+#define DUK_COMPILE_NORESULT              (1U << 8)    /* (internal) omit eval result */
+#define DUK_COMPILE_NOSOURCE              (1U << 9)    /* (internal) no source string on stack */
+#define DUK_COMPILE_STRLEN                (1U << 10)   /* (internal) take strlen() of src_buffer (avoids double evaluation in macro) */
+#define DUK_COMPILE_NOFILENAME            (1U << 11)   /* (internal) no filename on stack */
+#define DUK_COMPILE_FUNCEXPR              (1U << 12)   /* (internal) source is a function expression (used for Function constructor) */
 
-/* Flags for duk_def_prop() and its variants */
-#define DUK_DEFPROP_WRITABLE              (1 << 0)    /* set writable (effective if DUK_DEFPROP_HAVE_WRITABLE set) */
-#define DUK_DEFPROP_ENUMERABLE            (1 << 1)    /* set enumerable (effective if DUK_DEFPROP_HAVE_ENUMERABLE set) */
-#define DUK_DEFPROP_CONFIGURABLE          (1 << 2)    /* set configurable (effective if DUK_DEFPROP_HAVE_CONFIGURABLE set) */
-#define DUK_DEFPROP_HAVE_WRITABLE         (1 << 3)    /* set/clear writable */
-#define DUK_DEFPROP_HAVE_ENUMERABLE       (1 << 4)    /* set/clear enumerable */
-#define DUK_DEFPROP_HAVE_CONFIGURABLE     (1 << 5)    /* set/clear configurable */
-#define DUK_DEFPROP_HAVE_VALUE            (1 << 6)    /* set value (given on value stack) */
-#define DUK_DEFPROP_HAVE_GETTER           (1 << 7)    /* set getter (given on value stack) */
-#define DUK_DEFPROP_HAVE_SETTER           (1 << 8)    /* set setter (given on value stack) */
-#define DUK_DEFPROP_FORCE                 (1 << 9)    /* force change if possible, may still fail for e.g. virtual properties */
+/* Flags for duk_def_prop() and its variants; base flags + a lot of convenience shorthands */
+#define DUK_DEFPROP_WRITABLE              (1U << 0)    /* set writable (effective if DUK_DEFPROP_HAVE_WRITABLE set) */
+#define DUK_DEFPROP_ENUMERABLE            (1U << 1)    /* set enumerable (effective if DUK_DEFPROP_HAVE_ENUMERABLE set) */
+#define DUK_DEFPROP_CONFIGURABLE          (1U << 2)    /* set configurable (effective if DUK_DEFPROP_HAVE_CONFIGURABLE set) */
+#define DUK_DEFPROP_HAVE_WRITABLE         (1U << 3)    /* set/clear writable */
+#define DUK_DEFPROP_HAVE_ENUMERABLE       (1U << 4)    /* set/clear enumerable */
+#define DUK_DEFPROP_HAVE_CONFIGURABLE     (1U << 5)    /* set/clear configurable */
+#define DUK_DEFPROP_HAVE_VALUE            (1U << 6)    /* set value (given on value stack) */
+#define DUK_DEFPROP_HAVE_GETTER           (1U << 7)    /* set getter (given on value stack) */
+#define DUK_DEFPROP_HAVE_SETTER           (1U << 8)    /* set setter (given on value stack) */
+#define DUK_DEFPROP_FORCE                 (1U << 9)    /* force change if possible, may still fail for e.g. virtual properties */
 #define DUK_DEFPROP_SET_WRITABLE          (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE)
 #define DUK_DEFPROP_CLEAR_WRITABLE        DUK_DEFPROP_HAVE_WRITABLE
 #define DUK_DEFPROP_SET_ENUMERABLE        (DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE)
 #define DUK_DEFPROP_CLEAR_ENUMERABLE      DUK_DEFPROP_HAVE_ENUMERABLE
 #define DUK_DEFPROP_SET_CONFIGURABLE      (DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE)
 #define DUK_DEFPROP_CLEAR_CONFIGURABLE    DUK_DEFPROP_HAVE_CONFIGURABLE
+#define DUK_DEFPROP_W                     DUK_DEFPROP_WRITABLE
+#define DUK_DEFPROP_E                     DUK_DEFPROP_ENUMERABLE
+#define DUK_DEFPROP_C                     DUK_DEFPROP_CONFIGURABLE
+#define DUK_DEFPROP_WE                    (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE)
+#define DUK_DEFPROP_WC                    (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_CONFIGURABLE)
+#define DUK_DEFPROP_WEC                   (DUK_DEFPROP_WRITABLE | DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_CONFIGURABLE)
+#define DUK_DEFPROP_HAVE_W                DUK_DEFPROP_HAVE_WRITABLE
+#define DUK_DEFPROP_HAVE_E                DUK_DEFPROP_HAVE_ENUMERABLE
+#define DUK_DEFPROP_HAVE_C                DUK_DEFPROP_HAVE_CONFIGURABLE
+#define DUK_DEFPROP_HAVE_WE               (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE)
+#define DUK_DEFPROP_HAVE_WC               (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_CONFIGURABLE)
+#define DUK_DEFPROP_HAVE_WEC              (DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE)
+#define DUK_DEFPROP_SET_W                 DUK_DEFPROP_SET_WRITABLE
+#define DUK_DEFPROP_SET_E                 DUK_DEFPROP_SET_ENUMERABLE
+#define DUK_DEFPROP_SET_C                 DUK_DEFPROP_SET_CONFIGURABLE
+#define DUK_DEFPROP_SET_WE                (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE)
+#define DUK_DEFPROP_SET_WC                (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE)
+#define DUK_DEFPROP_SET_WEC               (DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_ENUMERABLE | DUK_DEFPROP_SET_CONFIGURABLE)
+#define DUK_DEFPROP_CLEAR_W               DUK_DEFPROP_CLEAR_WRITABLE
+#define DUK_DEFPROP_CLEAR_E               DUK_DEFPROP_CLEAR_ENUMERABLE
+#define DUK_DEFPROP_CLEAR_C               DUK_DEFPROP_CLEAR_CONFIGURABLE
+#define DUK_DEFPROP_CLEAR_WE              (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE)
+#define DUK_DEFPROP_CLEAR_WC              (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE)
+#define DUK_DEFPROP_CLEAR_WEC             (DUK_DEFPROP_CLEAR_WRITABLE | DUK_DEFPROP_CLEAR_ENUMERABLE | DUK_DEFPROP_CLEAR_CONFIGURABLE)
+#define DUK_DEFPROP_ATTR_W                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_W)
+#define DUK_DEFPROP_ATTR_E                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_E)
+#define DUK_DEFPROP_ATTR_C                (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_C)
+#define DUK_DEFPROP_ATTR_WE               (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WE)
+#define DUK_DEFPROP_ATTR_WC               (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WC)
+#define DUK_DEFPROP_ATTR_WEC              (DUK_DEFPROP_HAVE_WEC | DUK_DEFPROP_WEC)
 
 /* Flags for duk_push_thread_raw() */
-#define DUK_THREAD_NEW_GLOBAL_ENV         (1 << 0)    /* create a new global environment */
+#define DUK_THREAD_NEW_GLOBAL_ENV         (1U << 0)    /* create a new global environment */
 
 /* Flags for duk_gc() */
-#define DUK_GC_COMPACT                    (1 << 0)    /* compact heap objects */
+#define DUK_GC_COMPACT                    (1U << 0)    /* compact heap objects */
 
 /* Error codes (must be 8 bits at most, see duk_error.h) */
 #define DUK_ERR_NONE                      0    /* no error (e.g. from duk_get_error_code()) */
@@ -400,6 +434,23 @@
 #define DUK_LEVEL_DDDEBUG                 2
 
 /*
+ *  Macros to create Symbols as C statically constructed strings.
+ *
+ *  Call e.g. as DUK_HIDDEN_SYMBOL("myProperty") <=> ("\xFF" "myProperty").
+ *  Local symbols have a unique suffix, caller should take care to avoid
+ *  conflicting with the Duktape internal representation by e.g. prepending
+ *  a '!' character: DUK_LOCAL_SYMBOL("myLocal", "!123").
+ *
+ *  Note that these can only be used for string constants, not dynamically
+ *  created strings.
+ */
+
+#define DUK_HIDDEN_SYMBOL(x)     ("\xFF" x)
+#define DUK_GLOBAL_SYMBOL(x)     ("\x80" x)
+#define DUK_LOCAL_SYMBOL(x,uniq) ("\x81" x "\xff" uniq)
+#define DUK_WELLKNOWN_SYMBOL(x)  ("\x81" x "\xff")
+
+/*
  *  If no variadic macros, __FILE__ and __LINE__ are passed through globals
  *  which is ugly and not thread safe.
  */
@@ -631,6 +682,7 @@
 DUK_EXTERNAL_DECL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_idx_t nargs);
 DUK_EXTERNAL_DECL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic);
 DUK_EXTERNAL_DECL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags);
+DUK_EXTERNAL_DECL duk_idx_t duk_push_proxy(duk_context *ctx, duk_uint_t proxy_flags);
 
 #define duk_push_thread(ctx) \
 	duk_push_thread_raw((ctx), 0 /*flags*/)
@@ -734,6 +786,8 @@
 
 #define duk_is_callable(ctx,idx) \
 	duk_is_function((ctx), (idx))
+DUK_EXTERNAL_DECL duk_bool_t duk_is_constructable(duk_context *ctx, duk_idx_t idx);
+
 DUK_EXTERNAL_DECL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx);
 DUK_EXTERNAL_DECL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t idx);
 DUK_EXTERNAL_DECL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx);
@@ -850,6 +904,7 @@
 DUK_EXTERNAL_DECL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t idx);
 DUK_EXTERNAL_DECL const char *duk_require_string(duk_context *ctx, duk_idx_t idx);
 DUK_EXTERNAL_DECL const char *duk_require_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len);
+DUK_EXTERNAL_DECL void duk_require_object(duk_context *ctx, duk_idx_t idx);
 DUK_EXTERNAL_DECL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
 DUK_EXTERNAL_DECL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size);
 DUK_EXTERNAL_DECL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx);
@@ -954,18 +1009,22 @@
 DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
 DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
 DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
+DUK_EXTERNAL_DECL duk_bool_t duk_get_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
 DUK_EXTERNAL_DECL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx);
 DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
 DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
 DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
+DUK_EXTERNAL_DECL duk_bool_t duk_put_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
 DUK_EXTERNAL_DECL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_idx);
 DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
 DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
 DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
+DUK_EXTERNAL_DECL duk_bool_t duk_del_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
 DUK_EXTERNAL_DECL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_idx);
 DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key);
 DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_lstring(duk_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len);
 DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx);
+DUK_EXTERNAL_DECL duk_bool_t duk_has_prop_heapptr(duk_context *ctx, duk_idx_t obj_idx, void *ptr);
 
 DUK_EXTERNAL_DECL void duk_get_prop_desc(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags);
 DUK_EXTERNAL_DECL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t flags);
@@ -1024,6 +1083,8 @@
 DUK_EXTERNAL_DECL void duk_compact(duk_context *ctx, duk_idx_t obj_idx);
 DUK_EXTERNAL_DECL void duk_enum(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t enum_flags);
 DUK_EXTERNAL_DECL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_idx, duk_bool_t get_value);
+DUK_EXTERNAL_DECL void duk_seal(duk_context *ctx, duk_idx_t obj_idx);
+DUK_EXTERNAL_DECL void duk_freeze(duk_context *ctx, duk_idx_t obj_idx);
 
 /*
  *  String manipulation