From 94ec7c778f0a98b91b6518575748875b6034edf0 Mon Sep 17 00:00:00 2001 From: Daniel-Constantin Mierla Date: Sat, 23 Jun 2018 13:09:52 +0200 Subject: [PATCH] app_jsdt: updated duktape js engine to v2.2.1 --- src/modules/app_jsdt/duk_config.h | 124 +- src/modules/app_jsdt/duktape.c | 26518 +++++++++++++++------------- src/modules/app_jsdt/duktape.h | 189 +- 3 files changed, 14375 insertions(+), 12456 deletions(-) diff --git a/src/modules/app_jsdt/duk_config.h b/src/modules/app_jsdt/duk_config.h index 7c53949353c..51e973f89f6 100644 --- a/src/modules/app_jsdt/duk_config.h +++ b/src/modules/app_jsdt/duk_config.h @@ -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,17 +540,40 @@ /* Windows 32-bit and 64-bit are currently the same. */ /* MSVC does not have sys/param.h */ + +#if defined(DUK_COMPILING_DUKTAPE) +/* Only include when compiling Duktape to avoid polluting application build + * with a lot of unnecessary defines. + */ +#include +#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. */ -#if defined(DUK_COMPILING_DUKTAPE) -/* Only include when compiling Duktape to avoid polluting application build - * with a lot of unnecessary defines. + +/* 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 */ -#include +#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" @@ -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 unsigned long long duk_uint64_t; 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 @@ typedef duk_uint_fast16_t duk_small_uint_fast_t; #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 @@ typedef double duk_double_t; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; #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 @@ typedef struct duk_hthread duk_context; /* 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 */ /* diff --git a/src/modules/app_jsdt/duktape.c b/src/modules/app_jsdt/duktape.c index ba4572cb83b..ba55550ed25 100644 --- a/src/modules/app_jsdt/duktape.c +++ b/src/modules/app_jsdt/duktape.c @@ -1,8 +1,8 @@ /* - * Single source autogenerated distributable for Duktape 2.1.0. + * Single source autogenerated distributable for Duktape 2.2.1. * - * 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. @@ -84,6 +84,9 @@ * * Remko Tron\u00e7on (https://el-tramo.be) * * Romero Malaquias (rbsm@ic.ufal.br) * * Michael Drake +* * Steven Don (https://github.com/shdon) +* * Simon Stone (https://github.com/sstone1) +* * \J. McC. (https://github.com/jmhmccr) * * Other contributions * =================== @@ -411,47 +414,47 @@ typedef union duk_double_union duk_double_union; #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 @@ typedef union duk_double_union duk_double_union; /* 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_hstring_external; 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_hstring_external duk_hstring_external; 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 @@ typedef struct { } 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 @@ typedef struct { #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 @@ typedef struct { (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 @@ typedef struct { 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 @@ typedef struct { #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 @@ typedef struct { (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 @@ DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv #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 @@ DUK_INTERNAL_DECL duk_double_t duk_tval_get_number_unpacked_fastint(duk_tval *tv /* 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 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double #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_value_of(duk_context *ctx); 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_linenumber_setter(duk_context 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_yield(duk_context *ctx); 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_encoding_getter(duk_con 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 @@ DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[166]; #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 @@ struct duk_bitencoder_ctx { /* * 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 @@ struct duk_bufwriter_ctx { 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 @@ struct duk_bufwriter_ctx { 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 @@ struct duk_bufwriter_ctx { 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 @@ struct duk_bufwriter_ctx { #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #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 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); /* 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 @@ typedef duk_uint32_t duk_instr_t; /* 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 @@ typedef duk_uint32_t duk_instr_t; #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 @@ typedef duk_uint32_t duk_instr_t; /* 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_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_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_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 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo #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 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo * 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 @@ struct duk_token { /* 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 @@ DUK_INTERNAL_DECL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_ * 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; +#define DUK_REGCONST_CONST_MARKER DUK_INT32_MIN /* = -0x80000000 */ -/* type to represent a straight register reference, with <0 indicating none */ -typedef duk_int32_t duk_reg_t; +/* 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 @@ struct duk_compiler_instr { * 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 @@ struct duk_compiler_func { 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 @@ struct duk_compiler_func { 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 @@ DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_b #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 @@ struct duk_heaphdr_string { #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 @@ DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h) #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 @@ DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h) */ #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_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_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_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_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_m2(duk_context *ctx); +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); +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_context *ctx, duk_idx_t idx); +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_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 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_this_coercible_to_string(duk_hthread *thr); -DUK_INTERNAL_DECL duk_hstring *duk_push_uint_to_hstring(duk_context *ctx, duk_uint_t i); +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); +DUK_INTERNAL_DECL void *duk_get_voidptr(duk_hthread *thr, 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_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_context *ctx, duk_tval *tv); +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_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_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_context *ctx, 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_context *ctx); -DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_context *ctx); +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); +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_context *ctx, duk_tval *tv); +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_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_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 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_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 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); +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_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 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_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 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 @@ DUK_INTERNAL_DECL const char *duk_push_string_tval_readable_error(duk_context *c * 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_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_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_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_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_context *ctx, duk_idx_t obj_idx, duk_small_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)))) + duk_del_prop_stridx_short_raw((thr), (((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)) +#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_context *ctx, duk_idx_t obj_idx, duk_small_uint_t 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)))) + duk_has_prop_stridx_short_raw((thr), (((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)) +#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_context *ctx, duk_idx_t obj_idx, duk_small_uint_t desc_flags); /* [key val] -> [] */ +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_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx, duk_small_uint_t desc_flags); /* [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))) + 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(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) +#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) -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); /* [] -> [] */ +#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_context *ctx, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ +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_context *ctx, duk_idx_t count); +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); +DUK_INTERNAL_DECL void duk_unpack(duk_hthread *thr); #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_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 void duk_resolve_nonbound_function(duk_context *ctx); +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 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_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_context *ctx); +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 @@ DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); #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_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); (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 @@ struct duk_hstring_external { */ 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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); */ #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); 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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); /* 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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); /* 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_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); 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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); */ #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 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); } 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_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t ho 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_unchecked(duk_heap *heap, duk_u 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_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr, 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_delprop(duk_hthread *thr, duk_tval *tv_ 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 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj #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 @@ DUK_INTERNAL_DECL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject * /* 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 @@ DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_ho /* 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 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_prototype_chain_contains(duk_hthread *t /* 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 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_function_prototype(duk_context *ctx); #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 @@ struct duk_hnatfunc { * 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 @@ struct duk_hbufobj { 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 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx /* * 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 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx * 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. - */ -#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. - */ +/* Initial valstack size, roughly 0.7kiB. */ +#define DUK_VALSTACK_INITIAL_SIZE 96U -/* 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. +/* 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 -#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 +/* 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 /* * 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_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) +#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 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx #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 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx * 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 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx * 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 @@ struct duk_activation { #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. + * 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 idx_bottom; + 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). + * 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 idx_retval; + duk_size_t retval_byteoff; - /* Current 'this' binding is the value just below idx_bottom. + /* 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. + * 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; }; -/* Note: it's nice if size is 2^N (not 4x4 = 16 bytes on 32 bit) */ 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 @@ struct duk_hthread { 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). + /* 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_end[ is - * not GC-reachable but kept initialized as 'undefined'. + /* 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 void duk_hthread_create_builtin_objects(duk_hthread *thr); 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 @@ DUK_INTERNAL_DECL void duk_hthread_sync_and_null_currpc(duk_hthread *thr); /* #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 @@ struct duk_hdecenv { */ duk_hthread *thread; duk_hobject *varmap; - duk_size_t regbase; + duk_size_t regbase_byteoff; }; struct duk_hobjenv { @@ -7700,9 +7895,6 @@ struct duk_hobjenv { * 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 @@ struct duk_hbuffer { * 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_resize(duk_hthread *thr, duk_hbuffer_dynamic 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 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * 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 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * } 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 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * /* 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 @@ -8078,6 +8294,18 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * } while (0) #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 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); /* * 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 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); /* 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 @@ struct duk_heap { #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 @@ struct duk_heap { */ 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 @@ struct duk_heap { 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). */ @@ -8504,6 +8757,51 @@ struct duk_heap { #else 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(duk_heap *heap, void *ptr, duk_size 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 @@ DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uin /* 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_attached(duk_heap *heap); 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 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #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 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #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 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #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 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #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) @@ -9322,6 +9638,19 @@ DUK_INTERNAL_DECL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb); #define DUK_ASSERT_VS_SPACE(thr) \ 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 * @@ -9361,8 +9690,11 @@ DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_create_and_throw(duk_hthread *thr, d 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_uint8_t duk_unicode_caseconv_lc[680]; 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 @@ DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp) #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 @@ typedef struct { 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 @@ typedef struct { #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_string(const duk_uint8_t *s 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 void duk_js_putvar_activation(duk_hthread *thr, duk_activation 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 @@ DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr); /* 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 @@ DUK_INTERNAL_DECL void duk_js_execute_bytecode(duk_hthread *exec_thr); #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_year_in_valid_range(duk_double_t year); 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(duk_double_t 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 @@ DUK_INTERNAL_DECL duk_uint_t duk_selftest_run_tests(duk_alloc_function alloc_fun #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 @@ DUK_INTERNAL void duk_debug_log(const char *fmt, ...) { #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 @@ DUK_INTERNAL const duk_uint8_t duk_strings_data[903] = { 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_INTERNAL const duk_c_function duk_bi_native_functions[166] = { 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_INTERNAL const duk_c_function duk_bi_native_functions[166] = { 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_INTERNAL const duk_c_function duk_bi_native_functions[166] = { 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 @@ DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, duk_errcode_t #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 @@ DUK_INTERNAL DUK_COLD void duk_err_type_invalid_trap_result(duk_hthread *thr, co * 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 @@ DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *ms /* 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 duk__slow_case_conversion(duk_hthread *thr, duk_codepoint_t start_i; duk_codepoint_t start_o; - DUK_UNREF(thr); DUK_ASSERT(bd_ctx != NULL); + DUK_UNREF(thr); DUK_DDD(DUK_DDDPRINT("slow case conversion for codepoint: %ld", (long) cp)); @@ -12153,15 +12545,14 @@ duk_codepoint_t duk__case_transform_helper(duk_hthread *thr, * 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_INTERNAL void duk_unicode_case_convert_string(duk_hthread *thr, duk_small_in } 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 @@ DUK_INTERNAL duk_bool_t duk_double_is_nan_or_inf(duk_double_t x) { */ #if defined(DUK_USE_64BIT_OPS) #if defined(DUK_USE_DOUBLE_ME) - return (du.ull[DUK_DBL_IDX_ULL0] & 0x000000007ff00000ULL) == 0x000000007ff00000ULL; + return (du.ull[DUK_DBL_IDX_ULL0] & DUK_U64_CONSTANT(0x000000007ff00000)) == DUK_U64_CONSTANT(0x000000007ff00000); #else - return (du.ull[DUK_DBL_IDX_ULL0] & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL; + 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 @@ DUK_INTERNAL duk_bool_t duk_double_is_nan_zero_inf(duk_double_t x) { 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_small_uint_t duk_double_signbit(duk_double_t x) { 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 @@ DUK_INTERNAL void duk_default_free_function(void *udata, void *ptr) { /* #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); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, idx); + 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 @@ DUK_EXTERNAL void *duk_resize_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t 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); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_dynamic *) duk_require_hbuffer(ctx, idx); + 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 @@ DUK_EXTERNAL void *duk_steal_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t 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); + DUK_ASSERT_API_ENTRY(thr); - h = (duk_hbuffer_external *) duk_require_hbuffer(ctx, idx); + h = (duk_hbuffer_external *) duk_require_hbuffer(thr, idx); DUK_ASSERT(h != NULL); if (!DUK_HBUFFER_HAS_EXTERNAL(h)) { @@ -12987,31 +13375,31 @@ DUK_EXTERNAL void duk_config_buffer(duk_context *ctx, duk_idx_t idx, void *ptr, #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_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, d 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 @@ DUK_LOCAL duk_uint8_t *duk__dump_buffer_prop(duk_hthread *thr, duk_uint8_t *p, d 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 @@ DUK_LOCAL duk_uint8_t *duk__dump_uint32_prop(duk_hthread *thr, duk_uint8_t *p, d } 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 @@ DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bu #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 @@ DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_b 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; + duk_harray *h; + duk_uint32_t i; - h = DUK_TVAL_GET_OBJECT(tv); + /* 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); + DUK_ASSERT(DUK_HOBJECT_IS_ARRAY((duk_hobject *) h)); + DUK_ASSERT(h->length <= DUK_HOBJECT_GET_ASIZE((duk_hobject *) h)); - /* 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++) { + 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_TVAL_IS_STRING(tv_val)); - 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); - } + 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 empty list")); + 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 */ } - p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p); - DUK_RAW_WRITE_U32_BE(p, 0); /* end of _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 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu 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 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu 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 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu 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 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu * 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 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t /* 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t /* 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t 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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t func_env = (duk_hobject *) new_env; - duk_push_hobject(ctx, (duk_hobject *) new_env); + duk_push_hobject(thr, (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_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 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t DUK_HOBJECT_INCREF(thr, func_env); DUK_HOBJECT_INCREF(thr, func_env); if (need_pop) { - duk_pop(ctx); + duk_pop(thr); } - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); + 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_push_u32(thr, tmp32); + duk_put_prop(thr, -3); } - duk_compact_m1(ctx); - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); + duk_compact_m1(thr); + duk_xdef_prop_stridx_short(thr, -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. + /* _Formals may have been missing in the original function, which is + * handled using a marker length. */ - 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; + 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_put_prop_index(ctx, -2, arr_idx); - } - if (arr_idx == 0 && h_fun->nargs == 0) { - duk_pop(ctx); + duk_compact_m1(thr); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); } else { - duk_compact_m1(ctx); - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); + 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. + * For now, bound functions are rejected with TypeError. */ - func = duk_require_hcompfunc(ctx, -1); + func = duk_require_hcompfunc(thr, -1); DUK_ASSERT(func != NULL); DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj)); @@ -13684,26 +14065,22 @@ DUK_EXTERNAL void duk_dump_function(duk_context *ctx) { 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_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1))); - duk_remove_m2(ctx); /* [ ... func buf ] -> [ ... buf ] */ + duk_remove_m2(thr); /* [ ... func buf ] -> [ ... buf ] */ } -DUK_EXTERNAL void duk_load_function(duk_context *ctx) { - duk_hthread *thr; +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); + DUK_ASSERT_API_ENTRY(thr); - p_buf = (duk_uint8_t *) duk_require_buffer(ctx, -1, &sz); + 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 @@ DUK_EXTERNAL void duk_load_function(duk_context *ctx) { * (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++; - p = duk__load_func(ctx, p, p_end); + 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_dump_function(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_UNSUPPORTED(thr); } -DUK_EXTERNAL void duk_load_function(duk_context *ctx) { - DUK_ERROR_UNSUPPORTED((duk_hthread *) ctx); +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 @@ DUK_EXTERNAL void duk_load_function(duk_context *ctx) { /* 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_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(ctx, -1))); + 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); + DUK_ASSERT_API_ENTRY(thr); - 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); - } + idx_func = duk__call_get_idx_func(thr, nargs, 1); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - /* XXX: awkward; we assume there is space for this, overwrite - * directly instead? - */ - duk_push_undefined(ctx); - duk_insert(ctx, idx_func + 1); + 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_handle_call_unprotected(thr, idx_func, call_flags); } -DUK_EXTERNAL void duk_call_method(duk_context *ctx, duk_idx_t nargs) { - duk_hthread *thr = (duk_hthread *) ctx; +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); + DUK_ASSERT_API_ENTRY(thr); - 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); - } + 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_handle_call_unprotected(thr, idx_func, call_flags); } -DUK_EXTERNAL void duk_call_prop(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs) { +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. + * the 'this' and 'func' values to handle bound functions, which is now + * done "in-place", so this is not a trivial change. */ - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj_idx = duk_require_normalize_index(ctx, obj_idx); /* make absolute */ + 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(ctx, obj_idx, nargs); + duk__call_prop_prep_stack(thr, obj_idx, nargs); - duk_call_method(ctx, nargs); + duk_call_method(thr, 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; +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_int_t ret; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_ASSERT_CTX_VALID(thr); + DUK_ASSERT(udata != 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. - */ + 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 */ } + args.call_flags = 0; + + return duk_safe_call(thr, duk__pcall_raw, (void *) &args /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); +} - /* awkward; we assume there is space for this */ - duk_push_undefined(ctx); - duk_insert(ctx, idx_func + 1); +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 ret; - call_flags = 0; /* respect reclimit, not constructor */ + DUK_ASSERT_CTX_VALID(thr); + DUK_ASSERT(udata != NULL); - rc = duk_handle_call_protected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ + args = (duk__pcall_method_args *) udata; - return rc; + 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_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; - duk_idx_t idx_func; - duk_int_t rc; +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_CTX_VALID(ctx); - DUK_ASSERT(thr != NULL); + DUK_ASSERT_API_ENTRY(thr); - idx_func = duk_get_top(ctx) - nargs - 2; /* must work for nargs <= 0 */ - if (idx_func < 0 || nargs < 0) { - /* See comments in duk_pcall(). */ + args.nargs = nargs; + if (DUK_UNLIKELY(nargs < 0)) { DUK_ERROR_TYPE_INVALID_ARGS(thr); return DUK_EXEC_ERROR; /* unreachable */ } + args.call_flags = call_flags; - call_flags = 0; /* respect reclimit, not constructor */ + return duk_safe_call(thr, duk__pcall_method_raw, (void *) &args /*udata*/, nargs + 2 /*nargs*/, 1 /*nrets*/); +} - rc = duk_handle_call_protected(thr, /* thread */ - nargs, /* num_stack_args */ - call_flags); /* call_flags */ +DUK_EXTERNAL duk_int_t duk_pcall_method(duk_hthread *thr, duk_idx_t nargs) { + DUK_ASSERT_API_ENTRY(thr); - return rc; + return duk_pcall_method_flags(thr, nargs, 0); } -struct duk__pcall_prop_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_LOCAL duk_ret_t duk__pcall_prop_raw(duk_hthread *thr, void *udata) { duk__pcall_prop_args *args; + duk_idx_t obj_idx; + duk_int_t ret; - DUK_ASSERT_CTX_VALID(ctx); + 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); + 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_context *ctx, duk_idx_t obj_idx, duk_idx_t nargs) { +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; + if (DUK_UNLIKELY(nargs < 0)) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); + return DUK_EXEC_ERROR; /* unreachable */ + } + args.call_flags = 0; - /* 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*/); + 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_context *ctx, duk_safe_call_function func, void *udata, duk_idx_t nargs, duk_idx_t nrets) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_EXTERNAL duk_int_t duk_safe_call(duk_context *ctx, duk_safe_call_function fu 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); - } +DUK_EXTERNAL void duk_new(duk_hthread *thr, duk_idx_t nargs) { + duk_idx_t idx_func; - /* - * 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. - */ + DUK_ASSERT_API_ENTRY(thr); -#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 + idx_func = duk__call_get_idx_func(thr, nargs, 1); + DUK_ASSERT(duk_is_valid_index(thr, idx_func)); - /* [... retval] */ + duk_push_object(thr); /* default instance; internal proto updated by call handling */ + duk_insert(thr, idx_func + 1); - 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_handle_call_unprotected(thr, idx_func, DUK_CALL_FLAG_CONSTRUCT); } -DUK_LOCAL duk_ret_t duk__pnew_helper(duk_context *ctx, void *udata) { +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); + duk_new(thr, nargs); return 1; } -DUK_EXTERNAL duk_int_t duk_pnew(duk_context *ctx, duk_idx_t nargs) { +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. + * simply use a protected duk_handle_call() because pushing the + * default instance might throw. */ - rc = duk_safe_call(ctx, duk__pnew_helper, (void *) &nargs /*udata*/, nargs + 1 /*nargs*/, 1 /*nrets*/); + 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 @@ DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_context *ctx) { /* 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_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_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_context *ctx) { * 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 @@ DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_context *ctx) { * 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 @@ DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) { 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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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 @@ DUK_EXTERNAL duk_int_t duk_get_magic(duk_context *ctx, duk_idx_t idx) { 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); + DUK_ASSERT_API_ENTRY(thr); - nf = duk_require_hnatfunc(ctx, idx); + nf = duk_require_hnatfunc(thr, idx); DUK_ASSERT(nf != NULL); nf->magic = (duk_int16_t) magic; } @@ -14342,30 +14617,40 @@ DUK_EXTERNAL void duk_set_magic(duk_context *ctx, duk_idx_t idx, duk_int_t magic * Misc helpers */ -DUK_INTERNAL void duk_resolve_nonbound_function(duk_context *ctx) { - duk_uint_t sanity; +/* 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; - 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; + DUK_ASSERT_HTHREAD_VALID(thr); - 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; + 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)); } - } while (--sanity > 0); + } + + /* Lightfuncs cannot be bound but are always callable and + * constructable. + */ } #line 1 "duk_api_codec.c" /* @@ -14382,21 +14667,21 @@ DUK_INTERNAL void duk_resolve_nonbound_function(duk_context *ctx) { * 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 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ } #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 @@ DUK_EXTERNAL const char *duk_base64_encode(duk_context *ctx, duk_idx_t idx) { 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_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t idx) { } /* 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_ERROR_TYPE(thr, DUK_STR_BASE64_DECODE_FAILED); } -DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx) { +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_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx) { duk_uint16_t *p16; #endif - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(ctx, idx); - inp = duk__prep_codec_arg(ctx, idx, &len); + 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 @@ DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t idx) { * 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_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t idx) { duk_size_t len_safe; #endif - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(ctx, idx); - inp = duk__prep_codec_arg(ctx, idx, &len); + 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 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t idx) { } /* 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 @@ DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t idx) { } #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); + DUK_ASSERT_API_ENTRY(thr); #if defined(DUK_USE_ASSERTIONS) - top_at_entry = duk_get_top(ctx); + top_at_entry = duk_get_top(thr); #endif - idx = duk_require_normalize_index(ctx, idx); - duk_bi_json_stringify_helper(ctx, + 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_is_string(thr, -1)); + duk_replace(thr, idx); + ret = duk_get_string(thr, idx); - DUK_ASSERT(duk_get_top(ctx) == top_at_entry); + 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); + DUK_ASSERT_API_ENTRY(thr); #if defined(DUK_USE_ASSERTIONS) - top_at_entry = duk_get_top(ctx); + top_at_entry = duk_get_top(thr); #endif - idx = duk_require_normalize_index(ctx, idx); - duk_bi_json_parse_helper(ctx, + 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_replace(thr, idx); - DUK_ASSERT(duk_get_top(ctx) == top_at_entry); + 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_ERROR_UNSUPPORTED(thr); } -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) { + 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 @@ struct duk__compile_raw_args { }; /* 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 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du /* [ ... 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 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du 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); + rc = duk_pcall_method(thr, 0); } else { - duk_call_method(ctx, 0); + duk_call_method(thr, 0); rc = DUK_EXEC_SUCCESS; } @@ -15077,20 +15361,19 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du 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 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) { 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 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) { 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 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) { 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 @@ DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, 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 @@ DUK_EXTERNAL duk_int_t duk_compile_raw(duk_context *ctx, const char *src_buffer, /* #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_EXTERNAL void duk_debugger_attach(duk_context *ctx, 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_EXTERNAL void duk_debugger_attach(duk_context *ctx, 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 @@ DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx, 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 @@ DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx, /* 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_pop(thr); } -DUK_EXTERNAL void duk_debugger_detach(duk_context *ctx) { - duk_hthread *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_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { 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 @@ DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) 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 @@ DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) 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 @@ DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) { #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_EXTERNAL void duk_debugger_attach(duk_context *ctx, 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_EXTERNAL void duk_debugger_attach(duk_context *ctx, DUK_UNREF(request_cb); DUK_UNREF(detached_cb); DUK_UNREF(udata); - DUK_ERROR_TYPE((duk_hthread *) ctx, "no debugger support"); + DUK_ERROR_TYPE(thr, "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_detach(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + DUK_ERROR_TYPE(thr, "no debugger support"); } -DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { +DUK_EXTERNAL void duk_debugger_cooperate(duk_hthread *thr) { /* nop */ - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(ctx); + DUK_ASSERT_API_ENTRY(thr); + DUK_UNREF(thr); } -DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) { +DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_hthread *thr, duk_idx_t nvalues) { duk_idx_t top; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - top = duk_get_top(ctx); + 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); + duk_pop_n(thr, nvalues); return 0; } -DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) { +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 @@ struct duk_internal_thread_state { 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 @@ duk_context *duk_create_heap(duk_alloc_function alloc_func, if (!heap) { return NULL; } - ctx = (duk_context *) heap->heap_thread; - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(((duk_hthread *) ctx)->heap != NULL); - return ctx; + thr = heap->heap_thread; + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + return thr; } -DUK_EXTERNAL void duk_destroy_heap(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) { duk_heap *heap; - if (!ctx) { + 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 @@ DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) { 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 @@ DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) { 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 @@ DUK_EXTERNAL void duk_resume(duk_context *ctx, const duk_thread_state *state) { 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))); + DUK_ASSERT_API_ENTRY(thr); + + DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1))); - h_glob = duk_require_hobject(ctx, -1); + h_glob = duk_require_hobject(thr, -1); DUK_ASSERT(h_glob != NULL); /* @@ -15679,7 +15943,7 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { /* [ ... new_glob ] */ - duk_pop(ctx); + duk_pop(thr); /* [ ... ] */ } @@ -15693,7 +15957,7 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { /* 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 @@ DUK_LOCAL void duk__inspect_multiple_uint(duk_context *ctx, const char *fmt, duk 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 @@ DUK_LOCAL void duk__inspect_multiple_uint(duk_context *ctx, const char *fmt, duk #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_EXTERNAL void duk_inspect_value(duk_context *ctx, duk_idx_t idx) { */ 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); + vals[DUK__IDX_ITAG] = (duk_int_t) DUK_TVAL_GET_TAG(tv); - duk_push_bare_object(ctx); /* Invalidates '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 @@ DUK_EXTERNAL void duk_inspect_value(duk_context *ctx, duk_idx_t idx) { 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); + DUK_ASSERT_API_ENTRY(thr); - /* -1 = top callstack entry, callstack[callstack_top - 1] - * -callstack_top = bottom callstack entry, callstack[0] + /* -1 = top callstack entry + * -2 = caller of level -1 + * etc */ - if (level >= 0 || -level > (duk_int_t) thr->callstack_top) { - duk_push_undefined(ctx); + act = duk_hthread_get_activation_for_level(thr, level); + if (act == NULL) { + duk_push_undefined(thr); return; } - duk_push_bare_object(ctx); - DUK_ASSERT(level >= -((duk_int_t) thr->callstack_top) && level <= -1); + duk_push_bare_object(thr); - act = thr->callstack + thr->callstack_top + level; /* 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_tval(thr, &act->tv_func); - duk_push_uint(ctx, (duk_uint_t) pc); - duk_put_prop_stridx_short(ctx, -3, DUK_STRIDX_PC); + 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_push_uint(thr, (duk_uint_t) line); + duk_put_prop_stridx_short(thr, -3, DUK_STRIDX_LINE_NUMBER); - duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_LC_FUNCTION); + 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 @@ DUK_EXTERNAL void duk_inspect_callstack_entry(duk_context *ctx, duk_int_t level) /* #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_EXTERNAL void duk_free(duk_hthread *thr, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_FREE(thr->heap, ptr); + DUK_FREE_CHECKED(thr, 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_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 @@ DUK_EXTERNAL void *duk_realloc(duk_context *ctx, void *ptr, duk_size_t size) { 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 @@ DUK_EXTERNAL void duk_get_memory_functions(duk_context *ctx, duk_memory_function 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 @@ DUK_EXTERNAL void duk_gc(duk_context *ctx, duk_uint_t flags) { * 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); + 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_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, obj_idx); - duk_push_uarridx(ctx, arr_idx); - return duk_get_prop(ctx, obj_idx); + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_get_prop(thr, 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_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_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_uint_t packed_args) { - return duk_get_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16), +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_LOCAL duk_bool_t duk__put_prop_shared(duk_context *ctx, duk_idx_t obj_idx, d 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(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_context *ctx, duk_idx_t obj_idx, const char *key) { - DUK_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, obj_idx); - duk_push_uarridx(ctx, arr_idx); - return duk__put_prop_shared(ctx, obj_idx, -1); + 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_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_EXTERNAL duk_bool_t duk_put_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); + 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); + 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_context *ctx, duk_uint_t packed_args) { - return duk_put_prop_stridx(ctx, (duk_idx_t) (duk_int16_t) (packed_args >> 16), +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); + 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_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, obj_idx); - duk_push_uarridx(ctx, arr_idx); - return duk_del_prop(ctx, obj_idx); + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_del_prop(thr, 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_EXTERNAL duk_bool_t duk_del_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); + 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); + 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_context *ctx, duk_idx_t obj_idx, const char *key, duk_size_t key_len) { - DUK_ASSERT_CTX_VALID(ctx); +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); + 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_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, obj_idx); - duk_push_uarridx(ctx, arr_idx); - return duk_has_prop(ctx, obj_idx); + obj_idx = duk_require_normalize_index(thr, obj_idx); + duk_push_uarridx(thr, arr_idx); + return duk_has_prop(thr, 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_EXTERNAL duk_bool_t duk_has_prop_heapptr(duk_hthread *thr, duk_idx_t obj_idx, void *ptr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); + 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 @@ DUK_INTERNAL duk_bool_t duk_has_prop_stridx_short_raw(duk_context *ctx, duk_uint * 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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_idx); + 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_pop(thr); /* 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_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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_idx); + 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_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f duk_uint_t is_data_desc; duk_uint_t is_acc_desc; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, obj_idx); + 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 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f 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 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f 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 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f } 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_require_valid_index(thr, idx_base); - duk_hobject_define_property_helper(ctx, + duk_hobject_define_property_helper(thr, flags /*defprop_flags*/, obj, key, @@ -16514,7 +16780,7 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f /* Clean up stack */ - duk_set_top(ctx, idx_base); + duk_set_top(thr, idx_base); /* [ ... obj ... ] */ @@ -16536,73 +16802,140 @@ DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_idx, duk_uint_t f * 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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, obj_idx); + 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_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_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_seal_freeze_raw(thr, obj_idx, 0 /*is_freeze*/); } -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_EXTERNAL void duk_freeze(duk_hthread *thr, duk_idx_t obj_idx) { + DUK_ASSERT_API_ENTRY(thr); - duk_require_hobject(ctx, enum_index); - duk_dup(ctx, enum_index); - return duk_hobject_enumerator_next(ctx, get_value); + 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); + DUK_ASSERT_API_ENTRY(thr); - obj_idx = duk_require_normalize_index(ctx, obj_idx); + 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); + DUK_ASSERT_API_ENTRY(thr); - obj_idx = duk_require_normalize_index(ctx, obj_idx); + 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 @@ DUK_EXTERNAL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_idx, const * 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 @@ DUK_EXTERNAL duk_bool_t duk_put_global_lstring(duk_context *ctx, const char *key * 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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, idx); + 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); + duk_push_hobject(thr, proto); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_set_prototype(duk_hthread *thr, duk_idx_t idx) { duk_hobject *obj; duk_hobject *proto; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_require_hobject(ctx, idx); + 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_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t idx) { DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto); - duk_pop(ctx); + duk_pop(thr); } /* @@ -16734,21 +17060,21 @@ DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t idx) { * 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_EXTERNAL void duk_get_finalizer(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - duk_get_prop_stridx(ctx, idx, DUK_STRIDX_INT_FINALIZER); + duk_get_prop_stridx(thr, idx, DUK_STRIDX_INT_FINALIZER); } -DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) { +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); + DUK_ASSERT_API_ENTRY(thr); - 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); + 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 @@ DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) { } } #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_ERROR_UNSUPPORTED(thr); } -DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); +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 @@ DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) { * 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 @@ DUK_EXTERNAL duk_int_t duk_api_global_line = 0; * 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 @@ DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = { }; #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 @@ DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = { } 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_small_uint_t duk__get_symbol_type(duk_hstring *h) { + const duk_uint8_t *data; + duk_size_t len; -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_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 @@ DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t idx, duk_int 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 @@ DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t idx, duk_u * 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 @@ DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t i 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_INTERNAL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t idx) { */ 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_INTERNAL duk_tval *duk_get_tval_or_unused(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - tv = duk_get_tval(ctx, idx); + + 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 @@ DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t idx) { } /* 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 @@ DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t idx) { * 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 @@ DUK_EXTERNAL duk_idx_t duk_get_top(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_context *ctx, duk_idx_t min_t * 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 void duk_set_top(duk_context *ctx, duk_idx_t idx) { } } -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 @@ DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) { /* 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 @@ DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) { * * 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. + * 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(). */ -/* 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. +/* 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_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 +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 + /* 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; - /* 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. + 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 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) 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; + 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 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) /* 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 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) 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); + 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_PREFER_SIZE) - old_size = (duk_size_t) (thr->valstack_end - thr->valstack); +#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 - DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); - old_size = thr->valstack_size; + /* New size is tight with no slack. This is sometimes preferred in + * low memory environments. + */ + new_size = min_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)) { + 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). + * plan limit accordingly. */ - if (throw_flag) { + if (throw_on_error) { DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT); - } else { - return 0; } + 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) { + if (duk__resize_valstack(thr, new_size) == 0) { + if (throw_on_error) { DUK_ERROR_ALLOC_FAILED(thr); - } else { - return 0; } + return 0; } - DUK_DDD(DUK_DDDPRINT("valstack resize successful")); + thr->valstack_end = thr->valstack + min_size; + DUK_ASSERT(thr->valstack_alloc_end >= thr->valstack_end); + 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; +/* 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; - 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)); + 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*/); +} - 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); +/* 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; -#if defined(DUK_USE_PREFER_SIZE) - old_size = (duk_size_t) (thr->valstack_end - thr->valstack); + 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 - DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); - old_size = thr->valstack_size; + 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 */ - 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; - } + 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); - return duk__valstack_do_resize(ctx, min_new_size, flags); + 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_context *ctx, duk_idx_t extra) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_size_t min_new_size; +DUK_EXTERNAL duk_bool_t duk_check_stack(duk_hthread *thr, duk_idx_t extra) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); - if (DUK_UNLIKELY(extra < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; + 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_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 */); + 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_context *ctx, duk_idx_t extra) { - duk_hthread *thr = (duk_hthread *) ctx; - duk_size_t min_new_size; +DUK_EXTERNAL void duk_require_stack(duk_hthread *thr, duk_idx_t extra) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr != NULL); - if (DUK_UNLIKELY(extra < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - extra = 0; + 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_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); + 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_context *ctx, duk_idx_t top) { - duk_size_t min_new_size; +DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_hthread *thr, duk_idx_t top) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(top < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; + 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; + } } - 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_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_context *ctx, duk_idx_t top) { - duk_size_t min_new_size; +DUK_EXTERNAL void duk_require_stack_top(duk_hthread *thr, duk_idx_t top) { + duk_size_t min_new_bytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - if (DUK_UNLIKELY(top < 0)) { - /* Clamping to zero makes the API more robust to calling code - * calculation errors. - */ - top = 0; + 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; + } } - 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); + 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); + DUK_ASSERT_API_ENTRY(thr); - 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); /* If tv1==tv2 this is a NOP, no check is needed */ @@ -17702,22 +18188,20 @@ DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) { 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_EXTERNAL void duk_swap_top(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - duk_swap(ctx, idx, -1); + duk_swap(thr, idx, -1); } -DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_idx) { - duk_hthread *thr; +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_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_idx) { 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); + duk_dup(thr, -1); #else - duk_hthread *thr; 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 @@ DUK_EXTERNAL void duk_dup_top(duk_context *ctx) { #endif } -DUK_INTERNAL void duk_dup_0(duk_context *ctx) { - duk_dup(ctx, 0); +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_context *ctx) { - duk_dup(ctx, 1); +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_context *ctx) { - duk_dup(ctx, 2); +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_context *ctx) { - duk_dup(ctx, -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_context *ctx) { - duk_dup(ctx, -3); +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_context *ctx) { - duk_dup(ctx, -4); +DUK_INTERNAL void duk_dup_m4(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_dup(thr, -4); } -DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_idx) { +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); + DUK_ASSERT_API_ENTRY(thr); - p = duk_require_tval(ctx, to_idx); + 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_insert(duk_context *ctx, duk_idx_t to_idx) { } } -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); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_require_tval(ctx, -1); + 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_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_idx) { 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 */ + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_require_tval(ctx, from_idx); + 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 @@ DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t idx) { #endif duk_size_t nbytes; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - p = duk_require_tval(ctx, idx); + 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 @@ DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t idx) { #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 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, /* 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); + 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_ctx == from_ctx)) { + 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; } + DUK_ASSERT(count >= 0); - nbytes = sizeof(duk_tval) * count; + nbytes = sizeof(duk_tval) * (duk_size_t) count; if (DUK_UNLIKELY(nbytes == 0)) { return; } @@ -17936,7 +18502,7 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, 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 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, } } +/* 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_EXTERNAL void duk_require_undefined(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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_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_EXTERNAL void duk_require_null(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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_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); + DUK_ASSERT_CTX_VALID(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_bool_t duk__get_boolean_raw(duk_context *ctx, du return ret; } -DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(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(ctx, idx, 0); /* default: false */ + return duk__get_boolean_raw(thr, 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); +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(ctx, idx, def_value); + return duk__get_boolean_raw(thr, 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_require_boolean(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; duk_bool_t ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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_BOOLEAN(tv))) { ret = DUK_TVAL_GET_BOOLEAN(tv); @@ -18045,22 +18639,22 @@ DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx) { } } -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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_boolean(ctx, idx); + return duk_require_boolean(thr, 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) { +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); + DUK_ASSERT_CTX_VALID(thr); - 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) if (DUK_TVAL_IS_FASTINT(tv)) { @@ -18083,22 +18677,23 @@ DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_context *ctx, d 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(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_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_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_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +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); + DUK_ASSERT_API_ENTRY(thr); - 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_NUMBER(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); @@ -18114,78 +18709,78 @@ DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx) { 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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + 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); + return duk_require_number(thr, idx); } -DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, 0 /*def_value*/, 0 /*require*/); + return (duk_int_t) duk__api_coerce_d2i(thr, 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); +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(ctx, idx, 0 /*def_value*/, 0 /*require*/); + 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_context *ctx, duk_idx_t idx, duk_int_t def_value) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, def_value, 0 /*require*/); + return (duk_int_t) duk__api_coerce_d2i(thr, 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); +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(ctx, idx, def_value, 0 /*require*/); + return (duk_uint_t) duk__api_coerce_d2ui(thr, 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); +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(ctx, idx, 0 /*def_value*/, 1 /*require*/); + return (duk_int_t) duk__api_coerce_d2i(thr, 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); +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(ctx, idx, 0 /*def_value*/, 1 /*require*/); + return (duk_uint_t) duk__api_coerce_d2ui(thr, 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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_int(ctx, idx); + return duk_require_int(thr, 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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_uint(ctx, idx); + return duk_require_uint(thr, idx); } -DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) { +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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring(ctx, idx); + 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 @@ DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_si 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_require_hstring(ctx, idx); + h = duk_require_hstring(thr, idx); DUK_ASSERT(h != NULL); if (out_len) { *out_len = DUK_HSTRING_GET_BYTELEN(h); @@ -18213,12 +18808,12 @@ DUK_EXTERNAL const char *duk_require_lstring(duk_context *ctx, duk_idx_t idx, du 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_require_hstring_notsymbol(ctx, idx); + 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 @@ DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_context *ctx, duk_idx 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring(ctx, idx); + 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_get_string(duk_context *ctx, duk_idx_t idx) { } } -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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + 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); + return duk_require_lstring(thr, 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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_ptr; } - return duk_require_string(ctx, idx); + return duk_require_string(thr, 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) { +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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring(ctx, idx); + 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 @@ DUK_EXTERNAL const char *duk_get_lstring_default(duk_context *ctx, duk_idx_t idx 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring(ctx, idx); + h = duk_get_hstring(thr, idx); if (h != NULL) { return (const char *) DUK_HSTRING_GET_DATA(h); } else { @@ -18295,12 +18890,12 @@ DUK_EXTERNAL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, } } -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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hstring_notsymbol(ctx, idx); + h = duk_get_hstring_notsymbol(thr, idx); if (h) { return (const char *) DUK_HSTRING_GET_DATA(h); } else { @@ -18308,29 +18903,41 @@ DUK_INTERNAL const char *duk_get_string_notsymbol(duk_context *ctx, duk_idx_t id } } -DUK_EXTERNAL const char *duk_require_string(duk_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL const char *duk_require_string(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - return duk_require_lstring(ctx, idx, NULL); + return duk_require_lstring(thr, idx, NULL); } -DUK_INTERNAL const char *duk_require_string_notsymbol(duk_context *ctx, duk_idx_t idx) { +DUK_INTERNAL const char *duk_require_string_notsymbol(duk_hthread *thr, duk_idx_t idx) { duk_hstring *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - h = duk_require_hstring_notsymbol(ctx, idx); + 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); + DUK_ASSERT_CTX_VALID(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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 @@ DUK_LOCAL void *duk__get_pointer_raw(duk_context *ctx, duk_idx_t idx, void *def_ 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_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_context *ctx, duk_idx_t idx, void *def_value) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_pointer(ctx, idx); + return duk_require_pointer(thr, 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_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_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx) { } #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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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 @@ DUK_INTERNAL void *duk_get_voidptr(duk_context *ctx, duk_idx_t idx) { } #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 @@ DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t idx, duk_size 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); +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(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/); + 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_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + 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); + return duk_require_buffer(thr, 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); +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(ctx, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/); + return duk__get_buffer_helper(thr, 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); +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(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 1 /*throw_flag*/); + 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 @@ DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_ * 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_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_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); if (out_isbuffer != NULL) { *out_isbuffer = 0; @@ -18480,7 +19084,7 @@ DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_ *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 @@ DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_ 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(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_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_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_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + 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); + return duk_require_buffer_data(thr, 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); +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 @@ DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_ * 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); + DUK_ASSERT_CTX_VALID(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t i } -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(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_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_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; + return h; } -DUK_INTERNAL duk_hstring *duk_require_hstring(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(ctx, idx, "string", DUK_STR_NOT_STRING); + DUK_ERROR_REQUIRE_TYPE_INDEX(thr, 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_notsymbol(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_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_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_context *ctx, duk_idx_t idx) { +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_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_context *ctx, duk_idx_t idx) { +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); + DUK_ASSERT_API_ENTRY(thr); - 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_OBJECT(tv))) { return NULL; @@ -18707,21 +19354,21 @@ DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx) 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); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + 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); + return duk_require_c_function(thr, idx); } -DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value) { +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); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_get_c_function(ctx, idx); + ret = duk_get_c_function(thr, idx); if (ret != NULL) { return ret; } @@ -18729,62 +19376,64 @@ DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx 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); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_get_c_function(ctx, idx); + 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_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_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx) { +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); +DUK_EXTERNAL duk_hthread *duk_get_context(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - return (duk_context *) duk_get_hthread(ctx, idx); + return duk_get_hthread(thr, idx); } -DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL duk_hthread *duk_require_context(duk_hthread *thr, duk_idx_t idx) { + DUK_ASSERT_API_ENTRY(thr); - return (duk_context *) duk_require_hthread(ctx, idx); + return duk_require_hthread(thr, idx); } -DUK_EXTERNAL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_context(ctx, idx); + return duk_require_context(thr, 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_EXTERNAL duk_hthread *duk_get_context_default(duk_hthread *thr, duk_idx_t idx, duk_hthread *def_value) { + duk_hthread *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_get_context(ctx, idx); + ret = duk_get_context(thr, idx); if (ret != NULL) { return ret; } @@ -18792,13 +19441,13 @@ DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx 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); + DUK_ASSERT_API_ENTRY(thr); - 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_HEAP_ALLOCATED(tv))) { return (void *) NULL; @@ -18809,21 +19458,21 @@ DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx) { return ret; } -DUK_EXTERNAL void *duk_opt_heapptr(duk_context *ctx, duk_idx_t idx, void *def_value) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (duk_check_type_mask(thr, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { return def_value; } - return duk_require_heapptr(ctx, idx); + return duk_require_heapptr(thr, idx); } -DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value) { +DUK_EXTERNAL void *duk_get_heapptr_default(duk_hthread *thr, duk_idx_t idx, void *def_value) { void *ret; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - ret = duk_get_heapptr(ctx, idx); + ret = duk_get_heapptr(thr, idx); if (ret != NULL) { return ret; } @@ -18831,14 +19480,13 @@ DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, 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); + DUK_ASSERT_API_ENTRY(thr); - 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_HEAP_ALLOCATED(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE); @@ -18850,22 +19498,22 @@ DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx) { } /* 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); + DUK_ASSERT_CTX_VALID(thr); - res = duk_get_hobject(ctx, idx); /* common case, not promoted */ + 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 @@ DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_context *ctx, duk_i } 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 @@ DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_context *ctx, duk_i * 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_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_context *ctx, duk_idx_t idx, duk_small_uint_t classnum) { +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 @@ DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_i return h; } -DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { +DUK_EXTERNAL duk_size_t duk_get_length(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + tv = duk_get_tval_or_unused(thr, idx); DUK_ASSERT(tv != NULL); switch (DUK_TVAL_GET_TAG(tv)) { @@ -18953,17 +19602,20 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { * * 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 @@ DUK_LOCAL duk_heaphdr *duk__known_heaphdr(duk_context *ctx, duk_idx_t idx) { 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_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_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_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_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_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_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_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_context *ctx, duk_idx_t idx) { - DUK_ASSERT(duk_get_hnatfunc(ctx, idx) != NULL); - return (duk_hnatfunc *) duk__known_heaphdr(ctx, 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_context *ctx, duk_idx_t idx, duk_size_t len) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx); - duk_push_uint(ctx, (duk_uint_t) len); - duk_put_prop_stridx(ctx, idx, DUK_STRIDX_LENGTH); + 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 @@ DUK_EXTERNAL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len /* 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 ] -> [ ... ] */ + duk_pop_unsafe(thr); /* [ ... func/retval ] -> [ ... ] */ return 0; } -DUK_EXTERNAL void duk_to_undefined(duk_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL void duk_to_undefined(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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_EXTERNAL void duk_to_null(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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); + idx = duk_require_normalize_index(thr, idx); - if (!duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_OBJECT | + 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 */ + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ return; } - class_number = duk_get_class_number(ctx, idx); + 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_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hi 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 @@ DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hi 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 */ + 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(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[1])) { + DUK_ASSERT(!duk_is_buffer(thr, idx)); /* duk_to_string() relies on this behavior */ return; } @@ -19197,16 +19855,14 @@ DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t idx, duk_int_t hi } /* 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); + DUK_ASSERT_API_ENTRY(thr); - 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); val = duk_js_toboolean(tv); @@ -19218,43 +19874,64 @@ DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t idx) { 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_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_context *ctx) { - return duk_to_number(ctx, -2); +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_context *ctx, duk_tval *tv) { +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_ASSERT_API_ENTRY(thr); - duk_push_tval(ctx, tv); - res = duk_to_number(ctx, -1); - duk_pop(ctx); + 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 @@ DUK_INTERNAL duk_double_t duk_to_number_tval(duk_context *ctx, duk_tval *tv) { 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); + DUK_ASSERT_CTX_VALID(thr); - tv = duk_require_tval(ctx, idx); + tv = duk_require_tval(thr, idx); DUK_ASSERT(tv != NULL); #if defined(DUK_USE_FASTINT) @@ -19287,93 +19963,92 @@ DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t idx, /* 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_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_context *ctx, duk_idx_t idx) { +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_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_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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 @@ DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t idx) { } #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); +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(ctx, idx); - DUK_ASSERT(duk_is_string(ctx, idx)); - return duk_require_lstring(ctx, idx, out_len); + (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_context *ctx, void *udata) { - DUK_ASSERT_CTX_VALID(ctx); +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); + duk_to_string(thr, -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); +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(ctx, idx); + 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 @@ DUK_EXTERNAL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t idx, du /* String; may be a symbol, accepted. */ ; } - DUK_ASSERT(duk_is_string(ctx, -1)); + DUK_ASSERT(duk_is_string(thr, -1)); - duk_replace(ctx, idx); - DUK_ASSERT(duk_get_string(ctx, idx) != NULL); - return duk_get_lstring(ctx, idx, out_len); + 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_context *ctx, duk_idx_t idx) { +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_ASSERT_API_ENTRY(thr); - duk_to_primitive(ctx, idx, DUK_HINT_STRING); /* needed for e.g. Symbol objects */ - h = duk_get_hstring(ctx, idx); + 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 @@ DUK_INTERNAL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv) { 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); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_require_tval(ctx, idx); + 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 @@ DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t idx, d /* '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 @@ DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t idx, d 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_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_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_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_context *ctx, duk_idx_t idx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_EXTERNAL const char *duk_to_string(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(thr); + DUK_ASSERT_API_ENTRY(thr); - 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); 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); + duk_push_hstring_stridx(thr, DUK_STRIDX_TRUE); } else { - duk_push_hstring_stridx(ctx, DUK_STRIDX_FALSE); + duk_push_hstring_stridx(thr, DUK_STRIDX_FALSE); } break; } @@ -19670,7 +20345,7 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) { * 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 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) { /* 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_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) { } } - duk_replace(ctx, idx); + duk_replace(thr, idx); skip_replace: - DUK_ASSERT(duk_is_string(ctx, idx)); - return duk_require_string(ctx, idx); + DUK_ASSERT(duk_is_string(thr, idx)); + return duk_require_string(thr, idx); } -DUK_INTERNAL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t 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_m1(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + return duk_to_hstring(thr, -1); } -DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_context *ctx, duk_idx_t idx) { +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 @@ DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_context *ctx, duk_idx_ * 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); + DUK_ASSERT_API_ENTRY(thr); + + idx = duk_require_normalize_index(thr, idx); - ptr_src = duk_require_buffer_data(ctx, idx, &len); + 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); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(ctx, idx); + idx = duk_require_normalize_index(thr, idx); - h_buf = duk_get_hbuffer(ctx, 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 @@ DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t * 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); + src_data = (const duk_uint8_t *) duk_to_lstring(thr, idx, &src_size); } - dst_data = (duk_uint8_t *) duk_push_buffer(ctx, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/); + 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_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t */ 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 @@ DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t idx, duk_size_t 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); + DUK_ASSERT_API_ENTRY(thr); - 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); switch (DUK_TVAL_GET_TAG(tv)) { @@ -19884,12 +20564,12 @@ DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_LOCAL void duk__push_func_from_lightfunc(duk_context *ctx, duk_c_function fu 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); + 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(ctx, -1); + 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); + DUK_ASSERT_API_ENTRY(thr); - 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); switch (DUK_TVAL_GET_TAG(tv)) { @@ -20018,7 +20694,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { 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 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { * 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_replace(thr, idx); + DUK_ASSERT(duk_is_object(thr, idx)); } -DUK_INTERNAL duk_hobject *duk_to_hobject(duk_context *ctx, duk_idx_t 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 @@ DUK_INTERNAL duk_hobject *duk_to_hobject(duk_context *ctx, duk_idx_t idx) { * Type checking */ -DUK_LOCAL duk_bool_t duk__tag_check(duk_context *ctx, duk_idx_t idx, duk_small_uint_t tag) { +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(ctx, idx); + 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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, idx); + 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 @@ DUK_INTERNAL duk_int_t duk_get_type_tval(duk_tval *tv) { #endif /* DUK_USE_PACKED_TVAL */ } -DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t idx) { +DUK_EXTERNAL duk_int_t duk_get_type(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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 @@ DUK_LOCAL const char *duk__type_names[] = { "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 +#endif /* DUK_USE_VERBOSE_ERRORS && DUK_USE_PARANOID_ERRORS */ -DUK_INTERNAL duk_small_uint_t duk_get_class_number(duk_context *ctx, duk_idx_t idx) { +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_INTERNAL duk_small_uint_t duk_get_class_number(duk_context *ctx, duk_idx_t i } } -DUK_EXTERNAL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t idx, duk_int_t type) { - DUK_ASSERT_CTX_VALID(ctx); +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(ctx, idx) == type) ? 1 : 0; + 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 @@ DUK_INTERNAL duk_uint_t duk_get_type_mask_tval(duk_tval *tv) { #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_EXTERNAL duk_uint_t duk_get_type_mask(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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); +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(ctx, idx) & mask)) { + if (DUK_LIKELY((duk_get_type_mask(thr, idx) & mask) != 0U)) { return 1; } if (mask & DUK_TYPE_MASK_THROW) { @@ -20257,25 +20937,25 @@ DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t idx, duk 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_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_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_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_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_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_context *ctx, duk_idx_t idx) { +DUK_EXTERNAL duk_bool_t duk_is_number(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); /* * Number is special because it doesn't have a specific @@ -20284,12 +20964,12 @@ DUK_EXTERNAL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t idx) { /* 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_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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)); + return (duk_bool_t) 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_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_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); - return duk_get_hstring_notsymbol(ctx, idx) != NULL; +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_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_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_context *ctx, duk_idx_t idx) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__tag_check(ctx, idx, DUK_TAG_BUFFER); +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_EXTERNAL duk_bool_t duk_is_buffer_data(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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)) { return 1; @@ -20350,29 +21030,29 @@ DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx) { 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); +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(ctx, idx); + 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_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_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_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_context *ctx, duk_idx_t idx) { +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 @@ DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_context *ctx, duk_idx_t idx) { 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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, idx); + 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_EXTERNAL duk_bool_t duk_is_function(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - tv = duk_get_tval_or_unused(ctx, idx); + 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); + 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_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_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); + DUK_ASSERT_API_ENTRY(thr); - obj = duk_get_hobject(ctx, idx); + 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_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); @@ -20458,12 +21175,12 @@ DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t idx) { return 0; } -DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx) { +DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); @@ -20473,12 +21190,12 @@ DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t idx) { return 0; } -DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx) { +DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_hthread *thr, duk_idx_t idx) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - 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)) { duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv); @@ -20488,20 +21205,22 @@ DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t idx) 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_get_hobject(ctx, idx); + 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 @@ DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t idx) { * 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_EXTERNAL void duk_push_undefined(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - DUK_ASSERT_CTX_VALID(ctx); - thr = (duk_hthread *) ctx; DUK__CHECK_SPACE(); /* Because value stack init policy is 'undefined above top', @@ -20561,60 +21277,50 @@ DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) { 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_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) { 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 @@ DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) { } #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 @@ DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) { #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 @@ DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) { } #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 @@ DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) { #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_EXTERNAL void duk_push_nan(duk_context *ctx) { 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 @@ DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk 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)); + return duk_push_lstring(thr, str, DUK_STRLEN(str)); } else { - duk_push_null(ctx); + 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_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t check_ob DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE); } -DUK_EXTERNAL void duk_push_this(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_push_this(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 0 /*check_object_coercible*/); + duk__push_this_helper(thr, 0 /*check_object_coercible*/); } -DUK_INTERNAL void duk_push_this_check_object_coercible(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_INTERNAL void duk_push_this_check_object_coercible(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 1 /*check_object_coercible*/); + duk__push_this_helper(thr, 1 /*check_object_coercible*/); } -DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx) { +DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_hthread *thr) { duk_hobject *h; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); - duk__push_this_helper(ctx, 1 /*check_object_coercible*/); - h = duk_to_hobject(ctx, -1); + 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_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - 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__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_context *ctx) { - duk_hthread *thr; - - DUK_ASSERT(ctx != NULL); - thr = (duk_hthread *) ctx; +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); + duk_push_tval(thr, &act->tv_func); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -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_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); + duk_push_hobject(thr, (duk_hobject *) thr->heap->curr_thread); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } } -DUK_EXTERNAL void duk_push_global_object(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_push_global_object(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL); + 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_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(ctx); + duk_remove_m2(thr); } -DUK_EXTERNAL void duk_push_heap_stash(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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_push_hobject(thr, heap->heap_object); + duk__push_stash(thr); } -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_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_context *ctx, duk_context *target_ctx) { - duk_hthread *thr = (duk_hthread *) ctx; - DUK_ASSERT_CTX_VALID(ctx); - if (DUK_UNLIKELY(target_ctx == NULL)) { +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 @@ DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size 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_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va 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 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va buf = stack_buf; } else if (!pushed_buf) { pushed_buf = 1; - buf = duk_push_dynamic_buffer(ctx, sz); + buf = duk_push_dynamic_buffer(thr, sz); } else { - buf = duk_resize_buffer(ctx, -1, sz); + 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 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va /* 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 @@ DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_context *ctx, duk_uint_t ho /* 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 @@ DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_context *ctx, duk_uint_t ho 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); + DUK_ASSERT_API_ENTRY(thr); - h = duk_push_object_helper(ctx, hobject_flags_and_class, -1); + 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); +DUK_EXTERNAL duk_idx_t duk_push_object(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); - (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), DUK_BIDX_OBJECT_PROTOTYPE); - return duk_get_top_index_unsafe(ctx); + return duk_get_top_index_unsafe(thr); } -DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) { 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 @@ DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_harray *duk_push_harray(duk_context *ctx) { /* 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_ASSERT_API_ENTRY(thr); - duk_hobject_realloc_props((duk_hthread *) ctx, + a = duk_push_harray(thr); + + duk_hobject_realloc_props(thr, (duk_hobject *) a, 0, size, @@ -21145,13 +21818,22 @@ DUK_INTERNAL duk_harray *duk_push_harray_with_size(duk_context *ctx, duk_uint32_ 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_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { 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]); + /* default prototype */ + DUK_HOBJECT_SET_PROTOTYPE_INIT_INCREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]); - /* Initial stack size satisfies the stack spare constraints so there + /* 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 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { 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 @@ DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_context *ctx) { 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_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_context *ctx) { 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 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu 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,35 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu 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_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 | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - return duk__push_c_function_raw(ctx, func, nargs, flags); + /* 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_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) { +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 | @@ -21316,15 +22030,17 @@ DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, nargs, flags); + /* 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_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) { +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_CTX_VALID(ctx); + 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 | @@ -21332,15 +22048,15 @@ DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk DUK_HOBJECT_FLAG_NOTAIL | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION); - (void) duk__push_c_function_raw(ctx, func, nargs, flags); + /* 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_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; +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_tval *tv_slot; - DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT_API_ENTRY(thr); DUK__CHECK_SPACE(); @@ -21358,11 +22074,12 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun 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 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun } #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 @@ DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_context *ctx, duk_uint_t hobje 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 @@ static const duk_uint32_t duk__bufobj_flags_lookup[] = { #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 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, 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 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, 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 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, * 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 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, 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; + duk_small_uint_t augment_flags; #endif - DUK_ASSERT_CTX_VALID(ctx); + 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 @@ DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcod /* ... 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 @@ DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcod * 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 @@ DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcod /* 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 */ + duk_err_augment_error_create(thr, thr, filename, line, augment_flags); /* may throw an error */ #endif - return duk_get_top_index_unsafe(ctx); + return duk_get_top_index_unsafe(thr); } -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_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 @@ DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_sm 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_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_context *ctx, duk_size_t len) { +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 @@ DUK_INTERNAL void *duk_push_fixed_buffer_zero(duk_context *ctx, duk_size_t len) return 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_context *ctx, void *ptr) { +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 @@ DUK_LOCAL void duk__validate_push_heapptr(duk_context *ctx, void *ptr) { } #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 @@ DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { */ #if defined(DUK_USE_ASSERTIONS) - duk__validate_push_heapptr(ctx, ptr); + duk__validate_push_heapptr(thr, ptr); #endif DUK__CHECK_SPACE(); @@ -21824,83 +22674,79 @@ DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { } /* 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); + return duk_get_top_index_unsafe(thr); } -DUK_INTERNAL void duk_push_hstring(duk_context *ctx, duk_hstring *h) { +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_push_tval(thr, &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_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_push_hstring(thr, 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_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_context *ctx, duk_hobject *h) { +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_push_tval(thr, &tv); } -DUK_INTERNAL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h) { +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_push_tval(thr, &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_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); - } + DUK_ASSERT_CTX_VALID(thr); + DUK_ASSERT(count >= 0); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); #if defined(DUK_USE_REFERENCE_COUNTING) tv = thr->valstack_top; @@ -21926,49 +22772,37 @@ DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) { 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(count >= 0); +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); - DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) count); -#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); + if (DUK_UNLIKELY((duk_uidx_t) (thr->valstack_top - thr->valstack_bottom) < (duk_uidx_t) count)) { + DUK_ERROR_RANGE_INVALID_COUNT(thr); + return; } - thr->valstack_top = tv; -#endif + DUK_ASSERT(count >= 0); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + duk__pop_n_unsafe_raw(thr, count); } -/* Pop N elements without DECREF (in effect "stealing" the refcounts). */ +#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_INTERNAL void duk_pop_n_nodecref_unsafe(duk_hthread *thr, duk_idx_t count) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + 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_INTERNAL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count) { 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,70 +22829,166 @@ DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count) { * 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); +DUK_EXTERNAL void duk_pop(duk_hthread *thr) { + DUK_ASSERT_API_ENTRY(thr); + duk_pop_n(thr, 1); } -#else -DUK_EXTERNAL void duk_pop(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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(ctx); + DUK_ASSERT_CTX_VALID(thr); + DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); - if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { - DUK_ERROR_RANGE_INVALID_COUNT(thr); - } + 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 */ #else DUK_TVAL_SET_UNDEFINED(tv); #endif + + 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 */ + +#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 */ -/* 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); +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_INTERNAL void duk_pop_unsafe(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_LOCAL DUK_ALWAYS_INLINE void duk__pop_2_unsafe_raw(duk_hthread *thr) { duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); + 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); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 2); - 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 */ #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_2(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 2); +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_EXTERNAL void duk_pop_3(duk_context *ctx) { - DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 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 @@ DUK_EXTERNAL void duk_pop_3(duk_context *ctx) { */ /* 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. + * 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) thr->valstack_max); + 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 @@ DUK_INTERNAL void duk_pack(duk_context *ctx, duk_idx_t count) { 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? - */ +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; } -#endif /* * 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_EXTERNAL void duk_throw_raw(duk_context *ctx) { 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))); + DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(thr, -1))); - tv_val = DUK_GET_TVAL_NEGIDX(ctx, -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_EXTERNAL void duk_throw_raw(duk_context *ctx) { 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_fatal_raw(duk_context *ctx, const char *err_msg) { } } -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_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(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); } -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_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_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_context *ctx, 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 @@ DUK_EXTERNAL duk_ret_t duk_uri_error_stash(duk_context *ctx, const char *fmt, .. * 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); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_get_tval(ctx, idx1); - tv2 = duk_get_tval(ctx, idx2); + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); if ((tv1 == NULL) || (tv2 == NULL)) { return 0; } @@ -22301,13 +23326,13 @@ DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t idx1, duk_idx_t i 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); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_get_tval(ctx, idx1); - tv2 = duk_get_tval(ctx, idx2); + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); if ((tv1 == NULL) || (tv2 == NULL)) { return 0; } @@ -22316,13 +23341,13 @@ DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t idx1, duk_ 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); + DUK_ASSERT_API_ENTRY(thr); - tv1 = duk_get_tval(ctx, idx1); - tv2 = duk_get_tval(ctx, idx2); + tv1 = duk_get_tval(thr, idx1); + tv2 = duk_get_tval(thr, idx2); if ((tv1 == NULL) || (tv2 == NULL)) { return 0; } @@ -22335,10 +23360,10 @@ DUK_EXTERNAL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_ * 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 @@ DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t idx1, duk_idx * 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 @@ DUK_INTERNAL void duk_push_lightfunc_name_raw(duk_context *ctx, duk_c_function f * 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_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_context *ctx, duk_tval *tv) { +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_push_lightfunc_name_raw(thr, func, lf_flags); } -DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) { +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_TVAL_GET_LIGHTFUNC(tv, func, lf_flags); /* read before 'tv' potentially invalidated */ + 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 @@ DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) { * 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 @@ DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, du *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 @@ DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, du * 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 @@ DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring 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 @@ DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring } *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE; - duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (q - buf)); + duk_push_lstring(thr, (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_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_LOCAL const char *duk__push_string_tval_readable(duk_context *ctx, duk_tval */ 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_hstring_readable_unicode(thr, DUK_TVAL_GET_STRING(tv_msg), DUK__READABLE_ERRMSG_MAXCHARS); + break; } } - duk_push_class_string_tval(ctx, tv); + duk_push_class_string_tval(thr, tv); break; } case DUK_TAG_BUFFER: { @@ -22551,49 +23591,51 @@ DUK_LOCAL const char *duk__push_string_tval_readable(duk_context *ctx, duk_tval /* 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); + duk_push_tval(thr, tv); break; } } } - return duk_to_string(ctx, -1); + return duk_to_string(thr, -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_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_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_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_context *ctx, duk_tval *tv) { - DUK_ASSERT_CTX_VALID(ctx); - return duk__push_string_tval_readable(ctx, tv, 1 /*error_aware*/); +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_context *ctx, duk_hstring *h) { +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 @@ DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_context *ctx, duk_hstri 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 @@ DUK_INTERNAL void duk_push_symbol_descriptive_string(duk_context *ctx, duk_hstri /* #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_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, 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 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, 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 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, 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 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, /* 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 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, 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 @@ DUK_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, /* 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); + duk_replace(thr, -((duk_idx_t) count) - 2); /* overwrite sep */ + duk_pop_n(thr, (duk_idx_t) 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) - 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_LOCAL void duk__concat_and_join_helper(duk_context *ctx, duk_idx_t count_in, 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_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; - duk__concat_and_join_helper(ctx, count, 0 /*is_join*/); + error_overflow: + DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); } +#endif /* DUK_USE_PREFER_SIZE */ -DUK_EXTERNAL void duk_join(duk_context *ctx, duk_idx_t count) { - DUK_ASSERT_CTX_VALID(ctx); +DUK_EXTERNAL void duk_join(duk_hthread *thr, duk_idx_t count) { + DUK_ASSERT_API_ENTRY(thr); - duk__concat_and_join_helper(ctx, count, 1 /*is_join*/); + 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); + DUK_ASSERT_API_ENTRY(thr); - h_input = duk_require_hstring(ctx, idx); /* Accept symbols. */ + 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_decode_string(duk_context *ctx, duk_idx_t idx, duk_decode_ } } -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); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_normalize_index(ctx, idx); + idx = duk_normalize_index(thr, idx); - h_input = duk_require_hstring(ctx, idx); /* Accept symbols. */ + h_input = duk_require_hstring(thr, idx); /* Accept symbols. */ DUK_ASSERT(h_input != NULL); bw = &bw_alloc; @@ -22805,22 +23932,21 @@ DUK_EXTERNAL void duk_map_string(duk_context *ctx, duk_idx_t idx, duk_map_char_f } DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(ctx, -1); /* Safe, extended UTF-8 encoded. */ - duk_replace(ctx, idx); + (void) duk_buffer_to_string(thr, -1); /* Safe, extended UTF-8 encoded. */ + duk_replace(thr, 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; +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); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(ctx, idx); /* Accept symbols. */ - h = duk_require_hstring(ctx, idx); + 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_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t star 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); + DUK_ASSERT_API_ENTRY(thr); - idx = duk_require_normalize_index(ctx, idx); /* Accept symbols. */ - h = duk_require_hstring(ctx, idx); + 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 @@ DUK_EXTERNAL void duk_trim(duk_context *ctx, duk_idx_t idx) { return; } - duk_push_lstring(ctx, (const char *) q_start, (duk_size_t) (q_end - q_start)); - duk_replace(ctx, idx); + 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_context *ctx, duk_idx_t idx, duk_size_t char_offset) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_EXTERNAL duk_codepoint_t duk_char_code_at(duk_context *ctx, duk_idx_t idx, d /* #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_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); } -DUK_EXTERNAL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, duk_time_components *comp) { +#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_EXTERNAL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, 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 @@ DUK_EXTERNAL void duk_time_to_components(duk_context *ctx, duk_double_t timeval, 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 @@ DUK_EXTERNAL duk_double_t duk_components_to_time(duk_context *ctx, duk_time_comp * 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 @@ DUK_LOCAL duk_uint32_t duk__push_this_obj_len_u32_limited(duk_context *ctx) { * 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 @@ DUK_LOCAL duk_harray *duk__arraypart_fastpath_this(duk_context *ctx) { * 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); + nargs = duk_get_top(thr); - if (nargs == 1 && duk_is_number(ctx, 0)) { + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_constructor_is_array(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_to_string(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { * (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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { #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_push_uarridx(thr, idx_last); + duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - DUK_ASSERT_TOP(ctx, n + 1); + DUK_ASSERT_TOP(thr, n + 1); return 1; } @@ -23352,39 +24514,39 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_concat(duk_context *ctx) { * 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); + 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(ctx, 0); + duk_to_string(thr, 0); } - len = duk__push_this_obj_len_u32(ctx); + 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); + 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(ctx); + duk_dup_0(thr); /* [ sep ToObject(this) len sep ] */ @@ -23397,9 +24559,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { /* [ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { 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_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(ctx, -1); + duk_to_string(thr, -1); } count++; @@ -23435,14 +24597,11 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_join_shared(duk_context *ctx) { */ #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 @@ DUK_LOCAL duk_ret_t duk__array_pop_fastpath(duk_context *ctx, duk_harray *h_arr) } #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 @@ DUK_LOCAL duk_ret_t duk__array_push_fastpath(duk_context *ctx, duk_harray *h_arr 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { } #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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { 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); + duk_dup(thr, i); + duk_put_prop_index(thr, -3, (duk_uarridx_t) (len + (duk_uint32_t) i)); } - len += n; + len += (duk_uint32_t) n; - duk_push_u32(ctx, len); - duk_dup_top(ctx); - duk_put_prop_stridx_short(ctx, -4, DUK_STRIDX_LENGTH); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_push(duk_context *ctx) { * 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id } } - 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id } } - 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id /* 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 @@ DUK_LOCAL duk_small_int_t duk__array_sort_compare(duk_context *ctx, duk_int_t id 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 @@ DUK_LOCAL void duk__array_sort_swap(duk_context *ctx, duk_int_t l, duk_int_t r) } /* 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); + duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) l); } else { - duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) l); - duk_pop(ctx); + 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); + duk_put_prop_index(thr, idx_obj, (duk_uarridx_t) r); } else { - duk_del_prop_index(ctx, idx_obj, (duk_uarridx_t) r); - duk_pop(ctx); + 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 @@ DUK_LOCAL void duk__debuglog_qsort_state(duk_context *ctx, duk_int_t lo, duk_int } #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))); + (long) lo, (long) hi, (duk_tval *) duk_get_tval(thr, 1))); - DUK_ASSERT_TOP(ctx, 3); + 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_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { 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 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { 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 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { 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_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { DUK_DDD(DUK_DDDPRINT("swap %ld and %ld", (long) l, (long) r)); - duk__array_sort_swap(ctx, l, r); + duk__array_sort_swap(thr, l, r); - DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(ctx, 1))); + DUK_DDD(DUK_DDDPRINT("after swap: %!T", (duk_tval *) duk_get_tval(thr, 1))); l++; r--; } @@ -23892,25 +25050,25 @@ DUK_LOCAL void duk__array_qsort(duk_context *ctx, duk_int_t lo, duk_int_t hi) { */ /* 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); + 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(ctx, 1))); - duk__array_qsort(ctx, lo, r - 1); - duk__array_qsort(ctx, r + 1, hi); + 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_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) { 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__array_qsort(thr, (duk_int_t) 0, (duk_int_t) (len - 1)); } - DUK_ASSERT_TOP(ctx, 3); - duk_pop(ctx); + DUK_ASSERT_TOP(thr, 3); + duk_pop_nodecref_unsafe(thr); return 1; /* return ToObject(this) */ } @@ -23940,9 +25098,10 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_sort(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { /* 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); + 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(ctx, 0, -((duk_int_t) len), (duk_int_t) len); + 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_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { 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_DCERROR_RANGE_INVALID_LENGTH(thr); } - duk_push_array(ctx); + duk_push_array(thr); /* stack[0] = start * stack[1] = deleteCount @@ -24008,19 +25169,19 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * 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) */ + 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(ctx); + duk_pop_undefined(thr); } } - duk_push_u32(ctx, (duk_uint32_t) del_count); - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ 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)); + 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(ctx); - duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); + duk_pop_undefined(thr); + duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); } } - DUK_ASSERT_TOP(ctx, nargs + 3); + 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_del_prop_index(thr, -3, (duk_uarridx_t) i); } - DUK_ASSERT_TOP(ctx, nargs + 3); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ 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)); + 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(ctx); - duk_del_prop_index(ctx, -3, (duk_uarridx_t) (i + item_count)); + duk_pop_undefined(thr); + duk_del_prop_index(thr, -3, (duk_uarridx_t) (i + item_count)); } } - DUK_ASSERT_TOP(ctx, nargs + 3); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * [ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_splice(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) { 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); + duk_put_prop_index(thr, -4, (duk_uarridx_t) lower); } else { - duk_del_prop_index(ctx, -4, (duk_uarridx_t) lower); - duk_pop(ctx); + 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); + duk_put_prop_index(thr, -3, (duk_uarridx_t) upper); } else { - duk_del_prop_index(ctx, -3, (duk_uarridx_t) upper); - duk_pop(ctx); + duk_del_prop_index(thr, -3, (duk_uarridx_t) upper); + duk_pop_undefined(thr); } - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); } - DUK_ASSERT_TOP(ctx, 2); - duk_pop(ctx); /* -> [ ToObject(this) ] */ + DUK_ASSERT_TOP(thr, 2); + duk_pop_unsafe(thr); /* -> [ ToObject(this) ] */ return 1; } @@ -24155,8 +25316,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reverse(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { * 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_ASSERT_TOP(thr, 5); } - duk_push_u32(ctx, res_length); - duk_xdef_prop_stridx_short(ctx, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); + duk_push_u32(thr, res_length); + duk_xdef_prop_stridx_short(thr, 4, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_W); - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); return 1; } @@ -24217,18 +25382,18 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_slice(duk_context *ctx) { * 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); + duk_push_int(thr, 0); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); return 0; } - duk_get_prop_index(ctx, 0, 0); + duk_get_prop_index(thr, 0, 0); /* stack[0] = object (this) * stack[1] = ToUint32(length) @@ -24236,22 +25401,22 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) { */ 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(thr, 0, (duk_uarridx_t) (i - 1)); + duk_pop_undefined(thr); } } - duk_del_prop_index(ctx, 0, (duk_uarridx_t) (len - 1)); + duk_del_prop_index(thr, 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_push_u32(thr, (duk_uint32_t) (len - 1)); + duk_put_prop_stridx_short(thr, 0, DUK_STRIDX_LENGTH); - DUK_ASSERT_TOP(ctx, 3); + DUK_ASSERT_TOP(thr, 3); return 1; } @@ -24259,20 +25424,20 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_shift(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) { 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_pop_undefined(thr); + duk_del_prop_index(thr, -2, (duk_uarridx_t) (i + (duk_uint32_t) nargs)); /* -> [ ... ToObject(this) ToUint32(length) ] */ } - DUK_ASSERT_TOP(ctx, nargs + 2); + 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(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(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_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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_unshift(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { */ for (i = from_idx; i >= 0 && i < len; i += idx_step) { - DUK_ASSERT_TOP(ctx, 4); + DUK_ASSERT_TOP(thr, 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); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_indexof_shared(duk_context *ctx) { * 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); + DUK_ASSERT_TOP(thr, 2); - len = duk__push_this_obj_len_u32(ctx); - duk_require_callable(ctx, 0); + 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); + duk_push_array(thr); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } /* stack[0] = callback @@ -24452,9 +25617,9 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { k = 0; /* result index for filter() */ for (i = 0; i < len; i++) { - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); - if (!duk_get_prop_index(ctx, 2, (duk_uarridx_t) i)) { + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { * counted towards result 'length'. */ #endif - duk_pop(ctx); + duk_pop_undefined(thr); continue; } @@ -24478,23 +25643,23 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { /* 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_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { DUK_UNREACHABLE(); break; } - duk_pop_2(ctx); + duk_pop_2_unsafe(thr); - DUK_ASSERT_TOP(ctx, 5); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_iter_shared(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { 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))); + (long) duk_get_top(thr), + (duk_tval *) duk_get_tval(thr, 4))); - DUK_ASSERT((have_acc && duk_get_top(ctx) == 5) || - (!have_acc && duk_get_top(ctx) == 4)); + DUK_ASSERT((have_acc && duk_get_top(thr) == 5) || + (!have_acc && duk_get_top(thr) == 4)); - if (!duk_has_prop_index(ctx, 2, (duk_uarridx_t) i)) { + 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); + DUK_ASSERT_TOP(thr, 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); + 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_DCERROR_TYPE_INVALID_ARGS(thr); } - DUK_ASSERT_TOP(ctx, 5); + DUK_ASSERT_TOP(thr, 5); return 1; } @@ -24659,18 +25824,18 @@ DUK_INTERNAL duk_ret_t duk_bi_array_prototype_reduce_shared(duk_context *ctx) { /* 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_INTERNAL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx 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); + duk_to_string(thr, -1); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +DUK_INTERNAL duk_ret_t duk_bi_boolean_constructor(duk_hthread *thr) { duk_hobject *h_this; - DUK_UNREF(thr); + duk_to_boolean(thr, 0); - duk_to_boolean(ctx, 0); - - if (duk_is_constructor_call(ctx)) { + 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 @@ static duk_uint16_t duk__buffer_elemtype_copy_compatible[9] = { }; #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))); + DUK_DD(DUK_DDPRINT("promoted 'this' automatically to an ArrayBuffer: %!iT", duk_get_tval(thr, -1))); - tv_dst = duk_get_borrowed_this_tval(ctx); + 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 @@ DUK_LOCAL duk_hbufobj *duk__hbufobj_promote_this(duk_context *ctx) { * 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; + DUK_ASSERT(thr != NULL); - tv = duk_get_borrowed_this_tval(ctx); + tv = duk_get_borrowed_this_tval(thr); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_OBJECT(tv)) { @@ -24890,7 +26048,7 @@ DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_context *ctx, duk_small_u * 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 @@ DUK_LOCAL duk_heaphdr *duk__getrequire_bufobj_this(duk_context *ctx, duk_small_u } /* 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 @@ DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_context *ctx, duk_idx_t idx 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 @@ DUK_LOCAL duk_hbufobj *duk__require_bufobj_value(duk_context *ctx, duk_idx_t idx 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 @@ DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufobj *h_bufobj, d } /* 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_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, 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 @@ DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, /* 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_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx, 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 @@ DUK_LOCAL void duk__clamp_startend_nonegidx_noshift(duk_context *ctx, * 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 @@ DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx, * 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 @@ DUK_LOCAL void duk__clamp_startend_negidx_shifted(duk_context *ctx, *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 @@ DUK_INTERNAL void duk_hbufobj_push_uint8array_from_plain(duk_hthread *thr, duk_h 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 @@ DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_context *ctx, duk_hbufobj 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 @@ DUK_INTERNAL void duk_hbufobj_push_validated_read(duk_context *ctx, duk_hbufobj } /* 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 @@ DUK_INTERNAL void duk_hbufobj_validated_write(duk_context *ctx, duk_hbufobj *h_b 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 @@ DUK_INTERNAL void duk_hbufobj_validated_write(duk_context *ctx, duk_hbufobj *h_b /* 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 @@ DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_context *ctx) { * 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_ERROR_TYPE_INVALID_ARGS(thr); } - duk_push_hbuffer(ctx, h_bufobj->buf); + 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 @@ DUK_LOCAL duk_hbuffer *duk__hbufobj_fixed_from_argvalue(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_constructor(duk_context *ctx) { */ #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_ASSERT_CTX_VALID(thr); - duk_require_constructor_call(ctx); + duk_require_constructor_call(thr); - len = duk_to_int(ctx, 0); + 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); + (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(ctx, + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * 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); + duk_hbufobj_promote_plain(thr, 0); - tv = duk_get_tval(ctx, 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { } 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_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { */ /* 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_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* 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_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * buffer functions. */ - duk_require_constructor_call(ctx); + duk_require_constructor_call(thr); - elem_length_signed = duk_require_int(ctx, 0); + 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); + duk_require_constructor_call(thr); - h_bufarg = duk__require_bufobj_value(ctx, 0); + 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_DCERROR_TYPE_INVALID_ARGS(thr); } - duk__resolve_offset_opt_length(ctx, h_bufarg, 1, 2, &offset, &length, 1 /*throw_flag*/); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) { (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 @@ DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_isview(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_uint8array_allocplain(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx) { * 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); + duk_push_undefined(thr); } else { - duk_push_hbuffer(ctx, h_bufobj->buf); + duk_push_hbuffer(thr, h_bufobj->buf); } return 1; } @@ -25920,27 +27056,23 @@ DUK_INTERNAL duk_ret_t duk_bi_uint8array_plainof(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { &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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tostring(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_tojson(duk_context *ctx) { */ #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); + h_bufarg1 = duk__require_bufobj_value(thr, 0); + h_bufarg2 = duk__require_bufobj_value(thr, 1); } else { - h_bufarg1 = duk__require_bufobj_this(ctx); - h_bufarg2 = duk__require_bufobj_value(ctx, 0); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_compare_shared(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { /* [ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { } 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { } /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_fill(duk_context *ctx) { */ #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); + str_data = (const duk_uint8_t *) duk_require_lstring_notsymbol(thr, 0, &str_len); - duk__resolve_offset_opt_length(ctx, h_this, 1, 2, &offset, &length, 0 /*throw_flag*/); + 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_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { /* [ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { } 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_copy(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { "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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_set(duk_context *ctx) { */ #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_LOCAL void duk__arraybuffer_plain_slice(duk_context *ctx, duk_hbuffer *h_val 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 @@ DUK_LOCAL void duk__arraybuffer_plain_slice(duk_context *ctx, duk_hbuffer *h_val /* 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_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { duk_uint_t slice_length; duk_tval *tv; - thr = (duk_hthread *) ctx; - DUK_UNREF(thr); - /* [ start end ] */ - magic = duk_get_current_magic(ctx); + magic = duk_get_current_magic(thr); - tv = duk_get_borrowed_this_tval(ctx); + tv = duk_get_borrowed_this_tval(thr); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_BUFFER(tv)) { @@ -26705,7 +27802,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { } 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { &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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { (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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_slice_shared(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_encoding(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) { } } - duk_push_boolean(ctx, ret); + duk_push_boolean(thr, ret); return 1; } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ @@ -26876,7 +27974,7 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_is_buffer(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { * (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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_byte_length(duk_context *ctx) { */ #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_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { 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); + 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(ctx, + 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_ASSERT_TOP(thr, 4); /* [ array totalLength bufres buf ] */ - duk_get_prop_index(ctx, 0, (duk_uarridx_t) i); - h_bufobj = duk__require_bufobj_value(ctx, 4); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { p += copy_size; space_left -= copy_size; - duk_pop(ctx); + duk_pop(thr); } - h_val = duk_known_hbuffer(ctx, -1); + h_val = duk_known_hbuffer(thr, -1); - duk__set_bufobj_buffer(ctx, h_bufres, h_val); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_concat(duk_context *ctx) { #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_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { if (magic_typedarray) { no_assert = 0; #if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(ctx, 1); /* 1=little endian */ + endswap = !duk_to_boolean(thr, 1); /* 1=little endian */ #else - endswap = duk_to_boolean(ctx, 1); /* 1=little endian */ + endswap = duk_to_boolean(thr, 1); /* 1=little endian */ #endif } else { - no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 2 : 1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { } tmp = buf[offset]; if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int8_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -27147,9 +28243,9 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { tmp = DUK_BSWAP16(tmp); } if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int16_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int16_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -27164,9 +28260,9 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { tmp = DUK_BSWAP32(tmp); } if (magic_signed) { - duk_push_int(ctx, (duk_int_t) ((duk_int32_t) tmp)); + duk_push_int(thr, (duk_int_t) ((duk_int32_t) tmp)); } else { - duk_push_uint(ctx, (duk_uint_t) tmp); + duk_push_uint(thr, (duk_uint_t) tmp); } break; } @@ -27181,7 +28277,7 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_readfield(duk_context *ctx) { #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_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { if (magic_typedarray) { no_assert = 0; #if defined(DUK_USE_INTEGER_LE) - endswap = !duk_to_boolean(ctx, 2); /* 1=little endian */ + endswap = !duk_to_boolean(thr, 2); /* 1=little endian */ #else - endswap = duk_to_boolean(ctx, 2); /* 1=little endian */ + endswap = duk_to_boolean(thr, 2); /* 1=little endian */ #endif - duk_swap(ctx, 0, 1); /* offset/value order different from Node.js */ + duk_swap(thr, 0, 1); /* offset/value order different from Node.js */ } else { - no_assert = duk_to_boolean(ctx, (magic_ftype == DUK__FLD_VARINT) ? 3 : 2); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { */ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_buffer_writefield(duk_context *ctx) { */ #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_LOCAL duk_hbufobj *duk__autospawn_arraybuffer(duk_context *ctx, duk_hbuffer 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_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_buffer_getter(duk_context *ctx) { /* 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); + duk_push_uint(thr, h_bufobj->offset); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx) { +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) { +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_context *ctx) { - duk_push_uint(ctx, 0); +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_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_bytelength_getter(duk_context *ctx) { * 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 const duk_uint32_t duk__parse_iso8601_control[] = { */ }; -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 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch } } 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 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch } 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 @@ DUK_LOCAL duk_bool_t duk__parse_string_iso8601_subset(duk_context *ctx, const ch * 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_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) { 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 @@ DUK_LOCAL duk_ret_t duk__parse_string(duk_context *ctx, const char *str) { * - 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 @@ DUK_LOCAL duk_double_t duk__make_day(duk_double_t year, duk_double_t month, duk_ 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_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, 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_INTERNAL void duk_bi_date_timeval_to_parts(duk_double_t d, duk_int_t *parts, 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 @@ DUK_INTERNAL duk_double_t duk_bi_date_get_timeval_from_dparts(duk_double_t *dpar * 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 @@ DUK_LOCAL duk_double_t duk__push_this_get_timeval_tzoffset(duk_context *ctx, duk 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 @@ DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, d /* 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)); + tmp = tzoffset; + fmt = "+%02d:%02d"; } 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"; } + 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 @@ DUK_LOCAL void duk__format_parts_iso8601(duk_int_t *parts, duk_int_t tzoffset, d * 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_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla 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 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * - 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 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * 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 @@ DUK_LOCAL duk_ret_t duk__to_string_helper(duk_context *ctx, duk_small_uint_t fla * 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_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag 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 @@ DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag * 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 @@ DUK_LOCAL duk_ret_t duk__get_part_helper(duk_context *ctx, duk_small_uint_t flag * 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_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag 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_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag 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); + duk__twodigit_year_fixup(thr, (duk_idx_t) i); } - dparts[idx] = duk_to_number(ctx, 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 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag * 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 @@ DUK_LOCAL duk_ret_t duk__set_part_helper(duk_context *ctx, duk_small_uint_t flag /* 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 @@ DUK_LOCAL void duk__twodigit_year_fixup(duk_context *ctx, duk_idx_t idx_val) { */ /* 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)) { + duk_to_number(thr, idx_val); + if (duk_is_nan(thr, 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_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_push_number(thr, d); + duk_replace(thr, idx_val); } - duk_pop(ctx); + 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 @@ DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, /* 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 @@ DUK_LOCAL void duk__set_parts_from_args(duk_context *ctx, duk_double_t *dparts, /* 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 @@ static duk_uint16_t duk__date_magics[] = { 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 @@ DUK_LOCAL duk_small_uint_t duk__date_get_indirect_magic(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_context *ctx) { */ 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 */ + duk__parse_string(thr, str); + duk_replace(thr, 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); + 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 ] */ + (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_context *ctx) { - return duk__parse_string(ctx, duk_to_string(ctx, 0)); +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_context *ctx) { - duk_idx_t nargs = duk_get_top(ctx); +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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_utc(duk_context *ctx) { */ if (nargs < 2) { - duk_push_nan(ctx); + duk_push_nan(thr); } else { - duk__set_parts_from_args(ctx, dparts, nargs); + duk__set_parts_from_args(thr, dparts, nargs); d = duk_bi_date_get_timeval_from_dparts(dparts, 0 /*flags*/); - duk_push_number(ctx, d); + duk_push_number(thr, d); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor_now(duk_context *ctx) { * 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_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_context *ctx) { +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); + duk_push_number(thr, d); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) { +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_push_this(thr); + duk_to_object(thr, -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_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_pop(thr); - 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_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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_to_json(duk_context *ctx) { * 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_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_context *ctx) { +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_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ct 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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_get_timezone_offset(duk_context *ct * 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_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_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_date_prototype_set_time(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_double_t duk_bi_date_get_now_gettimeofday(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { #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 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, cons 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 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_strptime(duk_context *ctx, cons #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 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const 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 @@ DUK_INTERNAL duk_bool_t duk_bi_date_parse_string_getdate(duk_context *ctx, const #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 @@ DUK_INTERNAL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx, duk_ (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 @@ DUK_LOCAL void duk__convert_systime_to_ularge(const SYSTEMTIME *st, ULARGE_INTEG 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 @@ DUK_LOCAL void duk__set_systime_jan1970(SYSTEMTIME *st) { #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_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t 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 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_do 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 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows_no_dst(duk_do #if defined(DUK_USE_DUKTAPE_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_duktape_object_info(duk_context *ctx) { - duk_inspect_value(ctx, -1); +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_context *ctx) { +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); + 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_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_enc(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_dec(duk_context *ctx) { * 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 @@ DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { } 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 @@ DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { /* 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 @@ DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *de * required side effect order. */ - if (duk_is_undefined(ctx, 0)) { - duk_push_fixed_buffer_nozero(ctx, 0); - duk_replace(ctx, 0); + if (duk_is_undefined(thr, 0)) { + duk_push_fixed_buffer_nozero(thr, 0); + duk_replace(thr, 0); } - (void) duk_require_buffer_data(ctx, 0, &len); /* Need 'len', avoid pointer. */ + (void) duk_require_buffer_data(thr, 0, &len); /* Need 'len', avoid pointer. */ - if (duk_check_type_mask(ctx, 1, DUK_TYPE_MASK_UNDEFINED | + 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 @@ DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *de * 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); + DUK_ERROR_TYPE(thr, 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 */ + 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(ctx, 0, &len_tmp); + 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 @@ DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *de } } - 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 @@ DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *de /* 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 @@ DUK_LOCAL duk_ret_t duk__decode_helper(duk_context *ctx, duk__decode_context *de */ #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); + duk_require_constructor_call(thr); return 0; } -DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encoding_getter(duk_context *ctx) { - duk_push_string(ctx, "utf-8"); +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_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx) { */ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_textencoder_prototype_encode(duk_context *ctx) { * returns a plain dynamic buffer. */ #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) - duk_push_buffer_object(ctx, -1, 0, final_len, DUK_BUFOBJ_UINT8ARRAY); + 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_context *ctx) { +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); + duk_to_string(thr, 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_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(ctx, 1, "ignoreBOM")) { - ignore_bom = duk_to_boolean(ctx, -1); + if (duk_get_prop_string(thr, 1, "ignoreBOM")) { + ignore_bom = duk_to_boolean(thr, -1); } } - duk_push_this(ctx); + 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); + duk_push_boolean(thr, dec_ctx->ignore_bom); break; } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_textdecoder_prototype_decode(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_textdecoder_decode_utf8_nodejs(duk_context *ctx) { /* #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 @@ DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { */ #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*/); + 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_context *ctx) { +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"); + 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(ctx, -1); + duk_to_string(thr, -1); } /* [ ... this name ] */ @@ -30695,28 +31856,28 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { * 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); + 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(ctx, -1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) { #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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o const char *str_directeval = " directeval"; const char *str_empty = ""; - DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */ - DUK_UNREF(thr); + DUK_ASSERT_TOP(thr, 0); /* fixed arg count */ - duk_push_this(ctx); - duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_TRACEDATA); - idx_td = duk_get_top_index(ctx); + 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(ctx, DUK_STRIDX_NEWLINE_4SPACE); - duk_push_this(ctx); + 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o /* [ ... 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o /* 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o (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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o (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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o */ 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * 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_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * 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 @@ DUK_LOCAL duk_ret_t duk__error_getter_helper(duk_context *ctx, duk_small_int_t o * 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_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_context *ctx) { - return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_FILENAME); +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_context *ctx) { - return duk__error_getter_helper(ctx, DUK__OUTPUT_TYPE_LINENUMBER); +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 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx * 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); + return duk_bi_error_prototype_to_string(thr); } -DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) { - DUK_UNREF(ctx); +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_context *ctx) { - DUK_UNREF(ctx); +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 @@ DUK_LOCAL duk_ret_t duk__error_setter_helper(duk_context *ctx, duk_small_uint_t * See https://github.com/svaarala/duktape/issues/387. */ - DUK_ASSERT_TOP(ctx, 1); /* fixed arg count: value */ + DUK_ASSERT_TOP(thr, 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_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_get_tval(thr, -3), duk_get_tval(thr, -2), duk_get_tval(thr, -1))); - duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | + 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_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_context *ctx) { - return duk__error_setter_helper(ctx, DUK_STRIDX_FILE_NAME); +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_context *ctx) { - return duk__error_setter_helper(ctx, DUK_STRIDX_LINE_NUMBER); +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 @@ DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_setter(duk_context *ctx /* #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 @@ DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { /* 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); + duk_push_hstring_empty(thr); } 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_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); + 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(ctx, -1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { * [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 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { return 1; type_error: - DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx); + DUK_DCERROR_TYPE_INVALID_ARGS(thr); } #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() +/* 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_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 ] */ +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; +} - 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; +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; +} - type_error: - DUK_DCERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx); +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; } -#endif #if defined(DUK_USE_FUNCTION_BUILTIN) -DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) { - duk_idx_t nargs; +/* 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; - /* Step 1 is not necessary because duk_call_method() will take - * care of it. - */ + /* XXX: C API call, e.g. duk_push_bound_function(thr, target_idx, nargs); */ - /* vararg function, thisArg needs special handling */ - nargs = duk_get_top(ctx); /* = 1 + arg count */ - if (nargs == 0) { - duk_push_undefined(ctx); + /* 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_push_undefined(thr); } - DUK_ASSERT(nargs >= 1); + DUK_ASSERT(nargs >= 0); - /* [ thisArg arg1 ... argN ] */ + /* 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(ctx); /* 'func' in the algorithm */ - duk_insert(ctx, 0); + duk_push_this(thr); + duk_require_callable(thr, -1); - /* [ func thisArg arg1 ... argN ] */ + /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs+1 total) */ + DUK_ASSERT_TOP(thr, nargs + 2); - 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; + /* 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); - /* 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); + /* [ thisArg arg1 ... argN func boundFunc ] */ - duk_push_this(ctx); - duk_require_callable(ctx, -1); + /* 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); - /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ - DUK_ASSERT_TOP(ctx, nargs + 1); + if (DUK_TVAL_IS_OBJECT(tv_tmp)) { + duk_hobject *h_target; + duk_hobject *bound_proto; - /* 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); + h_target = DUK_TVAL_GET_OBJECT(tv_tmp); + DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(h_target)); - /* [ 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); + /* 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); - duk_dup_0(ctx); /* thisArg */ - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); + /* 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); + } - duk_push_array(ctx); + if (DUK_HOBJECT_HAS_BOUNDFUNC(h_target)) { + duk_hboundfunc *h_boundtarget; - /* [ thisArg arg1 ... argN func boundFunc argArray ] */ + h_boundtarget = (duk_hboundfunc *) h_target; - 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); + /* 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)))); - /* [ thisArg arg1 ... argN func boundFunc ] */ + DUK_TVAL_SET_TVAL(&h_bound->target, &h_boundtarget->target); + DUK_TVAL_SET_TVAL(&h_bound->this_binding, &h_boundtarget->this_binding); - h_target = duk_get_hobject(ctx, -2); + tv_prevbound = h_boundtarget->args; + n_prevbound = h_boundtarget->nargs; + } + } else { + /* Lightfuncs are always strict. */ + duk_hobject *bound_proto; - /* 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)); + 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); } - /* 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_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); } - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C); /* attrs in E6 Section 9.2.4 */ + 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; - /* 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); + 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); - /* 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)) { + /* [ thisArg arg1 ... argN func boundFunc ] */ + + /* 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_pop(thr); + duk_push_hstring_empty(thr); } - duk_concat(ctx, 2); - duk_xdef_prop_stridx_short(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); -#endif + 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); + 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 - /* 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_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 @@ DUK_LOCAL duk_small_int_t duk__decode_hex_escape(const duk_uint8_t *p, duk_small 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_LOCAL int duk__transform_helper(duk_context *ctx, duk__transform_callback ca 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 @@ DUK_LOCAL void duk__transform_callback_encode_uri(duk__transform_context *tfm_ct 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_LOCAL void duk__transform_callback_decode_uri(duk__transform_context *tfm_ct 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 @@ DUK_LOCAL void duk__transform_callback_unescape(duk__transform_context *tfm_ctx, * 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_small_uint_t call_flags; - DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2); /* 2 when called by debugger */ + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { } 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_js_init_activation_environment_records_delayed(thr, act_caller); } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); + DUK_ASSERT(act_caller->lex_env != NULL); + DUK_ASSERT(act_caller->var_env != NULL); this_to_global = 0; @@ -31968,14 +33141,13 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { "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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { "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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { */ #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. */ + DUK_ASSERT_TOP(thr, 2); + duk_to_string(thr, 0); /* Reject symbols. */ - radix = duk_to_int32(ctx, 1); + radix = duk_to_int32(thr, 1); /* While parseInt() recognizes 0xdeadbeef, it doesn't recognize * ES2015 0o123 or 0b10001. @@ -32083,25 +33264,22 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_int(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_parse_float(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_is_finite(duk_context *ctx) { */ #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(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_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_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_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(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_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_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_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_context *ctx) { - return duk__transform_helper(ctx, duk__transform_callback_unescape, (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 @@ DUK_LOCAL_DECL void duk__enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_buf #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_LOCAL duk_uint_fast32_t duk__dec_decode_hex_escape(duk_json_dec_ctx *js_ctx, 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 duk_small_int_t duk__dec_string_escape(duk_json_dec_ctx *js_ctx, duk_u 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 @@ DUK_LOCAL void duk__dec_string(duk_json_dec_ctx *js_ctx) { #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_string(duk_json_dec_ctx *js_ctx) { */ 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 @@ DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_plain_string(duk_json_dec_ctx *js_ctx) { #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 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_pointer(duk_json_dec_ctx *js_ctx) { #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 @@ DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) { /* 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 @@ DUK_LOCAL void duk__dec_buffer(duk_json_dec_ctx *js_ctx) { /* 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 @@ DUK_LOCAL void duk__dec_number(duk_json_dec_ctx *js_ctx) { 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_objarr_exit(duk_json_dec_ctx *js_ctx) { } 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_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { /* [ ... obj key val ] */ - duk_xdef_prop_wec(ctx, -3); + duk_xdef_prop_wec(thr, -3); /* [ ... obj ] */ @@ -32987,7 +34161,7 @@ DUK_LOCAL void duk__dec_object(duk_json_dec_ctx *js_ctx) { /* [ ... 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_object(duk_json_dec_ctx *js_ctx) { } 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_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { /* [ ... 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 @@ DUK_LOCAL void duk__dec_array(duk_json_dec_ctx *js_ctx) { * 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_array(duk_json_dec_ctx *js_ctx) { } 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 @@ DUK_LOCAL void duk__dec_value(duk_json_dec_ctx *js_ctx) { #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 @@ DUK_LOCAL void duk__dec_value(duk_json_dec_ctx *js_ctx) { } } 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 @@ DUK_LOCAL void duk__dec_value(duk_json_dec_ctx *js_ctx) { * 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))); + (long) duk_get_top(thr), + (duk_tval *) duk_get_tval(thr, -2), + (duk_tval *) duk_get_tval(thr, -1))); - duk_dup_top(ctx); - duk_get_prop(ctx, -3); /* -> [ ... holder name val ] */ + duk_dup_top(thr); + duk_get_prop(thr, -3); /* -> [ ... holder name val ] */ - h = duk_get_hobject(ctx, -1); + 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))); + (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(ctx); - (void) duk_push_uint_to_hstring(ctx, (duk_uint_t) i); /* -> [ ... holder name val val ToString(i) ] */ + 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 @@ DUK_LOCAL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx) { * 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_put_prop(thr, -4); } } - duk_pop(ctx); /* pop enum */ + 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 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin #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 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin 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_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st */ 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_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) { */ 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); + duk_numconv_stringify(thr, 10 /*radix*/, 0 /*digits*/, n2s_flags); } - h_str = duk_known_hstring(ctx, -1); + h_str = duk_known_hstring(thr, -1); DUK__EMIT_HSTR(js_ctx, h_str); return; } @@ -33761,7 +34933,7 @@ DUK_LOCAL void duk__enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuff 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 @@ DUK_LOCAL void duk__enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj) * 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 @@ DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth } } #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 @@ DUK_LOCAL void duk__enc_newline_indent(duk_json_enc_ctx *js_ctx, duk_int_t depth /* 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); + *entry_top = duk_get_top(thr); - duk_require_stack(ctx, DUK_JSON_ENC_REQSTACK); + 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 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_ 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_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(ctx); /* -> [ ... voidp true ] */ - duk_put_prop(ctx, js_ctx->idx_loop); /* -> [ ... ] */ + 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 @@ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_t /* 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 @@ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_idx_t *entry_t * 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_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { * 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))); + (duk_tval *) duk_get_tval(thr, idx_obj), + (duk_tval *) duk_get_tval(thr, -1))); - h_key = duk_known_hstring(ctx, -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_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__enc_object(duk_json_enc_ctx *js_ctx) { * 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_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { 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_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { 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_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { 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 @@ DUK_LOCAL void duk__enc_array(duk_json_enc_ctx *js_ctx) { * 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); + (long) idx_holder, (duk_tval *) duk_get_tval(thr, idx_holder), + (duk_tval *) duk_get_tval(thr, -1))); - tv_holder = DUK_GET_TVAL_POSIDX(ctx, idx_holder); + 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' ] */ + /* 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(ctx); /* -> [ ... key val ] */ + 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))); + DUK_DDD(DUK_DDDPRINT("value=%!T", (duk_tval *) duk_get_tval(thr, -1))); - tv = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv = DUK_GET_TVAL_NEGIDX(thr, -1); if (DUK_TVAL_IS_OBJECT(tv)) { duk_hobject *h; @@ -34249,19 +35418,19 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold 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 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold #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 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold /* [ ... 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 (duk_check_type_mask(ctx, -1, js_ctx->mask_for_undefined)) { + 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 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold /* 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 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold #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 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du * 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 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du * 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 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du 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_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du 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 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du * 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_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du 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_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du 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 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du 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_LOCAL duk_ret_t duk__json_stringify_fast(duk_context *ctx, void *udata) { */ 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 @@ void duk_bi_json_parse_helper(duk_context *ctx, 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 @@ void duk_bi_json_parse_helper(duk_context *ctx, 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 @@ void duk_bi_json_parse_helper(duk_context *ctx, 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_tval *) duk_get_tval(thr, -1), + (long) duk_get_top(thr))); - DUK_ASSERT(duk_get_top(ctx) == entry_top + 1); + 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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))); + (long) duk_get_top(thr))); - entry_top = duk_get_top(ctx); + entry_top = duk_get_top(thr); /* * Context init @@ -35180,7 +36347,7 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * 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); + duk_to_string(thr, idx_space); } } - if (duk_is_number(ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, }; /* 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, 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 @@ void duk_bi_json_stringify_helper(duk_context *ctx, #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_tval *) duk_get_tval(thr, -1), + (long) duk_get_top(thr))); - DUK_ASSERT(duk_get_top(ctx) == entry_top + 1); + DUK_ASSERT(duk_get_top(thr) == entry_top + 1); } #if defined(DUK_USE_JSON_BUILTIN) @@ -35415,16 +36582,16 @@ void duk_bi_json_stringify_helper(duk_context *ctx, * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_json_object_stringify(duk_context *ctx) { 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 @@ DUK_LOCAL duk_ret_t duk__math_minmax(duk_context *ctx, duk_double_t initial, duk */ 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_LOCAL duk_ret_t duk__math_minmax(duk_context *ctx, duk_double_t initial, duk } } - duk_push_number(ctx, res); + duk_push_number(thr, res); return 1; } @@ -35765,49 +36932,49 @@ DUK_LOCAL const duk__two_arg_func duk__two_arg_funcs[] = { #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)); + duk_push_number(thr, (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_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)); + 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_context *ctx) { - return duk__math_minmax(ctx, -DUK_DOUBLE_INFINITY, duk__fmax_fixed); +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_context *ctx) { - return duk__math_minmax(ctx, DUK_DOUBLE_INFINITY, duk__fmin_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_context *ctx) { - duk_push_number(ctx, (duk_double_t) DUK_UTIL_GET_RANDOM_DOUBLE((duk_hthread *) ctx)); +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_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_math_object_hypot(duk_context *ctx) { #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_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(ctx, -1, DUK_STRIDX_INT_VALUE); - DUK_ASSERT(duk_is_number(ctx, -1)); + 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); + return duk_get_number(thr, -1); } -DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; +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 @@ DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_context *ctx) { * 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_push_int(thr, 0); } - duk_to_number(ctx, 0); - duk_set_top(ctx, 1); - DUK_ASSERT_TOP(ctx, 1); + duk_to_number(thr, 0); + duk_set_top(thr, 1); + DUK_ASSERT_TOP(thr, 1); - if (!duk_is_constructor_call(ctx)) { + if (!duk_is_constructor_call(thr)) { return 1; } @@ -35952,50 +37209,50 @@ DUK_INTERNAL duk_ret_t duk_bi_number_constructor(duk_context *ctx) { */ /* 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); +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_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_locale_string(duk_context *ctx /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_fixed(duk_context *ctx) { 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); + DUK_ASSERT_TOP(thr, 2); + duk_to_string(thr, -1); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_exponential(duk_context *ctx) { +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); + d = duk__push_this_number_plain(thr); - frac_undefined = duk_is_undefined(ctx, 0); - duk_to_int(ctx, 0); /* for side effects */ + 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); + DUK_ASSERT_TOP(thr, 2); + duk_to_string(thr, -1); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx) { +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_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx) { duk_small_int_t c; duk_small_uint_t n2s_flags; - DUK_ASSERT_TOP(ctx, 1); + DUK_ASSERT_TOP(thr, 1); - d = duk__push_this_number_plain(ctx); - if (duk_is_undefined(ctx, 0)) { + d = duk__push_this_number_plain(thr); + if (duk_is_undefined(thr, 0)) { goto use_to_string; } - DUK_ASSERT_TOP(ctx, 2); + DUK_ASSERT_TOP(thr, 2); - duk_to_int(ctx, 0); /* for side effects */ + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_number_prototype_to_precision(duk_context *ctx) { /* #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_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) { duk_tval *tv; - tv = DUK_HTHREAD_THIS_PTR((duk_hthread *) ctx); + 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); + arg_mask = duk_get_type_mask(thr, 0); - if (!duk_is_constructor_call(ctx) && /* not a constructor call */ + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_context *ctx) { #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*/); + nargs = duk_get_top_require_min(thr, 1 /*min_top*/); - duk_to_object(ctx, 0); + 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_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_context *ctx) { */ } - 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); + duk_hbufobj_promote_plain(thr, 0); #endif - proto = duk_require_hobject_accept_mask(ctx, 0, DUK_TYPE_MASK_NULL); - DUK_ASSERT(proto != NULL || duk_is_null(ctx, 0)); + 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(ctx, + (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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx) { #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_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context * */ 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); + duk_set_top(thr, 3); - if (!duk_next(ctx, 2, 1 /*get_value*/)) { + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context * } /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context * * 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_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_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 1); - 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); + duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/); 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); + duk_require_callable(thr, 1); #endif - duk_dup_0(ctx); /* -> [ O toString O ] */ - duk_call_method(ctx, 0); /* XXX: call method tail call? */ + 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); + DUK_ASSERT_TOP(thr, 1); - h_v = duk_get_hobject(ctx, 0); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_contex * * 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_TOP(thr, 0); + duk_push_this_coercible_to_object(thr); } - DUK_ASSERT(duk_get_top(ctx) >= 1); + DUK_ASSERT(duk_get_top(thr) >= 1); if (magic < 2) { /* ES2015 Section 19.1.2.9, step 1 */ - duk_to_object(ctx, 0); + duk_to_object(thr, 0); } - tv = DUK_GET_TVAL_POSIDX(ctx, 0); + tv = DUK_GET_TVAL_POSIDX(thr, 0); switch (DUK_TVAL_GET_TAG(tv)) { case DUK_TAG_BUFFER: @@ -36537,9 +37744,9 @@ DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { DUK_DCERROR_TYPE_INVALID_ARGS(thr); } if (proto != NULL) { - duk_push_hobject(ctx, proto); + duk_push_hobject(thr, proto); } else { - duk_push_null(ctx); + duk_push_null(thr); } return 1; } @@ -36552,14 +37759,13 @@ DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { ret_success = 0; } else { if (magic == 1) { - duk_require_object_coercible(ctx, 0); + duk_require_object_coercible(thr, 0); } else { - duk_require_hobject_accept_mask(ctx, 0, + 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); + duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT); } - h_new_proto = duk_get_hobject(ctx, 1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { } 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { 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_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ct 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ct * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ct * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ct * 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_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx) 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); + h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER); } - duk_push_boolean(ctx, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h)); + 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_LOCAL const duk_small_uint_t duk__object_keys_enum_flags[4] = { 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_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) { duk_small_uint_t enum_flags; duk_int_t magic; - DUK_ASSERT_TOP(ctx, 1); - DUK_UNREF(thr); + DUK_ASSERT_TOP(thr, 1); - magic = duk_get_current_magic(ctx); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) { #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_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context 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 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context * 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); + if (duk_get_top(thr) == 0) { + duk_push_pointer(thr, NULL); } else { - duk_to_pointer(ctx, 0); + duk_to_pointer(thr, 0); } - DUK_ASSERT(duk_is_pointer(ctx, 0)); - duk_set_top(ctx, 1); + DUK_ASSERT(duk_is_pointer(thr, 0)); + duk_set_top(thr, 1); - if (duk_is_constructor_call(ctx)) { - (void) duk_push_object_helper(ctx, + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx) { * toString(), valueOf() */ -DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx) { +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(ctx); + duk_small_int_t to_string = duk_get_current_magic(thr); - duk_push_this(ctx); - tv = duk_require_tval(ctx, -1); + duk_push_this(thr); + tv = duk_require_tval(thr, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_POINTER(tv)) { @@ -36991,19 +38277,64 @@ DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx 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 @@ DUK_INTERNAL duk_ret_t duk_bi_pointer_prototype_tostring_shared(duk_context *ctx * 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 @@ DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h * 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 @@ DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h #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); +DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_hthread *thr) { + DUK_ASSERT_TOP(thr, 2); /* [ target handler ] */ - /* 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_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 @@ DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx) { /* #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); + 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); + duk_push_boolean(thr, ret); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_get(duk_context *ctx) { - duk_hthread *thr; +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); + duk_push_boolean(thr, ret); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx) { - duk_hthread *thr; +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 @@ DUK_INTERNAL duk_ret_t duk_bi_reflect_object_set(duk_context *ctx) { #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); + DUK_ASSERT_TOP(thr, 2); + h_pattern = duk_get_hobject(thr, 0); - if (!duk_is_constructor_call(ctx) && + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx) { 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); + duk_dup_1(thr); } } else { - if (duk_is_undefined(ctx, 0)) { - duk_push_hstring_empty(ctx); + if (duk_is_undefined(thr, 0)) { + duk_push_hstring_empty(thr); } else { - duk_dup_0(ctx); - duk_to_string(ctx, -1); /* Rejects Symbols. */ + duk_dup_0(thr); + duk_to_string(thr, -1); /* Rejects Symbols. */ } - if (duk_is_undefined(ctx, 1)) { - duk_push_hstring_empty(ctx); + if (duk_is_undefined(thr, 1)) { + duk_push_hstring_empty(thr); } else { - duk_dup_1(ctx); - duk_to_string(ctx, -1); /* Rejects Symbols. */ + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_constructor(duk_context *ctx) { 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)); + duk_push_boolean(thr, (duk_is_null(thr, -1) ? 0 : 1)); return 1; } -DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_tostring(duk_context *ctx) { +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); + 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_context *ctx) { +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_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_flags(duk_context *ctx) { *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_ASSERT_TOP(thr, 0); - duk_push_this(ctx); - h = duk_require_hobject(ctx, -1); - magic = duk_get_current_magic(ctx); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) { */ case 3: /* sticky */ case 4: { /* unicode */ - duk_push_false(ctx); + duk_push_false(thr); break; } #endif @@ -37516,19 +38782,19 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) { * 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); + if (duk_get_class_number(thr, idx) == DUK_HOBJECT_CLASS_REGEXP) { + DUK_ERROR_TYPE_INVALID_ARGS(thr); } - h = duk_to_hstring(ctx, idx); + 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_LOCAL duk_int_t duk__str_search_shared(duk_context *ctx, duk_hstring *h_this } 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 @@ DUK_LOCAL duk_int_t duk__str_search_shared(duk_context *ctx, duk_hstring *h_this * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_context *ctx) { * current magic call sites. */ - if (duk_get_top(ctx) == 0) { - duk_push_hstring_empty(ctx); + if (duk_get_top(thr) == 0) { + duk_push_hstring_empty(thr); } 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); + 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(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. */ + 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(ctx)) { + 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 @@ DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_context *ctx, duk_bool_t * 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 @@ DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_context *ctx, duk_bool_t * 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 @@ DUK_LOCAL duk_ret_t duk__construct_from_codepoints(duk_context *ctx, duk_bool_t * 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_LOCAL duk_ret_t duk__construct_from_codepoints(duk_context *ctx, duk_bool_t } DUK_BW_COMPACT(thr, bw); - (void) duk_buffer_to_string(ctx, -1); /* Safe, extended UTF-8 or CESU-8 encoded. */ + (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_context *ctx) { - return duk__construct_from_codepoints(ctx, 0 /*nonbmp*/); +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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_constructor_from_code_point(duk_context *ct * toString(), valueOf() */ -DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx) { +DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_hthread *thr) { duk_tval *tv; - duk_push_this(ctx); - tv = duk_require_tval(ctx, -1); + duk_push_this(thr); + tv = duk_require_tval(thr, -1); DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_STRING(tv)) { @@ -37739,37 +39004,36 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_to_string(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) { /* XXX: faster implementation */ - DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(ctx, 0))); + DUK_DDD(DUK_DDDPRINT("arg=%!T", (duk_tval *) duk_get_tval(thr, 0))); - h = duk_push_this_coercible_to_string(ctx); + 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); + magic = duk_get_current_magic(thr); #else - DUK_ASSERT(duk_get_current_magic(ctx) == 0); + DUK_ASSERT(duk_get_current_magic(thr) == 0); magic = 0; #endif if (clamped) { @@ -37801,10 +39065,11 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) { if (magic != 0) { return 0; } - duk_push_nan(ctx); + duk_push_nan(thr); } 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_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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx) { */ /* 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_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_slice(duk_context *ctx) { * 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); +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(ctx); + (void) duk_push_this_coercible_to_string(thr); duk_unicode_case_convert_string(thr, (duk_bool_t) uppercase); return 1; } @@ -37947,33 +39211,33 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx) * 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 */ + 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(ctx); + 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_to_int_clamped(thr, 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__str_search_shared(thr, h_this, h_search, cpos, is_lastindexof /*backwards*/); + duk_push_int(thr, cpos); return 1; } @@ -37992,8 +39256,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) * - 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { * 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_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(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_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_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { */ 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_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(ctx, -1) <= DUK_INT_MAX); /* string limits */ - match_caps = (duk_int_t) duk_get_length(ctx, -1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { /* 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_dup_0(thr); } - duk_push_int(ctx, match_start_coff); - duk_dup_2(ctx); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { } /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { * 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_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { 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); + duk_push_array(thr); - if (duk_is_undefined(ctx, 1)) { + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { (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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { hit_limit: #if defined(DUK_USE_REGEXP_SUPPORT) if (is_regexp) { - duk_pop(ctx); + duk_pop(thr); } #endif @@ -38653,7 +39914,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { */ #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 @@ DUK_LOCAL void duk__to_regexp_helper(duk_context *ctx, duk_idx_t idx, duk_bool_t 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_search(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) { /* [ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_match(duk_context *ctx) { arr_idx = 0; for (;;) { - DUK_ASSERT_TOP(ctx, 3); + DUK_ASSERT_TOP(thr, 3); - duk_dup_0(ctx); - duk_dup_1(ctx); + 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); + if (!duk_is_object(thr, -1)) { + duk_pop(thr); 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); + 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)); + (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_context *ctx) { - DUK_ASSERT_TOP(ctx, 0); - (void) duk_push_this_coercible_to_string(ctx); - duk_trim(ctx, 0); - DUK_ASSERT_TOP(ctx, 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_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) { } /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx) /* 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx) 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context * 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_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context * 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_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context * } 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context * 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_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context * } } - 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 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx) { * 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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx) { * +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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_constructor_shared(duk_context *ctx) { 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))); + 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_context *ctx, duk_tval *tv_arg) { +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_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_context *ctx, duk_tval *tv_arg 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 @@ DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_context *ctx, duk_tval *tv_arg 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); + duk_push_hstring(thr, h_str); } return 1; } -DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx) { +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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx) { */ 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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_key_for(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_toprimitive(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_context *ctx) { * 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_require_callable(thr, 0); - duk_push_thread(ctx); - new_thr = (duk_hthread *) duk_known_hobject(ctx, -1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_constructor(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { 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 */ + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL); /* caller */ - caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { 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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { */ #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { 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 */ + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr->parent) != NULL); /* caller */ - caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1); + 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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { #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 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_current(duk_context *ctx) { /* #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 @@ DUK_INTERNAL void duk_fb_sprintf(duk_fixedbuffer *fb, const char *fmt, ...) { } } else { /* normal */ - fb->offset += res; + fb->offset += (duk_size_t) res; } } va_end(ap); @@ -39695,9 +40951,9 @@ DUK_INTERNAL duk_bool_t duk_fb_is_full(duk_fixedbuffer *fb) { #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 @@ DUK_LOCAL const char *duk__bc_optab[256] = { "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", + "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", - "INSTOF_RR", "INSTOF_CR", "INSTOF_RC", "INSTOF_CC", "IN_RR", "IN_CR", "IN_RC", "IN_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", + "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", - "UNUSED192", "UNUSED193", "UNUSED194", "UNUSED195", "UNUSED196", "UNUSED197", "UNUSED198", "UNUSED199", - "UNUSED200", "UNUSED201", "UNUSED202", "UNUSED203", "UNUSED204", "UNUSED205", "UNUSED206", "UNUSED207", - "UNUSED208", "UNUSED209", "UNUSED210", "UNUSED211", "UNUSED212", "UNUSED213", "UNUSED214", "UNUSED215", + "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 @@ DUK_LOCAL void duk__print_hstring(duk__dprint_state *st, duk_hstring *h, duk_boo 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_LOCAL void duk__print_hstring(duk__dprint_state *st, duk_hstring *h, duk_boo 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 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { 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_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { 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_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { 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 @@ DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) { 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 @@ DUK_LOCAL void duk__print_instr(duk__dprint_state *st, duk_instr_t ins) { /* 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 @@ DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_u #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); } } @@ -40668,6 +41933,24 @@ DUK_INTERNAL void duk_debug_format_funcptr(char *buf, duk_size_t buf_size, duk_u #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 @@ DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) { /* 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_LOCAL void duk__debug_do_detach2(duk_heap *heap) { 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 @@ DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) { * 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; @@ -40806,12 +42086,41 @@ DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) { /* keep heap->dbg_detached_cb */ } +/* + * 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 @@ DUK_INTERNAL duk_bool_t duk_debug_read_peek(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_debug_read_flush(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_debug_write_flush(duk_hthread *thr) { return; } + DUK__DBG_TPORT_ENTER(); heap->dbg_write_flush_cb(heap->dbg_udata); + DUK__DBG_TPORT_EXIT(); } /* @@ -40942,7 +42258,10 @@ DUK_INTERNAL void duk_debug_read_bytes(duk_hthread *thr, duk_uint8_t *data, duk_ #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_LOCAL duk_uint32_t duk__debug_read_uint32_raw(duk_hthread *thr) { (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_INTERNAL duk_int32_t duk_debug_read_int(duk_hthread *thr) { } 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); + duk_push_lstring(thr, (const char *) buf, (duk_size_t) len); } else { - 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); - (void) duk_buffer_to_string(ctx, -1); /* Safety relies on debug client, which is OK. */ + (void) duk_buffer_to_string(thr, -1); /* Safety relies on debug client, which is OK. */ } - return duk_require_hstring(ctx, -1); + 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 @@ DUK_INTERNAL duk_hstring *duk_debug_read_hstring(duk_hthread *thr) { 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_heaphdr *duk_debug_read_any_ptr(duk_hthread *thr) { } 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 @@ DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) { 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 @@ DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) { 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 @@ DUK_INTERNAL duk_tval *duk_debug_read_tval(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_debug_write_bytes(duk_hthread *thr, const duk_uint8_t *dat #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_INTERNAL void duk_debug_write_hstring(duk_hthread *thr, duk_hstring *h) { } 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_error_eom(duk_hthread *thr, duk_small_uint_t e 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 void duk_debug_write_eom(duk_hthread *thr) { */ 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 @@ DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) { /* 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_INTERNAL void duk_debug_send_status(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) { * NFY 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); + duk_pop(thr); - if (duk_is_error(ctx, -1)) { + 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 @@ DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) { */ 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 @@ DUK_LOCAL duk_bool_t duk__debug_skip_dvalue(duk_hthread *thr) { 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 @@ DUK_LOCAL void duk__debug_skip_to_eom(duk_hthread *thr) { } } +/* 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 @@ DUK_LOCAL duk_int32_t duk__debug_read_validate_csindex(duk_hthread *thr) { 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_trigger_status(duk_hthread *thr, duk_heap *heap 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; + 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); - 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")); - } duk_debug_write_reply(thr); duk_debug_write_eom(thr); } @@ -41978,40 +43318,27 @@ DUK_LOCAL void duk__debug_handle_del_break(duk_hthread *thr, duk_heap *heap) { } 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_get_var(duk_hthread *thr, duk_heap *heap) { } 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 @@ DUK_LOCAL void duk__debug_handle_put_var(duk_hthread *thr, duk_heap *heap) { 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_put_var(duk_hthread *thr, duk_heap *heap) { } 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 @@ DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap /* 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,20 +43423,17 @@ DUK_LOCAL void duk__debug_handle_get_call_stack(duk_hthread *thr, duk_heap *heap } 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) { + 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; + duk_debug_write_reply(thr); /* XXX: several nice-to-have improvements here: * - Use direct reads avoiding value stack operations @@ -42131,18 +43441,18 @@ DUK_LOCAL void duk__debug_handle_get_locals(duk_hthread *thr, duk_heap *heap) { * - 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_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, curr_act, varname, 0 /*throw_flag*/); + 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_get_locals(duk_hthread *thr, duk_heap *heap) { } 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 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { */ /* 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 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { (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; + duk_push_int(thr, level - 1); /* compensate for eval() call */ } - /* [ ... eval "eval" eval_input level ] */ + /* [ ... 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; + 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_handle_call_protected(thr, num_eval_args, call_flags); + 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 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { * 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_detach(duk_hthread *thr, duk_heap *heap) { } 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 @@ DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) { */ 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 @@ DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) { } 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_debug_write_error_eom(thr, DUK_DBG_ERR_APPLICATION, duk_get_string(thr, -1)); } - duk_set_top(ctx, old_top); /* restore stack top */ + 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 @@ DUK_LOCAL void duk__debug_handle_apprequest(duk_hthread *thr, duk_heap *heap) { 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 @@ DUK_LOCAL void duk__debug_dump_heaphdr(duk_hthread *thr, duk_heap *heap, duk_hea 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 @@ DUK_LOCAL void duk__debug_handle_get_bytecode(duk_hthread *thr, duk_heap *heap) } 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 duk_uint_t duk__debug_getinfo_hstring_masks[] = { DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = { "extensible", "constructable", + "callable", "boundfunc", "compfunc", "natfunc", @@ -42589,17 +43897,18 @@ DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = { "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_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = { 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 @@ DUK_LOCAL void duk__debug_getinfo_bitmask(duk_hthread *thr, const char * const * for (;;) { mask = *masks++; - if (!mask) { + if (mask == 0) { break; } key = *keys++; @@ -42741,6 +44050,10 @@ DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h 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_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h 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 @@ DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h } } + 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_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h 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_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_h 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 @@ DUK_LOCAL void duk__debug_handle_get_obj_prop_desc_range(duk_hthread *thr, duk_h * 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_LOCAL void duk__debug_process_message(duk_hthread *thr) { 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 @@ DUK_LOCAL void duk__debug_process_message(duk_hthread *thr) { } } /* 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_LOCAL void duk__check_resend_status(duk_hthread *thr) { } 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 @@ DUK_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t /* 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_INTERNAL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bool_t 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 @@ DUK_INTERNAL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstr 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 @@ DUK_INTERNAL void duk_debug_set_paused(duk_heap *heap) { } 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 @@ DUK_INTERNAL void duk_debug_clear_paused(duk_heap *heap) { 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_paused(duk_heap *heap) { } } -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 @@ DUK_INTERNAL void duk_debug_clear_step_state(duk_heap *heap) { #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 @@ DUK_INTERNAL void duk_debug_clear_step_state(duk_heap *heap) { #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_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_c } 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); + DUK_ASSERT(thr->heap->augmenting_error == 0); + thr->heap->augmenting_error = 1; - call_flags = DUK_CALL_FLAG_IGNORE_RECLIMIT; /* ignore reclimit, not constructor */ - - rc = duk_handle_call_protected(thr, - 1, /* num args */ - call_flags); /* call_flags */ + 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 @@ DUK_LOCAL void duk__err_augment_user(duk_hthread *thr, duk_small_uint_t stridx_c */ #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_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, DUK_ASSERT(thr != NULL); DUK_ASSERT(thr_callstack != NULL); - DUK_ASSERT(ctx != NULL); /* [ ... error ] */ @@ -43692,13 +44998,25 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, */ 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 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, * 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_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, 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) + /* 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--) { + 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 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, * 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 @@ DUK_LOCAL void duk__add_traceback(duk_hthread *thr, duk_hthread *thr_callstack, */ #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); + entry_top = duk_get_top(thr); #endif /* @@ -43841,37 +45149,33 @@ DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, c /* 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 @@ DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, c * 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 @@ DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, c 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 @@ DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, c * .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); + DUK_ASSERT(duk_get_top(thr) == 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_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 @@ DUK_LOCAL void duk__add_fileline(duk_hthread *thr, duk_hthread *thr_callstack, c #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 @@ DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) { /* [ ... 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))); + (duk_tval *) duk_get_tval(thr, -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); + 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(ctx); + 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 @@ DUK_LOCAL void duk__add_compiler_error_line(duk_hthread *thr) { */ #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 @@ DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *th 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); + duk__add_fileline(thr, thr_callstack, c_filename, c_line, flags); #endif #if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(duk_get_top(ctx) == entry_top); + DUK_ASSERT(duk_get_top(thr) == entry_top); #endif } #endif /* DUK_USE_AUGMENT_ERROR_CREATE */ @@ -44027,22 +45328,18 @@ DUK_LOCAL void duk__err_augment_builtin_create(duk_hthread *thr, duk_hthread *th * 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 @@ DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *th * - 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 @@ DUK_INTERNAL void duk_err_augment_error_create(duk_hthread *thr, duk_hthread *th } 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 @@ DUK_INTERNAL void duk_err_augment_error_throw(duk_hthread *thr) { #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]; + char buf[DUK_USE_FATAL_MAXLEN]; - summary = duk_push_string_tval_readable((duk_context *) thr, &thr->heap->lj.value1); + 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]; + char buf[DUK_USE_FATAL_MAXLEN]; - summary = duk_push_string_tval_readable_error((duk_context *) thr, &thr->heap->lj.value1); + 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 @@ DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { * 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? + /* 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_activation *act; + duk_catcher *cat; 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 */ + 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 */ + } } } - thr = thr->resumer; } return 0; } -#endif /* DUK_USE_DEBUGGER_SUPPORT && (DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) */ +#endif /* DUK_USE_DEBUGGER_SUPPORT */ /* * Get prototype object for an integer error code. @@ -44274,10 +45570,8 @@ DUK_INTERNAL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_er */ #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 @@ DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) { #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); + duk_debug_send_throw(thr, uncaught); #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*/); + 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*/); + } } -#endif /* 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); + duk_pop(thr); } -#endif /* DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT */ #endif /* DUK_USE_DEBUGGER_SUPPORT */ /* @@ -44412,8 +45706,6 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, #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 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) #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_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) 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 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) /* 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. + /* 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. */ -#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; 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 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) */ #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_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) */ 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 @@ DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t r * 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_INTERNAL void *duk_hbuffer_get_dynalloc_ptr(duk_heap *heap, void *ud) { /* * 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 @@ DUK_INTERNAL void duk_free_hobject(duk_heap *heap, duk_hobject *h) { /* 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 @@ DUK_INTERNAL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr) { * 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 @@ DUK_INTERNAL void duk_heap_free(duk_heap *heap) { * 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 @@ DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) { /* 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 @@ DUK_LOCAL void duk__dump_type_sizes(void) { #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); @@ -45626,6 +46986,12 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #if defined(DUK_USE_ASSERTIONS) 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; @@ -45657,7 +47023,7 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, 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 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #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 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, */ #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); + duk_require_stack(thr, 100000); - /* Force a reallocation with pointer change for value, call, and - * catch stacks to maximize side effects. + /* Force a reallocation with pointer change for value stack + * 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_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 duk_ret_t duk__fake_global_finalizer(duk_context *ctx) { 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. + /* 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 >= 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")); + 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_INTERNAL void duk_heap_process_finalize_list(duk_heap *heap) { 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 @@ DUK_INTERNAL void duk_heap_process_finalize_list(duk_heap *heap) { * 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 @@ DUK_LOCAL duk_ret_t duk__finalize_helper(duk_context *ctx, void *udata) { * 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_LOCAL duk_ret_t duk__finalize_helper(duk_context *ctx, void *udata) { } 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_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) { 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 @@ DUK_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) { #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")); + DUK_D(DUK_DPRINT("object is a Proxy, skip finalizer call")); return; } +#endif /* DUK_USE_ES6_PROXY */ - 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_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))); + (void *) obj, (duk_tval *) duk_get_tval(thr, -1))); } - duk_pop_2(ctx); /* -> [...] */ + duk_pop_2(thr); /* -> [...] */ - DUK_ASSERT_TOP(ctx, entry_top); + DUK_ASSERT_TOP(thr, entry_top); } #else /* DUK_USE_FINALIZER_SUPPORT */ @@ -46490,7 +47852,9 @@ DUK_INTERNAL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t /* #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 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { 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_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { } 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 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { 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 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { } 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; - } + for (cat = act->cat; cat != NULL; cat = cat->parent) { + } #endif + } duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer); @@ -46638,22 +48019,30 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { 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; } -#if defined(DUK_USE_ROM_OBJECTS) - if (DUK_HEAPHDR_HAS_READONLY(h)) { - DUK_DDD(DUK_DDDPRINT("readonly object %p, skip", (void *) h)); - return; - } -#endif + + DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(h) || DUK_HEAPHDR_HAS_REACHABLE(h)); + #if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) - h->h_assert_refcount++; /* Comparison refcount: bump even if already reachable. */ + 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) + /* 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,10 +48080,35 @@ DUK_LOCAL void duk__mark_tval(duk_heap *heap, duk_tval *tv) { 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); +} + /* * Mark the heap. */ @@ -46785,7 +48199,7 @@ DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { 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 @@ DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) { 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 @@ DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr, duk_size_t #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 @@ DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) { #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 @@ DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep * 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 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ #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 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ 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 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ * 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 @@ DUK_LOCAL void duk__compact_object_list(duk_heap *heap, duk_hthread *thr, duk_he #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), @@ -47465,6 +48892,56 @@ DUK_LOCAL void duk__check_assert_refcounts(duk_heap *heap) { #endif /* DUK_USE_REFERENCE_COUNTING */ #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. * @@ -47480,6 +48957,13 @@ DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags 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 @@ DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags */ 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_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags */ 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))); @@ -47543,6 +49026,15 @@ DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags heap->ms_prevent_count = 1; 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 @@ -47685,6 +49177,14 @@ DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags (long) count_keep_obj, (long) count_keep_str)); #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 @@ DUK_INTERNAL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { */ #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 @@ DUK_INTERNAL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { #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 @@ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t ne */ #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 @@ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t ne #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 @@ DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr */ #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 @@ DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr #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 @@ DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr 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_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr } 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 @@ DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { * 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 @@ DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject /* 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 @@ DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject #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; - } + 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 @@ DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hobject(duk_heap *heap, duk_hobj /* 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 @@ DUK_LOCAL duk_hstring *duk__strtable_alloc_hstring(duk_heap *heap, * 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 @@ DUK_LOCAL duk_hstring *duk__strtable_alloc_hstring(duk_heap *heap, * 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_LOCAL void duk__strtable_grow_inplace(duk_heap *heap) { 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_LOCAL void duk__strtable_shrink_inplace(duk_heap *heap) { 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_LOCAL DUK_COLD DUK_NOINLINE void duk__strtable_resize_check(duk_heap *heap) 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_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uin 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 @@ DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uin #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(duk_heap *heap, const duk_uin */ 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 @@ DUK_INTERNAL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t /* 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 @@ DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobje 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 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t 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 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t } } #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_INTERNAL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject 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 @@ DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject 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 @@ DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject */ #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 @@ DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject * 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); +/* 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; +} -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; +/* 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); + val_a = duk__hstring_sort_key(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. - */ + if (val_a > val_b) { 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. - */ return 1; } } @@ -50714,7 +52307,7 @@ DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk 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 @@ DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk * (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 @@ DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk 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 @@ DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk * 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_push_hstring(thr, k); + duk_push_true(thr); + duk_put_prop(thr, -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_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_context *ctx, duk_small_uint_t enum_flags) { - duk_hthread *thr = (duk_hthread *) ctx; +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_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint duk_uint_fast32_t i, len; /* used for array, stack, and entry indices */ duk_uint_fast32_t sort_start_index; - DUK_ASSERT(ctx != NULL); + DUK_ASSERT(thr != NULL); - enum_target = duk_require_hobject(ctx, -1); + 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint * 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint * 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_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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_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(ctx); /* -> [ ... enum_target res ] */ + 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint /* 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint */ 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint /* [enum_target res] */ - duk_remove_m2(ctx); + duk_remove_m2(thr); /* [res] */ @@ -51208,7 +52717,7 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint /* 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint /* 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 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint * * 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t * 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_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t 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] */ + 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(ctx); /* -> [... key] */ + duk_remove_m2(thr); /* -> [... key] */ } return 1; } else { - duk_pop(ctx); /* -> [...] */ + duk_pop(thr); /* -> [...] */ return 0; } } @@ -51324,25 +52832,22 @@ DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t * 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_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_sma 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 @@ DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_sma } /* [enum_target enum res] */ - duk_remove_m2(ctx); + duk_remove_m2(thr); /* [enum_target res] */ @@ -51451,7 +52954,6 @@ DUK_INTERNAL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject /* 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_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr 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_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr 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 @@ DUK_INTERNAL void duk_hobject_pc2line_pack(duk_hthread *thr, duk_compiler_instr 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 @@ DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk 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 @@ DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_i * 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 @@ DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_i #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_LOCAL duk_uint32_t duk__tval_fastint_to_arr_idx(duk_tval *tv) { 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 @@ DUK_LOCAL duk_uint32_t duk__tval_fastint_to_arr_idx(duk_tval *tv) { * 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 @@ DUK_LOCAL duk_uint32_t duk__to_property_key(duk_context *ctx, duk_idx_t idx, duk * 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 @@ DUK_LOCAL duk_uint32_t duk__to_property_key(duk_context *ctx, duk_idx_t idx, duk */ 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 @@ DUK_LOCAL duk_uint32_t duk__to_property_key(duk_context *ctx, duk_idx_t idx, duk 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 @@ DUK_LOCAL void duk__compute_a_stats(duk_hthread *thr, duk_hobject *obj, duk_uint * 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 @@ DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t arr_idx */ #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_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(thr != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT(out_target != NULL); DUK_ASSERT(out_handler != NULL); @@ -52065,31 +53552,16 @@ DUK_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hthread *thr, duk_hobject *o /* 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); + if (DUK_LIKELY(!DUK_HOBJECT_IS_PROXY(obj))) { 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 */ + h_proxy = (duk_hproxy *) obj; + DUK_ASSERT_HPROXY_VALID(h_proxy); - 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 */ + 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_proxy_check(duk_hthread *thr, duk_hobject *o * 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. + * a Proxy loop: user code cannot create such a loop (it would only be + * possible by editing duk_hproxy references 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; - } + 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 @@ DUK_INTERNAL duk_hobject *duk_hobject_resolve_proxy_target(duk_hthread *thr, duk #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_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, d 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 @@ DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, d /* 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_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, #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_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, */ #if 0 /* XXX: inject test */ if (1) { + new_p = NULL; goto alloc_failed; } #endif @@ -52410,6 +53879,8 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, */ 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, /* 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, /* 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_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, * 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, 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 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, * 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 @@ DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj) * 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 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o 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 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o 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 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o } #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_INTERNAL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap *heap, 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_INTERNAL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_he 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 @@ DUK_INTERNAL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap * * 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 @@ DUK_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject *obj 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 @@ DUK_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject *obj 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_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject *obj 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_INTERNAL duk_bool_t duk_hobject_get_internal_value(duk_heap *heap, duk_hobje 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_bool_t duk__lookup_arguments_map(duk_hthread *thr, 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 @@ duk_bool_t duk__lookup_arguments_map(duk_hthread *thr, 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 @@ duk_bool_t duk__lookup_arguments_map(duk_hthread *thr, /* [... 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 @@ duk_bool_t duk__lookup_arguments_map(duk_hthread *thr, * 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 @@ DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, duk_hobj /* [... 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 @@ DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, duk_hobj /* [... value this_binding] */ - duk_pop(ctx); + duk_pop_unsafe(thr); /* leave result on stack top */ return 1; @@ -53218,7 +54736,6 @@ DUK_LOCAL duk_bool_t duk__check_arguments_map_for_get(duk_hthread *thr, duk_hobj * 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 @@ DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject *o /* [... 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 @@ DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject *o * 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 @@ DUK_LOCAL void duk__check_arguments_map_for_put(duk_hthread *thr, duk_hobject *o * 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 @@ DUK_LOCAL void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject 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 void duk__check_arguments_map_for_delete(duk_hthread *thr, duk_hobject */ 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob (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,51 +54857,39 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob 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; +#endif /* - * Array part - */ - - if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) { - if (arr_idx < DUK_HOBJECT_GET_ASIZE(obj)) { - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); - if (!DUK_TVAL_IS_UNUSED(tv)) { - DUK_DDD(DUK_DDDPRINT("-> found in array part")); - if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { - duk_push_tval(ctx, tv); - } - /* implicit attributes */ - out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE | - DUK_PROPDESC_FLAG_CONFIGURABLE | - DUK_PROPDESC_FLAG_ENUMERABLE; - out_desc->a_idx = arr_idx; - goto prop_found; - } - } - /* assume array part is comprehensive (contains all array indexed elements - * or none of them); hence no need to check the entries part here. - */ - DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property (has array part, " - "should be there if present)")); - goto prop_not_found_concrete; - } - - /* - * Entries part + * 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. */ - duk_hobject_find_existing_entry(thr->heap, obj, key, &out_desc->e_idx, &out_desc->h_idx); - if (out_desc->e_idx >= 0) { + 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); - if (out_desc->flags & DUK_PROPDESC_FLAG_ACCESSOR) { + 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); @@ -53395,23 +54897,49 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob /* a dummy undefined value is pushed to make valstack * behavior uniform for caller */ - duk_push_undefined(ctx); + 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(ctx, tv); + duk_push_tval(thr, tv); } } goto prop_found; } /* - * Not found as a concrete property, check for virtual properties. + * Try array part. */ - prop_not_found_concrete: + if (DUK_HOBJECT_HAS_ARRAY_PART(obj) && arr_idx != DUK__NO_ARRAY_INDEX) { + if (arr_idx < DUK_HOBJECT_GET_ASIZE(obj)) { + tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, arr_idx); + if (!DUK_TVAL_IS_UNUSED(tv)) { + DUK_DDD(DUK_DDDPRINT("-> found in array part")); + if (flags & DUK_GETDESC_FLAG_PUSH_VALUE) { + duk_push_tval(thr, tv); + } + /* implicit attributes */ + out_desc->flags = DUK_PROPDESC_FLAG_WRITABLE | + DUK_PROPDESC_FLAG_CONFIGURABLE | + 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 = (duk_int_t) arr_idx; /* XXX: limit 2G due to being signed */ + goto prop_found; + } + } + } + + DUK_DDD(DUK_DDDPRINT("-> not found as a concrete property")); + + /* + * Not found as a concrete property, check for virtual properties. + */ if (!DUK_HOBJECT_HAS_VIRTUAL_PROPERTIES(obj)) { /* Quick skip. */ @@ -53431,15 +54959,20 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob */ 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob */ 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob /* 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob */ /* - * 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)")); + DUK_STATS_INC(thr->heap, stats_getownpropdesc_miss); return 0; /* - * Found + * Found. * * Arguments object has exotic post-processing, see E5 Section 10.6, * description of [[GetOwnProperty]] variant for arguments. @@ -53592,15 +55129,15 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob 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 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob */ 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_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_h 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 @@ DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_h 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 @@ DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_h * 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_LOCAL duk_tval *duk__getprop_shallow_fastpath_array_tval(duk_hthread *thr, d !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 @@ DUK_LOCAL duk_bool_t duk__putprop_shallow_fastpath_array_tval(duk_hthread *thr, #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_LOCAL duk_bool_t duk__getprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hob 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 @@ DUK_LOCAL duk_bool_t duk__getprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hob #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_LOCAL duk_bool_t duk__putprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hob 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_LOCAL duk_bool_t duk__putprop_fastpath_bufobj_tval(duk_hthread *thr, duk_hob */ 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, (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_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, #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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, #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_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, } 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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] */ + duk_dup_m3(thr); + duk_call_method(thr, 1); /* [key getter this key] -> [key retval] */ #else - duk_call_method(ctx, 0); /* [key getter this] -> [key retval] */ + duk_call_method(thr, 0); /* [key getter this] -> [key retval] */ #endif } else { /* [key value] or [key undefined] */ @@ -54372,7 +55900,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, * Not found */ - duk_to_undefined(ctx, -1); /* [key] -> [undefined] (default value) */ + duk_to_undefined(thr, -1); /* [key] -> [undefined] (default value) */ - DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(ctx, -1))); + DUK_DDD(DUK_DDDPRINT("-> %!T (not found)", (duk_tval *) duk_get_tval(thr, -1))); return 0; /* @@ -54453,7 +55981,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, */ 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, } #endif /* !DUK_USE_NONSTD_FUNC_CALLER_PROPERTY */ - duk_remove_m2(ctx); /* [key result] -> [result] */ + duk_remove_m2(thr); /* [key result] -> [result] */ - DUK_DDD(DUK_DDDPRINT("-> %!T (found)", (duk_tval *) duk_get_tval(ctx, -1))); + 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_getprop(duk_hthread *thr, duk_tval *tv_obj, */ 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, 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; - } + arr_idx = duk__push_tval_to_property_key(thr, tv_key, &key); - /* If not found, resume existence check from Function.prototype. + /* 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_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, } } - duk_pop_2(ctx); /* [ key trap_result ] -> [] */ + duk_pop_2_unsafe(thr); /* [ key trap_result ] -> [] */ return tmp_bool; } @@ -54603,7 +56126,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj, /* fall through */ pop_and_return: - duk_pop(ctx); /* [ key ] -> [] */ + duk_pop_unsafe(thr); /* [ key ] -> [] */ return rc; } @@ -54656,7 +56179,7 @@ DUK_LOCAL duk_uint32_t duk__to_new_array_length_checked(duk_hthread *thr, duk_tv /* 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 @@ duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr, 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 @@ duk_bool_t duk__handle_put_array_length_smaller(duk_hthread *thr, /* 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_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject DUK_DDD(DUK_DDDPRINT("handling a put operation to array 'length' exotic property, " "new val: %!T", - (duk_tval *) duk_get_tval(ctx, -1))); + (duk_tval *) duk_get_tval(thr, -1))); DUK_ASSERT(thr != NULL); - DUK_ASSERT(ctx != NULL); DUK_ASSERT(obj != NULL); DUK_ASSERT_VALSTACK_SPACE(thr, DUK__VALSTACK_SPACE); @@ -54910,14 +56431,14 @@ DUK_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject 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_LOCAL duk_bool_t duk__handle_put_array_length(duk_hthread *thr, duk_hobject */ 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, */ 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, #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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, #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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, /* 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, } 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. + /* 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. */ - - arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key); - - if (duk__key_is_lightfunc_ownprop(thr, key)) { - goto fail_not_writable; - } - 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, } 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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] */ + duk_dup_m4(thr); + duk_call_method(thr, 2); /* [key setter this val key] -> [key retval] */ #else - duk_call_method(ctx, 1); /* [key setter this val] -> [key retval] */ + duk_call_method(thr, 1); /* [key setter this val] -> [key retval] */ #endif - duk_pop(ctx); /* ignore retval -> [key] */ + duk_pop_unsafe(thr); /* ignore retval -> [key] */ goto success_no_arguments_exotic; } @@ -55423,9 +56941,9 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, * 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, * 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, * 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_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 } - duk_pop(ctx); /* remove key */ + duk_pop_unsafe(thr); /* remove key */ return 0; fail_not_extensible: @@ -55861,7 +57379,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, 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_raw(duk_hthread *thr, duk_hobject *o */ 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, (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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, goto fail_invalid_base_uncond; } - duk_push_tval(ctx, tv_obj); - duk_push_tval(ctx, tv_key); + duk_push_tval(thr, tv_obj); + duk_push_tval(thr, tv_key); - tv_obj = DUK_GET_TVAL_NEGIDX(ctx, -2); + 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, } #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_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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. + /* Lightfunc has no virtual properties since Duktape 2.2 + * so success. Still must coerce key for side effects. */ - arr_idx = duk__to_property_key(ctx, -1, &key); + 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, 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 duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, */ 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_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob 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_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob 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_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob 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_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob } 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 @@ DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob 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 @@ DUK_INTERNAL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hob 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(duk_hthread *thr, duk_hob */ 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_INTERNAL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, 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 void duk_hobject_define_property_internal_arridx(duk_hthread *thr, */ 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 @@ DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *ob } /* 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 @@ DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj) { * [ ... 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); + 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(ctx); + duk_push_object(thr); /* [ ... key value desc ] */ @@ -56554,32 +58060,32 @@ DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_context *ct * still have the property present with the value 'undefined'. */ if (pd.get) { - duk_push_hobject(ctx, pd.get); + duk_push_hobject(thr, pd.get); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } - duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_GET); + duk_put_prop_stridx_short(thr, -2, DUK_STRIDX_GET); if (pd.set) { - duk_push_hobject(ctx, pd.set); + duk_push_hobject(thr, pd.set); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } - duk_put_prop_stridx_short(ctx, -2, DUK_STRIDX_SET); + duk_put_prop_stridx_short(thr, -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_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(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_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 @@ DUK_INTERNAL void duk_hobject_object_get_own_property_descriptor(duk_context *ct /* 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, * 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); + idx_value = duk_get_top_index(thr); } - if (duk_get_prop_stridx(ctx, idx_in, DUK_STRIDX_WRITABLE)) { + 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, * 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, * 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, 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 @@ void duk_hobject_prepare_property_descriptor(duk_context *ctx, /* 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_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, (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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, } /* 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, } /* 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, } /* 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_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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_bool_t duk_hobject_define_property_helper(duk_context *ctx, 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 @@ duk_bool_t duk_hobject_define_property_helper(duk_context *ctx, * 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_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk 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 @@ DUK_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk 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_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk } /* - * 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 @@ DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { 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_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr 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_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr 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_INTERNAL void *duk_hthread_get_valstack_ptr(duk_heap *heap, void *ud) { 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 @@ DUK_INTERNAL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud) { #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 @@ DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { #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 @@ DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { * 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_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { 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_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { #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_push_hstring_stridx(thr, n); } -DUK_LOCAL void duk__push_string(duk_context *ctx, duk_bitdecoder_ctx *bd) { +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_LOCAL void duk__push_string(duk_context *ctx, duk_bitdecoder_ctx *bd) { 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_push_lstring(thr, (const char *) tmp, (duk_size_t) len); } -DUK_LOCAL void duk__push_stridx_or_string(duk_context *ctx, duk_bitdecoder_ctx *bd) { +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_push_hstring_stridx(thr, n); } } -DUK_LOCAL void duk__push_double(duk_context *ctx, duk_bitdecoder_ctx *bd) { +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 @@ DUK_LOCAL void duk__push_double(duk_context *ctx, duk_bitdecoder_ctx *bd) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { * into thr->builtins[]. These are objects referenced in some way * from thr->builtins[] roots but which don't need to be indexed by * Duktape through thr->builtins[] (e.g. user custom objects). + * + * Internal prototypes will be incorrect (NULL) at this stage. */ - duk_require_stack(ctx, DUK_NUM_ALL_BUILTINS); + 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { } /* 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { /* 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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); + duk_push_hobject(thr, (duk_hobject *) env); - global = duk_known_hobject(ctx, DUK_BIDX_GLOBAL); + global = duk_known_hobject(thr, DUK_BIDX_GLOBAL); DUK_ASSERT(global != NULL); env->target = global; DUK_HOBJECT_INCREF(thr, global); @@ -58331,14 +59849,14 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { } 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { */ 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { /* * 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { */ 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { */ 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { } } - 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { (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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { /* [ (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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { /* [ (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_push_uint(thr, c_length); + duk_xdef_prop_stridx_short(thr, -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_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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { */ #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); + 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(ctx, DUK_BIDX_DOUBLE_ERROR); + 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { #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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { #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 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { * 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_copy_builtin_objects(duk_hthread *thr_from, duk_ht 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. + * 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. */ - /* XXX: Shrink the stacks to minimize memory usage? May not - * be worth the effort because terminated threads are usually - * garbage collected quite soon. - */ + DUK_REFZERO_CHECK_SLOW(thr); } #if defined(DUK_USE_DEBUGGER_SUPPORT) @@ -58963,585 +60510,411 @@ DUK_INTERNAL void duk_hthread_sync_and_null_currpc(duk_hthread *thr) { } #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. + * Thread stack (mainly call stack) primitives: allocation of activations, + * unwinding catchers and activations, etc. * - * Value stack handling is mostly a part of the API implementation. + * 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; +/* 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_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_ASSERT(act != NULL); + DUK_ASSERT(act->cat != NULL); /* caller must check */ + cat = act->cat; + DUK_ASSERT(cat != NULL); - DUK_DD(DUK_DDPRINT("growing callstack %ld -> %ld", (long) old_size, (long) new_size)); + DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is done)", (void *) cat)); - /* - * Note: must use indirect variant of DUK_REALLOC() because underlying - * pointer may be changed by mark-and-sweep. - */ + if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) { + duk_hobject *env; - 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; + 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); - if (thr->callstack_top > 0) { - thr->callstack_curr = thr->callstack + thr->callstack_top - 1; - } else { - thr->callstack_curr = NULL; + /* There is no need to decref anything else than 'env': if 'env' + * becomes unreachable, refzero will handle decref'ing its prototype. + */ } - /* note: any entries above the callstack top are garbage and not zeroed */ + act->cat = cat->parent; + duk_hthread_catcher_free(thr, cat); } -/* 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; +/* 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_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_ASSERT(act != NULL); + DUK_ASSERT(act->cat != NULL); /* caller must check */ + cat = act->cat; + DUK_ASSERT(cat != NULL); - DUK_DD(DUK_DDPRINT("shrinking callstack %ld -> %ld", (long) thr->callstack_size, (long) new_size)); + DUK_DDD(DUK_DDDPRINT("unwinding catch stack entry %p (lexenv check is not done)", (void *) cat)); - /* - * Note: must use indirect variant of DUK_REALLOC() because underlying - * pointer may be changed by mark-and-sweep. - */ + DUK_ASSERT(!DUK_CAT_HAS_LEXENV_ACTIVE(cat)); - /* 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; + act->cat = cat->parent; + duk_hthread_catcher_free(thr, cat); +} - 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")); - } +DUK_LOCAL +#if defined(DUK_USE_CACHE_CATCHER) +DUK_NOINLINE +#endif +duk_catcher *duk__hthread_catcher_alloc_slow(duk_hthread *thr) { + duk_catcher *cat; - /* note: any entries above the callstack top are garbage and not zeroed */ + cat = (duk_catcher *) DUK_ALLOC_CHECKED(thr, sizeof(duk_catcher)); + DUK_ASSERT(cat != NULL); + return cat; } -DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { +#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); - 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; + cat = thr->heap->catcher_free; + if (DUK_LIKELY(cat != NULL)) { + thr->heap->catcher_free = cat->parent; + return cat; } - duk__hthread_do_callstack_shrink(thr); + 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_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 */ +DUK_INTERNAL void duk_hthread_catcher_free(duk_hthread *thr, duk_catcher *cat) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(cat != NULL); - /* - * 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. - */ +#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 +} - idx = thr->callstack_top; - while (idx > new_top) { - duk_activation *act; - duk_hobject *func; - duk_hobject *tmp; -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_heap *heap; +DUK_LOCAL +#if defined(DUK_USE_CACHE_ACTIVATION) +DUK_NOINLINE #endif +duk_activation *duk__hthread_activation_alloc_slow(duk_hthread *thr) { + duk_activation *act; - idx--; - DUK_ASSERT_DISABLE(idx >= 0); /* unsigned */ - DUK_ASSERT((duk_size_t) idx < thr->callstack_size); /* true, despite side effect resizes */ + act = (duk_activation *) DUK_ALLOC_CHECKED(thr, sizeof(duk_activation)); + DUK_ASSERT(act != NULL); + return act; +} - act = thr->callstack + idx; - /* With lightfuncs, act 'func' may be NULL */ +#if defined(DUK_USE_CACHE_ACTIVATION) +DUK_INTERNAL DUK_INLINE duk_activation *duk_hthread_activation_alloc(duk_hthread *thr) { + duk_activation *act; -#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) - /* - * Restore 'caller' property for non-strict callee functions. - */ + DUK_ASSERT(thr != NULL); - 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; + act = thr->heap->activation_free; + if (DUK_LIKELY(act != NULL)) { + thr->heap->activation_free = act->parent; + return act; + } - tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_CALLER(thr)); + 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 */ - /* 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 +DUK_INTERNAL void duk_hthread_activation_free(duk_hthread *thr, duk_activation *act) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(act != NULL); - /* - * Unwind debugger state. If we unwind while stepping - * (either step over or step into), pause execution. - */ +#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) - 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); - } - } + duk_heap *heap; #endif + duk_activation *act; + duk_hobject *func; + duk_hobject *tmp; - /* - * 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. - */ + 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. */ - 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 */ + /* With duk_activation records allocated separately, 'act' is a stable + * pointer and not affected by side effects. + */ - /* 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 defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) + /* + * Restore 'caller' property for non-strict callee functions. + */ - 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 */ - } + 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; - skip_env_close: + tv_caller = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_CALLER(thr)); - /* - * Update preventcount + /* 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 (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { - DUK_ASSERT(thr->callstack_preventcount >= 1); - thr->callstack_preventcount--; + 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); + } } - - /* - * 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; + DUK_ASSERT(act->prev_caller == NULL); } +#endif /* - * We could clear the book-keeping variables for the topmost activation, - * but don't do so now. + * 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 0 - if (thr->callstack_curr != NULL) { - duk_activation *act = thr->callstack_curr; - act->idx_retval = 0; + +#if defined(DUK_USE_DEBUGGER_SUPPORT) + 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 - /* 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. + /* + * 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. */ -} - -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); + while (act->cat != NULL) { + duk_hthread_catcher_unwind_norz(thr, act); } - 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. + * 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. */ - 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); + 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; } - thr->catchstack = new_ptr; - thr->catchstack_size = new_size; + /* func is NULL for lightfunc */ - /* 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); + /* 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 (DUK_LIKELY(thr->catchstack_top < thr->catchstack_size)) { - return; + /* 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); } - 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)); + skip_env_close: /* - * Note: must use indirect variant of DUK_REALLOC() because underlying - * pointer may be changed by mark-and-sweep. + * Update preventcount */ - /* 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")); + if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { + DUK_ASSERT(thr->callstack_preventcount >= 1); + thr->callstack_preventcount--; } - /* 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; - } + /* + * 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__hthread_do_catchstack_shrink(thr); + 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); } -DUK_INTERNAL void duk_hthread_catchstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) { - duk_size_t idx; +/* 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_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__activation_unwind_nofree_norz(thr); - 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 */ + DUK_ASSERT(thr->callstack_curr != NULL); + DUK_ASSERT(thr->callstack_top > 0); + act = thr->callstack_curr; + thr->callstack_curr = act->parent; + thr->callstack_top--; - /* - * 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. + /* 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. */ - 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); + /* XXX: inline for performance builds? */ + duk_hthread_activation_free(thr, act); - p = thr->catchstack + idx; + /* 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. + */ +} - 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)); +DUK_INTERNAL void duk_hthread_activation_unwind_reuse_norz(duk_hthread *thr) { + duk__activation_unwind_nofree_norz(thr); +} - /* 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. - */ +/* 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; - /* 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. - */ + if (level >= 0) { + return NULL; + } + act = thr->callstack_curr; + for (;;) { + if (act == NULL) { + return act; + } + if (level == -1) { + return act; } + level++; + act = act->parent; } - - 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); + /* 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) { + 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) { + DUK_D(DUK_DPRINT("skip valstack torture realloc, alloc_size is zero")); return; } - new_ptr = (duk_tval *) DUK_ALLOC(thr->heap, alloc_size); + /* 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 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) { /* * Call handling. * - * Main functions are: + * duk_handle_call_unprotected(): * - * - 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 + * - Unprotected call to Ecmascript or Duktape/C function, from native + * code or bytecode executor. * - * See 'execution.rst'. + * - 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(): * - * 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. + * - 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'. */ /* #include duk_internal.h -> already included */ @@ -59708,46 +61089,78 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) { /* 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__interrupt_fixup(duk_hthread *thr, duk_hthread *entry_curr_th 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_LOCAL void duk__create_arguments_object(duk_hthread *thr, 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_ASSERT(num_stack_args >= 0); - duk_push_hobject(ctx, func); - duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_FORMALS); - formals = duk_get_hobject(ctx, -1); + 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_LOCAL void duk__create_arguments_object(duk_hthread *thr, 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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, * - '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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, "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); + 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 + * Init argument related properties. */ /* step 11 */ @@ -59909,8 +61320,8 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, (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_LOCAL void duk__create_arguments_object(duk_hthread *thr, 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_get_prop_index(thr, i_formals, (duk_uarridx_t) idx); + DUK_ASSERT(duk_is_string(thr, -1)); - duk_dup_top(ctx); /* [ ... name name ] */ + duk_dup_top(thr); /* [ ... name name ] */ - if (!duk_has_prop(ctx, i_mappednames)) { + 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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, 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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, /* 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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, * 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_LOCAL void duk__create_arguments_object(duk_hthread *thr, 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 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, "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))); - /* [ args(n) [crud] formals arguments map mappednames ] */ + /* [ args(n) envobj formals arguments map mappednames ] */ - duk_pop_2(ctx); - duk_remove_m2(ctx); + duk_pop_2(thr); + duk_remove_m2(thr); - /* [ args [crud] arguments ] */ + /* [ 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 @@ DUK_LOCAL void duk__handle_createargs_for_call(duk_hthread *thr, } /* - * Helper for handling a "bound function" chain when a call is being made. + * 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. * - * 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]]. + * 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. * - * 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. + * 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_idx_t len; DUK_ASSERT(thr != NULL); - DUK_ASSERT(p_num_stack_args != NULL); /* On entry, item at idx_func is a bound, non-lightweight function, * but we don't rely on that below. */ - num_stack_args = *p_num_stack_args; - - sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY; - do { - duk_idx_t i, len; + DUK_ASSERT(duk_get_top(thr) >= idx_func + 2); - tv_func = duk_require_tval(ctx, idx_func); - DUK_ASSERT(tv_func != NULL); + tv_func = duk_require_tval(thr, 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); + if (DUK_TVAL_IS_OBJECT(tv_func)) { + func = DUK_TVAL_GET_OBJECT(tv_func); - /* XXX: this could be more compact by accessing the internal properties - * directly as own properties (they cannot be inherited, and are not - * externally visible). - */ + /* 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; - 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)); + h_bound = (duk_hboundfunc *) func; + tv_args = h_bound->args; + len = h_bound->nargs; + DUK_ASSERT(len == 0 || tv_args != NULL); - /* [ ... func this arg1 ... argN ] */ + DUK_DDD(DUK_DDDPRINT("bound function encountered, ptr=%p: %!T", + (void *) DUK_TVAL_GET_OBJECT(tv_func), tv_func)); - 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 ] */ - /* [ ... 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 */ + } - /* 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 arg1 ... argN ] */ - /* [ ... func this 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); + duk_require_stack(thr, len); - /* [ ... func this arg1 ... argN ] */ + tv_gap = duk_reserve_gap(thr, idx_func + 2, len); + duk_copy_tvals_incref(thr, tv_gap, tv_args, (duk_size_t) len); - duk_get_prop_stridx(ctx, idx_func, DUK_STRIDX_INT_TARGET); - duk_replace(ctx, idx_func); /* replace in stack */ + /* [ ... func this arg1 ... argN ] */ - 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); + duk_push_tval(thr, &h_bound->target); + duk_replace(thr, idx_func); /* replace in stack */ - if (DUK_UNLIKELY(sanity == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT); + 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_DDD(DUK_DDDPRINT("final non-bound function is: %!T", duk_get_tval(ctx, idx_func))); + 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(ctx, idx_func); + 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_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr, DUK_HOBJECT_HAS_NATFUNC(func)); } #endif +} + +/* + * 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. + */ - /* write back */ - *p_num_stack_args = num_stack_args; + 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_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) 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 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) * 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 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) * 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 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) /* '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 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) #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). + * 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. * - * 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 + * 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. * - * The final 'this' binding for the target function may still be - * different, and is determined as 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 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) * - 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 @@ DUK_LOCAL void duk__coerce_effective_this_binding(duk_hthread *thr, 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 */ + duk_to_object(thr, 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_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; -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; + 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; - for (;;) { - /* Use loop to minimize code size of relookup after bound function case */ - tv_func = DUK_GET_TVAL_POSIDX(ctx, idx_func); + 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); + DUK_ASSERT(!DUK_HOBJECT_HAS_SPECIAL_CALL(func)); + DUK_ASSERT(!DUK_HOBJECT_IS_NATFUNC(func)); - /* 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. + /* 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; + goto finished; } else { - goto not_callable_error; + goto not_callable; } - 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; - return func; - - not_callable_error: - DUK_ASSERT(tv_func != NULL); -#if defined(DUK_USE_PARANOID_ERRORS) - DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); -#else - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(ctx, tv_func)); -#endif - DUK_UNREACHABLE(); - return NULL; /* never executed */ -} - -/* - * Value stack resize and stack top adjustment helper. - * - * XXX: This should all be merged to duk_valstack_resize_raw(). - */ + DUK_ASSERT(func != NULL); -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 (!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 (func == NULL || DUK_HOBJECT_IS_NATFUNC(func)) { - vs_min_size += DUK_VALSTACK_API_ENTRY_MINIMUM; /* Duktape/C API guaranteed entries (on top of args) */ + 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); + } } - 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. - */ + finished: + +#if defined(DUK_USE_ASSERTIONS) + { + duk_tval *tv_tmp; - if (vs_min_size < (duk_size_t) (thr->valstack_top - thr->valstack)) { - DUK_DDD(DUK_DDDPRINT(("final size smaller, set top before resize"))); + tv_tmp = duk_get_tval(thr, idx_func); + DUK_ASSERT(tv_tmp != NULL); - 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; + 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 - (void) duk_valstack_resize_raw((duk_context *) thr, - vs_min_size, - DUK_VSRESIZE_FLAG_SHRINK | /* flags */ - 0 /* no compact */ | - DUK_VSRESIZE_FLAG_THROW); + return func; - 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_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); +#endif + DUK_UNREACHABLE(); + return NULL; /* never executed */ + + 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__adjust_valstack_and_top(duk_hthread *thr, */ 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_LOCAL void duk__safe_call_adjust_valstack(duk_hthread *thr, duk_idx_t idx_re 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. + /* 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. */ - 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) { + /* 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 + /* Remove values between irc_rcbase (start of intended return + * values) and idx_retbase to lower return values to idx_retbase. */ - - DUK_ASSERT(count >= 0); - - for (i = 0; i < count; i++) { - /* XXX: inefficient; block remove primitive */ - duk_remove(ctx, 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 + /* Insert 'undefined' at idx_rcbase (start of intended return + * values) to lift 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); - } + 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); } /* - * Misc shared helpers. + * Activation setup for tailcalls and non-tailcalls. */ -/* 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) */ +#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_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 - /* 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_UNREF(entry_valstack_end_byteoff); -/* - * 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.) - */ + /* Tailcall cannot be flagged to resume calls, and a + * previous frame must exist. + */ + DUK_ASSERT(thr->callstack_top >= 1); -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) */ + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); + *out_act = act; - /* 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(). + 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; + } - 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); + /* + * 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))); - /* Argument validation and func/args offset. */ - idx_func = duk__get_idx_func(thr, num_stack_args); + 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); - /* Preliminaries, required by setjmp() handler. Must be careful not - * to throw an unintended error here. + /* 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; + } - 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; + 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 - 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)); + 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); - old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; - thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; + /* 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. + */ -#if defined(DUK_USE_CPP_EXCEPTIONS) - try { -#else - DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == &our_jmpbuf); - if (DUK_SETJMP(our_jmpbuf.jb) == 0) { + /* 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 - /* 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; + /* 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); - return DUK_EXEC_SUCCESS; -#if defined(DUK_USE_CPP_EXCEPTIONS) - } catch (duk_internal_exception &exc) { -#else - } else { + 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 - /* Error; error value is in heap->lj.value1. */ -#if defined(DUK_USE_CPP_EXCEPTIONS) - DUK_UNREF(exc); + 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; - 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); + /* + * 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. + */ - 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); + 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 */ - duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func); -} + idx_args = idx_func + 2; + duk_remove_n(thr, 0, idx_args); /* may be NORZ */ -DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, - duk_idx_t num_stack_args, - duk_small_uint_t call_flags, - duk_idx_t idx_func) { - duk_context *ctx; - duk_size_t entry_valstack_bottom_index; - duk_size_t entry_valstack_end; - duk_size_t entry_callstack_top; - duk_size_t entry_catchstack_top; - duk_int_t entry_call_recursion_depth; - duk_hthread *entry_curr_thread; - duk_uint_fast8_t entry_thread_state; - duk_instr_t **entry_ptr_curr_pc; - duk_idx_t nargs; /* # argument registers target function wants (< 0 => "as is") */ - duk_idx_t nregs; /* # total registers target function wants on entry (< 0 => "as is") */ - duk_hobject *func; /* 'func' on stack (borrowed reference) */ - duk_tval *tv_func; /* duk_tval ptr for 'func' on stack (borrowed reference) or tv_func_copy */ - duk_tval tv_func_copy; /* to avoid relookups */ - duk_activation *act; - duk_hobject *env; - duk_ret_t rc; + idx_func = 0; DUK_UNREF(idx_func); /* really 'not applicable' anymore, should not be referenced after this */ + idx_args = 0; - 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); + *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); - 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; +#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) +#if defined(DUK_USE_TAILCALL) +#error incorrect options: tail calls enabled with function caller property #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. + /* 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 - 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 */ + /* [ ... this_new | arg1 ... argN ] */ - DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) || - DUK_HOBJECT_IS_NATFUNC(func))); + return 1; +} +#endif /* DUK_USE_TAILCALL */ - 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))); +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; - /* [ ... func this arg1 ... argN ] */ + DUK_UNREF(entry_valstack_end_byteoff); - /* - * 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_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); + 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. + * the Ecmascript call's retval_byteoff must be set for things to work. */ - act->idx_retval = entry_valstack_bottom_index + idx_func; + act->retval_byteoff = entry_valstack_bottom_byteoff + (duk_size_t) idx_func * sizeof(duk_tval); } - DUK_ASSERT(thr->callstack_top < thr->callstack_size); - act = thr->callstack + thr->callstack_top; + 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->flags = 0; + act->cat = NULL; - /* For now all calls except Ecma-to-Ecma calls prevent a yield. */ - act->flags |= DUK_ACT_FLAG_PREVENT_YIELD; - if (call_flags & DUK_CALL_FLAG_CONSTRUCTOR_CALL) { + act->flags = 0; + 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_tval *tv_func; + act->flags |= DUK_ACT_FLAG_STRICT; + + 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); - nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags); - if (nargs == DUK_LFUNC_NARGS_VARARGS) { - nargs = -1; /* vararg */ + *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 */ } - nregs = nargs; - - act->flags |= DUK_ACT_FLAG_STRICT; + *out_nregs = *out_nargs; } - act->func = func; /* NULL for lightfunc */ act->var_env = NULL; act->lex_env = NULL; #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) @@ -61089,20 +62829,15 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, #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; + 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 - 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? + /* 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. */ - if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) { - /* duk_hthread_callstack_unwind() will decrease this on unwind */ - thr->callstack_preventcount++; - } + 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,26 +62848,18 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) if (func) { duk__update_func_caller_prop(thr, func); - act = thr->callstack_curr; } #endif +} - /* [ ... func this arg1 ... argN ] */ +/* + * Environment setup. + */ - /* - * Environment record creation and 'arguments' object creation. - * Named function expression name binding is handled by the - * compiler; the compiled function's parent env will contain - * the (immutable) binding already. - * - * This handling is now identical for C and Ecmascript functions. - * C functions always have the 'NEWENV' flag set, so their - * environment record initialization is delayed (which is good). - * - * Delayed creation (on demand) is handled in duk_js_var.c. - */ +DUK_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 chain has already been resolved */ + 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))) { @@ -61148,23 +62875,22 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, * 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); + /* 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, num_stack_args); + duk__handle_createargs_for_call(thr, func, env, idx_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); + duk_pop(thr); } } else { /* Use existing env (e.g. for non-strict eval); cannot have @@ -61174,7 +62900,6 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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); @@ -61184,51 +62909,324 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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 value stack: clamp to 'nargs', fill up to 'nregs' + * 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. * - * 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'). + * Because 'act' is not zeroed, all fields must be filled in. */ - /* XXX: optimize value stack operation */ - /* XXX: don't want to shrink allocation here */ +#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 - duk__adjust_valstack_and_top(thr, - num_stack_args, - idx_func + 2, - nregs, - nargs, - func); + 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 ] */ /* - * Determine call type, then finalize activation, shift to - * new value stack bottom, and call the target. + * Environment record creation and 'arguments' object creation. + * Named function expression name binding is handled by the + * compiler; the compiled function's parent env will contain + * the (immutable) binding already. + * + * This handling is now identical for C and Ecmascript functions. + * C functions always have the 'NEWENV' flag set, so their + * environment record initialization is delayed (which is good). + * + * Delayed creation (on demand) is handled in duk_js_var.c. + */ + + duk__call_env_setup(thr, func, act, idx_args); + + /* [ ... func this arg1 ... argN ] */ + + /* + * 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. */ - act = thr->callstack_curr; if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) { /* - * Ecmascript call + * Ecmascript call. */ - duk_tval *tv_ret; - duk_tval *tv_funret; - 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,268 +63247,122 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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 ] */ - - tv_ret = thr->valstack_bottom + idx_func; - tv_funret = thr->valstack_top - 1; -#if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - DUK_TVAL_CHKFAST_INPLACE_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); + 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((duk_context *) thr); + rc = ((duk_hnatfunc *) func)->func(thr); } else { - duk_c_function funcptr = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv_func); - rc = funcptr((duk_context *) thr); + 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) { + 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 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_ERROR_TYPE(thr, DUK_STR_INVALID_CFUNC_RC); } } + DUK_ASSERT(thr->ptr_curr_pc == NULL); + DUK_ASSERT(use_tailcall == 0); - duk_set_top(ctx, idx_func + 1); /* XXX: unnecessary, handle in adjust */ - - /* [ ... retval ] */ - - /* Ensure there is internal valstack spare before we exit; this may - * throw an alloc error. The same guaranteed size must be available - * as before the call. This is not optimal now: we store the valstack - * allocated size during entry; this value may be higher than the - * minimal guarantee for an application. - */ - - /* XXX: we should never shrink here; when we error out later, we'd - * need to potentially grow the value stack in error unwind which could - * cause another error. + /* + * Constructor call post processing. */ - (void) duk_valstack_resize_raw((duk_context *) thr, - entry_valstack_end, /* same as during entry */ - DUK_VSRESIZE_FLAG_SHRINK | /* flags */ - DUK_VSRESIZE_FLAG_COMPACT | - DUK_VSRESIZE_FLAG_THROW); - - /* Restore entry thread executor curr_pc stack frame pointer. */ - thr->ptr_curr_pc = entry_ptr_curr_pc; - - DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */ - thr->state = (duk_uint8_t) entry_thread_state; - - /* 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; +#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 -#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) - duk__interrupt_fixup(thr, entry_curr_thread); -#endif - - /* 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; + /* + * Unwind, restore valstack bottom and other book-keeping. + */ - DUK_DDD(DUK_DDDPRINT("error caught during duk__handle_call_inner(): %!T", - (duk_tval *) &thr->heap->lj.value1)); + 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); - /* 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); + 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); - /* 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); + /* Return value handling. */ - /* 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; + /* [ ... func this (crud) retval ] */ - /* 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); + { + duk_tval *tv_ret; + duk_tval *tv_funret; - 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 */ + tv_ret = thr->valstack_bottom + idx_func; + tv_funret = thr->valstack_top - 1; #if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - DUK_TVAL_CHKFAST_INPLACE_FAST(tv_ret); + /* Explicit check for fastint downgrade. */ + DUK_TVAL_CHKFAST_INPLACE_FAST(tv_funret); #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. - */ + DUK_TVAL_SET_TVAL_UPDREF(thr, tv_ret, tv_funret); /* side effects */ + } - /* 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. - */ + duk_set_top_unsafe(thr, idx_func + 1); - (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); + /* [ ... retval ] */ + /* 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); - /* 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. + /* XXX: Trial value stack shrink would be OK here, but we'd need + * to prevent side effects of the potential realloc. */ - 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; @@ -61549,268 +63401,85 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, 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)); - + /* 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_SLOW(thr); -} - -/* - * duk_handle_safe_call(): make a "C protected call" within the - * current activation. - * - * The allowed thread states for making a call are the same as for - * duk_handle_call_xxx(). - * - * Error handling is similar to duk_handle_call_xxx(); errors may be thrown - * (and result in a fatal error) for insane arguments. - */ - -/* XXX: bump preventcount by one for the duration of this call? */ - -DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, - duk_safe_call_function func, - 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_REFZERO_CHECK_FAST(thr); - DUK_ASSERT_LJSTATE_UNSET(thr->heap); + return 0; /* 0=call handled inline */ +} - 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); +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); +} - return retval; +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); } +/* + * duk_handle_safe_call(): make a "C protected call" within the + * current activation. + * + * The allowed thread states for making a call are the same as for + * duk_handle_call_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, - duk_idx_t idx_retbase, - duk_idx_t num_stack_rets, - duk_size_t entry_valstack_bottom_index, +#if defined(DUK_USE_ASSERTIONS) + duk_size_t entry_valstack_bottom_byteoff, duk_size_t entry_callstack_top, - duk_size_t entry_catchstack_top) { - duk_context *ctx; +#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_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 + * Make the C call. */ - rc = func(ctx, udata); + rc = func(thr, udata); DUK_DDD(DUK_DDDPRINT("safe_call, func rc=%ld", (long) rc)); @@ -61820,48 +63489,35 @@ DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr, /* 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__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc); /* throws for insane 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_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_CTX_VALID(thr); /* * Error during call. The error value is at heap->lj.value1. @@ -61878,51 +63534,56 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, */ 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 */ + 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 and - * process pending finalizers. + /* 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 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, 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); + /* 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(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_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); - ctx = (duk_context *) thr; - DUK_ASSERT_CTX_VALID(ctx); - DUK_UNREF(ctx); + DUK_ASSERT_CTX_VALID(thr); DUK_UNREF(idx_retbase); DUK_UNREF(num_stack_rets); + DUK_UNREF(entry_curr_thread); - /* Restore entry thread executor curr_pc stack frame pointer. */ - thr->ptr_curr_pc = entry_ptr_curr_pc; + DUK_ASSERT(thr->callstack_top == entry_callstack_top); - /* 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. + /* 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. */ - 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->ptr_curr_pc = entry_ptr_curr_pc; 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 @@ DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr, #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_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); - DUK_ASSERT(!((call_flags & DUK_CALL_FLAG_IS_RESUME) != 0 && (call_flags & DUK_CALL_FLAG_IS_TAILCALL) != 0)); + DUK_ASSERT(duk_get_top(thr) >= num_stack_args); /* Caller ensures. */ - /* XXX: assume these? */ - DUK_ASSERT(thr->valstack != NULL); - DUK_ASSERT(thr->callstack != NULL); - DUK_ASSERT(thr->catchstack != NULL); + DUK_STATS_INC(thr->heap, stats_safecall_all); - /* 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. + /* 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. */ - 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 - */ + /* 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) - 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_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); - 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_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. */ - 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", + /* 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)); + (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)); - if (DUK_UNLIKELY(idx_func < 0 || idx_args < 0)) { - /* XXX: assert? compiler is responsible for this never happening */ - DUK_ERROR_TYPE_INVALID_ARGS(thr); - } + /* Setjmp catchpoint setup. */ + old_jmpbuf_ptr = thr->heap->lj.jmpbuf_ptr; + thr->heap->lj.jmpbuf_ptr = &our_jmpbuf; - /* - * 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. + /* 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++; - 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. - */ +#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")); - /* 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 */ + duk__handle_safe_call_inner(thr, + func, + udata, +#if defined(DUK_USE_ASSERTIONS) + entry_valstack_bottom_byteoff, + entry_callstack_top, #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); + entry_curr_thread, + entry_thread_state, + idx_retbase, + num_stack_rets); - 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; - } - } + DUK_STATS_INC(thr->heap, stats_safecall_nothrow); - 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; + /* Either pointer may be NULL (at entry), so don't assert */ + thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; - /* - * 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. + /* 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); - 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; + 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); - act->flags = (DUK_HOBJECT_HAS_STRICT(func) ? - DUK_ACT_FLAG_STRICT | DUK_ACT_FLAG_TAILCALLED : - DUK_ACT_FLAG_TAILCALLED); + DUK_STATS_INC(thr->heap, stats_safecall_throw); - 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; + 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); - /* - * 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; + 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_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; + 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 - 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; + 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 - /* [ ... 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. - */ + DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr); /* success/error path both do this */ - /* XXX: unify handling with native call. */ + DUK_ASSERT_LJSTATE_UNSET(thr->heap); - DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound function chain has already been resolved */ + 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); - 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) - */ + /* Restore preventcount. */ + thr->callstack_preventcount--; + DUK_ASSERT(thr->callstack_preventcount == entry_callstack_preventcount); - duk__handle_oldenv_for_call(thr, func, act); - /* No need to re-lookup 'act' at present: no side effects. */ + /* 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); - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - goto env_done; - } + /* Pending side effects. */ + DUK_REFZERO_CHECK_FAST(thr); - DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func)); + return retval; +} - 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; - } +/* + * 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. + */ - /* 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); +#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; - /* [ ... arg1 ... argN envobj ] */ + entry_top = duk_get_top(thr); - /* original input stack before nargs/nregs handling must be - * intact for 'arguments' object + /* 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_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); + duk_push_tval(thr, tv_base); + duk_push_tval(thr, tv_key); + duk_push_tval(thr, tv_targ); - env_done: - /* [ ... arg1 ... argN ] */ + DUK_GC_TORTURE(thr->heap); - /* - * Setup value stack: clamp to 'nargs', fill up to 'nregs' + /* 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. */ - duk__adjust_valstack_and_top(thr, - num_stack_args, - idx_args, - nregs, - nargs, - func); - - /* - * Shift to new valstack_bottom. - */ +#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 - 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); + duk_push_true(thr); + duk_put_prop_stridx(thr, -2, DUK_STRIDX_INT_TARGET); /* Marker property, reuse _Target. */ - /* - * Return to bytecode executor, which will resume execution from - * the topmost activation. - */ + /* [ propValue error ] */ + duk_replace(thr, entry_top - 1); + duk_set_top(thr, entry_top); - DUK_REFZERO_CHECK_FAST(thr); - return 1; + 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 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, * * 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 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, /* 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 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, #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 @@ DUK_LOCAL_DECL void duk__advance(duk_compiler_ctx *ctx); /* 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_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t o 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_fromstack(duk_compiler_ctx *comp_ctx, duk_iv 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 @@ DUK_LOCAL_DECL void duk__exprtop(duk_compiler_ctx *ctx, duk_ivalue *res, duk_sma /* 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); +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_reg_t forced_reg); +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); +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_reg_t forced_reg); +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_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalu 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 @@ DUK_LOCAL_DECL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, d #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 @@ DUK_LOCAL const duk_uint8_t duk__token_lbp[] = { * 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 @@ DUK_LOCAL void duk__recursion_increase(duk_compiler_ctx *comp_ctx) { 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 duk_bool_t duk__hstring_is_eval_or_arguments_in_strict_mode(duk_compil 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 @@ DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t e 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 @@ DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t e /* 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 @@ DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t e (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__advance(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { 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__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { 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 void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { */ 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 @@ DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { /* [ ... 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_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { } } - duk_compact_m1(ctx); + duk_compact_m1(thr); return ret; } @@ -63114,7 +64579,6 @@ DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { /* 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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { (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_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { 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_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { #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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { */ #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_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { * 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_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins) { 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_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f 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_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f 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 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f * 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 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f /* 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 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f /* 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 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f /* 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 @@ DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) { } #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_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_fl 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_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, du 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_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, du 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_raw(duk_compiler_ctx *comp_ctx, duk_reg_t re } } -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 @@ DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, d /* 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_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump 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 @@ DUK_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump #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_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_p 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 @@ DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { * 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__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { "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__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { 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_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_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) { - duk_reg_t res; +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 @@ DUK_LOCAL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t 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 @@ DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_ /* 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); + n = (duk_int_t) duk_get_length(thr, f->consts_idx); - tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); + tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); DUK_ASSERT(tv1 != NULL); #if defined(DUK_USE_FASTINT) @@ -64428,8 +65905,8 @@ DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { 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_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { 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_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_r 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, * 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, } 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, /* 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, } 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, #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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, 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; + return dest; } } - duk_dup(ctx, x->valstack_idx); + 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 @@ duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, 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 @@ DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, * 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 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x 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 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x /* 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 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x /* 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 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x */ 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__emit_a_b_c(comp_ctx, x->op | DUK__EMIT_FLAG_BC_REGCONST, dest, arg1, arg2); - duk__ivalue_regconst(x, (duk_regconst_t) dest); + 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 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x 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, ®_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 @@ DUK_LOCAL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { /* 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 void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue 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 @@ duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, /* 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 @@ DUK_LOCAL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk * 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 @@ DUK_LOCAL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_c * 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); + 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(ctx); + 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_LOCAL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_c 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 @@ DUK_LOCAL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_c * 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 duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_reg_t *out_ 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_LOCAL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, } } - 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 @@ DUK_LOCAL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t lab /* 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_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring 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 @@ DUK_LOCAL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *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__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t 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 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re } } + /* 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_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re } 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_LOCAL void duk__objlit_flush_keys(duk_compiler_ctx *comp_ctx, duk__objlit_st 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 duk_bool_t duk__objlit_load_key(duk_compiler_ctx *comp_ctx, duk_ivalue 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 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r 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 */ + st.num_total_pairs = 0; /* number of key/value pairs emitted overall */ - duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj); /* XXX: patch initial size hint afterwards? */ +#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 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r /* 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_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r 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_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r 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_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r 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 void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r */ 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 duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { /* 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { (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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { * 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { * 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { * 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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, ®_varbind, &rc_varname)) { /* register bound variables are non-configurable -> always false */ duk__emit_bc(comp_ctx, DUK_OP_LDFALSE, - (duk_regconst_t) reg_temp); + reg_temp); } else { - duk_dup(ctx, res->x1.valstack_idx); + 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); + reg_temp, + rc_varname); } - duk__ivalue_regconst(res, (duk_regconst_t) reg_temp); + 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_regconst_t reg_temp; - duk_dup(ctx, res->x1.valstack_idx); + duk_dup(thr, res->x1.valstack_idx); if (!duk__lookup_lhs(comp_ctx, ®_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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { /* 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { /* 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { */ 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { * 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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, ®_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_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { 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_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { */ 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i } 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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, ®_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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i * 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i /* 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i */ { - 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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, ®_varbind, &rc_varname); if (args_op == DUK_OP_NONE) { @@ -66836,8 +68393,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i /* '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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i * can change X, but when we do 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i * one instruction, so use explicit PC computation. */ DUK_DD(DUK_DDPRINT("rhs is side effect free, rewind and avoid unnecessary temp for reg-based =")); - 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i /* '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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i * 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 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i 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, ®_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_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i } 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 void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i } 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_small_uint_t tok = comp_ctx->curr_token.t; - DUK_ASSERT(tok >= DUK_TOK_MINVAL && tok <= DUK_TOK_MAXVAL); + 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 @@ DUK_LOCAL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx) { /* 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 @@ DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_ (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 @@ DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_ 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 @@ DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_ 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 @@ DUK_LOCAL void duk__exprtop(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_sma */ #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_LOCAL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue * 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 @@ DUK_LOCAL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalu * 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_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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); + 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(ctx, h_varname); /* push before advancing to keep reachable */ + 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, ®_varbind, &rc_varname); duk__advance(comp_ctx); /* eat identifier */ @@ -67459,11 +69014,11 @@ DUK_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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_LOCAL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, } } - 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_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, } 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_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, * 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 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, } 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, ®_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_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, { 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 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, 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 @@ DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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 @@ DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re } 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_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, d } 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 @@ DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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 @@ DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re 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_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re } 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 @@ DUK_LOCAL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res 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_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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 @@ DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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 @@ DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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_tval *) duk_get_tval(thr, 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_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); + 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(ctx); - duk_push_null(ctx); - duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); + 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_push_int(thr, varmap_value); } - duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); + 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 @@ DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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_LOCAL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) 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 duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t l */ 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 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ 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_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ 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_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ 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); + 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(ctx) == top_before); + DUK_ASSERT(duk_get_top(thr) == top_before); #endif } @@ -68973,7 +70525,7 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ /* 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 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ /* 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 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ */ 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_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ 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_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_sou 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 @@ DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_sou /* Tear down state. */ - duk_pop_2(ctx); + duk_pop_2(thr); } /* @@ -69244,9 +70795,8 @@ DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_sou * 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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct #endif #if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(ctx); + entry_top = duk_get_top(thr); #endif /* @@ -69276,21 +70826,21 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct * (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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct /* 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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct *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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct * 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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct * 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_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct 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))); + DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(thr, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(thr, -1))); #endif - duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ + duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ } /* @@ -69445,7 +70995,7 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct * '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 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct 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 */ + duk_get_prop_index(thr, comp_ctx->curr_func.decls_idx, i); /* decl name */ - if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) { + 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_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct 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); + duk_push_int(thr, (duk_int_t) reg_bind); } else { - duk_dup_top(ctx); + 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_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct 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*/); + 0 /*value*/); } - duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ + duk_put_prop(thr, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ } } @@ -69521,10 +71071,10 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct */ 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__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct 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_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expec 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_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expec 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 @@ DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expec /* 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 @@ DUK_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) { */ 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_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) { (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_formals(duk_compiler_ctx *comp_ctx) { */ 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 @@ DUK_LOCAL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_ui 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 void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_small_ui */ 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_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm 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 @@ DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm * 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_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm 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 @@ DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm } /* 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 @@ DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm 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); + 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(ctx, entry_top); + duk_set_top(thr, entry_top); } DUK_MEMCPY((void *) &comp_ctx->curr_func, (void *) &old_func, sizeof(duk_compiler_func)); @@ -70147,8 +71692,7 @@ DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_sm /* 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 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { * 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 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { 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 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { comp_ctx->curr_token.str2 = NULL; #endif - duk_require_stack(ctx, DUK__COMPILE_ENTRY_SLOTS); + duk_require_stack(thr, 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_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 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { 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_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { */ 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_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { } 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 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer 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 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer #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 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer * 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 @@ DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, du /* 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_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, du 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 @@ DUK_LOCAL_DECL void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, du } 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv * 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_add(duk_hthread *thr, duk_tval *tv * 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. + * in duk_concat_2() 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] */ + 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_pop_2_unsafe(thr); + duk_push_number(thr, du.d); /* will NaN normalize result */ } - duk_replace(ctx, (duk_idx_t) idx_z); /* side effects */ + 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_idx_t idx_z, duk_small_uint_fast_t opcode) { +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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv * 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv #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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv * 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv } 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_binary_op(duk_hthread *thr, duk_tv } #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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_ * 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_ #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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_ 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_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_ 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_binary_op(duk_hthread *thr, duk_ } /* 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx * 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); + DUK_ASSERT_DISABLE(idx_src >= 0); + DUK_ASSERT_DISABLE(idx_dst >= 0); - tv = DUK_GET_TVAL_POSIDX(ctx, idx_src); + 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx */ 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx /* 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_arith_unary_op(duk_hthread *thr, duk_idx } /* 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_not(duk_hthread *thr, duk_uint_f * 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 */ + 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(ctx, idx_src); + 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__vm_bitwise_not(duk_hthread *thr, duk_uint_f 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_reg_helper(duk_hthread *thr, 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_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_reg_helper(duk_hthread *thr, 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, * 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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, /* [... 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 ] */ + 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(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_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 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, * 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; + 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); - (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_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); - duk_set_top((duk_context *) thr, h_func->nregs); + /* XXX: a best effort shrink check would be OK here */ } -DUK_LOCAL void duk__reconfig_valstack_ecma_catcher(duk_hthread *thr, duk_size_t act_idx, duk_size_t cat_idx) { - duk_activation *act; +/* 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 */ + 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); - act = thr->callstack + act_idx; - cat = thr->catchstack + cat_idx; 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; + 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); - (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_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); - duk_set_top((duk_context *) thr, h_func->nregs); + /* XXX: a best effort shrink check would be OK here */ } -/* 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) { +/* 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 */ + DUK_TVAL_SET_TVAL_UPDREF_NORZ(thr, tv1, tv_val_unstable); - tv1 = thr->valstack + thr->catchstack[cat_idx].idx_base + 1; + 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_TVAL_SET_U32_UPDREF_NORZ(thr, tv1, (duk_uint32_t) lj_type); } -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_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); + 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_hthread_catchstack_unwind_norz(thr, cat_idx + 1); - duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1); + 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 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval * 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_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval 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 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval */ /* 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 */ + 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 */ - act = thr->callstack_curr; + 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 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval * prototype, decref for act->lex_env overwrite. */ - DUK_CAT_SET_LEXENV_ACTIVE(&thr->catchstack[cat_idx]); + DUK_CAT_SET_LEXENV_ACTIVE(cat); - duk_pop(ctx); + 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_CAT_CLEAR_CATCH_ENABLED(cat); } -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_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); + 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_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); + 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; + cat = act->cat; + DUK_ASSERT(cat != NULL); + + act->curr_pc = cat->pc_base + 1; /* +1 = finally */ - DUK_CAT_CLEAR_FINALLY_ENABLED(&thr->catchstack[cat_idx]); + DUK_CAT_CLEAR_FINALLY_ENABLED(cat); } -DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small_uint_t lj_type) { +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 */ + cat = act->cat; + DUK_ASSERT(cat != NULL); + DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_LABEL); - duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1); /* keep label catcher */ - /* no need to unwind callstack */ + 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 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small /* 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 */ + 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 */ - duk_hthread_callstack_unwind_norz(resumer, act_idx + 1); /* unwind to 'resume' caller */ + 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 */ - /* no need to unwind catchstack */ - duk__reconfig_valstack_ecma_return(resumer, act_idx); + 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, (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_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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 */ + /* 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 = resumee->valstack + resumee->callstack[act_idx].idx_retval; /* return value from Duktape.Thread.yield() */ + 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_TVAL_SET_TVAL_UPDREF(thr, tv, tv2); /* side effects */ /* XXX: avoid side effects */ - duk_hthread_callstack_unwind_norz(resumee, act_idx + 1); /* unwind to 'yield' caller */ + duk_hthread_activation_unwind_norz(resumee); /* unwind to 'yield' caller */ + /* no need to unwind catch stack */ - /* no need to unwind catchstack */ - - duk__reconfig_valstack_ecma_return(resumee, act_idx); + duk__reconfig_valstack_ecma_return(resumee); DUK_ASSERT(resumee->resumer == NULL); resumee->resumer = thr; @@ -71614,22 +73145,21 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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 */ + call_flags = DUK_CALL_FLAG_ALLOW_ECMATOECMA; /* not tailcall, ecma-to-ecma (assumed to succeed) */ - setup_rc = duk_handle_ecma_call_setup(resumee, - 1, /* num_stack_args */ - call_flags); /* call_flags */ + 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, /* 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, * 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); + for (;;) { + cat = act->cat; + if (cat == NULL) { + break; + } - duk__handle_catch(thr, - cat - thr->catchstack, - &thr->heap->lj.value1, - DUK_LJ_TYPE_THROW); + if (DUK_CAT_HAS_CATCH_ENABLED(cat)) { + DUK_ASSERT(DUK_CAT_GET_TYPE(cat) == DUK_CAT_TYPE_TCF); - DUK_DD(DUK_DDPRINT("-> throw caught by a 'catch' clause, restart execution")); - retval = DUK__LONGJMP_RESTART; - goto wipe_and_return; - } + 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)); + 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__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_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); } - cat--; - } + 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; + } - 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 */ + 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 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, * 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. + /* 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. + * 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)); + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); - while (cat >= thr->catchstack) { - if (cat->callstack_index != orig_callstack_index) { + for (;;) { + cat = act->cat; + if (cat == NULL) { break; } - DUK_DDD(DUK_DDDPRINT("considering catcher %ld: type=%ld label=%ld", - (long) (cat - thr->catchstack), + + 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--; + + duk_hthread_catcher_unwind_norz(thr, act); } - /* should never happen, but be robust */ + /* 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 @@ DUK_LOCAL void duk__handle_break_or_continue(duk_hthread *thr, * 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_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, 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; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); - while (cat >= thr->catchstack) { - if (cat->callstack_index != orig_callstack_index) { + 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")); + duk_hthread_catcher_unwind_norz(thr, act); + } - if (thr == entry_thread && - thr->callstack_top == entry_callstack_top) { - /* Return to the bytecode executor caller which will unwind stacks. + 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) + * match entry_act check). */ - - DUK_DDD(DUK_DDDPRINT("return to Ecmascript caller, idx_retval=%ld, lj_value1=%!T", - (long) (thr->callstack_curr - 1)->idx_retval, + 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 */ + 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 = thr->valstack + (thr->callstack_curr - 1)->idx_retval; + 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))); + /* Catch stack unwind happens inline in callstack unwind. */ + duk_hthread_activation_unwind_norz(thr); - 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); + 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_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, 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. */ - DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); - duk__handle_yield(thr, resumer, resumer->callstack_top - 2, thr->valstack_top - 1); + /* 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_hthread_terminate(thr); /* updates thread state, minimizes its allocations */ - DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_TERMINATED); + DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom); + 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 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, #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_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ 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); @@ -72165,23 +73694,29 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ * (probably due to unnecessary spilling) at least on x64. */ + /* + * 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_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ 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 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ ; } - act->prev_line = line; + act->prev_line = (duk_uint32_t) line; } /* @@ -72240,7 +73775,8 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ } /* 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_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ 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 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ * 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 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ */ 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 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ 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 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ } #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_LOCAL DUK_NOINLINE DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hth 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 @@ DUK_LOCAL DUK_NOINLINE DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hth * 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_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * 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 @@ -72572,6 +74111,519 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * } #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. * @@ -72607,7 +74659,9 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * #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 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * #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 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * (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 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * #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 @@ DUK_LOCAL void duk__handle_executor_error(duk_heap *heap, */ 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_LOCAL void duk__handle_executor_error(duk_heap *heap, 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_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { 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 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } /* 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * (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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * /* 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * (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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } } - duk_pop(ctx); + duk_pop_unsafe(thr); break; } @@ -74161,8 +76246,8 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * (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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } case DUK_OP_GETVAR: { - duk_context *ctx = (duk_context *) thr; duk_activation *act; duk_tval *tv1; duk_hstring *name; @@ -74262,8 +76345,9 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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) */ + /* Allocate catcher and populate it (must be atomic). */ - duk_hthread_catchstack_grow(thr); - cat = thr->catchstack + thr->catchstack_top; - thr->catchstack_top++; + cat = duk_hthread_catcher_alloc(thr); + DUK_ASSERT(cat != NULL); - cat->flags = DUK_CAT_TYPE_LABEL | (bc << DUK_CAT_LABEL_SHIFT); - cat->callstack_index = thr->callstack_top - 1; + 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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); + 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); - duk_hthread_catchstack_unwind(thr, thr->catchstack_top - 1); /* no need to unwind callstack */ break; } @@ -74454,384 +76539,34 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * /* 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_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * */ 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))); + (duk_tval *) duk_get_tval(thr, -1))); #endif - duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1)); + 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * */ #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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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. - * - * 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. - */ + /* 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). + */ - /* 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. + 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. * - * If an Ecma-to-Ecma call is not possible, the actual call handling - * will do another (unnecessary) attempt to resolve the bound function. + * 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_context *ctx = (duk_context *) thr; - duk_small_uint_fast_t nargs; - duk_uint_fast_t idx; - duk_idx_t num_stack_args; + 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) - */ + DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0); + DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) == 0); - /* 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_idx_t) DUK_DEC_A(ins); + call_flags = (ins & 0x07U) | DUK_CALL_FLAG_ALLOW_ECMATOECMA; + idx = (duk_idx_t) DUK_DEC_BC(ins); - 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() */ + 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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. - */ + DUK_ASSERT((DUK_OP_CALL0 & 0x0fU) == 0); + DUK_ASSERT((ins & DUK_BC_CALL_FLAG_INDIRECT) != 0); - /* Don't need to sync curr_pc here; duk_new() will do that - * when it augments the created error. - */ + 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 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. - */ + 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * */ 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * */ 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * */ 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * } 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * * 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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread * #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 @@ DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) { /* 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_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) { 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_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) { } 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 @@ DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) { 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_ASSERT(duk_get_tval(thr, -1) != NULL); + d = duk_js_tonumber(thr, duk_get_tval(thr, -1)); - duk_pop(ctx); + duk_pop_unsafe(thr); return d; } case DUK_TAG_POINTER: { @@ -75883,7 +77484,7 @@ DUK_INTERNAL duk_double_t duk_js_tointeger_number(duk_double_t x) { /* 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 @@ DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) { #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 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d 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 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d 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 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d */ 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 @@ DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d 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_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, d { 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 @@ DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk } #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 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, /* 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); + duk_to_primitive(thr, -2, DUK_HINT_NUMBER); + duk_to_primitive(thr, -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, -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 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, 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 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, /* 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); + d1 = duk_to_number_m2(thr); + d2 = duk_to_number_m1(thr); } else { - d2 = duk_to_number_m1(ctx); - d1 = duk_to_number_m2(ctx); + d2 = duk_to_number_m1(thr); + d1 = duk_to_number_m2(thr); } #endif - d1 = duk_to_number_m2(ctx); - d2 = duk_to_number_m1(ctx); + d1 = duk_to_number_m2(thr); + d2 = duk_to_number_m1(thr); - /* We want to duk_pop_2(ctx); because the values are numbers + /* 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); + duk_pop_2_nodecref_unsafe(thr); #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_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_compare_helper(duk_hthread *thr, duk_tval *tv_x, */ 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_uint_t sanity; duk_bool_t skip_first; + duk_uint_t sanity; /* * Get the values onto the stack first. It would be possible to cover @@ -76633,52 +78231,41 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ * 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. + * + * The bound function chain is now "collapsed" so there can be only + * one bound function in the chain. */ - /* 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? */ + goto error_invalid_rval; + } - 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_HOBJECT_HAS_BOUNDFUNC(func)) { + duk_push_tval(thr, &((duk_hboundfunc *) func)->target); + duk_replace(thr, -2); + func = duk_require_hobject(thr, -1); /* lightfunc throws */ - if (DUK_UNLIKELY(sanity == 0)) { - DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT); + /* 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 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ * 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 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ * 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_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ 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 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ */ 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_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ } DUK_UNREACHABLE(); - pop_and_false: - duk_pop_2(ctx); + pop2_and_false: + duk_pop_2_unsafe(thr); + return 0; + + pop3_and_false: + duk_pop_3_unsafe(thr); return 0; - pop_and_true: - duk_pop_2(ctx); + 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_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ */ 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 @@ DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv /* 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); + 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(ctx, -2); + (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_GET_TVAL_NEGIDX(thr, -1), + DUK_GET_TVAL_NEGIDX(thr, -2)); - duk_pop_2(ctx); + duk_pop_2_unsafe(thr); return retval; } @@ -77006,8 +78616,8 @@ DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring * /* 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 @@ typedef struct { 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 @@ void duk_js_push_closure(duk_hthread *thr, 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 @@ void duk_js_push_closure(duk_hthread *thr, 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 @@ void duk_js_push_closure(duk_hthread *thr, 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 @@ void duk_js_push_closure(duk_hthread *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, proto); @@ -77292,10 +78905,10 @@ void duk_js_push_closure(duk_hthread *thr, * 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 @@ void duk_js_push_closure(duk_hthread *thr, 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 @@ void duk_js_push_closure(duk_hthread *thr, /* [ ... 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 @@ void duk_js_push_closure(duk_hthread *thr, /* 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_pop_unsafe(thr); - 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_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 @@ void duk_js_push_closure(duk_hthread *thr, /* [ ... 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 @@ void duk_js_push_closure(duk_hthread *thr, /* [ ... 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 @@ void duk_js_push_closure(duk_hthread *thr, /* 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 @@ void duk_js_push_closure(duk_hthread *thr, * through the API). */ - duk_compact(ctx, -2); + duk_compact(thr, -2); /* * Some assertions (E5 Section 13.2). @@ -77489,13 +79102,13 @@ void duk_js_push_closure(duk_hthread *thr, 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 @@ void duk_js_push_closure(duk_hthread *thr, /* [ ... 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_tval *) duk_get_tval(thr, -1), + (duk_tval *) duk_get_tval(thr, -2))); - duk_pop(ctx); + duk_pop_unsafe(thr); /* [ ... closure ] */ } @@ -77523,13 +79136,11 @@ void duk_js_push_closure(duk_hthread *thr, 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 *duk_create_activation_environment_record(duk_hthread *thr, 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_hobject *duk_create_activation_environment_record(duk_hthread *thr, 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 *duk_create_activation_environment_record(duk_hthread *thr, 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_hobject *duk_create_activation_environment_record(duk_hthread *thr, 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 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, 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 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, 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 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, */ 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 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject * 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 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject 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_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr, 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 @@ duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr, #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_bool_t duk__getid_activation_regs(duk_hthread *thr, 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_bool_t duk__getid_activation_regs(duk_hthread *thr, 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 duk__get_identifier_reference(duk_hthread *thr, 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 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, 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 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, * 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_bool_t duk__get_identifier_reference(duk_hthread *thr, 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_bool_t duk__getvar_helper(duk_hthread *thr, 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_bool_t duk__getvar_helper(duk_hthread *thr, 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 @@ duk_bool_t duk__getvar_helper(duk_hthread *thr, (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [value] */ if (ref.has_this) { - duk_push_hobject(ctx, ref.holder); + duk_push_hobject(thr, ref.holder); } else { - duk_push_undefined(ctx); + duk_push_undefined(thr); } /* [value this] */ @@ -78272,6 +79879,8 @@ void duk__putvar_helper(duk_hthread *thr, 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_bool_t duk__declvar_helper(duk_hthread *thr, 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 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr, 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 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr, /* 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_bool_t duk__declvar_helper(duk_hthread *thr, 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 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr, 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_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_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 @@ duk_bool_t duk_js_declvar_activation(duk_hthread *thr, 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 @@ DUK_LOCAL void duk__fill_lexer_buffer(duk_lexer_ctx *lex_ctx, duk_small_uint_t s 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 @@ DUK_LOCAL duk_codepoint_t duk__read_char(duk_lexer_ctx *lex_ctx) { 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 void duk__appendbuffer_ascii(duk_lexer_ctx *lex_ctx, duk_codepoint_t x */ 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_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bo 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 @@ DUK_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bo * 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_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, 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 @@ DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, /* 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, } 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, * 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, #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 "" SingleLineHTMLCloseComment + * ES2015: B.1.3, handle "-->" SingleLineHTMLCloseComment * Only allowed: * - on new line * - preceded only by whitespace @@ -80232,7 +81837,7 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, * 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, */ 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, 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 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, */ 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token } 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token * 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token * 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 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token 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_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range 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_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range 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 @@ DUK_LOCAL void duk__bi_copy(duk__bigint *x, duk__bigint *y) { 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 @@ DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) { 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 @@ DUK_LOCAL void duk__bi_mul(duk__bigint *x, duk__bigint *y, duk__bigint *z) { 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 @@ DUK_LOCAL void duk__bi_twoexp(duk__bigint *x, duk_small_int_t y) { 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_LOCAL void duk__bi_exp_small(duk__bigint *x, duk_small_int_t b, duk_small_in 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 @@ DUK_LOCAL void duk__bi_exp_small(duk__bigint *x, duk_small_int_t b, duk_small_in */ /* 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_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x 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 @@ DUK_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x 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 @@ DUK_LOCAL void duk__dragon4_prepare(duk__numconv_stringify_ctx *nc_ctx) { "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 @@ DUK_LOCAL void duk__dragon4_prepare(duk__numconv_stringify_ctx *nc_ctx) { "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 @@ DUK_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) { * 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_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) { 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 @@ DUK_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) { * 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_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) { 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_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) { 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_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify 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 @@ DUK_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify #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 @@ DUK_LOCAL void duk__dragon4_convert_and_push(duk__numconv_stringify_ctx *nc_ctx, 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 @@ DUK_LOCAL void duk__dragon4_ctx_to_double(duk__numconv_stringify_ctx *nc_ctx, du (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 @@ DUK_LOCAL void duk__dragon4_ctx_to_double(duk__numconv_stringify_ctx *nc_ctx, du * 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_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, 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_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, 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 @@ DUK_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, *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_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, } 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_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, */ } - 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 @@ DUK_INTERNAL void duk_numconv_stringify(duk_context *ctx, duk_small_int_t radix, * 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_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk 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 @@ DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk * sometimes not. After white space trimming, all valid input * characters are pure ASCII. */ - duk_trim(ctx, -1); + duk_trim(thr, -1); } - h_str = duk_require_hstring(ctx, -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 @@ DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk /* 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 @@ DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk * 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 @@ DUK_INTERNAL void duk_numconv_parse(duk_context *ctx, duk_small_int_t radix, duk 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_LOCAL duk_uint32_t duk__insert_u32(duk_re_compiler_ctx *re_ctx, duk_uint32_t 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_LOCAL duk_uint32_t duk__append_jump_offset(duk_re_compiler_ctx *re_ctx, duk_ /* * 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). - */ + * 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__generate_ranges(void *userdata, duk_codepoint_t r1, duk_codepoint_t r2, duk_bool_t direct) { +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_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__generate_ranges(): re_ctx=%p, range=[%ld,%ld] direct=%ld", + 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_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); - duk_codepoint_t i; - duk_codepoint_t t; - duk_codepoint_t r_start, r_end; + 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); - 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++; +#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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex } 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); + atom_code_length = (duk_int32_t) (DUK__RE_BUFLEN(re_ctx) - (duk_size_t) atom_start_offset); - offset = 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 @@ DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex (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 @@ DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex */ 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex */ 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 @@ DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex } /* 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex 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 @@ DUK_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex /* 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_LOCAL void duk__parse_disjunction(duk_re_compiler_ctx *re_ctx, duk_bool_t ex 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 duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h) */ 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_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) { 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_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) { } 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_LOCAL void duk__create_escaped_source(duk_hthread *thr, int idx_pattern) { */ 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_INTERNAL void duk_regexp_compile(duk_hthread *thr) { 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 @@ DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) { /* [ ... 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 @@ DUK_INTERNAL void duk_regexp_compile(duk_hthread *thr) { * 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_compile(duk_hthread *thr) { */ 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 @@ DUK_INTERNAL void duk_regexp_create_instance(duk_hthread *thr) { * 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_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const } 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_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const 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_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const 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 @@ DUK_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const * 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_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const 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 @@ DUK_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const 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_LOCAL const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const 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 const duk_uint8_t *duk__match_regexp(duk_re_matcher_ctx *re_ctx, const */ 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_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ */ /* 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ /* [ ... 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_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ * * - 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ * objects are usually short lived. */ - duk_push_array(ctx); + duk_push_array(thr); #if defined(DUK_USE_ASSERTIONS) - h_res = duk_require_hobject(ctx, -1); + 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ /* [ ... 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_push_u32(thr, char_offset); + duk_xdef_prop_stridx_short_wec(thr, -2, DUK_STRIDX_INDEX); - duk_dup_m4(ctx); - duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INPUT); + 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ * (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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ * 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 */ + char_end_offset = char_offset + (duk_uint32_t) duk_get_length(thr, -1); /* add charlen */ } } else { - duk_push_undefined(ctx); + 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 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ 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_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ 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_double_rounding(void) { 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 @@ DUK_LOCAL duk_uint_t duk__selftest_fmod(void) { 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 @@ DUK_LOCAL duk_uint_t duk__selftest_64bit_arithmetic(void) { /* 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 @@ DUK_LOCAL duk_uint_t duk__selftest_alloc_funcs(duk_alloc_function alloc_func, } 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 @@ DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast_fast(duk_tval *t 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 @@ DUK_INTERNAL DUK_ALWAYS_INLINE void duk_tval_set_number_chkfast_fast(duk_tval *t 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 @@ DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_packed(duk_tval 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 @@ DUK_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked(duk_tva 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_INTERNAL DUK_ALWAYS_INLINE duk_double_t duk_tval_get_number_unpacked_fastint 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 @@ const duk_uint16_t duk_unicode_re_canon_lookup[65536] = { 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 @@ DUK_INTERNAL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t * 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_LOCAL const duk_uint8_t duk__bitpacked_lookup[16] = { 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 @@ DUK_INTERNAL void duk_be_finish(duk_bitencoder_ctx *ctx) { } #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_INTERNAL void duk_bw_init(duk_hthread *thr, duk_bufwriter_ctx *bw_ctx, duk_h 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 @@ DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_ */ 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_INTERNAL void duk_bw_write_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx 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_INTERNAL void duk_bw_insert_raw_bytes(duk_hthread *thr, duk_bufwriter_ctx *b 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_INTERNAL void duk_bw_insert_ensure_bytes(duk_hthread *thr, duk_bufwriter_ctx 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 @@ DUK_INTERNAL void duk_bw_insert_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *b 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_INTERNAL void duk_bw_insert_ensure_slice(duk_hthread *thr, duk_bufwriter_ctx 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_INTERNAL duk_uint8_t *duk_bw_insert_raw_area(duk_hthread *thr, duk_bufwriter 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_INTERNAL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_bufwri 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 @@ DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { #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 @@ DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { * 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 { diff --git a/src/modules/app_jsdt/duktape.h b/src/modules/app_jsdt/duktape.h index 614954e3ddb..bf330a1f4f1 100644 --- a/src/modules/app_jsdt/duktape.h +++ b/src/modules/app_jsdt/duktape.h @@ -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 + * * 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 @@ struct duk_time_components { /* 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 @@ struct duk_time_components { #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) */ - -/* 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 */ +#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; 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()) */ @@ -399,6 +433,23 @@ struct duk_time_components { #define DUK_LEVEL_DDEBUG 1 #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_array(duk_context *ctx); 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 @@ DUK_EXTERNAL_DECL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx); #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_int_t duk_require_int(duk_context *ctx, duk_idx_t idx); 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(duk_context *ctx, duk_idx_t obj_idx); 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_put_number_list(duk_context *ctx, duk_idx_t obj_idx, 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