From db9b0dfe002be7368809b4017c871335073715b2 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 16 Apr 2017 16:57:01 +0100 Subject: Duktape: Update to 2.1.0 release. --- content/handlers/javascript/duktape/duk_config.h | 114 +- content/handlers/javascript/duktape/duktape.c | 11937 ++++++++++++--------- content/handlers/javascript/duktape/duktape.h | 577 +- 3 files changed, 7089 insertions(+), 5539 deletions(-) (limited to 'content/handlers') diff --git a/content/handlers/javascript/duktape/duk_config.h b/content/handlers/javascript/duktape/duk_config.h index c28a4b8e5..4a16a68da 100644 --- a/content/handlers/javascript/duktape/duk_config.h +++ b/content/handlers/javascript/duktape/duk_config.h @@ -12,6 +12,7 @@ * - Generic BSD * - Atari ST TOS * - AmigaOS + * - Durango (XboxOne) * - Windows * - Flashplayer (Crossbridge) * - QNX @@ -128,6 +129,11 @@ #endif #endif +/* Durango (Xbox One) */ +#if defined(_DURANGO) || defined(_XBOX_ONE) +#define DUK_F_DURANGO +#endif + /* Windows, both 32-bit and 64-bit */ #if defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64) || \ defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) @@ -350,15 +356,15 @@ #define DUK_F_VBCC #endif +#if defined(ANDROID) || defined(__ANDROID__) +#define DUK_F_ANDROID +#endif + /* Atari Mint */ #if defined(__MINT__) #define DUK_F_MINT #endif -#if defined(ANDROID) || defined(__ANDROID__) -#define DUK_F_ANDROID -#endif - /* * Platform autodetection */ @@ -483,6 +489,40 @@ #if !defined(DUK_USE_BYTEORDER) && (defined(DUK_F_M68K) || defined(DUK_F_PPC)) #define DUK_USE_BYTEORDER 3 #endif +#elif defined(DUK_F_DURANGO) +/* --- Durango (XboxOne) --- */ +/* Durango = XboxOne + * Configuration is nearly identical to Windows, except for + * DUK_USE_DATE_TZO_WINDOWS. + */ + +/* 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. + */ +#if defined(DUK_COMPILING_DUKTAPE) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +/* MSVC does not have sys/param.h */ +#define DUK_USE_DATE_NOW_WINDOWS +#define DUK_USE_DATE_TZO_WINDOWS_NO_DST +/* 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. + */ +#include +#endif + +#define DUK_USE_OS_STRING "durango" + +#if !defined(DUK_USE_BYTEORDER) +#define DUK_USE_BYTEORDER 1 +#endif #elif defined(DUK_F_WINDOWS) /* --- Windows --- */ /* Initial fix: disable secure CRT related warnings when compiling Duktape @@ -552,6 +592,10 @@ #define DUK_USE_OS_STRING "qnx" #elif defined(DUK_F_TINSPIRE) /* --- TI-Nspire --- */ +#if defined(DUK_COMPILING_DUKTAPE) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE /* e.g. strptime */ +#endif + #define DUK_USE_DATE_NOW_GETTIMEOFDAY #define DUK_USE_DATE_TZO_GMTIME_R #define DUK_USE_DATE_PRS_STRPTIME @@ -832,12 +876,8 @@ /* --- MIPS 32-bit --- */ #define DUK_USE_ARCH_STRING "mips32" /* MIPS byte order varies so rely on autodetection. */ -/* Based on 'make checkalign' there are no alignment requirements on - * Linux MIPS except for doubles, which need align by 4. Alignment - * requirements vary based on target though. - */ #if !defined(DUK_USE_ALIGN_BY) -#define DUK_USE_ALIGN_BY 4 +#define DUK_USE_ALIGN_BY 8 #endif #define DUK_USE_PACKED_TVAL #define DUK_F_PACKED_TVAL_PROVIDED @@ -845,9 +885,6 @@ /* --- MIPS 64-bit --- */ #define DUK_USE_ARCH_STRING "mips64" /* MIPS byte order varies so rely on autodetection. */ -/* Good default is a bit arbitrary because alignment requirements - * depend on target. See https://github.com/svaarala/duktape/issues/102. - */ #if !defined(DUK_USE_ALIGN_BY) #define DUK_USE_ALIGN_BY 8 #endif @@ -971,6 +1008,9 @@ #define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) #endif +/* DUK_HOT */ +/* DUK_COLD */ + #if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) /* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're * compiling Duktape or the application. @@ -1081,6 +1121,12 @@ #define DUK_ALWAYS_INLINE inline __attribute__((always_inline)) #endif +#if (defined(DUK_F_C99) || defined(DUK_F_CPP11)) && \ + defined(DUK_F_GCC_VERSION) && (DUK_F_GCC_VERSION >= 40300) +#define DUK_HOT __attribute__((hot)) +#define DUK_COLD __attribute__((cold)) +#endif + #if defined(DUK_F_DLL_BUILD) && defined(DUK_F_WINDOWS) /* MSVC dllexport/dllimport: appropriate __declspec depends on whether we're * compiling Duktape or the application. @@ -2276,9 +2322,9 @@ typedef struct duk_hthread duk_context; * for platforms that don't include them. MSVC isn't detected as C99, but * these functions also exist in MSVC 2013 and later so include a clause for * that too. Android doesn't have log2; disable all of these for Android. - * Atari MINT also lacks these. */ -#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT) +#if (defined(DUK_F_C99) || defined(DUK_F_CPP11) || (defined(_MSC_VER) && (_MSC_VER >= 1800))) && \ + !defined(DUK_F_ANDROID) && !defined(DUK_F_MINT) #if !defined(DUK_CBRT) #define DUK_CBRT cbrt #endif @@ -2504,7 +2550,8 @@ typedef struct duk_hthread duk_context; /* Macro for suppressing warnings for potentially unreferenced variables. * The variables can be actually unreferenced or unreferenced in some * specific cases only; for instance, if a variable is only debug printed, - * it is unreferenced when debug printing is disabled. + * it is unreferenced when debug printing is disabled. May cause warnings + * for volatile arguments. */ #define DUK_UNREF(x) do { (void) (x); } while (0) #endif @@ -2546,6 +2593,13 @@ typedef struct duk_hthread duk_context; #define DUK_ALWAYS_INLINE /*nop*/ #endif +#if !defined(DUK_HOT) +#define DUK_HOT /*nop*/ +#endif +#if !defined(DUK_COLD) +#define DUK_COLD /*nop*/ +#endif + #if !defined(DUK_EXTERNAL_DECL) #define DUK_EXTERNAL_DECL extern #endif @@ -2769,6 +2823,7 @@ typedef struct duk_hthread duk_context; #define DUK_USE_FAST_REFCOUNT_DEFAULT #undef DUK_USE_FATAL_HANDLER #define DUK_USE_FINALIZER_SUPPORT +#undef DUK_USE_FINALIZER_TORTURE #undef DUK_USE_FUNCPTR16 #undef DUK_USE_FUNCPTR_DEC16 #undef DUK_USE_FUNCPTR_ENC16 @@ -2777,16 +2832,26 @@ typedef struct duk_hthread duk_context; #define DUK_USE_FUNC_NAME_PROPERTY #undef DUK_USE_GC_TORTURE #undef DUK_USE_GET_RANDOM_DOUBLE +#undef DUK_USE_GLOBAL_BINDING #define DUK_USE_GLOBAL_BUILTIN #undef DUK_USE_HEAPPTR16 #undef DUK_USE_HEAPPTR_DEC16 #undef DUK_USE_HEAPPTR_ENC16 #define DUK_USE_HEX_FASTPATH +#define DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT 2 +#define DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT 9 +#define DUK_USE_HOBJECT_ARRAY_MINGROW_ADD 16 +#define DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR 8 +#define DUK_USE_HOBJECT_ENTRY_MINGROW_ADD 16 +#define DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR 8 #define DUK_USE_HOBJECT_HASH_PART +#define DUK_USE_HOBJECT_HASH_PROP_LIMIT 8 #define DUK_USE_HSTRING_ARRIDX #define DUK_USE_HSTRING_CLEN #undef DUK_USE_HSTRING_EXTDATA +#define DUK_USE_HTML_COMMENTS #define DUK_USE_IDCHAR_FASTPATH +#undef DUK_USE_INJECT_HEAP_ALLOC_ERROR #undef DUK_USE_INTERRUPT_COUNTER #undef DUK_USE_INTERRUPT_DEBUG_FIXUP #define DUK_USE_JC @@ -2802,10 +2867,8 @@ typedef struct duk_hthread duk_context; #define DUK_USE_JX #define DUK_USE_LEXER_SLIDING_WINDOW #undef DUK_USE_LIGHTFUNC_BUILTINS -#undef DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE #define DUK_USE_MARK_AND_SWEEP_RECLIMIT 256 #define DUK_USE_MATH_BUILTIN -#define DUK_USE_MS_STRINGTABLE_RESIZE #define DUK_USE_NATIVE_CALL_RECLIMIT 1000 #define DUK_USE_NONSTD_ARRAY_CONCAT_TRAILER #define DUK_USE_NONSTD_ARRAY_MAP_TRAILER @@ -2825,9 +2888,9 @@ typedef struct duk_hthread duk_context; #undef DUK_USE_PREFER_SIZE #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 -#undef DUK_USE_REFZERO_FINALIZER_TORTURE #undef DUK_USE_REGEXP_CANON_WORKAROUND #define DUK_USE_REGEXP_COMPILER_RECLIMIT 10000 #define DUK_USE_REGEXP_EXECUTOR_RECLIMIT 10000 @@ -2839,6 +2902,7 @@ typedef struct duk_hthread duk_context; #undef DUK_USE_ROM_STRINGS #define DUK_USE_SECTION_B #undef DUK_USE_SELF_TESTS +#define DUK_USE_SHEBANG_COMMENTS #undef DUK_USE_SHUFFLE_TORTURE #define DUK_USE_SOURCE_NONBMP #undef DUK_USE_STRHASH16 @@ -2848,9 +2912,13 @@ typedef struct duk_hthread duk_context; #undef DUK_USE_STRICT_UTF8_SOURCE #define DUK_USE_STRING_BUILTIN #undef DUK_USE_STRLEN16 -#undef DUK_USE_STRTAB_CHAIN -#undef DUK_USE_STRTAB_CHAIN_SIZE -#define DUK_USE_STRTAB_PROBE +#define DUK_USE_STRTAB_GROW_LIMIT 17 +#define DUK_USE_STRTAB_MAXSIZE 268435456L +#define DUK_USE_STRTAB_MINSIZE 1024 +#undef DUK_USE_STRTAB_PTRCOMP +#define DUK_USE_STRTAB_RESIZE_CHECK_MASK 255 +#define DUK_USE_STRTAB_SHRINK_LIMIT 6 +#undef DUK_USE_STRTAB_TORTURE #undef DUK_USE_SYMBOL_BUILTIN #define DUK_USE_TAILCALL #define DUK_USE_TARGET_INFO "unknown" @@ -2901,10 +2969,12 @@ typedef struct duk_hthread duk_context; #if defined(DUK_USE_DATE_GET_LOCAL_TZOFFSET) /* External provider already defined. */ -#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME) +#elif defined(DUK_USE_DATE_TZO_GMTIME_R) || defined(DUK_USE_DATE_TZO_GMTIME_S) || defined(DUK_USE_DATE_TZO_GMTIME) #define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_gmtime((d)) #elif defined(DUK_USE_DATE_TZO_WINDOWS) #define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows((d)) +#elif defined(DUK_USE_DATE_TZO_WINDOWS_NO_DST) +#define DUK_USE_DATE_GET_LOCAL_TZOFFSET(d) duk_bi_date_get_local_tzoffset_windows_no_dst((d)) #else #error no provider for DUK_USE_DATE_GET_LOCAL_TZOFFSET() #endif diff --git a/content/handlers/javascript/duktape/duktape.c b/content/handlers/javascript/duktape/duktape.c index 2ab83656b..7ca0f6534 100644 --- a/content/handlers/javascript/duktape/duktape.c +++ b/content/handlers/javascript/duktape/duktape.c @@ -1,7 +1,5 @@ -/* Omit from static analysis. */ -#ifndef __clang_analyzer__ /* - * Single source autogenerated distributable for Duktape 2.0.2. + * Single source autogenerated distributable for Duktape 2.1.0. * * Git commit external (external). * Git branch external. @@ -84,6 +82,8 @@ * * Brett Vickers (https://github.com/beevik) * * Dominik Okwieka (https://github.com/okitec) * * Remko Tron\u00e7on (https://el-tramo.be) +* * Romero Malaquias (rbsm@ic.ufal.br) +* * Michael Drake * * Other contributions * =================== @@ -179,6 +179,430 @@ DUK_USE_USER_DECLARE() * dependencies. */ +/* #include duk_dblunion.h */ +/* + * Union to access IEEE double memory representation, indexes for double + * memory representation, and some macros for double manipulation. + * + * Also used by packed duk_tval. Use a union for bit manipulation to + * minimize aliasing issues in practice. The C99 standard does not + * guarantee that this should work, but it's a very widely supported + * practice for low level manipulation. + * + * IEEE double format summary: + * + * seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff + * A B C D E F G H + * + * s sign bit + * eee... exponent field + * fff... fraction + * + * See http://en.wikipedia.org/wiki/Double_precision_floating-point_format. + * + * NaNs are represented as exponent 0x7ff and mantissa != 0. The NaN is a + * signaling NaN when the highest bit of the mantissa is zero, and a quiet + * NaN when the highest bit is set. + * + * At least three memory layouts are relevant here: + * + * A B C D E F G H Big endian (e.g. 68k) DUK_USE_DOUBLE_BE + * H G F E D C B A Little endian (e.g. x86) DUK_USE_DOUBLE_LE + * D C B A H G F E Mixed/cross endian (e.g. ARM) DUK_USE_DOUBLE_ME + * + * ARM is a special case: ARM double values are in mixed/cross endian + * format while ARM duk_uint64_t values are in standard little endian + * format (H G F E D C B A). When a double is read as a duk_uint64_t + * from memory, the register will contain the (logical) value + * E F G H A B C D. This requires some special handling below. + * + * Indexes of various types (8-bit, 16-bit, 32-bit) in memory relative to + * the logical (big endian) order: + * + * byte order duk_uint8_t duk_uint16_t duk_uint32_t + * BE 01234567 0123 01 + * LE 76543210 3210 10 + * ME (ARM) 32107654 1032 01 + * + * Some processors may alter NaN values in a floating point load+store. + * For instance, on X86 a FLD + FSTP may convert a signaling NaN to a + * quiet one. This is catastrophic when NaN space is used in packed + * duk_tval values. See: misc/clang_aliasing.c. + */ + +#if !defined(DUK_DBLUNION_H_INCLUDED) +#define DUK_DBLUNION_H_INCLUDED + +/* + * Union for accessing double parts, also serves as packed duk_tval + */ + +union duk_double_union { + double d; + float f[2]; +#if defined(DUK_USE_64BIT_OPS) + duk_uint64_t ull[1]; +#endif + duk_uint32_t ui[2]; + duk_uint16_t us[4]; + duk_uint8_t uc[8]; +#if defined(DUK_USE_PACKED_TVAL) + void *vp[2]; /* used by packed duk_tval, assumes sizeof(void *) == 4 */ +#endif +}; + +typedef union duk_double_union duk_double_union; + +/* + * Indexes of various types with respect to big endian (logical) layout + */ + +#if defined(DUK_USE_DOUBLE_LE) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 +#endif +#define DUK_DBL_IDX_UI0 1 +#define DUK_DBL_IDX_UI1 0 +#define DUK_DBL_IDX_US0 3 +#define DUK_DBL_IDX_US1 2 +#define DUK_DBL_IDX_US2 1 +#define DUK_DBL_IDX_US3 0 +#define DUK_DBL_IDX_UC0 7 +#define DUK_DBL_IDX_UC1 6 +#define DUK_DBL_IDX_UC2 5 +#define DUK_DBL_IDX_UC3 4 +#define DUK_DBL_IDX_UC4 3 +#define DUK_DBL_IDX_UC5 2 +#define DUK_DBL_IDX_UC6 1 +#define DUK_DBL_IDX_UC7 0 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#elif defined(DUK_USE_DOUBLE_BE) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 +#endif +#define DUK_DBL_IDX_UI0 0 +#define DUK_DBL_IDX_UI1 1 +#define DUK_DBL_IDX_US0 0 +#define DUK_DBL_IDX_US1 1 +#define DUK_DBL_IDX_US2 2 +#define DUK_DBL_IDX_US3 3 +#define DUK_DBL_IDX_UC0 0 +#define DUK_DBL_IDX_UC1 1 +#define DUK_DBL_IDX_UC2 2 +#define DUK_DBL_IDX_UC3 3 +#define DUK_DBL_IDX_UC4 4 +#define DUK_DBL_IDX_UC5 5 +#define DUK_DBL_IDX_UC6 6 +#define DUK_DBL_IDX_UC7 7 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#elif defined(DUK_USE_DOUBLE_ME) +#if defined(DUK_USE_64BIT_OPS) +#define DUK_DBL_IDX_ULL0 0 /* not directly applicable, byte order differs from a double */ +#endif +#define DUK_DBL_IDX_UI0 0 +#define DUK_DBL_IDX_UI1 1 +#define DUK_DBL_IDX_US0 1 +#define DUK_DBL_IDX_US1 0 +#define DUK_DBL_IDX_US2 3 +#define DUK_DBL_IDX_US3 2 +#define DUK_DBL_IDX_UC0 3 +#define DUK_DBL_IDX_UC1 2 +#define DUK_DBL_IDX_UC2 1 +#define DUK_DBL_IDX_UC3 0 +#define DUK_DBL_IDX_UC4 7 +#define DUK_DBL_IDX_UC5 6 +#define DUK_DBL_IDX_UC6 5 +#define DUK_DBL_IDX_UC7 4 +#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ +#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ +#else +#error internal error +#endif + +/* + * Helper macros for reading/writing memory representation parts, used + * by duk_numconv.c and duk_tval.h. + */ + +#define DUK_DBLUNION_SET_DOUBLE(u,v) do { \ + (u)->d = (v); \ + } while (0) + +#define DUK_DBLUNION_SET_HIGH32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ + } while (0) + +#if defined(DUK_USE_64BIT_OPS) +#if defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ + } while (0) +#else +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = ((duk_uint64_t) (v)) << 32; \ + } while (0) +#endif +#else /* DUK_USE_64BIT_OPS */ +#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0; \ + } while (0) +#endif /* DUK_USE_64BIT_OPS */ + +#define DUK_DBLUNION_SET_LOW32(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ + } while (0) + +#define DUK_DBLUNION_GET_DOUBLE(u) ((u)->d) +#define DUK_DBLUNION_GET_HIGH32(u) ((u)->ui[DUK_DBL_IDX_UI0]) +#define DUK_DBLUNION_GET_LOW32(u) ((u)->ui[DUK_DBL_IDX_UI1]) + +#if defined(DUK_USE_64BIT_OPS) +#if defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_SET_UINT64(u,v) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) ((v) >> 32); \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ + } while (0) +#define DUK_DBLUNION_GET_UINT64(u) \ + ((((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI0]) << 32) | \ + ((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI1])) +#else +#define DUK_DBLUNION_SET_UINT64(u,v) do { \ + (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ + } while (0) +#define DUK_DBLUNION_GET_UINT64(u) ((u)->ull[DUK_DBL_IDX_ULL0]) +#endif +#define DUK_DBLUNION_SET_INT64(u,v) DUK_DBLUNION_SET_UINT64((u), (duk_uint64_t) (v)) +#define DUK_DBLUNION_GET_INT64(u) ((duk_int64_t) DUK_DBLUNION_GET_UINT64((u))) +#endif /* DUK_USE_64BIT_OPS */ + +/* + * Double NaN manipulation macros related to NaN normalization needed when + * using the packed duk_tval representation. NaN normalization is necessary + * to keep double values compatible with the duk_tval format. + * + * When packed duk_tval is used, the NaN space is used to store pointers + * and other tagged values in addition to NaNs. Actual NaNs are normalized + * to a specific quiet NaN. The macros below are used by the implementation + * to check and normalize NaN values when they might be created. The macros + * are essentially NOPs when the non-packed duk_tval representation is used. + * + * A FULL check is exact and checks all bits. A NOTFULL check is used by + * the packed duk_tval and works correctly for all NaNs except those that + * begin with 0x7ff0. Since the 'normalized NaN' values used with packed + * duk_tval begin with 0x7ff8, the partial check is reliable when packed + * duk_tval is used. The 0x7ff8 prefix means the normalized NaN will be a + * quiet NaN regardless of its remaining lower bits. + * + * The ME variant below is specifically for ARM byte order, which has the + * feature that while doubles have a mixed byte order (32107654), unsigned + * long long values has a little endian byte order (76543210). When writing + * a logical double value through a ULL pointer, the 32-bit words need to be + * swapped; hence the #if defined()s below for ULL writes with DUK_USE_DOUBLE_ME. + * This is not full ARM support but suffices for some environments. + */ + +#if defined(DUK_USE_64BIT_OPS) +#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; \ + } 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)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff80000ULL) +#define DUK__DBLUNION_IS_ANYINF(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x000000007ff00000ULL) +#define DUK__DBLUNION_IS_POSINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff00000ULL) +#define DUK__DBLUNION_IS_NEGINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x00000000fff00000ULL) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x0000000000000000ULL) +#define DUK__DBLUNION_IS_POSZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000080000000ULL) +#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; \ + } 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)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff8000000000000ULL) +#define DUK__DBLUNION_IS_ANYINF(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x7ff0000000000000ULL) +#define DUK__DBLUNION_IS_POSINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff0000000000000ULL) +#define DUK__DBLUNION_IS_NEGINF(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0xfff0000000000000ULL) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + (((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x0000000000000000ULL) +#define DUK__DBLUNION_IS_POSZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + ((u)->ull[DUK_DBL_IDX_ULL0] == 0x8000000000000000ULL) +#endif +#else /* DUK_USE_64BIT_OPS */ +/* Macros for no 64-bit ops, any endianness. */ +#define DUK__DBLUNION_SET_NAN_FULL(u) do { \ + (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) 0x7ff80000UL; \ + (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0x00000000UL; \ + } while (0) +#define DUK__DBLUNION_IS_NAN_FULL(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL) && \ + (((u)->ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) != 0 || \ + (u)->ui[DUK_DBL_IDX_UI1] != 0)) +#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff80000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_ANYINF(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x7ff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_POSINF(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_NEGINF(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_ANYZERO(u) \ + ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_POSZERO(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#define DUK__DBLUNION_IS_NEGZERO(u) \ + (((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && \ + ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) +#endif /* DUK_USE_64BIT_OPS */ + +#define DUK__DBLUNION_SET_NAN_NOTFULL(u) do { \ + (u)->us[DUK_DBL_IDX_US0] = 0x7ff8UL; \ + } while (0) + +#define DUK__DBLUNION_IS_NAN_NOTFULL(u) \ + /* E == 0x7ff, topmost four bits of F != 0 => assume NaN */ \ + ((((u)->us[DUK_DBL_IDX_US0] & 0x7ff0UL) == 0x7ff0UL) && \ + (((u)->us[DUK_DBL_IDX_US0] & 0x000fUL) != 0x0000UL)) + +#define DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL(u) \ + /* E == 0x7ff, F == 8 => normalized NaN */ \ + ((u)->us[DUK_DBL_IDX_US0] == 0x7ff8UL) + +#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL(u) do { \ + if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ + DUK__DBLUNION_SET_NAN_FULL((u)); \ + } \ + } while (0) + +#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL(u) do { \ + if (DUK__DBLUNION_IS_NAN_NOTFULL((u))) { \ + DUK__DBLUNION_SET_NAN_NOTFULL((u)); \ + } \ + } while (0) + +/* Concrete macros for NaN handling used by the implementation internals. + * Chosen so that they match the duk_tval representation: with a packed + * duk_tval, ensure NaNs are properly normalized; with a non-packed duk_tval + * these are essentially NOPs. + */ + +#if defined(DUK_USE_PACKED_TVAL) +#if defined(DUK_USE_FULL_TVAL) +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL((u)) +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_FULL((u)) +#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_FULL((d)) +#else +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL((u)) +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_NOTFULL((u)) +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL((u)) +#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_NOTFULL((d)) +#endif +#define DUK_DBLUNION_IS_NORMALIZED(u) \ + (!DUK_DBLUNION_IS_NAN((u)) || /* either not a NaN */ \ + DUK_DBLUNION_IS_NORMALIZED_NAN((u))) /* or is a normalized NaN */ +#else /* DUK_USE_PACKED_TVAL */ +#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) /* nop: no need to normalize */ +#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ +#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ +#define DUK_DBLUNION_IS_NORMALIZED(u) 1 /* all doubles are considered normalized */ +#define DUK_DBLUNION_SET_NAN(u) do { \ + /* in non-packed representation we don't care about which NaN is used */ \ + (u)->d = DUK_DOUBLE_NAN; \ + } while (0) +#endif /* DUK_USE_PACKED_TVAL */ + +#define DUK_DBLUNION_IS_ANYINF(u) DUK__DBLUNION_IS_ANYINF((u)) +#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u)) +#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u)) + +#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u)) +#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u)) +#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u)) + +/* XXX: native 64-bit byteswaps when available */ + +/* 64-bit byteswap, same operation independent of target endianness. */ +#define DUK_DBLUNION_BSWAP64(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp2; \ + (u)->ui[1] = duk__bswaptmp1; \ + } while (0) + +/* Byteswap an IEEE double in the duk_double_union from host to network + * order. For a big endian target this is a no-op. + */ +#if defined(DUK_USE_DOUBLE_LE) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp2; \ + (u)->ui[1] = duk__bswaptmp1; \ + } while (0) +#elif defined(DUK_USE_DOUBLE_ME) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ + duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ + duk__bswaptmp1 = (u)->ui[0]; \ + duk__bswaptmp2 = (u)->ui[1]; \ + duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ + duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ + (u)->ui[0] = duk__bswaptmp1; \ + (u)->ui[1] = duk__bswaptmp2; \ + } while (0) +#elif defined(DUK_USE_DOUBLE_BE) +#define DUK_DBLUNION_DOUBLE_HTON(u) do { } while (0) +#else +#error internal error, double endianness insane +#endif + +/* Reverse operation is the same. */ +#define DUK_DBLUNION_DOUBLE_NTOH(u) DUK_DBLUNION_DOUBLE_HTON((u)) + +/* 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_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) +#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] >> 31U)) +#endif + +#endif /* DUK_DBLUNION_H_INCLUDED */ /* #include duk_replacements.h */ #if !defined(DUK_REPLACEMENTS_H_INCLUDED) #define DUK_REPLACEMENTS_H_INCLUDED @@ -282,6 +706,8 @@ struct duk_hcompfunc; struct duk_hnatfunc; struct duk_hthread; struct duk_hbufobj; +struct duk_hdecenv; +struct duk_hobjenv; struct duk_hbuffer; struct duk_hbuffer_fixed; struct duk_hbuffer_dynamic; @@ -336,8 +762,10 @@ 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_hbufobj duk_hbufobj; 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_hbuffer duk_hbuffer; typedef struct duk_hbuffer_fixed duk_hbuffer_fixed; typedef struct duk_hbuffer_dynamic duk_hbuffer_dynamic; @@ -1310,233 +1738,224 @@ DUK_INTERNAL_DECL void duk_tval_set_number_chkfast_slow(duk_tval *tv, duk_double #define DUK_STRIDX_INT_PC2LINE 95 /* '\xffPc2line' */ #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_ARGS 96 /* '\xffArgs' */ +#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 97 /* '\xffMap' */ +#define DUK_STRIDX_INT_MAP 98 /* '\xffMap' */ #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 98 /* '\xffVarenv' */ +#define DUK_STRIDX_INT_VARENV 99 /* '\xffVarenv' */ #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 99 /* '\xffFinalizer' */ +#define DUK_STRIDX_INT_FINALIZER 100 /* '\xffFinalizer' */ #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_HANDLER 100 /* '\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_INT_CALLEE 101 /* '\xffCallee' */ -#define DUK_HEAP_STRING_INT_CALLEE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_CALLEE) -#define DUK_HTHREAD_STRING_INT_CALLEE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_CALLEE) -#define DUK_STRIDX_INT_THREAD 102 /* '\xffThread' */ -#define DUK_HEAP_STRING_INT_THREAD(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_THREAD) -#define DUK_HTHREAD_STRING_INT_THREAD(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_THREAD) -#define DUK_STRIDX_INT_REGBASE 103 /* '\xffRegbase' */ -#define DUK_HEAP_STRING_INT_REGBASE(heap) DUK_HEAP_GET_STRING((heap),DUK_STRIDX_INT_REGBASE) -#define DUK_HTHREAD_STRING_INT_REGBASE(thr) DUK_HTHREAD_GET_STRING((thr),DUK_STRIDX_INT_REGBASE) -#define DUK_STRIDX_INT_TARGET 104 /* '\xffTarget' */ +#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_THIS 105 /* '\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_COMPILE 106 /* 'compile' */ +#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_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 107 /* 'input' */ +#define DUK_STRIDX_INPUT 104 /* '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 108 /* 'errCreate' */ +#define DUK_STRIDX_ERR_CREATE 105 /* '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 109 /* 'errThrow' */ +#define DUK_STRIDX_ERR_THROW 106 /* '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 110 /* 'env' */ +#define DUK_STRIDX_ENV 107 /* '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 111 /* 'hex' */ +#define DUK_STRIDX_HEX 108 /* '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 112 /* 'base64' */ +#define DUK_STRIDX_BASE64 109 /* '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 113 /* 'jx' */ +#define DUK_STRIDX_JX 110 /* '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 114 /* 'jc' */ +#define DUK_STRIDX_JC 111 /* '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 115 /* 'resume' */ +#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 116 /* '{"_undef":true}' */ +#define DUK_STRIDX_JSON_EXT_UNDEFINED 113 /* '{"_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 117 /* '{"_nan":true}' */ +#define DUK_STRIDX_JSON_EXT_NAN 114 /* '{"_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 118 /* '{"_inf":true}' */ +#define DUK_STRIDX_JSON_EXT_POSINF 115 /* '{"_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 119 /* '{"_ninf":true}' */ +#define DUK_STRIDX_JSON_EXT_NEGINF 116 /* '{"_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 120 /* '{"_func":true}' */ +#define DUK_STRIDX_JSON_EXT_FUNCTION1 117 /* '{"_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 121 /* '{_func:true}' */ +#define DUK_STRIDX_JSON_EXT_FUNCTION2 118 /* '{_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 122 /* 'break' */ +#define DUK_STRIDX_BREAK 119 /* '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 123 /* 'case' */ +#define DUK_STRIDX_CASE 120 /* '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 124 /* 'catch' */ +#define DUK_STRIDX_CATCH 121 /* '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 125 /* 'continue' */ +#define DUK_STRIDX_CONTINUE 122 /* '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 126 /* 'debugger' */ +#define DUK_STRIDX_DEBUGGER 123 /* '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 127 /* 'default' */ +#define DUK_STRIDX_DEFAULT 124 /* '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 128 /* 'delete' */ +#define DUK_STRIDX_DELETE 125 /* '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 129 /* 'do' */ +#define DUK_STRIDX_DO 126 /* '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 130 /* 'else' */ +#define DUK_STRIDX_ELSE 127 /* '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 131 /* 'finally' */ +#define DUK_STRIDX_FINALLY 128 /* '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 132 /* 'for' */ +#define DUK_STRIDX_FOR 129 /* '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 133 /* 'function' */ +#define DUK_STRIDX_LC_FUNCTION 130 /* '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 134 /* 'if' */ +#define DUK_STRIDX_IF 131 /* '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 135 /* 'in' */ +#define DUK_STRIDX_IN 132 /* '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 136 /* 'instanceof' */ +#define DUK_STRIDX_INSTANCEOF 133 /* '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 137 /* 'new' */ +#define DUK_STRIDX_NEW 134 /* '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 138 /* 'return' */ +#define DUK_STRIDX_RETURN 135 /* '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 139 /* 'switch' */ +#define DUK_STRIDX_SWITCH 136 /* '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 140 /* 'this' */ +#define DUK_STRIDX_THIS 137 /* '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 141 /* 'throw' */ +#define DUK_STRIDX_THROW 138 /* '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 142 /* 'try' */ +#define DUK_STRIDX_TRY 139 /* '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 143 /* 'typeof' */ +#define DUK_STRIDX_TYPEOF 140 /* '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 144 /* 'var' */ +#define DUK_STRIDX_VAR 141 /* '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 145 /* 'const' */ +#define DUK_STRIDX_CONST 142 /* '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 146 /* 'void' */ +#define DUK_STRIDX_VOID 143 /* '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 147 /* 'while' */ +#define DUK_STRIDX_WHILE 144 /* '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 148 /* 'with' */ +#define DUK_STRIDX_WITH 145 /* '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 149 /* 'class' */ +#define DUK_STRIDX_CLASS 146 /* '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 150 /* 'enum' */ +#define DUK_STRIDX_ENUM 147 /* '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 151 /* 'export' */ +#define DUK_STRIDX_EXPORT 148 /* '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 152 /* 'extends' */ +#define DUK_STRIDX_EXTENDS 149 /* '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 153 /* 'import' */ +#define DUK_STRIDX_IMPORT 150 /* '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 154 /* 'super' */ +#define DUK_STRIDX_SUPER 151 /* '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 155 /* 'null' */ +#define DUK_STRIDX_LC_NULL 152 /* '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 156 /* 'true' */ +#define DUK_STRIDX_TRUE 153 /* '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 157 /* 'false' */ +#define DUK_STRIDX_FALSE 154 /* '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 158 /* 'implements' */ +#define DUK_STRIDX_IMPLEMENTS 155 /* '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 159 /* 'interface' */ +#define DUK_STRIDX_INTERFACE 156 /* '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 160 /* 'let' */ +#define DUK_STRIDX_LET 157 /* '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 161 /* 'package' */ +#define DUK_STRIDX_PACKAGE 158 /* '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 162 /* 'private' */ +#define DUK_STRIDX_PRIVATE 159 /* '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 163 /* 'protected' */ +#define DUK_STRIDX_PROTECTED 160 /* '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 164 /* 'public' */ +#define DUK_STRIDX_PUBLIC 161 /* '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 165 /* 'static' */ +#define DUK_STRIDX_STATIC 162 /* '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 166 /* 'yield' */ +#define DUK_STRIDX_YIELD 163 /* '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 167 -#define DUK_STRIDX_START_RESERVED 122 -#define DUK_STRIDX_START_STRICT_RESERVED 158 -#define DUK_STRIDX_END_RESERVED 167 /* exclusive endpoint */ +#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 */ /* 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[921]; +DUK_INTERNAL_DECL const duk_uint8_t duk_strings_data[903]; #endif /* !DUK_SINGLE_FILE */ #define DUK_STRDATA_MAX_STRLEN 17 -#define DUK_STRDATA_DATA_LENGTH 921 +#define DUK_STRDATA_DATA_LENGTH 903 #endif /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_ROM_OBJECTS) @@ -1628,6 +2047,8 @@ DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substring(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_trim(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_repeat(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx); +DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_includes(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_string_prototype_substr(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_boolean_prototype_tostring_shared(duk_context *ctx); DUK_INTERNAL_DECL duk_ret_t duk_bi_number_prototype_to_string(duk_context *ctx); @@ -1706,7 +2127,7 @@ 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); #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[164]; +DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[166]; #endif /* !DUK_SINGLE_FILE */ #define DUK_BIDX_GLOBAL 0 #define DUK_BIDX_GLOBAL_ENV 1 @@ -1787,19 +2208,19 @@ DUK_INTERNAL_DECL const duk_c_function duk_bi_native_functions[164]; #define DUK_NUM_ALL_BUILTINS 74 #if defined(DUK_USE_DOUBLE_LE) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3790]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3790 +#define DUK_BUILTINS_DATA_LENGTH 3819 #elif defined(DUK_USE_DOUBLE_BE) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3790]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3790 +#define DUK_BUILTINS_DATA_LENGTH 3819 #elif defined(DUK_USE_DOUBLE_ME) #if !defined(DUK_SINGLE_FILE) -DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3790]; +DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3819]; #endif /* !DUK_SINGLE_FILE */ -#define DUK_BUILTINS_DATA_LENGTH 3790 +#define DUK_BUILTINS_DATA_LENGTH 3819 #else #error invalid endianness defines #endif @@ -1814,10 +2235,6 @@ DUK_INTERNAL_DECL const duk_uint8_t duk_builtins_data[3790]; #if !defined(DUK_UTIL_H_INCLUDED) #define DUK_UTIL_H_INCLUDED -#define DUK_UTIL_MIN_HASH_PRIME 17 /* must match genhashsizes.py */ - -#define DUK_UTIL_GET_HASH_PROBE_STEP(hash) (duk_util_probe_steps[(hash) & 0x1f]) - #if defined(DUK_USE_GET_RANDOM_DOUBLE) #define DUK_UTIL_GET_RANDOM_DOUBLE(thr) DUK_USE_GET_RANDOM_DOUBLE((thr)->heap_udata) #else @@ -2303,7 +2720,7 @@ DUK_INTERNAL_DECL const duk_int8_t duk_base64_dectab[256]; #endif /* !DUK_SINGLE_FILE */ /* Note: assumes that duk_util_probe_steps size is 32 */ -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) +#if defined(DUK_USE_HOBJECT_HASH_PART) #if !defined(DUK_SINGLE_FILE) DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32]; #endif /* !DUK_SINGLE_FILE */ @@ -2313,13 +2730,10 @@ DUK_INTERNAL_DECL duk_uint8_t duk_util_probe_steps[32]; DUK_INTERNAL_DECL duk_uint32_t duk_util_hashbytes(const duk_uint8_t *data, duk_size_t len, duk_uint32_t seed); #endif -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) -DUK_INTERNAL_DECL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size); -#endif - DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode(duk_bitdecoder_ctx *ctx, duk_small_int_t bits); DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx); DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value); +DUK_INTERNAL_DECL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value); DUK_INTERNAL_DECL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx); DUK_INTERNAL_DECL duk_small_uint_t duk_bd_decode_bitpacked_string(duk_bitdecoder_ctx *bd, duk_uint8_t *out); @@ -2346,12 +2760,12 @@ DUK_INTERNAL_DECL duk_uint8_t *duk_bw_insert_ensure_area(duk_hthread *thr, duk_b DUK_INTERNAL_DECL void duk_bw_remove_raw_slice(duk_hthread *thr, duk_bufwriter_ctx *bw, duk_size_t off, duk_size_t len); /* No duk_bw_remove_ensure_slice(), functionality would be identical. */ -DUK_INTERNAL_DECL DUK_INLINE duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p); -DUK_INTERNAL_DECL DUK_INLINE duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p); -DUK_INTERNAL_DECL DUK_INLINE duk_double_t duk_raw_read_double_be(duk_uint8_t **p); -DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val); -DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val); -DUK_INTERNAL_DECL DUK_INLINE void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val); +DUK_INTERNAL_DECL duk_uint16_t duk_raw_read_u16_be(duk_uint8_t **p); +DUK_INTERNAL_DECL duk_uint32_t duk_raw_read_u32_be(duk_uint8_t **p); +DUK_INTERNAL_DECL duk_double_t duk_raw_read_double_be(duk_uint8_t **p); +DUK_INTERNAL_DECL void duk_raw_write_u16_be(duk_uint8_t **p, duk_uint16_t val); +DUK_INTERNAL_DECL void duk_raw_write_u32_be(duk_uint8_t **p, duk_uint32_t val); +DUK_INTERNAL_DECL void duk_raw_write_double_be(duk_uint8_t **p, duk_double_t val); #if defined(DUK_USE_DEBUGGER_SUPPORT) /* For now only needed by the debugger. */ DUK_INTERNAL_DECL void duk_byteswap_bytes(duk_uint8_t *p, duk_small_uint_t len); @@ -2512,6 +2926,7 @@ DUK_INTERNAL_DECL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y); #define DUK_STR_INVALID_REGEXP_ESCAPE "invalid regexp escape" #define DUK_STR_INVALID_BACKREFS "invalid backreference(s)" #define DUK_STR_INVALID_REGEXP_CHARACTER "invalid regexp character" +#define DUK_STR_INVALID_REGEXP_GROUP "invalid regexp group" #define DUK_STR_UNTERMINATED_CHARCLASS "unterminated character class" #define DUK_STR_INVALID_RANGE "invalid range" @@ -2997,8 +3412,7 @@ typedef duk_uint32_t duk_instr_t; #define DUK_BC_TRYCATCH_FLAG_WITH_BINDING (1 << 3) /* DUK_OP_DECLVAR flags in A; bottom bits are reserved for propdesc flags (DUK_PROPDESC_FLAG_XXX) */ -#define DUK_BC_DECLVAR_FLAG_UNDEF_VALUE (1 << 4) /* use 'undefined' for value automatically */ -#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1 << 5) /* function declaration */ +#define DUK_BC_DECLVAR_FLAG_FUNC_DECL (1 << 4) /* function declaration */ /* Misc constants and helper macros. */ #define DUK_BC_LDINT_BIAS (1L << 15) @@ -3041,7 +3455,7 @@ typedef void (*duk_re_range_callback)(void *user, duk_codepoint_t r1, duk_codepo #define DUK_LEXER_GETPOINT(ctx,pt) duk_lexer_getpoint((ctx), (pt)) -/* currently 6 characters of lookup are actually needed (duk_lexer.c) */ +/* Currently 6 characters of lookup are actually needed (duk_lexer.c). */ #define DUK_LEXER_WINDOW_SIZE 6 #if defined(DUK_USE_LEXER_SLIDING_WINDOW) #define DUK_LEXER_BUFFER_SIZE 64 @@ -3419,6 +3833,8 @@ struct duk_lexer_ctx { duk_int_t token_count; /* number of tokens parsed */ duk_int_t token_limit; /* maximum token count before error (sanity backstop) */ + + duk_small_uint_t flags; /* lexer flags, use compiler flag defines for now */ }; /* @@ -3666,10 +4082,6 @@ struct duk_compiler_ctx { * Prototypes */ -#define DUK_JS_COMPILE_FLAG_EVAL (1 << 0) /* source is eval code (not global) */ -#define DUK_JS_COMPILE_FLAG_STRICT (1 << 1) /* strict outer context */ -#define DUK_JS_COMPILE_FLAG_FUNCEXPR (1 << 2) /* source is a function expression (used for Function constructor) */ - DUK_INTERNAL_DECL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags); #endif /* DUK_JS_COMPILER_H_INCLUDED */ @@ -3772,30 +4184,45 @@ DUK_INTERNAL_DECL void duk_regexp_match_force_global(duk_hthread *thr); /* hack * * All heap objects share the same flags and refcount fields. Objects other * than strings also need to have a single or double linked list pointers - * for insertion into the "heap allocated" list. Strings are held in the - * heap-wide string table so they don't need link pointers. + * for insertion into the "heap allocated" list. Strings have single linked + * list pointers for string table chaining. * * Technically, 'h_refcount' must be wide enough to guarantee that it cannot - * wrap (otherwise objects might be freed incorrectly after wrapping). This - * means essentially that the refcount field must be as wide as data pointers. - * On 64-bit platforms this means that the refcount needs to be 64 bits even - * if an 'int' is 32 bits. This is a bit unfortunate, and compromising on - * this might be reasonable in the future. + * wrap; otherwise objects might be freed incorrectly after wrapping. The + * default refcount field is 32 bits even on 64-bit systems: while that's in + * theory incorrect, the Duktape heap needs to be larger than 64GB for the + * count to actually wrap (assuming 16-byte duk_tvals). This is very unlikely + * to ever be an issue, but if it is, disabling DUK_USE_REFCOUNT32 causes + * Duktape to use size_t for refcounts which should always be safe. * * Heap header size on 32-bit platforms: 8 bytes without reference counting, * 16 bytes with reference counting. + * + * Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not + * defined without DUK_USE_REFERENCE_COUNTING, so caller must #if defined() + * around them. */ +/* XXX: macro for shared header fields (avoids some padding issues) */ + struct duk_heaphdr { duk_uint32_t h_flags; #if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_ASSERTIONS) + /* When assertions enabled, used by mark-and-sweep for refcount + * validation. Largest reasonable type; also detects overflows. + */ + duk_size_t h_assert_refcount; +#endif #if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount16; + duk_uint16_t h_refcount; +#elif defined(DUK_USE_REFCOUNT32) + duk_uint32_t h_refcount; #else duk_size_t h_refcount; #endif -#endif +#endif /* DUK_USE_REFERENCE_COUNTING */ #if defined(DUK_USE_HEAPPTR16) duk_uint16_t h_next16; @@ -3835,15 +4262,26 @@ struct duk_heaphdr_string { duk_uint32_t h_flags; #if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_ASSERTIONS) + /* When assertions enabled, used by mark-and-sweep for refcount + * validation. Largest reasonable type; also detects overflows. + */ + duk_size_t h_assert_refcount; +#endif #if defined(DUK_USE_REFCOUNT16) - duk_uint16_t h_refcount16; + duk_uint16_t h_refcount; duk_uint16_t h_strextra16; /* round out to 8 bytes */ +#elif defined(DUK_USE_REFCOUNT32) + duk_uint32_t h_refcount; #else duk_size_t h_refcount; #endif #else duk_uint16_t h_strextra16; -#endif +#endif /* DUK_USE_REFERENCE_COUNTING */ + + duk_hstring *h_next; + /* No 'h_prev' pointer for strings. */ }; #define DUK_HEAPHDR_FLAGS_TYPE_MASK 0x00000003UL @@ -3899,21 +4337,13 @@ struct duk_heaphdr_string { #endif #if defined(DUK_USE_REFERENCE_COUNTING) -#if defined(DUK_USE_REFCOUNT16) -#define DUK_HEAPHDR_GET_REFCOUNT(h) ((h)->h_refcount16) -#define DUK_HEAPHDR_SET_REFCOUNT(h,val) do { \ - (h)->h_refcount16 = (val); \ - } while (0) -#define DUK_HEAPHDR_PREINC_REFCOUNT(h) (++(h)->h_refcount16) /* result: updated refcount */ -#define DUK_HEAPHDR_PREDEC_REFCOUNT(h) (--(h)->h_refcount16) /* result: updated refcount */ -#else #define DUK_HEAPHDR_GET_REFCOUNT(h) ((h)->h_refcount) #define DUK_HEAPHDR_SET_REFCOUNT(h,val) do { \ (h)->h_refcount = (val); \ + DUK_ASSERT((h)->h_refcount == (val)); /* No truncation. */ \ } while (0) #define DUK_HEAPHDR_PREINC_REFCOUNT(h) (++(h)->h_refcount) /* result: updated refcount */ #define DUK_HEAPHDR_PREDEC_REFCOUNT(h) (--(h)->h_refcount) /* result: updated refcount */ -#endif #else /* refcount macros not defined without refcounting, caller must #if defined() now */ #endif /* DUK_USE_REFERENCE_COUNTING */ @@ -4001,18 +4431,23 @@ struct duk_heaphdr_string { } while (0) #endif -#define DUK_HEAPHDR_STRING_INIT_NULLS(h) /* currently nop */ +#define DUK_HEAPHDR_STRING_INIT_NULLS(h) do { \ + (h)->h_next = NULL; \ + } while (0) /* * Type tests */ -#define DUK_HEAPHDR_IS_OBJECT(h) \ - (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_OBJECT) -#define DUK_HEAPHDR_IS_STRING(h) \ - (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_STRING) -#define DUK_HEAPHDR_IS_BUFFER(h) \ - (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_BUFFER) +/* Take advantage of the fact that for DUK_HTYPE_xxx numbers the lowest bit + * is only set for DUK_HTYPE_OBJECT (= 1). + */ +#if 0 +#define DUK_HEAPHDR_IS_OBJECT(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_OBJECT) +#endif +#define DUK_HEAPHDR_IS_OBJECT(h) ((h)->h_flags & 0x01UL) +#define DUK_HEAPHDR_IS_STRING(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_STRING) +#define DUK_HEAPHDR_IS_BUFFER(h) (DUK_HEAPHDR_GET_TYPE((h)) == DUK_HTYPE_BUFFER) /* * Assert helpers @@ -4035,17 +4470,23 @@ struct duk_heaphdr_string { #define DUK_ASSERT_HEAPHDR_LINKS(heap,h) do {} while (0) #endif +#define DUK_ASSERT_HEAPHDR_VALID(h) do { \ + DUK_ASSERT((h) != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID((h))); \ + } while (0) + +#endif /* DUK_HEAPHDR_H_INCLUDED */ +/* #include duk_refcount.h */ /* * Reference counting helper macros. The macros take a thread argument * and must thus always be executed in a specific thread context. The - * thread argument is needed for features like finalization. Currently - * it is not required for INCREF, but it is included just in case. - * - * Note that 'raw' macros such as DUK_HEAPHDR_GET_REFCOUNT() are not - * defined without DUK_USE_REFERENCE_COUNTING, so caller must #if defined() - * around them. + * thread argument is not really needed anymore: DECREF can operate with + * a heap pointer only, and INCREF needs neither. */ +#if !defined(DUK_REFCOUNT_H_INCLUDED) +#define DUK_REFCOUNT_H_INCLUDED + #if defined(DUK_USE_REFERENCE_COUNTING) #if defined(DUK_USE_ROM_OBJECTS) @@ -4075,6 +4516,7 @@ struct duk_heaphdr_string { DUK_ASSERT(duk__h != NULL); \ DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ } \ } while (0) #define DUK_TVAL_DECREF_FAST(thr,tv) do { \ @@ -4109,6 +4551,7 @@ struct duk_heaphdr_string { DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ if (DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE(duk__h)) { \ DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) != 0); /* No wrapping. */ \ } \ } while (0) #define DUK_HEAPHDR_DECREF_FAST_RAW(thr,h,rzcall,rzcast) do { \ @@ -4275,17 +4718,22 @@ struct duk_heaphdr_string { } \ } while (0) -/* Free pending refzero entries; quick check to avoid call because often - * the queue is empty. +/* Called after one or more DECREF NORZ calls to handle pending side effects. + * At present DECREF NORZ does freeing inline but doesn't execute finalizers, + * so these macros check for pending finalizers and execute them. The FAST + * variant is performance critical. */ +#if defined(DUK_USE_FINALIZER_SUPPORT) #define DUK_REFZERO_CHECK_FAST(thr) do { \ - if ((thr)->heap->refzero_list != NULL) { \ - duk_refzero_free_pending((thr)); \ - } \ + duk_refzero_check_fast((thr)); \ } while (0) #define DUK_REFZERO_CHECK_SLOW(thr) do { \ - duk_refzero_free_pending((thr)); \ + duk_refzero_check_slow((thr)); \ } while (0) +#else /* DUK_USE_FINALIZER_SUPPORT */ +#define DUK_REFZERO_CHECK_FAST(thr) do { } while (0) +#define DUK_REFZERO_CHECK_SLOW(thr) do { } while (0) +#endif /* DUK_USE_FINALIZER_SUPPORT */ /* * Macros to set a duk_tval and update refcount of the target (decref the @@ -4692,7 +5140,41 @@ struct duk_heaphdr_string { #endif /* DUK_USE_REFERENCE_COUNTING */ -#endif /* DUK_HEAPHDR_H_INCLUDED */ +#if defined(DUK_USE_REFERENCE_COUNTING) +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL_DECL void duk_refzero_check_slow(duk_hthread *thr); +DUK_INTERNAL_DECL void duk_refzero_check_fast(duk_hthread *thr); +#endif +DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr); +DUK_INTERNAL_DECL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h); +#if 0 /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */ +DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h); +DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h); +DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h); +DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h); +DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h); +DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h); +#endif +DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h); +#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h); /* no 'norz' variant */ +DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h); /* no 'norz' variant */ +DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h); +DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h); +#else +DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); +DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv); +DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h); +#endif +#else /* DUK_USE_REFERENCE_COUNTING */ +/* no refcounting */ +#endif /* DUK_USE_REFERENCE_COUNTING */ + +#endif /* DUK_REFCOUNT_H_INCLUDED */ /* #include duk_api_internal.h */ /* * Internal API calls which have (stack and other) semantics similar @@ -4797,7 +5279,7 @@ 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, duk_bool_t throw_flag, duk_bool_t *out_found); +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); @@ -4977,6 +5459,8 @@ DUK_INTERNAL_DECL void duk_resolve_nonbound_function(duk_context *ctx); DUK_INTERNAL_DECL duk_idx_t duk_get_top_require_min(duk_context *ctx, duk_idx_t min_top); DUK_INTERNAL_DECL duk_idx_t duk_get_top_index_unsafe(duk_context *ctx); +DUK_INTERNAL_DECL void duk_pop_n_unsafe(duk_context *ctx, duk_idx_t count); +DUK_INTERNAL_DECL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count); DUK_INTERNAL_DECL void duk_pop_unsafe(duk_context *ctx); DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); @@ -5086,7 +5570,7 @@ DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); */ #define DUK_HSTRING_IS_ASCII(x) (DUK_HSTRING_GET_BYTELEN((x)) == DUK_HSTRING_GET_CHARLEN((x))) #endif -#define DUK_HSTRING_IS_ASCII(x) DUK_HSTRING_HAS_ASCII((x)) +#define DUK_HSTRING_IS_ASCII(x) DUK_HSTRING_HAS_ASCII((x)) /* lazily set! */ #define DUK_HSTRING_IS_EMPTY(x) (DUK_HSTRING_GET_BYTELEN((x)) == 0) #if defined(DUK_USE_STRHASH16) @@ -5107,7 +5591,7 @@ DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); (x)->hdr.h_strextra16 = (v); \ } while (0) #if defined(DUK_USE_HSTRING_CLEN) -#define DUK_HSTRING_GET_CHARLEN(x) ((x)->clen16) +#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) #define DUK_HSTRING_SET_CHARLEN(x,v) do { \ (x)->clen16 = (v); \ } while (0) @@ -5122,7 +5606,7 @@ DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); #define DUK_HSTRING_SET_BYTELEN(x,v) do { \ (x)->blen = (v); \ } while (0) -#define DUK_HSTRING_GET_CHARLEN(x) ((x)->clen) +#define DUK_HSTRING_GET_CHARLEN(x) duk_hstring_get_charlen((x)) #define DUK_HSTRING_SET_CHARLEN(x,v) do { \ (x)->clen = (v); \ } while (0) @@ -5153,11 +5637,11 @@ DUK_INTERNAL_DECL void duk_compact_m1(duk_context *ctx); * avoids helper call if string has no array index value. */ #define DUK_HSTRING_GET_ARRIDX_FAST(h) \ - (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_string_helper((h)) : DUK_HSTRING_NO_ARRAY_INDEX) + (DUK_HSTRING_HAS_ARRIDX((h)) ? duk_js_to_arrayindex_hstring_fast_known((h)) : DUK_HSTRING_NO_ARRAY_INDEX) /* Slower but more compact variant. */ #define DUK_HSTRING_GET_ARRIDX_SLOW(h) \ - (duk_js_to_arrayindex_string_helper((h))) + (duk_js_to_arrayindex_hstring_fast((h))) #endif /* @@ -5172,30 +5656,26 @@ struct duk_hstring { */ duk_heaphdr_string hdr; - /* Note: we could try to stuff a partial hash (e.g. 16 bits) into the - * shared heap header. Good hashing needs more hash bits though. - */ - - /* string hash */ + /* String hash. */ #if defined(DUK_USE_STRHASH16) /* If 16-bit hash is in use, stuff it into duk_heaphdr_string flags. */ #else duk_uint32_t hash; #endif - /* precomputed array index (or DUK_HSTRING_NO_ARRAY_INDEX) */ + /* Precomputed array index (or DUK_HSTRING_NO_ARRAY_INDEX). */ #if defined(DUK_USE_HSTRING_ARRIDX) duk_uarridx_t arridx; #endif - /* length in bytes (not counting NUL term) */ + /* Length in bytes (not counting NUL term). */ #if defined(DUK_USE_STRLEN16) /* placed in duk_heaphdr_string */ #else duk_uint32_t blen; #endif - /* length in codepoints (must be E5 compatible) */ + /* Length in codepoints (must be E5 compatible). */ #if defined(DUK_USE_STRLEN16) #if defined(DUK_USE_HSTRING_CLEN) duk_uint16_t clen16; @@ -5207,7 +5687,7 @@ struct duk_hstring { #endif /* - * String value of 'blen+1' bytes follows (+1 for NUL termination + * String data of 'blen+1' bytes follows (+1 for NUL termination * convenience for C API). No alignment needs to be guaranteed * for strings, but fields above should guarantee alignment-by-4 * (but not alignment-by-8). @@ -5232,10 +5712,7 @@ 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); - -#if !defined(DUK_USE_HSTRING_CLEN) DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); -#endif #endif /* DUK_HSTRING_H_INCLUDED */ /* #include duk_hobject.h */ @@ -5273,8 +5750,8 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #if !defined(DUK_HOBJECT_H_INCLUDED) #define DUK_HOBJECT_H_INCLUDED -/* Object flag. There are currently 25 flag bits available. Make sure - * this stays in sync with debugger object inspection code. +/* Object flags. Make sure this stays in sync with debugger object + * inspection code. */ /* XXX: some flags are object subtype specific (e.g. common to all function @@ -5286,14 +5763,14 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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) */ -#define DUK_HOBJECT_FLAG_THREAD DUK_HEAPHDR_USER_FLAG(7) /* object is a thread (duk_hthread) */ +#define DUK_HOBJECT_FLAG_FASTREFS DUK_HEAPHDR_USER_FLAG(7) /* object has no fields needing DECREF/marking beyond base duk_hobject header */ #define DUK_HOBJECT_FLAG_ARRAY_PART DUK_HEAPHDR_USER_FLAG(8) /* object has an array part (a_size may still be 0) */ #define DUK_HOBJECT_FLAG_STRICT DUK_HEAPHDR_USER_FLAG(9) /* function: function object is strict */ #define DUK_HOBJECT_FLAG_NOTAIL DUK_HEAPHDR_USER_FLAG(10) /* function: function must not be tail called */ #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_ENVRECCLOSED DUK_HEAPHDR_USER_FLAG(14) /* envrec: (declarative) record is closed */ +#define DUK_HOBJECT_FLAG_HAVE_FINALIZER DUK_HEAPHDR_USER_FLAG(14) /* object has a callable 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) */ @@ -5373,7 +5850,6 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #define DUK_HOBJECT_CMASK_OBJENV (1UL << DUK_HOBJECT_CLASS_OBJENV) #define DUK_HOBJECT_CMASK_DECENV (1UL << DUK_HOBJECT_CLASS_DECENV) #define DUK_HOBJECT_CMASK_POINTER (1UL << DUK_HOBJECT_CLASS_POINTER) -#define DUK_HOBJECT_CMASK_THREAD (1UL << DUK_HOBJECT_CLASS_THREAD) #define DUK_HOBJECT_CMASK_ARRAYBUFFER (1UL << DUK_HOBJECT_CLASS_ARRAYBUFFER) #define DUK_HOBJECT_CMASK_DATAVIEW (1UL << DUK_HOBJECT_CLASS_DATAVIEW) #define DUK_HOBJECT_CMASK_INT8ARRAY (1UL << DUK_HOBJECT_CLASS_INT8ARRAY) @@ -5407,7 +5883,7 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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) #define DUK_HOBJECT_IS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#define DUK_HOBJECT_IS_THREAD(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#define DUK_HOBJECT_IS_THREAD(h) (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_THREAD) #define DUK_HOBJECT_IS_NONBOUND_FUNCTION(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, \ DUK_HOBJECT_FLAG_COMPFUNC | \ @@ -5445,14 +5921,14 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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) #define DUK_HOBJECT_HAS_BUFOBJ(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#define DUK_HOBJECT_HAS_THREAD(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#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) #define DUK_HOBJECT_HAS_NOTAIL(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_HAS_NEWENV(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_HAS_NAMEBINDING(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_HAS_CREATEARGS(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_HAS_ENVRECCLOSED(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_HAS_HAVE_FINALIZER(h) DUK_HEAPHDR_CHECK_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #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) @@ -5465,14 +5941,14 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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) #define DUK_HOBJECT_SET_BUFOBJ(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#define DUK_HOBJECT_SET_THREAD(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#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) #define DUK_HOBJECT_SET_NOTAIL(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_SET_NEWENV(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_SET_NAMEBINDING(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_SET_CREATEARGS(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_SET_ENVRECCLOSED(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_SET_HAVE_FINALIZER(h) DUK_HEAPHDR_SET_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #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) @@ -5485,20 +5961,28 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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) #define DUK_HOBJECT_CLEAR_BUFOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_BUFOBJ) -#define DUK_HOBJECT_CLEAR_THREAD(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_THREAD) +#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) #define DUK_HOBJECT_CLEAR_NOTAIL(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NOTAIL) #define DUK_HOBJECT_CLEAR_NEWENV(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NEWENV) #define DUK_HOBJECT_CLEAR_NAMEBINDING(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_NAMEBINDING) #define DUK_HOBJECT_CLEAR_CREATEARGS(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_CREATEARGS) -#define DUK_HOBJECT_CLEAR_ENVRECCLOSED(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_ENVRECCLOSED) +#define DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_HAVE_FINALIZER) #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) #define DUK_HOBJECT_CLEAR_EXOTIC_PROXYOBJ(h) DUK_HEAPHDR_CLEAR_FLAG_BITS(&(h)->hdr, DUK_HOBJECT_FLAG_EXOTIC_PROXYOBJ) +/* Object can/cannot use FASTREFS, i.e. has no strong reference fields beyond + * duk_hobject base header. + */ +#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))) +#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. */ @@ -5879,6 +6363,16 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); /* note: this updates refcounts */ #define DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr,h,p) duk_hobject_set_prototype_updref((thr), (h), (p)) +/* + * Finalizer check + */ + +#if defined(DUK_USE_HEAPPTR16) +#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((heap), (h)) +#else +#define DUK_HOBJECT_HAS_FINALIZER_FAST(heap,h) duk_hobject_has_finalizer_fast_raw((h)) +#endif + /* * Resizing and hash behavior */ @@ -5892,22 +6386,9 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #if defined(DUK_USE_OBJSIZES16) #define DUK_HOBJECT_MAX_PROPERTIES 0x0000ffffUL #else -#define DUK_HOBJECT_MAX_PROPERTIES 0x7fffffffUL /* 2**31-1 ~= 2G properties */ +#define DUK_HOBJECT_MAX_PROPERTIES 0x3fffffffUL /* 2**30-1 ~= 1G properties */ #endif -/* higher value conserves memory; also note that linear scan is cache friendly */ -#define DUK_HOBJECT_E_USE_HASH_LIMIT 32 - -/* hash size relative to entries size: for value X, approx. hash_prime(e_size + e_size / X) */ -#define DUK_HOBJECT_H_SIZE_DIVISOR 4 /* hash size approx. 1.25 times entries size */ - -/* if new_size < L * old_size, resize without abandon check; L = 3-bit fixed point, e.g. 9 -> 9/8 = 112.5% */ -#define DUK_HOBJECT_A_FAST_RESIZE_LIMIT 9 /* 112.5%, i.e. new size less than 12.5% higher -> fast resize */ - -/* if density < L, abandon array part, L = 3-bit fixed point, e.g. 2 -> 2/8 = 25% */ -/* limit is quite low: one array entry is 8 bytes, one normal entry is 4+1+8+4 = 17 bytes (with hash entry) */ -#define DUK_HOBJECT_A_ABANDON_LIMIT 2 /* 25%, i.e. less than 25% used -> abandon */ - /* internal align target for props allocation, must be 2*n for some n */ #if (DUK_USE_ALIGN_BY == 4) #define DUK_HOBJECT_ALIGN_TARGET 4 @@ -5919,18 +6400,6 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #error invalid DUK_USE_ALIGN_BY #endif -/* controls for minimum entry part growth */ -#define DUK_HOBJECT_E_MIN_GROW_ADD 16 -#define DUK_HOBJECT_E_MIN_GROW_DIVISOR 8 /* 2^3 -> 1/8 = 12.5% min growth */ - -/* controls for minimum array part growth */ -#define DUK_HOBJECT_A_MIN_GROW_ADD 16 -#define DUK_HOBJECT_A_MIN_GROW_DIVISOR 8 /* 2^3 -> 1/8 = 12.5% min growth */ - -/* probe sequence */ -#define DUK_HOBJECT_HASH_INITIAL(hash,h_size) ((hash) % (h_size)) -#define DUK_HOBJECT_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash)) - /* * PC-to-line constants */ @@ -5960,7 +6429,7 @@ union duk_propvalue { struct duk_propdesc { /* read-only values 'lifted' for ease of use */ - duk_small_int_t flags; + duk_small_uint_t flags; duk_hobject *get; duk_hobject *set; @@ -6084,17 +6553,18 @@ DUK_INTERNAL_DECL duk_uint8_t duk_class_number_to_stridx[32]; */ /* alloc and init */ -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobject_flags); -#if 0 /* unused */ -DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, duk_uint_t hobject_flags); -#endif -DUK_INTERNAL_DECL duk_harray *duk_harray_alloc(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hcompfunc *duk_hcompfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags); -DUK_INTERNAL_DECL duk_hnatfunc *duk_hnatfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags); +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); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags); #endif -DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_flags); +DUK_INTERNAL_DECL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags); +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); /* resize */ DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr, @@ -6131,6 +6601,11 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_hasprop_raw(duk_hthread *thr, duk_hobje DUK_INTERNAL_DECL void duk_hobject_define_property_internal(duk_hthread *thr, duk_hobject *obj, duk_hstring *key, duk_small_uint_t flags); DUK_INTERNAL_DECL void duk_hobject_define_property_internal_arridx(duk_hthread *thr, duk_hobject *obj, duk_uarridx_t arr_idx, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *obj); +#if defined(DUK_USE_HEAPPTR16) +DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj); +#else +DUK_INTERNAL_DECL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj); +#endif /* helpers for defineProperty() and defineProperties() */ DUK_INTERNAL_DECL @@ -6177,11 +6652,6 @@ DUK_INTERNAL_DECL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_b /* macros */ DUK_INTERNAL_DECL void duk_hobject_set_prototype_updref(duk_hthread *thr, duk_hobject *h, duk_hobject *p); -/* finalization */ -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_INTERNAL_DECL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj); -#endif - /* 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); @@ -6777,8 +7247,6 @@ DUK_INTERNAL_DECL void duk_hbufobj_promote_plain(duk_context *ctx, duk_idx_t idx #endif #endif /* DUK_USE_ROM_STRINGS */ -#define DUK_HTHREAD_GET_CURRENT_ACTIVATION(thr) (&(thr)->callstack[(thr)->callstack_top - 1]) - /* values for the state field */ #define DUK_HTHREAD_STATE_INACTIVE 1 /* thread not currently running */ #define DUK_HTHREAD_STATE_RUNNING 2 /* thread currently running (only one at a time) */ @@ -6960,8 +7428,9 @@ struct duk_hthread { /* Call stack. [0,callstack_top[ is GC reachable. */ duk_activation *callstack; + 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 */ + duk_size_t callstack_top; /* next to use, highest used is top - 1 (or none if top == 0) */ duk_size_t callstack_preventcount; /* number of activation records in callstack preventing a yield */ /* Catch stack. [0,catchstack_top[ is GC reachable. */ @@ -7023,12 +7492,19 @@ 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_get_current_activation(duk_hthread *thr); +#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 */ @@ -7089,6 +7565,55 @@ struct duk_harray { }; #endif /* DUK_HARRAY_H_INCLUDED */ +/* #include duk_henv.h */ +/* + * Environment object representation. + */ + +#if !defined(DUK_HENV_H_INCLUDED) +#define DUK_HENV_H_INCLUDED + +#define DUK_ASSERT_HDECENV_VALID(h) do { \ + DUK_ASSERT((h) != NULL); \ + DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) (h))); \ + DUK_ASSERT((h)->thread == NULL || (h)->varmap != NULL); \ + } while (0) + +#define DUK_ASSERT_HOBJENV_VALID(h) do { \ + DUK_ASSERT((h) != NULL); \ + DUK_ASSERT(DUK_HOBJECT_IS_OBJENV((duk_hobject *) (h))); \ + DUK_ASSERT((h)->target != NULL); \ + DUK_ASSERT((h)->has_this == 0 || (h)->has_this == 1); \ + } while (0) + +struct duk_hdecenv { + /* Shared object part. */ + duk_hobject obj; + + /* These control variables provide enough information to access live + * variables for a closure that is still open. If thread == NULL, + * the record is closed and the identifiers are in the property table. + */ + duk_hthread *thread; + duk_hobject *varmap; + duk_size_t regbase; +}; + +struct duk_hobjenv { + /* Shared object part. */ + duk_hobject obj; + + /* Target object and 'this' binding for object binding. */ + duk_hobject *target; + + /* The 'target' object is used as a this binding in only some object + * environments. For example, the global environment does not provide + * a this binding, but a with statement does. + */ + duk_bool_t has_this; +}; + +#endif /* DUK_HENV_H_INCLUDED */ /* #include duk_hbuffer.h */ /* * Heap buffer representation. @@ -7435,13 +7960,11 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * Heap flags */ -#define DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING (1 << 0) /* mark-and-sweep is currently running */ -#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED (1 << 1) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */ -#define DUK_HEAP_FLAG_REFZERO_FREE_RUNNING (1 << 2) /* refcount code is processing refzero list */ -#define DUK_HEAP_FLAG_ERRHANDLER_RUNNING (1 << 3) /* an error handler (user callback to augment/replace error) is running */ -#define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1 << 4) /* executor interrupt running (used to avoid nested interrupts) */ -#define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1 << 5) /* heap destruction ongoing, finalizer rescue no longer possible */ -#define DUK_HEAP_FLAG_DEBUGGER_PAUSED (1 << 6) /* debugger is paused: talk with debug client until step/resume */ +#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_HAS_FLAGS(heap,bits) ((heap)->flags & (bits)) #define DUK__HEAP_SET_FLAGS(heap,bits) do { \ @@ -7451,25 +7974,19 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * (heap)->flags &= ~(bits); \ } while (0) -#define DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) #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_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) #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_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING) #define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) -#define DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING) #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) @@ -7496,11 +8013,25 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * * field and the GC caller can impose further flags. */ -#define DUK_MS_FLAG_EMERGENCY (1 << 0) /* emergency mode: try extra hard */ -#define DUK_MS_FLAG_NO_STRINGTABLE_RESIZE (1 << 1) /* don't resize stringtable (but may sweep it); needed during stringtable resize */ -#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1 << 2) /* don't compact objects; needed during object property allocation resize */ -#define DUK_MS_FLAG_NO_FINALIZERS (1 << 3) /* don't run finalizers; leave finalizable objects in finalize_list for next round */ -#define DUK_MS_FLAG_SKIP_FINALIZERS (1 << 4) /* don't run finalizers; queue finalizable objects back to heap_allocated */ +/* Emergency mark-and-sweep: try extra hard, even at the cost of + * performance. + */ +#define DUK_MS_FLAG_EMERGENCY (1 << 0) + +/* Voluntary mark-and-sweep: triggered periodically. */ +#define DUK_MS_FLAG_VOLUNTARY (1 << 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) + +/* 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) /* * Thread switching @@ -7542,39 +8073,28 @@ DUK_INTERNAL_DECL void duk_hbuffer_reset(duk_hthread *thr, duk_hbuffer_dynamic * #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L #endif +/* GC torture. */ +#if defined(DUK_USE_GC_TORTURE) +#define DUK_GC_TORTURE(heap) do { duk_heap_mark_and_sweep((heap), 0); } while (0) +#else +#define DUK_GC_TORTURE(heap) do { } while (0) +#endif + /* Stringcache is used for speeding up char-offset-to-byte-offset * translations for non-ASCII strings. */ #define DUK_HEAP_STRCACHE_SIZE 4 #define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT 16 /* strings up to the this length are not cached */ -/* helper to insert a (non-string) heap object into heap allocated list */ -#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap),(hdr)) - -/* - * Stringtable - */ - -/* initial stringtable size, must be prime and higher than DUK_UTIL_MIN_HASH_PRIME */ -#define DUK_STRTAB_INITIAL_SIZE 17 - -/* indicates a deleted string; any fixed non-NULL, non-hstring pointer works */ -#define DUK_STRTAB_DELETED_MARKER(heap) ((duk_hstring *) heap) - -/* resizing parameters */ -#define DUK_STRTAB_MIN_FREE_DIVISOR 4 /* load factor max 75% */ -#define DUK_STRTAB_MIN_USED_DIVISOR 4 /* load factor min 25% */ -#define DUK_STRTAB_GROW_ST_SIZE(n) ((n) + (n)) /* used entries + approx 100% -> reset load to 50% */ - -#define DUK_STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */ -#define DUK_STRTAB_HIGHEST_32BIT_PRIME 0xfffffffbUL - -/* probe sequence (open addressing) */ -#define DUK_STRTAB_HASH_INITIAL(hash,h_size) ((hash) % (h_size)) -#define DUK_STRTAB_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash)) - -/* fixed top level hashtable size (separate chaining) */ -#define DUK_STRTAB_CHAIN_SIZE DUK_USE_STRTAB_CHAIN_SIZE +/* Some list management macros. */ +#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap), (hdr)) +#if defined(DUK_USE_REFERENCE_COUNTING) +#define DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap,hdr) duk_heap_remove_from_heap_allocated((heap), (hdr)) +#endif +#if defined(DUK_USE_FINALIZER_SUPPORT) +#define DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap,hdr) duk_heap_insert_into_finalize_list((heap), (hdr)) +#define DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap,hdr) duk_heap_remove_from_finalize_list((heap), (hdr)) +#endif /* * Built-in strings @@ -7644,11 +8164,18 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); #define DUK_REALLOC_INDIRECT(heap,cb,ud,newsize) duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize)) #define DUK_FREE(heap,ptr) duk_heap_mem_free((heap), (ptr)) +/* + * Checked allocation, relative to a thread + */ + +#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)) + /* * Memory constants */ -#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 5 /* Retry allocation after mark-and-sweep for this +#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 10 /* Retry allocation after mark-and-sweep for this * many times. A single mark-and-sweep round is * not guaranteed to free all unreferenced memory * because of finalization (in fact, ANY number of @@ -7689,27 +8216,6 @@ struct duk_breakpoint { duk_uint32_t line; }; -#if defined(DUK_USE_DEBUGGER_SUPPORT) -#define DUK_HEAP_IS_DEBUGGER_ATTACHED(heap) ((heap)->dbg_read_cb != NULL) -#define DUK_HEAP_CLEAR_STEP_STATE(heap) do { \ - (heap)->dbg_step_type = DUK_STEP_TYPE_NONE; \ - (heap)->dbg_step_thread = NULL; \ - (heap)->dbg_step_csindex = 0; \ - (heap)->dbg_step_startline = 0; \ - } while (0) -#define DUK_HEAP_SET_PAUSED(heap) do { \ - DUK_HEAP_SET_DEBUGGER_PAUSED(heap); \ - (heap)->dbg_state_dirty = 1; \ - DUK_HEAP_CLEAR_STEP_STATE((heap)); \ - } while (0) -#define DUK_HEAP_CLEAR_PAUSED(heap) do { \ - DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap); \ - (heap)->dbg_state_dirty = 1; \ - DUK_HEAP_CLEAR_STEP_STATE((heap)); \ - } while (0) -#define DUK_HEAP_IS_PAUSED(heap) (DUK_HEAP_HAS_DEBUGGER_PAUSED((heap))) -#endif /* DUK_USE_DEBUGGER_SUPPORT */ - /* * String cache should ideally be at duk_hthread level, but that would * cause string finalization to slow down relative to the number of @@ -7738,28 +8244,17 @@ struct duk_ljstate { duk_tval value2; /* 2nd related value (type specific) */ }; -/* - * Stringtable entry for fixed size stringtable - */ - -struct duk_strtab_entry { -#if defined(DUK_USE_HEAPPTR16) - /* A 16-bit listlen makes sense with 16-bit heap pointers: there - * won't be space for 64k strings anyway. - */ - duk_uint16_t listlen; /* if 0, 'str16' used, if > 0, 'strlist16' used */ - union { - duk_uint16_t strlist16; - duk_uint16_t str16; - } u; -#else - duk_size_t listlen; /* if 0, 'str' used, if > 0, 'strlist' used */ - union { - duk_hstring **strlist; - duk_hstring *str; - } u; -#endif -}; +#define DUK_ASSERT_LJSTATE_UNSET(heap) do { \ + DUK_ASSERT(heap != NULL); \ + DUK_ASSERT(heap->lj.type == DUK_LJ_TYPE_UNKNOWN); \ + DUK_ASSERT(heap->lj.iserror == 0); \ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value1)); \ + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value2)); \ + } while (0) +#define DUK_ASSERT_LJSTATE_SET(heap) do { \ + DUK_ASSERT(heap != NULL); \ + DUK_ASSERT(heap->lj.type != DUK_LJ_TYPE_UNKNOWN); \ + } while (0) /* * Main heap structure @@ -7778,12 +8273,6 @@ struct duk_heap { */ void *heap_udata; - /* Precomputed pointers when using 16-bit heap pointer packing. */ -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t heapptr_null16; - duk_uint16_t heapptr_deleted16; -#endif - /* Fatal error handling, called e.g. when a longjmp() is needed but * lj.jmpbuf_ptr is NULL. fatal_func must never return; it's not * declared as "noreturn" because doing that for typedefs is a bit @@ -7791,53 +8280,114 @@ struct duk_heap { */ duk_fatal_function fatal_func; - /* allocated heap objects */ + /* Main list of allocated heap objects. Objects are either here, + * in finalize_list waiting for processing, or in refzero_list + * temporarily while a DECREF refzero cascade finishes. + */ duk_heaphdr *heap_allocated; - /* work list for objects whose refcounts are zero but which have not been - * "finalized"; avoids recursive C calls when refcounts go to zero in a - * chain of objects. + /* Temporary work list for freeing a cascade of objects when a DECREF + * (or DECREF_NORZ) encounters a zero refcount. Using a work list + * allows fixed C stack size when refcounts go to zero for a chain of + * objects. Outside of DECREF this is always a NULL because DECREF is + * processed without side effects (only memory free calls). */ #if defined(DUK_USE_REFERENCE_COUNTING) duk_heaphdr *refzero_list; - duk_heaphdr *refzero_list_tail; #endif - /* mark-and-sweep control */ +#if defined(DUK_USE_FINALIZER_SUPPORT) + /* Work list for objects to be finalized. */ + duk_heaphdr *finalize_list; +#if defined(DUK_USE_ASSERTIONS) + /* Object whose finalizer is executing right now (no nesting). */ + duk_heaphdr *currently_finalizing; +#endif +#endif + + /* Voluntary mark-and-sweep trigger counter. Intentionally signed + * because we continue decreasing the value when voluntary GC cannot + * run. + */ #if defined(DUK_USE_VOLUNTARY_GC) - duk_int_t mark_and_sweep_trigger_counter; + duk_int_t ms_trigger_counter; #endif - duk_int_t mark_and_sweep_recursion_depth; - /* mark-and-sweep flags automatically active (used for critical sections) */ - duk_small_uint_t mark_and_sweep_base_flags; + /* Mark-and-sweep recursion control: too deep recursion causes + * multi-pass processing to avoid growing C stack without bound. + */ + duk_uint_t ms_recursion_depth; - /* work list for objects to be finalized (by mark-and-sweep) */ - duk_heaphdr *finalize_list; + /* Mark-and-sweep flags automatically active (used for critical sections). */ + duk_small_uint_t ms_base_flags; - /* longjmp state */ - duk_ljstate lj; + /* Mark-and-sweep running flag. Prevents re-entry, and also causes + * refzero events to be ignored (= objects won't be queued to refzero_list). + */ + duk_uint_t ms_running; + + /* Mark-and-sweep prevent count, stacking. Used to avoid M&S side + * effects (besides finalizers which are controlled separately) such + * as compacting the string table or object property tables. This + * is also bumped when ms_running is set to prevent recursive re-entry. + * Can also be bumped when mark-and-sweep is not running. + */ + duk_uint_t ms_prevent_count; + + /* Finalizer processing prevent count, stacking. Bumped when finalizers + * are processed to prevent recursive finalizer processing (first call site + * processing finalizers handles all finalizers until the list is empty). + * Can also be bumped explicitly to prevent finalizer execution. + */ + duk_uint_t pf_prevent_count; - /* marker for detecting internal "double faults", see duk_error_throw.c */ - duk_bool_t handling_error; + /* When processing finalize_list, don't actually run finalizers but + * queue finalizable objects back to heap_allocated as is. This is + * used during heap destruction to deal with finalizers that keep + * on creating more finalizable garbage. + */ + duk_uint_t pf_skip_finalizers; - /* heap thread, used internally and for finalization */ +#if defined(DUK_USE_ASSERTIONS) + /* Set when we're in a critical path where an error throw would cause + * e.g. sandboxing/protected call violations or state corruption. This + * is just used for asserts. + */ + duk_bool_t error_not_allowed; +#endif + +#if defined(DUK_USE_ASSERTIONS) + /* Set when heap is still being initialized, helps with writing + * some assertions. + */ + duk_bool_t heap_initializing; +#endif + + /* Marker for detecting internal "double faults", errors thrown when + * we're trying to create an error object, see duk_error_throw.c. + */ + duk_bool_t creating_error; + + /* Longjmp state. */ + duk_ljstate lj; + + /* Heap thread, used internally and for finalization. */ duk_hthread *heap_thread; - /* current thread */ - duk_hthread *curr_thread; /* currently running thread */ + /* Current running thread. */ + duk_hthread *curr_thread; - /* heap level "stash" object (e.g., various reachability roots) */ + /* Heap level "stash" object (e.g., various reachability roots). */ duk_hobject *heap_object; /* duk_handle_call / duk_handle_safe_call recursion depth limiting */ duk_int_t call_recursion_depth; duk_int_t call_recursion_limit; - /* mix-in value for computing string hashes; should be reasonably unpredictable */ + /* Mix-in value for computing string hashes; should be reasonably unpredictable. */ duk_uint32_t hash_seed; - /* rnd_state for duk_util_tinyrandom.c */ + /* Random number state for duk_util_tinyrandom.c. */ #if !defined(DUK_USE_GET_RANDOM_DOUBLE) #if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) duk_uint32_t rnd_state; /* State for Shamir's three-op algorithm */ @@ -7846,7 +8396,7 @@ struct duk_heap { #endif #endif - /* counter for unique local symbol creation */ + /* Counter for unique local symbol creation. */ /* XXX: When 64-bit types are available, it would be more efficient to * use a duk_uint64_t at least for incrementing but maybe also for * string formatting in the Symbol constructor. @@ -7862,10 +8412,9 @@ struct duk_heap { duk_int_t inst_count_interrupt; #endif - /* debugger */ - + /* Debugger state. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - /* callbacks and udata; dbg_read_cb != NULL is used to indicate attached state */ + /* Callbacks and udata; dbg_read_cb != NULL is used to indicate attached state. */ duk_debug_read_function dbg_read_cb; /* required, NULL implies detached */ duk_debug_write_function dbg_write_cb; /* required */ duk_debug_peek_function dbg_peek_cb; @@ -7875,7 +8424,7 @@ struct duk_heap { duk_debug_detached_function dbg_detached_cb; void *dbg_udata; - /* debugger state, only relevant when attached */ + /* The following are only relevant when debugger is attached. */ duk_bool_t dbg_processing; /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. no breakpoints when processing debugger eval) */ 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) */ @@ -7899,30 +8448,25 @@ struct duk_heap { duk_uint8_t dbg_next_byte; #endif - /* string intern table (weak refs) */ -#if defined(DUK_USE_STRTAB_PROBE) -#if defined(DUK_USE_HEAPPTR16) + /* String intern table (weak refs). */ +#if defined(DUK_USE_STRTAB_PTRCOMP) duk_uint16_t *strtable16; #else duk_hstring **strtable; #endif - duk_uint32_t st_size; /* alloc size in elements */ - duk_uint32_t st_used; /* used elements (includes DELETED) */ + duk_uint32_t st_mask; /* mask for lookup, st_size - 1 */ + duk_uint32_t st_size; /* stringtable size */ +#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) + duk_uint32_t st_count; /* string count for resize load factor checks */ #endif + duk_bool_t st_resizing; /* string table is being resized; avoid recursive resize */ - /* XXX: static alloc is OK until separate chaining stringtable - * resizing is implemented. - */ -#if defined(DUK_USE_STRTAB_CHAIN) - duk_strtab_entry strtable[DUK_STRTAB_CHAIN_SIZE]; -#endif - - /* string access cache (codepoint offset -> byte offset) for fast string + /* String access cache (codepoint offset -> byte offset) for fast string * character looping; 'weak' reference which needs special handling in GC. */ duk_strcache strcache[DUK_HEAP_STRCACHE_SIZE]; - /* built-in strings */ + /* Built-in strings. */ #if defined(DUK_USE_ROM_STRINGS) /* No field needed when strings are in ROM. */ #else @@ -7951,32 +8495,32 @@ DUK_INTERNAL_DECL void duk_free_hstring(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr); DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); +#if defined(DUK_USE_REFERENCE_COUNTING) +DUK_INTERNAL_DECL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); +#endif +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL_DECL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr); +DUK_INTERNAL_DECL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr); +#endif +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL_DECL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr); #endif #if defined(DUK_USE_INTERRUPT_COUNTER) DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr); #endif -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); -#endif -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); -#if 0 /*unused*/ -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val); -#endif -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val); -DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val); +DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); #if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h); +DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h); #endif -#if defined(DUK_USE_MS_STRINGTABLE_RESIZE) -DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap); -#endif -DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap); +DUK_INTERNAL_DECL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev); +DUK_INTERNAL_DECL void duk_heap_strtable_force_resize(duk_heap *heap); +DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap); #if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap); +DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap); #endif DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h); @@ -7990,41 +8534,18 @@ DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr); DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size); +DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size); +DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize); 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); -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL_DECL void duk_refzero_free_pending(duk_hthread *thr); -DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr); -#if 0 /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */ -DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h); -DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h); -DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h); -DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h); -#endif -DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h); -#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) -DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h); /* no 'norz' variant */ -DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h); /* no 'norz' variant */ -DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h); -DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h); -#else -DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); -DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h); -#endif -#else /* DUK_USE_REFERENCE_COUNTING */ -/* no refcounting */ -#endif /* DUK_USE_REFERENCE_COUNTING */ +#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); +#endif /* DUK_USE_FINALIZER_SUPPORT */ -DUK_INTERNAL_DECL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); +DUK_INTERNAL_DECL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len); @@ -8172,7 +8693,13 @@ DUK_INTERNAL_DECL duk_bool_t duk_debug_process_messages(duk_hthread *thr, duk_bo DUK_INTERNAL_DECL duk_small_int_t duk_debug_add_breakpoint(duk_hthread *thr, duk_hstring *filename, duk_uint32_t line); DUK_INTERNAL_DECL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_uint_t breakpoint_index); -#endif + +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); +#endif /* DUK_USE_DEBUGGER_SUPPORT */ #endif /* DUK_DEBUGGER_H_INCLUDED */ /* #include duk_debug.h */ @@ -8838,7 +9365,10 @@ DUK_NORETURN(DUK_INTERNAL_DECL void duk_err_longjmp(duk_hthread *thr)); DUK_NORETURN(DUK_INTERNAL_DECL void duk_default_fatal_handler(void *udata, const char *msg)); -DUK_INTERNAL_DECL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type); +DUK_INTERNAL_DECL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val); +#if defined(DUK_USE_DEBUGGER_SUPPORT) +DUK_INTERNAL_DECL void duk_err_check_debugger_integration(duk_hthread *thr); +#endif DUK_INTERNAL_DECL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_errcode_t err_code); @@ -9222,8 +9752,11 @@ DUK_INTERNAL_DECL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv); DUK_INTERNAL_DECL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t *str, duk_uint32_t blen, duk_uarridx_t *out_idx); -DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h); +DUK_INTERNAL_DECL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen); +#if !defined(DUK_USE_HSTRING_ARRIDX) +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_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); @@ -9276,7 +9809,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_js_delvar_envrec(duk_hthread *thr, duk_hobject 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 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_hobject *func, duk_size_t regbase); +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, @@ -9436,6 +9969,9 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d #if defined(DUK_USE_DATE_TZO_WINDOWS) DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t d); #endif +#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); +#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); #endif @@ -9653,10 +10189,16 @@ DUK_INTERNAL void duk_debug_log(const char *fmt, ...) { /* #include duk_internal.h -> already included */ +#if defined(DUK_USE_ASSERTIONS) +#define DUK__REFCINIT(refc) 0 /*h_assert_refcount*/, (refc) /*actual*/ +#else +#define DUK__REFCINIT(refc) (refc) /*actual*/ +#endif + #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[921] = { +DUK_INTERNAL const duk_uint8_t duk_strings_data[903] = { 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, @@ -9684,31 +10226,31 @@ DUK_INTERNAL const duk_uint8_t duk_strings_data[921] = { 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,224,34,105,19,115,140,3,207,185,202,130,36,109,85,185, -194,161,160,90,50,72,163,115,135,3,70,178,68,251,156,16,22,178,16,251,156, -153,226,64,13,27,156,137,12,16,72,135,220,228,193,19,18,101,220,228,206, -137,28,78,99,208,178,21,13,125,38,146,70,60,20,72,9,145,4,140,121,51,197, -214,25,27,81,156,151,48,65,34,107,106,9,55,18,68,104,146,84,97,31,191,189, -181,70,140,133,222,249,212,227,66,125,245,187,251,219,77,3,119,190,117,56, -208,159,125,110,254,246,210,26,93,239,157,78,52,39,223,93,191,189,180,212, -52,187,223,58,156,104,79,190,187,127,123,104,180,104,183,190,117,56,208, -159,125,102,254,209,104,209,124,234,113,161,62,250,80,196,128,81,4,9,16, -162,4,196,116,9,205,154,27,66,32,100,13,12,98,68,227,33,65,69,204,195,34, -201,50,8,110,33,23,34,28,168,104,22,188,12,174,138,11,70,138,104,115,68, -130,137,13,82,27,41,129,162,35,138,54,146,198,137,39,72,180,210,178,38,35, -146,103,68,139,51,197,214,28,227,131,79,15,35,138,58,130,37,19,155,41,146, -174,64,203,99,161,100,37,145,51,148,75,4,164,66,54,140,49,46,247,70,103,37, -230,70,142,70,67,30,232,204,178,163,201,18,54,139,89,39,26,16,165,2,228,69, -33,143,89,24,70,206,73,67,102,72,148,2,32,214,73,157,224,18,128,98,29,241, -69,65,50,37,241,116,200,41,144,102,125,2,180,8,210,152,38,129,23,8,34,198, +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, }; #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: 164 */ -DUK_INTERNAL const duk_c_function duk_bi_native_functions[164] = { +/* native functions: 166 */ +DUK_INTERNAL const duk_c_function duk_bi_native_functions[166] = { NULL, duk_bi_array_constructor, duk_bi_array_constructor_is_array, @@ -9843,6 +10385,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[164] = { duk_bi_string_prototype_char_at, duk_bi_string_prototype_char_code_at, duk_bi_string_prototype_concat, + duk_bi_string_prototype_includes, duk_bi_string_prototype_indexof_shared, duk_bi_string_prototype_locale_compare, duk_bi_string_prototype_match, @@ -9851,6 +10394,7 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[164] = { duk_bi_string_prototype_search, duk_bi_string_prototype_slice, duk_bi_string_prototype_split, + duk_bi_string_prototype_startswith_endswith, duk_bi_string_prototype_substr, duk_bi_string_prototype_substring, duk_bi_string_prototype_to_string, @@ -9875,540 +10419,549 @@ DUK_INTERNAL const duk_c_function duk_bi_native_functions[164] = { duk_bi_uint8array_plainof, }; #if defined(DUK_USE_DOUBLE_LE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3790] = { +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,224,59, -147,60,93,110,79,15,39,9,24,186,33,13,63,79,185,38,154,121,223,110,76,66, -53,116,1,120,248,186,248,136,67,76,196,200,134,186,137,177,13,31,192,174, -79,15,32,248,8,196,24,8,107,254,39,97,161,175,248,159,16,215,252,80,186,26, -255,138,57,136,107,254,41,100,33,175,248,167,170,134,191,226,166,138,26, -255,138,187,40,107,254,43,111,33,171,86,181,16,209,241,11,228,201,121,240, -141,19,134,72,196,52,123,168,95,38,75,207,131,32,156,50,70,33,195,3,152, -128,0,0,0,0,0,1,240,255,153,128,0,0,0,0,0,1,224,255,151,137,0,214,9,188,35, -131,12,225,196,56,177,78,60,99,147,28,229,200,57,162,120,74,129,124,36,98, -232,156,241,92,136,26,98,112,145,139,162,116,71,114,36,41,34,70,136,156,36, -98,232,157,49,124,150,27,48,95,132,140,93,19,170,39,147,195,201,194,70,46, -137,215,17,218,69,4,236,98,232,157,153,39,110,81,220,15,193,209,83,3,200, -119,130,241,241,117,240,120,80,252,137,10,178,10,103,134,180,122,9,135,136, -154,120,169,199,142,158,121,10,7,146,162,121,74,71,150,166,121,138,135,154, -170,121,202,199,158,23,201,146,243,225,26,39,12,145,61,16,190,76,151,159,6, -65,56,100,137,233,35,93,205,144,33,224,140,137,196,54,121,244,5,60,17,145, -56,85,184,19,207,16,21,18,227,65,198,231,72,16,137,112,168,106,38,76,225,2, -70,65,56,100,237,34,140,177,4,134,65,56,100,237,34,129,117,204,123,154,70, -207,46,64,146,52,78,25,59,72,163,48,65,34,52,78,25,59,72,160,93,115,30,230, -145,179,204,144,24,146,16,30,76,209,2,40,210,72,64,121,52,4,0,156,88,97,5, -194,96,227,18,124,124,93,55,79,15,39,28,94,49,38,159,154,136,96,196,159,29, -102,241,241,115,201,25,227,131,36,133,20,62,110,142,253,2,102,36,248,235, -55,143,139,158,72,207,28,104,24,73,112,201,3,2,82,65,155,187,94,6,20,72,9, -147,120,128,225,144,168,105,56,248,185,228,140,241,190,96,128,200,84,52, -156,124,92,242,70,104,36,183,168,4,145,0,190,41,1,139,18,19,36,226,146,17, -124,73,82,54,124,37,230,70,201,14,108,184,132,8,68,185,34,1,100,31,8,129,8, -151,11,23,100,141,225,18,12,68,184,75,204,141,146,2,178,112,72,8,162,98,92, -50,10,152,147,227,172,222,62,46,121,35,60,114,88,96,92,185,112,201,65,34, -92,4,1,147,81,159,141,205,32,234,121,96,97,57,64,97,121,128,14,56,37,199, -89,188,124,92,242,70,120,227,144,53,18,227,226,233,186,120,121,56,226,242, -8,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,36,136,48,64,114,0, -250,156,168,1,64,247,175,25,36,2,8,11,94,80,248,16,40,104,242,103,200,48, -193,3,162,92,4,98,12,41,14,66,40,106,101,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,160,163,11,23,51,130,56,35,193, -56,100,238,31,6,150,46,103,4,225,147,143,114,27,62,233,241,200,137,182,133, -42,142,167,216,6,23,216,0,97,28,17,224,39,223,32,80,142,8,240,78,25,56,9, -248,8,22,39,12,156,123,144,217,240,19,240,18,6,19,154,32,79,194,124,14,134, -140,151,227,139,226,52,11,88,37,62,33,163,37,248,226,248,141,32,213,184,64, -89,56,39,49,224,137,60,100,5,96,38,35,249,8,15,18,61,96,17,60,200,6,145,1, -17,31,206,64,89,45,2,39,161,0,178,122,209,63,74,2,101,64,202,113,67,77,235, -64,92,221,197,186,196,143,4,9,19,188,1,25,187,139,112,128,178,113,110,177, -35,193,2,68,239,0,46,110,229,30,242,71,130,4,137,222,4,35,55,113,110,16,22, -78,81,239,36,120,32,72,157,224,64,147,138,25,237,0,52,72,242,2,126,82,3,74, -129,148,227,234,66,12,112,28,140,155,104,203,169,158,9,133,158,4,25,36,1, -61,96,47,181,80,46,132,129,255,255,255,255,255,255,222,254,39,172,67,118, -170,5,208,144,0,64,0,0,0,0,0,0,51,16,0,0,0,0,0,0,62,31,200,245,238,146,38, -138,147,105,13,42,26,137,226,0,0,0,0,0,0,7,131,249,30,180,134,4,209,82,109, -33,165,67,81,60,64,0,0,0,0,0,0,240,255,28,144,155,104,0,0,0,0,0,0,0,0,16, -117,59,130,48,155,98,48,187,144,3,205,220,42,46,65,237,72,27,55,112,151, -123,154,70,205,0,94,208,129,115,119,31,18,9,18,67,155,183,34,12,176,96,175, -4,100,74,228,3,237,38,43,31,192,109,117,171,0,228,164,219,72,0,0,0,0,0,0, -248,127,196,234,111,0,50,110,224,193,50,114,83,138,26,107,192,131,38,238, -77,12,39,37,56,161,166,188,11,132,188,12,74,110,226,220,32,44,156,24,38,78, -74,113,67,77,120,28,148,221,197,184,64,89,57,52,48,156,148,226,134,154,240, -64,195,94,8,56,123,193,11,85,116,140,45,240,3,152,147,228,208,194,95,0,89, -137,62,22,139,95,48,64,70,200,67,28,98,79,180,152,139,218,45,124,193,1,27, -33,16,65,137,62,49,205,153,236,132,81,102,36,251,73,137,157,115,102,123,33, -24,57,137,62,12,19,37,144,142,40,196,159,105,49,15,160,153,44,132,128,198, -36,248,48,98,200,73,18,98,79,180,152,135,208,98,200,74,16,98,79,135,117,35, -43,33,44,89,137,62,210,98,63,93,72,202,200,76,20,98,79,140,67,105,50,74, -200,77,26,98,79,180,152,153,212,54,147,36,172,132,225,70,36,249,34,9,205, -28,172,132,241,166,36,251,73,138,93,32,156,209,202,200,80,30,98,79,140,66, -214,137,16,78,104,229,100,40,146,49,39,218,76,76,234,22,180,72,130,115,71, -43,33,72,137,137,62,77,12,38,92,210,113,197,44,137,59,64,7,145,39,201,161, -132,184,64,249,18,124,98,22,180,72,130,115,71,43,101,76,148,137,62,210,98, -103,80,181,162,68,19,154,57,91,42,130,164,73,242,68,19,154,57,91,95,84,108, -137,62,210,98,151,72,39,52,114,182,190,176,169,18,124,98,27,73,146,86,223, -215,27,34,79,180,152,153,212,54,147,36,173,191,176,34,68,159,14,234,70,86, -231,217,23,34,79,180,152,143,215,82,50,183,62,208,121,18,124,24,38,75,101, -108,84,137,62,210,98,31,65,50,91,43,130,36,73,241,142,108,207,109,125,209, -114,36,251,73,137,157,115,102,123,107,239,11,145,39,194,209,107,230,8,8, -219,127,124,116,137,62,210,98,47,104,181,243,4,4,109,191,192,135,49,39,204, -16,17,178,24,32,242,36,249,130,2,54,203,7,6,104,14,76,131,140,144,0,0,0,0, -0,0,0,1,141,207,215,12,78,126,193,46,190,126,192,98,179,246,4,197,231,236, -10,193,9,114,11,172,64,73,146,83,236,145,169,237,1,6,120,14,78,129,179,40, -249,18,149,175,207,141,199,27,76,248,156,81,177,207,139,198,9,169,199,129, -58,136,19,202,11,179,20,240,149,2,248,72,197,209,200,148,162,117,48,39,148, -151,102,42,228,64,211,19,132,140,93,28,137,74,39,85,2,121,81,118,98,238,68, -133,36,72,209,19,132,140,93,28,137,74,39,87,2,121,89,118,98,190,75,13,152, -47,194,70,46,142,68,165,19,172,129,60,176,187,49,79,39,135,147,132,140,93, -28,137,74,39,91,2,121,105,118,98,142,210,40,39,99,23,71,34,82,135,8,128, -120,72,13,42,226,145,97,87,224,168,1,58,182,232,232,64,22,85,181,187,177, -107,2,64,7,213,183,74,7,121,207,215,242,17,119,49,248,94,173,198,210,36,15, -232,34,182,84,113,95,115,240,221,91,141,163,160,72,1,220,164,194,175,121, -123,103,224,186,244,64,24,45,68,84,251,33,9,64,15,217,66,51,209,218,210, -129,154,118,254,205,61,65,204,126,23,178,132,103,165,3,52,237,253,154,122, -131,216,252,167,224,121,44,48,46,95,203,166,238,74,113,67,77,201,128,219, -152,164,82,6,0,203,76,64,64,9,210,211,18,4,4,144,221,49,40,64,76,13,211,19, -5,4,192,221,45,66,1,4,24,207,76,82,2,8,136,94,152,156,24,157,45,49,64,6,75, -191,76,80,66,149,110,116,116,197,8,41,240,247,79,70,188,6,183,27,76,80,194, -45,198,210,211,20,144,171,113,180,116,52,197,40,27,1,125,34,240,27,16,221, -42,240,27,221,109,66,32,104,129,163,115,52,224,5,139,168,209,233,138,32,57, -33,186,98,138,18,80,140,244,197,24,28,192,221,49,71,11,56,209,162,211,20, -183,1,66,188,17,145,52,40,9,148,226,134,153,5,198,137,136,32,14,12,30,164, -140,144,230,192,0,0,0,0,128,136,211,64,182,120,43,135,126,16,68,52,174,195, -144,12,2,158,4,128,70,22,24,128,101,67,112,163,192,100,104,176,131,192,99, -32,176,99,192,226,115,30,1,79,4,68,28,16,54,0,0,41,254,232,116,62,204,7,21, -35,18,54,127,80,28,192,132,28,32,14,96,197,212,243,193,48,188,240,39,130, -236,224,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,32,128,56,18,2,129,239,94,50,76,130,68,230, -202,113,160,167,146,94,163,134,66,161,164,227,226,231,146,51,198,249,147, -71,209,67,73,210,94,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15, -155,163,191,68,28,98,79,143,139,166,233,225,228,227,139,198,37,210,244,208, -24,137,112,151,153,27,36,5,100,224,146,105,184,100,196,95,18,84,141,159,9, -121,145,178,67,155,46,33,38,187,168,252,211,243,81,92,2,14,40,16,50,37,202, -160,150,154,67,152,148,20,28,76,156,89,26,105,158,63,232,16,44,150,129,18, -146,44,28,96,14,98,216,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,18,195,152,147,226,27,61,138,41, -140,16,98,79,148,67,103,177,69,45,136,49,39,196,54,122,58,212,83,26,36,196, -159,40,134,207,71,90,138,92,16,98,79,136,108,244,244,168,166,56,73,137,62, -81,13,158,158,149,20,186,40,196,159,10,183,2,122,122,84,82,240,163,18,124, -42,220,9,235,106,81,75,225,228,73,241,13,158,197,54,198,8,145,39,202,33, -179,216,166,214,196,72,147,226,27,61,29,106,109,141,19,34,79,148,67,103, -163,173,77,174,8,145,39,196,54,122,122,84,219,28,38,68,159,40,134,207,79, -74,155,93,21,34,79,133,91,129,61,61,42,109,120,84,137,62,21,110,4,245,181, -41,181,248,56,224,28,24,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,11,174,254,160,34,84,8,35,16,98, -146,38,55,32,33,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,0,0,0,0,30, -7,230,56,199,161,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,0,0,0,0, -30,7,230,55,36,33,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,0,0,0,0, -30,7,234,40,11,91,133,199,172,8,111,248,128,239,88,16,222,56,191,242,49, -198,69,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,240,63, -49,185,65,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0,240, -63,49,198,77,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0,0, -240,63,49,185,97,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0, -0,0,64,49,198,85,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0,0, -0,0,64,49,185,129,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0, -0,0,0,64,49,198,93,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,0, -0,0,0,64,49,185,161,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0, -0,0,0,16,64,49,198,101,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0, -0,0,0,0,16,64,49,185,193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0, -0,0,0,0,0,16,64,49,198,109,8,244,56,153,37,180,242,71,104,139,35,8,217,192, -0,0,0,0,0,0,16,64,49,185,225,8,244,56,153,37,180,242,71,104,139,35,8,217, -192,0,0,0,0,0,0,16,64,49,198,117,8,244,56,153,37,180,242,71,104,139,35,8, -217,192,0,0,0,0,0,0,16,64,49,186,1,8,244,56,153,37,180,242,71,104,139,35,8, -217,192,0,0,0,0,0,0,32,64,49,198,125,8,244,56,153,37,180,242,71,104,139,35, -8,217,192,0,0,0,0,0,0,32,64,32,232,130,0,97,57,162,4,245,72,10,68,184,70, -137,195,67,77,175,32,66,37,192,208,165,36,117,196,10,14,38,78,44,141,52, -207,169,64,56,156,199,130,36,160,141,146,52,38,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,59,22, -53,91,0,2,21,11,94,181,128,196,133,0,185,80,32,56,156,199,130,36,160,72,16, -78,126,53,144,5,146,208,34,82,72,1,109,20,76,155,40,32,233,0,115,70,130,8, -209,56,104,105,187,252,193,3,17,162,112,201,242,18,65,211,0,230,149,132,17, -162,112,208,211,119,248,0,82,130,96,95,127,128,130,80,102,186,36,232,92, -206,255,1,80,48,200,39,12,158,241,64, +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, }; #elif defined(DUK_USE_DOUBLE_BE) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3790] = { +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,224,59, -147,60,93,110,79,15,39,9,24,186,33,13,63,79,185,38,154,121,223,110,76,66, -53,116,1,120,248,186,248,136,67,76,196,200,134,186,137,177,13,31,192,174, -79,15,32,248,8,196,24,8,107,254,39,97,161,175,248,159,16,215,252,80,186,26, -255,138,57,136,107,254,41,100,33,175,248,167,170,134,191,226,166,138,26, -255,138,187,40,107,254,43,111,33,171,86,181,16,209,241,11,228,201,121,240, -141,19,134,72,196,52,123,168,95,38,75,207,131,32,156,50,70,33,195,3,152, -128,255,240,0,0,0,0,0,1,153,128,255,224,0,0,0,0,0,1,151,137,0,214,9,188,35, -131,12,225,196,56,177,78,60,99,147,28,229,200,57,162,120,74,129,124,36,98, -232,156,241,92,136,26,98,112,145,139,162,116,71,114,36,41,34,70,136,156,36, -98,232,157,49,124,150,27,48,95,132,140,93,19,170,39,147,195,201,194,70,46, -137,215,17,218,69,4,236,98,232,157,153,39,110,81,220,15,193,209,83,3,200, -119,130,241,241,117,240,120,80,252,137,10,178,10,103,134,180,122,9,135,136, -154,120,169,199,142,158,121,10,7,146,162,121,74,71,150,166,121,138,135,154, -170,121,202,199,158,23,201,146,243,225,26,39,12,145,61,16,190,76,151,159,6, -65,56,100,137,233,35,93,205,144,33,224,140,137,196,54,121,244,5,60,17,145, -56,85,184,19,207,16,21,18,227,65,198,231,72,16,137,112,168,106,38,76,225,2, -70,65,56,100,237,34,140,177,4,134,65,56,100,237,34,129,117,204,123,154,70, -207,46,64,146,52,78,25,59,72,163,48,65,34,52,78,25,59,72,160,93,115,30,230, -145,179,204,144,24,146,16,30,76,209,2,40,210,72,64,121,52,4,0,156,88,97,5, -194,96,227,18,124,124,93,55,79,15,39,28,94,49,38,159,154,136,96,196,159,29, -102,241,241,115,201,25,227,131,36,133,20,62,110,142,253,2,102,36,248,235, -55,143,139,158,72,207,28,104,24,73,112,201,3,2,82,65,155,187,94,6,20,72,9, -147,120,128,225,144,168,105,56,248,185,228,140,241,190,96,128,200,84,52, -156,124,92,242,70,104,36,183,168,4,145,0,190,41,1,139,18,19,36,226,146,17, -124,73,82,54,124,37,230,70,201,14,108,184,132,8,68,185,34,1,100,31,8,129,8, -151,11,23,100,141,225,18,12,68,184,75,204,141,146,2,178,112,72,8,162,98,92, -50,10,152,147,227,172,222,62,46,121,35,60,114,88,96,92,185,112,201,65,34, -92,4,1,147,81,159,141,205,32,234,121,96,97,57,64,97,121,128,14,56,37,199, -89,188,124,92,242,70,120,227,144,53,18,227,226,233,186,120,121,56,226,242, -8,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,36,136,48,64,114,0, -250,156,168,1,64,247,175,25,36,2,8,11,94,80,248,16,40,104,242,103,200,48, -193,3,162,92,4,98,12,41,14,66,40,106,101,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,160,163,11,23,51,130,56,35,193, -56,100,238,31,6,150,46,103,4,225,147,143,114,27,62,233,241,200,137,182,133, -42,142,167,216,6,23,216,0,97,28,17,224,39,223,32,80,142,8,240,78,25,56,9, -248,8,22,39,12,156,123,144,217,240,19,240,18,6,19,154,32,79,194,124,14,134, -140,151,227,139,226,52,11,88,37,62,33,163,37,248,226,248,141,32,213,184,64, -89,56,39,49,224,137,60,100,5,96,38,35,249,8,15,18,61,96,17,60,200,6,145,1, -17,31,206,64,89,45,2,39,161,0,178,122,209,63,74,2,101,64,202,113,67,77,235, -64,92,221,197,186,196,143,4,9,19,188,1,25,187,139,112,128,178,113,110,177, -35,193,2,68,239,0,46,110,229,30,242,71,130,4,137,222,4,35,55,113,110,16,22, -78,81,239,36,120,32,72,157,224,64,147,138,25,237,0,52,72,242,2,126,82,3,74, -129,148,227,234,66,12,112,28,140,155,104,203,169,158,9,133,158,4,25,36,1, -61,96,47,181,80,46,132,128,255,223,255,255,255,255,255,254,39,172,67,118, -170,5,208,144,0,0,0,0,0,0,0,0,115,16,31,254,0,0,0,0,0,0,8,245,238,146,38, -138,147,105,13,42,26,137,226,3,255,128,0,0,0,0,0,1,30,180,134,4,209,82,109, -33,165,67,81,60,64,255,240,0,0,0,0,0,0,28,144,155,104,0,0,0,0,0,0,0,0,16, -117,59,130,48,155,98,48,187,144,3,205,220,42,46,65,237,72,27,55,112,151, -123,154,70,205,0,94,208,129,115,119,31,18,9,18,67,155,183,34,12,176,96,175, -4,100,74,228,3,237,38,43,31,192,109,117,171,0,228,164,219,72,127,248,0,0,0, -0,0,0,196,234,111,0,50,110,224,193,50,114,83,138,26,107,192,131,38,238,77, -12,39,37,56,161,166,188,11,132,188,12,74,110,226,220,32,44,156,24,38,78,74, -113,67,77,120,28,148,221,197,184,64,89,57,52,48,156,148,226,134,154,240,64, -195,94,8,56,123,193,11,85,116,140,45,240,3,152,147,228,208,194,95,0,89,137, -62,22,139,95,48,64,70,200,67,28,98,79,180,152,139,218,45,124,193,1,27,33, -16,65,137,62,49,205,153,236,132,81,102,36,251,73,137,157,115,102,123,33,24, -57,137,62,12,19,37,144,142,40,196,159,105,49,15,160,153,44,132,128,198,36, -248,48,98,200,73,18,98,79,180,152,135,208,98,200,74,16,98,79,135,117,35,43, -33,44,89,137,62,210,98,63,93,72,202,200,76,20,98,79,140,67,105,50,74,200, -77,26,98,79,180,152,153,212,54,147,36,172,132,225,70,36,249,34,9,205,28, -172,132,241,166,36,251,73,138,93,32,156,209,202,200,80,30,98,79,140,66,214, -137,16,78,104,229,100,40,146,49,39,218,76,76,234,22,180,72,130,115,71,43, -33,72,137,137,62,77,12,38,92,210,113,197,44,137,59,64,7,145,39,201,161,132, -184,64,249,18,124,98,22,180,72,130,115,71,43,101,76,148,137,62,210,98,103, -80,181,162,68,19,154,57,91,42,130,164,73,242,68,19,154,57,91,95,84,108,137, -62,210,98,151,72,39,52,114,182,190,176,169,18,124,98,27,73,146,86,223,215, -27,34,79,180,152,153,212,54,147,36,173,191,176,34,68,159,14,234,70,86,231, -217,23,34,79,180,152,143,215,82,50,183,62,208,121,18,124,24,38,75,101,108, -84,137,62,210,98,31,65,50,91,43,130,36,73,241,142,108,207,109,125,209,114, -36,251,73,137,157,115,102,123,107,239,11,145,39,194,209,107,230,8,8,219, -127,124,116,137,62,210,98,47,104,181,243,4,4,109,191,192,135,49,39,204,16, -17,178,24,32,242,36,249,130,2,54,203,7,6,104,14,76,131,140,144,0,0,0,0,0,0, -0,1,141,207,215,12,78,126,193,46,190,126,192,98,179,246,4,197,231,236,10, -193,9,114,11,172,64,73,146,83,236,145,169,237,1,6,120,14,78,129,179,40,249, -18,149,175,207,141,199,27,76,248,156,81,177,207,139,198,9,169,199,129,58, -136,19,202,11,179,20,240,149,2,248,72,197,209,200,148,162,117,48,39,148, -151,102,42,228,64,211,19,132,140,93,28,137,74,39,85,2,121,81,118,98,238,68, -133,36,72,209,19,132,140,93,28,137,74,39,87,2,121,89,118,98,190,75,13,152, -47,194,70,46,142,68,165,19,172,129,60,176,187,49,79,39,135,147,132,140,93, -28,137,74,39,91,2,121,105,118,98,142,210,40,39,99,23,71,34,82,135,8,128, -120,72,8,0,183,225,81,98,138,237,33,58,182,232,232,64,64,2,107,177,187,181, -85,22,7,213,183,74,1,255,49,114,23,247,209,207,120,94,173,198,210,36,3,255, -113,84,118,82,184,47,224,221,91,141,163,160,72,7,251,121,111,98,164,220, -161,192,186,244,64,64,9,33,251,84,68,45,24,15,217,66,51,209,218,210,128, -127,205,65,60,204,254,119,154,23,178,132,103,165,0,255,218,130,121,153,252, -239,52,167,224,121,44,48,46,95,203,166,238,74,113,67,77,201,128,219,152, -164,82,6,0,203,76,64,64,9,210,211,18,4,4,144,221,49,40,64,76,13,211,19,5,4, -192,221,45,66,1,4,24,207,76,82,2,8,136,94,152,156,24,157,45,49,64,6,75,191, -76,80,66,149,110,116,116,197,8,41,240,247,79,70,188,6,183,27,76,80,194,45, -198,210,211,20,144,171,113,180,116,52,197,40,27,1,125,34,240,27,16,221,42, -240,27,221,109,66,32,104,129,163,115,52,224,5,139,168,209,233,138,32,57,33, -186,98,138,18,80,140,244,197,24,28,192,221,49,71,11,56,209,162,211,20,183, -1,66,188,17,145,52,40,9,148,226,134,153,5,198,137,136,32,14,12,30,164,140, -144,230,192,64,211,136,128,0,0,0,0,182,120,43,135,126,16,68,52,174,195,144, -12,2,158,4,128,70,22,24,128,101,67,112,163,192,100,104,176,131,192,99,32, -176,99,192,226,115,30,1,79,4,68,28,16,54,0,0,41,254,232,116,62,204,7,21,35, -18,54,127,80,28,192,132,28,32,14,96,197,212,243,193,48,188,240,39,130,236, -224,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,32,128,56,18,2,129,239,94,50,76,130,68,230, -202,113,160,167,146,94,163,134,66,161,164,227,226,231,146,51,198,249,147, -71,209,67,73,210,94,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15, -155,163,191,68,28,98,79,143,139,166,233,225,228,227,139,198,37,210,244,208, -24,137,112,151,153,27,36,5,100,224,146,105,184,100,196,95,18,84,141,159,9, -121,145,178,67,155,46,33,38,187,168,252,211,243,81,92,2,14,40,16,50,37,202, -160,150,154,67,152,148,20,28,76,156,89,26,105,158,63,232,16,44,150,129,18, -146,44,28,96,14,98,216,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,18,195,152,147,226,27,61,138,41, -140,16,98,79,148,67,103,177,69,45,136,49,39,196,54,122,58,212,83,26,36,196, -159,40,134,207,71,90,138,92,16,98,79,136,108,244,244,168,166,56,73,137,62, -81,13,158,158,149,20,186,40,196,159,10,183,2,122,122,84,82,240,163,18,124, -42,220,9,235,106,81,75,225,228,73,241,13,158,197,54,198,8,145,39,202,33, -179,216,166,214,196,72,147,226,27,61,29,106,109,141,19,34,79,148,67,103, -163,173,77,174,8,145,39,196,54,122,122,84,219,28,38,68,159,40,134,207,79, -74,155,93,21,34,79,133,91,129,61,61,42,109,120,84,137,62,21,110,4,245,181, -41,181,248,56,224,28,24,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,11,174,254,160,34,84,8,35,16,98, -146,38,55,32,33,30,135,19,36,182,158,72,237,17,100,97,27,56,7,254,0,0,0,0, -0,0,6,56,199,161,30,135,19,36,182,158,72,237,17,100,97,27,56,7,254,0,0,0,0, -0,0,6,55,36,33,30,135,19,36,182,158,72,237,17,100,97,27,56,7,254,0,0,0,0,0, -0,10,40,11,91,133,199,172,8,111,248,128,239,88,16,222,56,191,242,49,198,69, -8,244,56,153,37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0,49,185, -65,8,244,56,153,37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0,49, -198,77,8,244,56,153,37,180,242,71,104,139,35,8,217,192,63,240,0,0,0,0,0,0, -49,185,97,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0, -49,198,85,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0,0, -49,185,129,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0, -0,49,198,93,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,0,0,0,0,0,0, -0,49,185,161,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0,0, -0,0,49,198,101,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0,0, -0,0,0,49,185,193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0,0, -0,0,0,0,49,198,109,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16,0, -0,0,0,0,0,49,185,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64,16, -0,0,0,0,0,0,49,198,117,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64, -16,0,0,0,0,0,0,49,186,1,8,244,56,153,37,180,242,71,104,139,35,8,217,192,64, -32,0,0,0,0,0,0,49,198,125,8,244,56,153,37,180,242,71,104,139,35,8,217,192, -64,32,0,0,0,0,0,0,32,232,130,0,97,57,162,4,245,72,10,68,184,70,137,195,67, -77,175,32,66,37,192,208,165,36,117,196,10,14,38,78,44,141,52,207,169,64,56, -156,199,130,36,160,141,146,52,38,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,59,22,53,91,0,2,21,11,94,181,128,196, -133,0,185,80,32,56,156,199,130,36,160,72,16,78,126,53,144,5,146,208,34,82, -72,1,109,20,76,155,40,32,233,0,115,70,130,8,209,56,104,105,187,252,193,3, -17,162,112,201,242,18,65,211,0,230,149,132,17,162,112,208,211,119,248,0,82, -130,96,95,127,128,130,80,102,186,36,232,92,206,255,1,80,48,200,39,12,158, -241,64, +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, }; #elif defined(DUK_USE_DOUBLE_ME) -DUK_INTERNAL const duk_uint8_t duk_builtins_data[3790] = { +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,224,59, -147,60,93,110,79,15,39,9,24,186,33,13,63,79,185,38,154,121,223,110,76,66, -53,116,1,120,248,186,248,136,67,76,196,200,134,186,137,177,13,31,192,174, -79,15,32,248,8,196,24,8,107,254,39,97,161,175,248,159,16,215,252,80,186,26, -255,138,57,136,107,254,41,100,33,175,248,167,170,134,191,226,166,138,26, -255,138,187,40,107,254,43,111,33,171,86,181,16,209,241,11,228,201,121,240, -141,19,134,72,196,52,123,168,95,38,75,207,131,32,156,50,70,33,195,3,152, -128,0,1,240,254,0,0,0,1,153,128,0,1,224,254,0,0,0,1,151,137,0,214,9,188,35, -131,12,225,196,56,177,78,60,99,147,28,229,200,57,162,120,74,129,124,36,98, -232,156,241,92,136,26,98,112,145,139,162,116,71,114,36,41,34,70,136,156,36, -98,232,157,49,124,150,27,48,95,132,140,93,19,170,39,147,195,201,194,70,46, -137,215,17,218,69,4,236,98,232,157,153,39,110,81,220,15,193,209,83,3,200, -119,130,241,241,117,240,120,80,252,137,10,178,10,103,134,180,122,9,135,136, -154,120,169,199,142,158,121,10,7,146,162,121,74,71,150,166,121,138,135,154, -170,121,202,199,158,23,201,146,243,225,26,39,12,145,61,16,190,76,151,159,6, -65,56,100,137,233,35,93,205,144,33,224,140,137,196,54,121,244,5,60,17,145, -56,85,184,19,207,16,21,18,227,65,198,231,72,16,137,112,168,106,38,76,225,2, -70,65,56,100,237,34,140,177,4,134,65,56,100,237,34,129,117,204,123,154,70, -207,46,64,146,52,78,25,59,72,163,48,65,34,52,78,25,59,72,160,93,115,30,230, -145,179,204,144,24,146,16,30,76,209,2,40,210,72,64,121,52,4,0,156,88,97,5, -194,96,227,18,124,124,93,55,79,15,39,28,94,49,38,159,154,136,96,196,159,29, -102,241,241,115,201,25,227,131,36,133,20,62,110,142,253,2,102,36,248,235, -55,143,139,158,72,207,28,104,24,73,112,201,3,2,82,65,155,187,94,6,20,72,9, -147,120,128,225,144,168,105,56,248,185,228,140,241,190,96,128,200,84,52, -156,124,92,242,70,104,36,183,168,4,145,0,190,41,1,139,18,19,36,226,146,17, -124,73,82,54,124,37,230,70,201,14,108,184,132,8,68,185,34,1,100,31,8,129,8, -151,11,23,100,141,225,18,12,68,184,75,204,141,146,2,178,112,72,8,162,98,92, -50,10,152,147,227,172,222,62,46,121,35,60,114,88,96,92,185,112,201,65,34, -92,4,1,147,81,159,141,205,32,234,121,96,97,57,64,97,121,128,14,56,37,199, -89,188,124,92,242,70,120,227,144,53,18,227,226,233,186,120,121,56,226,242, -8,40,248,185,228,140,241,196,75,132,109,24,72,128,43,39,36,136,48,64,114,0, -250,156,168,1,64,247,175,25,36,2,8,11,94,80,248,16,40,104,242,103,200,48, -193,3,162,92,4,98,12,41,14,66,40,106,101,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,160,163,11,23,51,130,56,35,193, -56,100,238,31,6,150,46,103,4,225,147,143,114,27,62,233,241,200,137,182,133, -42,142,167,216,6,23,216,0,97,28,17,224,39,223,32,80,142,8,240,78,25,56,9, -248,8,22,39,12,156,123,144,217,240,19,240,18,6,19,154,32,79,194,124,14,134, -140,151,227,139,226,52,11,88,37,62,33,163,37,248,226,248,141,32,213,184,64, -89,56,39,49,224,137,60,100,5,96,38,35,249,8,15,18,61,96,17,60,200,6,145,1, -17,31,206,64,89,45,2,39,161,0,178,122,209,63,74,2,101,64,202,113,67,77,235, -64,92,221,197,186,196,143,4,9,19,188,1,25,187,139,112,128,178,113,110,177, -35,193,2,68,239,0,46,110,229,30,242,71,130,4,137,222,4,35,55,113,110,16,22, -78,81,239,36,120,32,72,157,224,64,147,138,25,237,0,52,72,242,2,126,82,3,74, -129,148,227,234,66,12,112,28,140,155,104,203,169,158,9,133,158,4,25,36,1, -61,96,47,181,80,46,132,129,255,255,222,255,255,255,255,254,39,172,67,118, -170,5,208,144,0,0,0,0,0,64,0,0,51,16,0,0,62,31,192,0,0,0,8,245,238,146,38, -138,147,105,13,42,26,137,226,0,0,7,131,248,0,0,0,1,30,180,134,4,209,82,109, -33,165,67,81,60,64,0,0,240,255,0,0,0,0,28,144,155,104,0,0,0,0,0,0,0,0,16, -117,59,130,48,155,98,48,187,144,3,205,220,42,46,65,237,72,27,55,112,151, -123,154,70,205,0,94,208,129,115,119,31,18,9,18,67,155,183,34,12,176,96,175, -4,100,74,228,3,237,38,43,31,192,109,117,171,0,228,164,219,72,0,0,248,127,0, -0,0,0,196,234,111,0,50,110,224,193,50,114,83,138,26,107,192,131,38,238,77, -12,39,37,56,161,166,188,11,132,188,12,74,110,226,220,32,44,156,24,38,78,74, -113,67,77,120,28,148,221,197,184,64,89,57,52,48,156,148,226,134,154,240,64, -195,94,8,56,123,193,11,85,116,140,45,240,3,152,147,228,208,194,95,0,89,137, -62,22,139,95,48,64,70,200,67,28,98,79,180,152,139,218,45,124,193,1,27,33, -16,65,137,62,49,205,153,236,132,81,102,36,251,73,137,157,115,102,123,33,24, -57,137,62,12,19,37,144,142,40,196,159,105,49,15,160,153,44,132,128,198,36, -248,48,98,200,73,18,98,79,180,152,135,208,98,200,74,16,98,79,135,117,35,43, -33,44,89,137,62,210,98,63,93,72,202,200,76,20,98,79,140,67,105,50,74,200, -77,26,98,79,180,152,153,212,54,147,36,172,132,225,70,36,249,34,9,205,28, -172,132,241,166,36,251,73,138,93,32,156,209,202,200,80,30,98,79,140,66,214, -137,16,78,104,229,100,40,146,49,39,218,76,76,234,22,180,72,130,115,71,43, -33,72,137,137,62,77,12,38,92,210,113,197,44,137,59,64,7,145,39,201,161,132, -184,64,249,18,124,98,22,180,72,130,115,71,43,101,76,148,137,62,210,98,103, -80,181,162,68,19,154,57,91,42,130,164,73,242,68,19,154,57,91,95,84,108,137, -62,210,98,151,72,39,52,114,182,190,176,169,18,124,98,27,73,146,86,223,215, -27,34,79,180,152,153,212,54,147,36,173,191,176,34,68,159,14,234,70,86,231, -217,23,34,79,180,152,143,215,82,50,183,62,208,121,18,124,24,38,75,101,108, -84,137,62,210,98,31,65,50,91,43,130,36,73,241,142,108,207,109,125,209,114, -36,251,73,137,157,115,102,123,107,239,11,145,39,194,209,107,230,8,8,219, -127,124,116,137,62,210,98,47,104,181,243,4,4,109,191,192,135,49,39,204,16, -17,178,24,32,242,36,249,130,2,54,203,7,6,104,14,76,131,140,144,0,0,0,0,0,0, -0,1,141,207,215,12,78,126,193,46,190,126,192,98,179,246,4,197,231,236,10, -193,9,114,11,172,64,73,146,83,236,145,169,237,1,6,120,14,78,129,179,40,249, -18,149,175,207,141,199,27,76,248,156,81,177,207,139,198,9,169,199,129,58, -136,19,202,11,179,20,240,149,2,248,72,197,209,200,148,162,117,48,39,148, -151,102,42,228,64,211,19,132,140,93,28,137,74,39,85,2,121,81,118,98,238,68, -133,36,72,209,19,132,140,93,28,137,74,39,87,2,121,89,118,98,190,75,13,152, -47,194,70,46,142,68,165,19,172,129,60,176,187,49,79,39,135,147,132,140,93, -28,137,74,39,91,2,121,105,118,98,142,210,40,39,99,23,71,34,82,135,8,128, -120,72,1,87,224,168,13,42,226,145,97,58,182,232,232,64,177,107,2,64,22,85, -181,187,7,213,183,74,2,17,119,49,255,121,207,215,240,94,173,198,210,36,4, -113,95,115,255,232,34,182,80,221,91,141,163,160,72,15,121,123,103,225,220, -164,194,160,186,244,64,251,33,9,64,24,45,68,84,15,217,66,51,209,218,210, -129,61,65,204,127,154,118,254,204,23,178,132,103,165,2,122,131,216,255,52, -237,253,152,167,224,121,44,48,46,95,203,166,238,74,113,67,77,201,128,219, -152,164,82,6,0,203,76,64,64,9,210,211,18,4,4,144,221,49,40,64,76,13,211,19, -5,4,192,221,45,66,1,4,24,207,76,82,2,8,136,94,152,156,24,157,45,49,64,6,75, -191,76,80,66,149,110,116,116,197,8,41,240,247,79,70,188,6,183,27,76,80,194, -45,198,210,211,20,144,171,113,180,116,52,197,40,27,1,125,34,240,27,16,221, -42,240,27,221,109,66,32,104,129,163,115,52,224,5,139,168,209,233,138,32,57, -33,186,98,138,18,80,140,244,197,24,28,192,221,49,71,11,56,209,162,211,20, -183,1,66,188,17,145,52,40,9,148,226,134,153,5,198,137,136,32,14,12,30,164, -140,144,230,192,128,136,211,64,0,0,0,0,182,120,43,135,126,16,68,52,174,195, -144,12,2,158,4,128,70,22,24,128,101,67,112,163,192,100,104,176,131,192,99, -32,176,99,192,226,115,30,1,79,4,68,28,16,54,0,0,41,254,232,116,62,204,7,21, -35,18,54,127,80,28,192,132,28,32,14,96,197,212,243,193,48,188,240,39,130, -236,224,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,32,128,56,18,2,129,239,94,50,76,130,68,230, -202,113,160,167,146,94,163,134,66,161,164,227,226,231,146,51,198,249,147, -71,209,67,73,210,94,24,49,39,199,89,188,124,92,242,70,120,224,201,33,69,15, -155,163,191,68,28,98,79,143,139,166,233,225,228,227,139,198,37,210,244,208, -24,137,112,151,153,27,36,5,100,224,146,105,184,100,196,95,18,84,141,159,9, -121,145,178,67,155,46,33,38,187,168,252,211,243,81,92,2,14,40,16,50,37,202, -160,150,154,67,152,148,20,28,76,156,89,26,105,158,63,232,16,44,150,129,18, -146,44,28,96,14,98,216,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,18,195,152,147,226,27,61,138,41, -140,16,98,79,148,67,103,177,69,45,136,49,39,196,54,122,58,212,83,26,36,196, -159,40,134,207,71,90,138,92,16,98,79,136,108,244,244,168,166,56,73,137,62, -81,13,158,158,149,20,186,40,196,159,10,183,2,122,122,84,82,240,163,18,124, -42,220,9,235,106,81,75,225,228,73,241,13,158,197,54,198,8,145,39,202,33, -179,216,166,214,196,72,147,226,27,61,29,106,109,141,19,34,79,148,67,103, -163,173,77,174,8,145,39,196,54,122,122,84,219,28,38,68,159,40,134,207,79, -74,155,93,21,34,79,133,91,129,61,61,42,109,120,84,137,62,21,110,4,245,181, -41,181,248,56,224,28,24,80,113,50,113,100,105,166,120,255,160,20,28,76,156, -113,75,34,78,63,236,3,6,133,41,35,31,242,11,174,254,160,34,84,8,35,16,98, -146,38,55,32,33,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,30,7,224,0, -0,0,6,56,199,161,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,30,7,224, -0,0,0,6,55,36,33,30,135,19,36,182,158,72,237,17,100,97,27,56,0,0,30,7,224, -0,0,0,10,40,11,91,133,199,172,8,111,248,128,239,88,16,222,56,191,242,49, -198,69,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0,0,0, -49,185,65,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0,0, -0,49,198,77,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,240,63,0,0, -0,0,49,185,97,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0, -0,0,49,198,85,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0,0, -0,0,49,185,129,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0, -0,0,0,49,198,93,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,0,64,0, -0,0,0,49,185,161,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16,64, -0,0,0,0,49,198,101,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0,16, -64,0,0,0,0,49,185,193,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0,0, -16,64,0,0,0,0,49,198,109,8,244,56,153,37,180,242,71,104,139,35,8,217,192,0, -0,16,64,0,0,0,0,49,185,225,8,244,56,153,37,180,242,71,104,139,35,8,217,192, -0,0,16,64,0,0,0,0,49,198,117,8,244,56,153,37,180,242,71,104,139,35,8,217, -192,0,0,16,64,0,0,0,0,49,186,1,8,244,56,153,37,180,242,71,104,139,35,8,217, -192,0,0,32,64,0,0,0,0,49,198,125,8,244,56,153,37,180,242,71,104,139,35,8, -217,192,0,0,32,64,0,0,0,0,32,232,130,0,97,57,162,4,245,72,10,68,184,70,137, -195,67,77,175,32,66,37,192,208,165,36,117,196,10,14,38,78,44,141,52,207, -169,64,56,156,199,130,36,160,141,146,52,38,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,59,22,53,91,0,2, -21,11,94,181,128,196,133,0,185,80,32,56,156,199,130,36,160,72,16,78,126,53, -144,5,146,208,34,82,72,1,109,20,76,155,40,32,233,0,115,70,130,8,209,56,104, -105,187,252,193,3,17,162,112,201,242,18,65,211,0,230,149,132,17,162,112, -208,211,119,248,0,82,130,96,95,127,128,130,80,102,186,36,232,92,206,255,1, -80,48,200,39,12,158,241,64, +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, }; #else #error invalid endianness defines #endif #endif /* DUK_USE_ROM_OBJECTS */ + +/* automatic undefs */ +#undef DUK__REFCINIT /* * Error and fatal handling. */ @@ -10419,7 +10972,7 @@ DUK_INTERNAL const duk_uint8_t duk_builtins_data[3790] = { #if defined(DUK_USE_VERBOSE_ERRORS) -DUK_INTERNAL void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error_fmt(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *fmt, ...) { va_list ap; char msg[DUK__ERRFMT_BUFSIZE]; va_start(ap, fmt); @@ -10429,13 +10982,13 @@ DUK_INTERNAL void duk_err_handle_error_fmt(duk_hthread *thr, const char *filenam va_end(ap); /* dead code, but ensures portability (see Linux man page notes) */ } -DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, const char *filename, duk_uint_t line_and_code, const char *msg) { duk_err_create_and_throw(thr, (duk_errcode_t) (line_and_code >> 24), msg, filename, (duk_int_t) (line_and_code & 0x00ffffffL)); } #else /* DUK_USE_VERBOSE_ERRORS */ -DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { +DUK_INTERNAL DUK_COLD void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { duk_err_create_and_throw(thr, code); } @@ -10447,41 +11000,41 @@ DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { #if defined(DUK_USE_VERBOSE_ERRORS) #if defined(DUK_USE_PARANOID_ERRORS) -DUK_INTERNAL 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_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); } #else -DUK_INTERNAL 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_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); } #endif -DUK_INTERNAL void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_error_internal(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_INTERNAL_ERROR); } -DUK_INTERNAL void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_error_alloc_failed(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, DUK_STR_ALLOC_FAILED); } -DUK_INTERNAL void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { +DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_ERROR, message); } -DUK_INTERNAL void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { +DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr, const char *filename, duk_int_t linenumber, const char *message) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, message); } -DUK_INTERNAL void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx) { +DUK_INTERNAL DUK_COLD void duk_err_range_index(duk_hthread *thr, const char *filename, duk_int_t linenumber, duk_idx_t idx) { DUK_ERROR_RAW_FMT1(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, "invalid stack index %ld", (long) (idx)); } -DUK_INTERNAL void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_range_push_beyond(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_RANGE_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK); } -DUK_INTERNAL void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_args(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_ARGS); } -DUK_INTERNAL void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_state(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_STATE); } -DUK_INTERNAL void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber) { +DUK_INTERNAL DUK_COLD void duk_err_type_invalid_trap_result(duk_hthread *thr, const char *filename, duk_int_t linenumber) { DUK_ERROR_RAW(thr, filename, linenumber, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_TRAP_RESULT); } #else @@ -10493,25 +11046,25 @@ DUK_NORETURN(DUK_LOCAL_DECL void duk__err_shared(duk_hthread *thr, duk_uint_t co DUK_LOCAL void duk__err_shared(duk_hthread *thr, duk_uint_t code) { DUK_ERROR_RAW(thr, NULL, 0, code, NULL); } -DUK_INTERNAL void duk_err_error(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_error(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_ERROR); } -DUK_INTERNAL void duk_err_range(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_range(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_RANGE_ERROR); } -DUK_INTERNAL void duk_err_eval(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_eval(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_EVAL_ERROR); } -DUK_INTERNAL void duk_err_reference(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_reference(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_REFERENCE_ERROR); } -DUK_INTERNAL void duk_err_syntax(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_syntax(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_SYNTAX_ERROR); } -DUK_INTERNAL void duk_err_type(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_type(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_TYPE_ERROR); } -DUK_INTERNAL void duk_err_uri(duk_hthread *thr) { +DUK_INTERNAL DUK_COLD void duk_err_uri(duk_hthread *thr) { duk__err_shared(thr, DUK_ERR_URI_ERROR); } #endif @@ -10520,7 +11073,7 @@ DUK_INTERNAL void duk_err_uri(duk_hthread *thr) { * Default fatal error handler */ -DUK_INTERNAL void duk_default_fatal_handler(void *udata, const char *msg) { +DUK_INTERNAL DUK_COLD void duk_default_fatal_handler(void *udata, const char *msg) { DUK_UNREF(udata); DUK_UNREF(msg); @@ -12133,86 +12686,6 @@ DUK_INTERNAL duk_double_t duk_double_fmax(duk_double_t x, duk_double_t y) { */ return (x > y ? x : y); } -/* - * Round a number upwards to a prime (not usually the nearest one). - * - * Uses a table of successive 32-bit primes whose ratio is roughly - * constant. This keeps the relative upwards 'rounding error' bounded - * and the data size small. A simple 'predict-correct' compression is - * used to compress primes to one byte per prime. See genhashsizes.py - * for details. - * - * The minimum prime returned here must be coordinated with the possible - * probe sequence steps in duk_hobject and duk_heap stringtable. - */ - -/* #include duk_internal.h -> already included */ - -/* Awkward inclusion condition: drop out of compilation if not needed by any - * call site: object hash part or probing stringtable. - */ -#if defined(DUK_USE_HOBJECT_HASH_PART) || defined(DUK_USE_STRTAB_PROBE) - -/* hash size ratio goal, must match genhashsizes.py */ -#define DUK__HASH_SIZE_RATIO 1177 /* floor(1.15 * (1 << 10)) */ - -/* prediction corrections for prime list (see genhashsizes.py) */ -DUK_LOCAL const duk_int8_t duk__hash_size_corrections[] = { - 17, /* minimum prime */ - 4, 3, 4, 1, 4, 1, 1, 2, 2, 2, 2, 1, 6, 6, 9, 5, 1, 2, 2, 5, 1, 3, 3, 3, - 5, 4, 4, 2, 4, 8, 3, 4, 23, 2, 4, 7, 8, 11, 2, 12, 15, 10, 1, 1, 5, 1, 5, - 8, 9, 17, 14, 10, 7, 5, 2, 46, 21, 1, 9, 9, 4, 4, 10, 23, 36, 6, 20, 29, - 18, 6, 19, 21, 16, 11, 5, 5, 48, 9, 1, 39, 14, 8, 4, 29, 9, 1, 15, 48, 12, - 22, 6, 15, 27, 4, 2, 17, 28, 8, 9, 4, 5, 8, 3, 3, 8, 37, 11, 15, 8, 30, - 43, 6, 33, 41, 5, 20, 32, 41, 38, 24, 77, 14, 19, 11, 4, 35, 18, 19, 41, - 10, 23, 16, 9, 2, - -1 -}; - -/* probe steps (see genhashsizes.py), currently assumed to be 32 entries long - * (DUK_UTIL_GET_HASH_PROBE_STEP macro). - */ -DUK_INTERNAL duk_uint8_t duk_util_probe_steps[32] = { - 2, 3, 5, 7, 11, 13, 19, 31, 41, 47, 59, 67, 73, 79, 89, 101, 103, 107, - 109, 127, 137, 139, 149, 157, 163, 167, 173, 181, 191, 193, 197, 199 -}; - -DUK_INTERNAL duk_uint32_t duk_util_get_hash_prime(duk_uint32_t size) { - const duk_int8_t *p = duk__hash_size_corrections; - duk_uint32_t curr; - - curr = (duk_uint32_t) *p++; - for (;;) { - duk_small_int_t t = (duk_small_int_t) *p++; - if (t < 0) { - /* may happen if size is very close to 2^32-1 */ - break; - } - - /* prediction: portable variant using doubles if 64-bit values not available */ -#if defined(DUK_USE_64BIT_OPS) - curr = (duk_uint32_t) ((((duk_uint64_t) curr) * ((duk_uint64_t) DUK__HASH_SIZE_RATIO)) >> 10); -#else - /* 32-bit x 11-bit = 43-bit, fits accurately into a double */ - curr = (duk_uint32_t) DUK_FLOOR(((double) curr) * ((double) DUK__HASH_SIZE_RATIO) / 1024.0); -#endif - - /* correction */ - curr += t; - - DUK_DDD(DUK_DDDPRINT("size=%ld, curr=%ld", (long) size, (long) curr)); - - if (curr >= size) { - return curr; - } - } - return 0; -} - -#endif /* DUK_USE_HOBJECT_HASH_PART || DUK_USE_STRTAB_PROBE */ - -/* automatic undefs */ -#undef DUK__HASH_SIZE_RATIO /* * Hobject Ecmascript [[Class]]. */ @@ -12730,6 +13203,7 @@ static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompfunc *func, duk_bu DUK_RAW_WRITE_U32_BE(p, 0); #endif tmp32 = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) func); /* masks flags, only duk_hobject flags */ + tmp32 &= ~(DUK_HOBJECT_FLAG_HAVE_FINALIZER); /* finalizer flag is lost */ DUK_RAW_WRITE_U32_BE(p, tmp32); /* Bytecode instructions: endian conversion needed unless @@ -12909,7 +13383,7 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj)); DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj)); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj)); @@ -13035,14 +13509,23 @@ static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t * Must create a lexical environment on loading to allow * recursive functions like 'function foo() { foo(); }'. */ - duk_hobject *new_env; + duk_hdecenv *new_env; - new_env = duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - func_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); - func_env = new_env; + DUK_ASSERT(new_env->thread == NULL); /* Closed. */ + DUK_ASSERT(new_env->varmap == NULL); + DUK_ASSERT(new_env->regbase == 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); + DUK_HOBJECT_INCREF(thr, func_env); + + func_env = (duk_hobject *) new_env; + + duk_push_hobject(ctx, (duk_hobject *) new_env); duk_dup_m2(ctx); /* -> [ func funcname env funcname ] */ duk_dup(ctx, idx_base); /* -> [ func funcname env funcname func ] */ @@ -13695,7 +14178,7 @@ DUK_EXTERNAL duk_bool_t duk_is_constructor_call(duk_context *ctx) { DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->callstack_top >= 0); - act = duk_hthread_get_current_activation(thr); + act = thr->callstack_curr; if (act != NULL) { return ((act->flags & DUK_ACT_FLAG_CONSTRUCT) != 0 ? 1 : 0); } @@ -13728,12 +14211,13 @@ DUK_EXTERNAL duk_bool_t duk_is_strict_call(duk_context *ctx) { DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->callstack_top >= 0); - act = duk_hthread_get_current_activation(thr); - if (act == NULL) { + act = thr->callstack_curr; + if (act != NULL) { + return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0); + } else { /* Strict by default. */ return 1; } - return ((act->flags & DUK_ACT_FLAG_STRICT) != 0 ? 1 : 0); } /* @@ -13749,7 +14233,7 @@ DUK_EXTERNAL duk_int_t duk_get_current_magic(duk_context *ctx) { DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->callstack_top >= 0); - act = duk_hthread_get_current_activation(thr); + act = thr->callstack_curr; if (act) { func = DUK_ACT_GET_FUNC(act); if (!func) { @@ -13853,7 +14337,10 @@ DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_context *ctx, duk_idx_t idx DUK_ASSERT(duk_is_valid_index(ctx, idx)); /* checked by caller */ - ptr = duk_get_buffer_data_raw(ctx, idx, out_len, 0 /*throw_flag*/, &isbuffer); + /* 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); if (isbuffer) { DUK_ASSERT(*out_len == 0 || ptr != NULL); return (const duk_uint8_t *) ptr; @@ -14045,13 +14532,13 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ t <<= 6; } else { DUK_ASSERT(x == -1); - goto error; + goto decode_error; } } else { DUK_ASSERT(x >= 0 && x <= 63); if (n_equal > 0) { /* Don't allow actual chars after equal sign. */ - goto error; + goto decode_error; } t = (t << 6) + x; } @@ -14077,7 +14564,7 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ /* XX== */ dst -= 2; } else { - goto error; /* invalid padding */ + goto decode_error; /* invalid padding */ } /* Continue parsing after padding, allows concatenated, @@ -14101,13 +14588,13 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not * accepted. */ - goto error; + goto decode_error; } *out_dst_final = dst; return 1; - error: + decode_error: return 0; } #else /* DUK_USE_BASE64_FASTPATH */ @@ -14149,12 +14636,12 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ /* allow basic ASCII whitespace */ continue; } else { - goto error; + goto decode_error; } if (n_equal > 0) { /* Don't allow mixed padding and actual chars. */ - goto error; + goto decode_error; } t = (t << 6) + y; skip_add: @@ -14173,7 +14660,7 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ } else if (n_equal == 2) { dst -= 2; } else { - goto error; /* invalid padding */ + goto decode_error; /* invalid padding */ } /* Here we can choose either to end parsing and ignore @@ -14196,13 +14683,13 @@ DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_ * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not * accepted. */ - goto error; + goto decode_error; } *out_dst_final = dst; return 1; - error: + decode_error: return 0; } #endif /* DUK_USE_BASE64_FASTPATH */ @@ -14503,7 +14990,6 @@ 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_uint_t comp_flags; duk_int_t rc; DUK_ASSERT_CTX_VALID(ctx); @@ -14517,9 +15003,7 @@ DUK_EXTERNAL duk_int_t duk_eval_raw(duk_context *ctx, const char *src_buffer, du /* [ ... source? filename? ] (depends on flags) */ - comp_flags = flags; - comp_flags |= DUK_COMPILE_EVAL; - rc = duk_compile_raw(ctx, src_buffer, src_length, comp_flags); /* may be safe, or non-safe depending on flags */ + rc = duk_compile_raw(ctx, src_buffer, src_length, flags | DUK_COMPILE_EVAL); /* may be safe, or non-safe depending on flags */ /* [ ... closure/error ] */ @@ -14552,7 +15036,6 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) { duk_hthread *thr = (duk_hthread *) ctx; duk__compile_raw_args *comp_args; duk_uint_t flags; - duk_small_uint_t comp_flags; duk_hcompfunc *h_templ; DUK_ASSERT_CTX_VALID(ctx); @@ -14591,22 +15074,13 @@ DUK_LOCAL duk_ret_t duk__do_compile(duk_context *ctx, void *udata) { } DUK_ASSERT(comp_args->src_buffer != NULL); - /* XXX: unnecessary translation of flags */ - comp_flags = 0; - if (flags & DUK_COMPILE_EVAL) { - comp_flags |= DUK_JS_COMPILE_FLAG_EVAL; - } if (flags & DUK_COMPILE_FUNCTION) { - comp_flags |= DUK_JS_COMPILE_FLAG_EVAL | - DUK_JS_COMPILE_FLAG_FUNCEXPR; - } - if (flags & DUK_COMPILE_STRICT) { - comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; + flags |= DUK_COMPILE_EVAL | DUK_COMPILE_FUNCEXPR; } /* [ ... source? filename ] */ - duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, comp_flags); + duk_js_compile(thr, comp_args->src_buffer, comp_args->src_length, flags); /* [ ... source? func_template ] */ @@ -14759,16 +15233,16 @@ DUK_EXTERNAL void duk_debugger_attach(duk_context *ctx, /* Start in paused state. */ heap->dbg_processing = 0; - DUK_HEAP_SET_DEBUGGER_PAUSED(heap); - heap->dbg_state_dirty = 1; + heap->dbg_state_dirty = 0; heap->dbg_force_restart = 0; - heap->dbg_step_type = 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_exec_counter = 0; heap->dbg_last_counter = 0; heap->dbg_last_time = 0.0; + duk_debug_set_paused(heap); /* XXX: overlap with fields above */ /* Send version identification and flush right afterwards. Note that * we must write raw, unframed bytes here. @@ -14808,7 +15282,7 @@ DUK_EXTERNAL void duk_debugger_cooperate(duk_context *ctx) { DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); - if (!DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (!duk_debug_is_attached(thr->heap)) { return; } if (thr->callstack_top > 0 || thr->heap->dbg_processing) { @@ -14841,7 +15315,7 @@ DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) DUK_ERROR_RANGE(thr, "not enough stack values for notify"); return ret; /* unreachable */ } - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + 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); @@ -14854,7 +15328,7 @@ DUK_EXTERNAL duk_bool_t duk_debugger_notify(duk_context *ctx, duk_idx_t nvalues) * a transport error was not indicated by the transport write * callback. This is not a 100% guarantee of course. */ - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (duk_debug_is_attached(thr->heap)) { ret = 1; } } @@ -14873,15 +15347,19 @@ DUK_EXTERNAL void duk_debugger_pause(duk_context *ctx) { DUK_D(DUK_DPRINT("application called duk_debugger_pause()")); /* Treat like a debugger statement: ignore when not attached. */ - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { - DUK_HEAP_SET_PAUSED(thr->heap); + if (duk_debug_is_attached(thr->heap)) { + if (duk_debug_is_paused(thr->heap)) { + DUK_D(DUK_DPRINT("duk_debugger_pause() called when already paused; ignoring")); + } else { + duk_debug_set_paused(thr->heap); - /* Pause on the next opcode executed. This is always safe to do even - * inside the debugger message loop: the interrupt counter will be reset - * to its proper value when the message loop exits. - */ - thr->interrupt_init = 1; - thr->interrupt_counter = 0; + /* Pause on the next opcode executed. This is always safe to do even + * inside the debugger message loop: the interrupt counter will be reset + * to its proper value when the message loop exits. + */ + thr->interrupt_init = 1; + thr->interrupt_counter = 0; + } } } @@ -14952,7 +15430,7 @@ typedef struct duk_internal_thread_state duk_internal_thread_state; struct duk_internal_thread_state { duk_ljstate lj; - duk_bool_t handling_error; + duk_bool_t creating_error; duk_hthread *curr_thread; duk_int_t call_recursion_depth; }; @@ -15033,14 +15511,27 @@ DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) { DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(state != NULL); /* unvalidated */ + /* Currently not supported when called from within a finalizer. + * If that is done, the finalizer will remain running indefinitely, + * preventing other finalizers from executing. The assert is a bit + * wider, checking that it would be OK to run pending finalizers. + */ + DUK_ASSERT(thr->heap->pf_prevent_count == 0); + + /* Currently not supported to duk_suspend() from an errCreate() + * call. + */ + DUK_ASSERT(thr->heap->creating_error == 0); + heap = thr->heap; lj = &heap->lj; duk_push_tval(ctx, &lj->value1); duk_push_tval(ctx, &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)); - snapshot->handling_error = heap->handling_error; + snapshot->creating_error = heap->creating_error; snapshot->curr_thread = heap->curr_thread; snapshot->call_recursion_depth = heap->call_recursion_depth; @@ -15048,7 +15539,7 @@ DUK_EXTERNAL void duk_suspend(duk_context *ctx, duk_thread_state *state) { lj->type = DUK_LJ_TYPE_UNKNOWN; DUK_TVAL_SET_UNDEFINED(&lj->value1); DUK_TVAL_SET_UNDEFINED(&lj->value2); - heap->handling_error = 0; + heap->creating_error = 0; heap->curr_thread = NULL; heap->call_recursion_depth = 0; } @@ -15063,10 +15554,16 @@ DUK_EXTERNAL void duk_resume(duk_context *ctx, const duk_thread_state *state) { DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(state != NULL); /* unvalidated */ + /* Shouldn't be necessary if duk_suspend() is called before + * duk_resume(), but assert in case API sequence is incorrect. + */ + DUK_ASSERT(thr->heap->pf_prevent_count == 0); + DUK_ASSERT(thr->heap->creating_error == 0); + heap = thr->heap; DUK_MEMCPY((void *) &heap->lj, (const void *) &snapshot->lj, sizeof(duk_ljstate)); - heap->handling_error = snapshot->handling_error; + heap->creating_error = snapshot->creating_error; heap->curr_thread = snapshot->curr_thread; heap->call_recursion_depth = snapshot->call_recursion_depth; @@ -15078,7 +15575,7 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *h_glob; duk_hobject *h_prev_glob; - duk_hobject *h_env; + duk_hobjenv *h_env; duk_hobject *h_prev_env; DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(ctx, -1))); @@ -15105,29 +15602,30 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { * same (initial) built-ins. */ - h_env = duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), - -1); /* no prototype, updated below */ + h_env = duk_hobjenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); DUK_ASSERT(h_env != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_env) == NULL); - duk_dup_m2(ctx); - duk_dup_m3(ctx); - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); + DUK_ASSERT(h_env->target == NULL); + DUK_ASSERT(h_glob != NULL); + h_env->target = h_glob; + DUK_HOBJECT_INCREF(thr, h_glob); + DUK_ASSERT(h_env->has_this == 0); - /* [ ... new_glob new_env ] */ + /* [ ... new_glob ] */ h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; - thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_env; - DUK_HOBJECT_INCREF(thr, h_env); + thr->builtins[DUK_BIDX_GLOBAL_ENV] = (duk_hobject *) h_env; + DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_env); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ DUK_UNREF(h_env); /* without refcounts */ DUK_UNREF(h_prev_env); - /* [ ... new_glob new_env ] */ + /* [ ... new_glob ] */ - duk_pop_2(ctx); + duk_pop(ctx); /* [ ... ] */ } @@ -15199,18 +15697,19 @@ DUK_EXTERNAL void duk_inspect_value(duk_context *ctx, duk_idx_t idx) { DUK_UNREF(thr); - tv = duk_get_tval_or_unused(ctx, idx); - h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL); - /* 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 */ - duk_push_bare_object(ctx); + tv = duk_get_tval_or_unused(ctx, idx); + h = (DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? DUK_TVAL_GET_HEAPHDR(tv) : NULL); vals[DUK__IDX_TYPE] = duk_get_type_tval(tv); vals[DUK__IDX_ITAG] = (duk_uint_t) DUK_TVAL_GET_TAG(tv); + duk_push_bare_object(ctx); /* Invalidates 'tv'. */ + tv = NULL; + if (h == NULL) { goto finish; } @@ -16185,9 +16684,31 @@ DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx) { } DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t idx) { + duk_hobject *h; + duk_bool_t callable; + DUK_ASSERT_CTX_VALID(ctx); + h = duk_require_hobject(ctx, idx); /* Get before 'put' so that 'idx' is correct. */ + callable = duk_is_callable(ctx, -1); duk_put_prop_stridx(ctx, idx, DUK_STRIDX_INT_FINALIZER); + + /* In addition to setting the finalizer property, keep a "have + * finalizer" flag in duk_hobject in sync so that refzero can do + * a very quick finalizer check by walking the prototype chain + * and checking the flag alone. (Note that this means that just + * setting _Finalizer on an object won't affect finalizer checks.) + * + * NOTE: if the argument is a Proxy object, this flag will be set + * on the Proxy, not the target. As a result, the target won't get + * a finalizer flag and the Proxy also won't be finalized as there's + * an explicit Proxy check in finalization now. + */ + if (callable) { + DUK_HOBJECT_SET_HAVE_FINALIZER(h); + } else { + DUK_HOBJECT_CLEAR_HAVE_FINALIZER(h); + } } #else /* DUK_USE_FINALIZER_SUPPORT */ DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t idx) { @@ -16280,7 +16801,7 @@ DUK_LOCAL const duk_uint_t duk__type_mask_from_tag[] = { DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t idx, duk_uint_t tag); -DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t idx, duk_bool_t require) { +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_tval *tv; duk_small_int_t c; @@ -16340,10 +16861,11 @@ DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t idx, duk_boo DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); /* not reachable */ } - return 0; + + return def_value; } -DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t idx, duk_bool_t require) { +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_tval *tv; duk_small_int_t c; @@ -16393,7 +16915,8 @@ DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t idx, duk_b DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); /* not reachable */ } - return 0; + + return def_value; } /* @@ -16560,7 +17083,7 @@ DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t idx) { DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(DUK_INVALID_INDEX < 0); - if (duk_normalize_index(ctx, idx) < 0) { + if (DUK_UNLIKELY(duk_normalize_index(ctx, idx) < 0)) { DUK_ERROR_RANGE_INDEX(thr, idx); return; /* unreachable */ } @@ -16588,7 +17111,7 @@ DUK_INTERNAL duk_idx_t duk_get_top_require_min(duk_context *ctx, duk_idx_t min_t DUK_ASSERT_CTX_VALID(ctx); ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - if (ret < min_top) { + if (DUK_UNLIKELY(ret < min_top)) { DUK_ERROR_TYPE_INVALID_ARGS(thr); } return ret; @@ -16800,7 +17323,7 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) 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 (!new_valstack) { + if (DUK_UNLIKELY(new_valstack == NULL)) { /* Because new_size != 0, if condition doesn't need to be * (new_valstack != NULL || new_size == 0). */ @@ -16890,26 +17413,16 @@ DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) 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_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_size_t new_size; - duk_bool_t is_shrink = 0; - duk_small_uint_t shrink_flag = (flags & DUK_VSRESIZE_FLAG_SHRINK); + 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_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, " - "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d", - (unsigned long) min_new_size, - (long) (thr->valstack_end - thr->valstack), - (long) (thr->valstack_top - thr->valstack), - (long) (thr->valstack_bottom - thr->valstack), - (int) shrink_flag, (int) compact_flag, (int) throw_flag)); - DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->valstack_bottom >= thr->valstack); @@ -16925,11 +17438,8 @@ duk_bool_t duk_valstack_resize_raw(duk_context *ctx, if (min_new_size <= old_size) { is_shrink = 1; - if (!shrink_flag || - old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD) { - DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack")); - return 1; - } + } else { + is_shrink = 0; } new_size = min_new_size; @@ -16948,7 +17458,7 @@ duk_bool_t duk_valstack_resize_raw(duk_context *ctx, (unsigned long) old_size, (unsigned long) new_size, (unsigned long) min_new_size)); - if (new_size > thr->valstack_max) { + if (DUK_UNLIKELY(new_size > thr->valstack_max)) { /* Note: may be triggered even if minimal new_size would not reach the limit, * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account). */ @@ -16971,7 +17481,7 @@ duk_bool_t duk_valstack_resize_raw(duk_context *ctx, * size_t and pointer arithmetic won't wrap in duk__resize_valstack(). */ - if (!duk__resize_valstack(ctx, new_size)) { + 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; @@ -16990,6 +17500,44 @@ duk_bool_t duk_valstack_resize_raw(duk_context *ctx, return 1; } +DUK_INTERNAL duk_bool_t duk_valstack_resize_raw(duk_context *ctx, + duk_size_t min_new_size, + duk_small_uint_t flags) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_size_t old_size; + + DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, " + "curr_bottom=%ld, flags=%lx", + (unsigned long) min_new_size, + (long) (thr->valstack_end - thr->valstack), + (long) (thr->valstack_top - thr->valstack), + (long) (thr->valstack_bottom - thr->valstack), + (unsigned long) flags)); + + DUK_ASSERT_CTX_VALID(ctx); + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->valstack_bottom >= thr->valstack); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT(thr->valstack_end >= thr->valstack_top); + +#if defined(DUK_USE_PREFER_SIZE) + old_size = (duk_size_t) (thr->valstack_end - thr->valstack); +#else + DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size); + old_size = thr->valstack_size; +#endif + + if (DUK_LIKELY(min_new_size <= old_size)) { + if (DUK_LIKELY((flags & DUK_VSRESIZE_FLAG_SHRINK) == 0 || + old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD)) { + DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack")); + return 1; + } + } + + return duk__valstack_do_resize(ctx, min_new_size, flags); +} + DUK_EXTERNAL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra) { duk_hthread *thr = (duk_hthread *) ctx; duk_size_t min_new_size; @@ -17131,7 +17679,7 @@ DUK_EXTERNAL void duk_dup_top(duk_context *ctx) { thr = (duk_hthread *) ctx; DUK__CHECK_SPACE(); - if (thr->valstack_top - thr->valstack_bottom <= 0) { + if (DUK_UNLIKELY(thr->valstack_top - thr->valstack_bottom <= 0)) { DUK_ERROR_RANGE_INDEX(thr, -1); return; /* unreachable */ } @@ -17306,27 +17854,27 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, DUK_ASSERT(to_ctx != NULL); DUK_ASSERT(from_ctx != NULL); - if (to_ctx == from_ctx) { + if (DUK_UNLIKELY(to_ctx == from_ctx)) { DUK_ERROR_TYPE(to_thr, DUK_STR_INVALID_CONTEXT); return; } - if ((count < 0) || - (count > (duk_idx_t) to_thr->valstack_max)) { + if (DUK_UNLIKELY((count < 0) || + (count > (duk_idx_t) to_thr->valstack_max))) { /* Maximum value check ensures 'nbytes' won't wrap below. */ DUK_ERROR_RANGE_INVALID_COUNT(to_thr); return; } nbytes = sizeof(duk_tval) * count; - if (nbytes == 0) { + if (DUK_UNLIKELY(nbytes == 0)) { return; } DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end); - if ((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes) { + if (DUK_UNLIKELY((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes)) { DUK_ERROR_RANGE_PUSH_BEYOND(to_thr); } src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes); - if (src < (void *) from_thr->valstack_bottom) { + if (DUK_UNLIKELY(src < (void *) from_thr->valstack_bottom)) { DUK_ERROR_RANGE_INVALID_COUNT(to_thr); } @@ -17361,7 +17909,7 @@ DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, } /* - * Get/require + * Get/opt/require */ DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t idx) { @@ -17372,7 +17920,7 @@ DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_UNDEFINED(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_UNDEFINED(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "undefined", DUK_STR_NOT_UNDEFINED); } } @@ -17385,13 +17933,13 @@ DUK_EXTERNAL void duk_require_null(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_NULL(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_NULL(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "null", DUK_STR_NOT_NULL); } } -DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx) { - duk_bool_t ret = 0; /* default: false */ +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_bool_t ret; duk_tval *tv; DUK_ASSERT_CTX_VALID(ctx); @@ -17400,12 +17948,27 @@ DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx) { DUK_ASSERT(tv != NULL); if (DUK_TVAL_IS_BOOLEAN(tv)) { ret = DUK_TVAL_GET_BOOLEAN(tv); + DUK_ASSERT(ret == 0 || ret == 1); + } else { + ret = def_value; + /* Not guaranteed to be 0 or 1. */ } - DUK_ASSERT(ret == 0 || ret == 1); return ret; } +DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t idx) { + DUK_ASSERT_CTX_VALID(ctx); + + return duk__get_boolean_raw(ctx, idx, 0); /* default: false */ +} + +DUK_EXTERNAL duk_bool_t duk_get_boolean_default(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value) { + DUK_ASSERT_CTX_VALID(ctx); + + return duk__get_boolean_raw(ctx, idx, def_value); +} + DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; @@ -17415,35 +17978,61 @@ DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_BOOLEAN(tv)) { + if (DUK_LIKELY(DUK_TVAL_IS_BOOLEAN(tv))) { + ret = DUK_TVAL_GET_BOOLEAN(tv); + DUK_ASSERT(ret == 0 || ret == 1); + return ret; + } else { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "boolean", DUK_STR_NOT_BOOLEAN); } - ret = DUK_TVAL_GET_BOOLEAN(tv); - DUK_ASSERT(ret == 0 || ret == 1); - return ret; } -DUK_EXTERNAL duk_double_t duk_get_number(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); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_boolean(ctx, idx); +} + +DUK_LOCAL DUK_ALWAYS_INLINE duk_double_t duk__get_number_raw(duk_context *ctx, duk_idx_t idx, duk_double_t def_value) { duk_double_union ret; duk_tval *tv; DUK_ASSERT_CTX_VALID(ctx); - ret.d = DUK_DOUBLE_NAN; /* default: NaN */ tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_NUMBER(tv)) { - ret.d = DUK_TVAL_GET_NUMBER(tv); +#if defined(DUK_USE_FASTINT) + if (DUK_TVAL_IS_FASTINT(tv)) { + ret.d = (duk_double_t) DUK_TVAL_GET_FASTINT(tv); /* XXX: cast trick */ + } + else +#endif + if (DUK_TVAL_IS_DOUBLE(tv)) { + /* When using packed duk_tval, number must be in NaN-normalized form + * for it to be a duk_tval, so no need to normalize. NOP for unpacked + * duk_tval. + */ + ret.d = DUK_TVAL_GET_DOUBLE(tv); + DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); + } else { + ret.d = def_value; + /* Default value (including NaN) may not be normalized. */ } - /* When using packed duk_tval, number must be in NaN-normalized form - * for it to be a duk_tval, so no need to normalize. NOP for unpacked - * duk_tval. - */ - DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&ret)); return ret.d; } +DUK_EXTERNAL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t idx) { + return duk__get_number_raw(ctx, idx, DUK_DOUBLE_NAN); /* default: NaN */ +} + +DUK_EXTERNAL duk_double_t duk_get_number_default(duk_context *ctx, duk_idx_t idx, duk_double_t def_value) { + return duk__get_number_raw(ctx, idx, def_value); +} + DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; @@ -17453,7 +18042,7 @@ DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_NUMBER(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_NUMBER(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "number", DUK_STR_NOT_NUMBER); } @@ -17467,56 +18056,89 @@ 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); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + /* User provided default is not NaN normalized. */ + return def_value; + } + return duk_require_number(ctx, idx); +} + DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t idx) { - /* Custom coercion for API */ DUK_ASSERT_CTX_VALID(ctx); - return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*require*/); + + return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 0 /*require*/); } DUK_EXTERNAL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t idx) { - /* Custom coercion for API */ DUK_ASSERT_CTX_VALID(ctx); - return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*require*/); + + return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 0 /*require*/); +} + +DUK_EXTERNAL duk_int_t duk_get_int_default(duk_context *ctx, duk_idx_t idx, duk_int_t def_value) { + DUK_ASSERT_CTX_VALID(ctx); + + return (duk_int_t) duk__api_coerce_d2i(ctx, idx, def_value, 0 /*require*/); +} + +DUK_EXTERNAL duk_uint_t duk_get_uint_default(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value) { + DUK_ASSERT_CTX_VALID(ctx); + + return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, def_value, 0 /*require*/); } DUK_EXTERNAL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t idx) { - /* Custom coercion for API */ DUK_ASSERT_CTX_VALID(ctx); - return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 1 /*require*/); + + return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 1 /*require*/); } DUK_EXTERNAL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t idx) { - /* Custom coercion for API */ DUK_ASSERT_CTX_VALID(ctx); - return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 1 /*require*/); + + return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 1 /*require*/); } -DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) { - const char *ret; - duk_tval *tv; +DUK_EXTERNAL duk_int_t duk_opt_int(duk_context *ctx, duk_idx_t idx, duk_int_t def_value) { + DUK_ASSERT_CTX_VALID(ctx); + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_int(ctx, idx); +} + +DUK_EXTERNAL duk_uint_t duk_opt_uint(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value) { DUK_ASSERT_CTX_VALID(ctx); - /* default: NULL, length 0 */ - ret = NULL; - if (out_len) { - *out_len = 0; + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; } + return duk_require_uint(ctx, idx); +} - tv = duk_get_tval_or_unused(ctx, idx); - DUK_ASSERT(tv != NULL); - if (DUK_TVAL_IS_STRING(tv)) { - /* Here we rely on duk_hstring instances always being zero - * terminated even if the actual string is not. - */ - duk_hstring *h = DUK_TVAL_GET_STRING(tv); - DUK_ASSERT(h != NULL); +DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len) { + duk_hstring *h; + const char *ret; + duk_size_t len; + + DUK_ASSERT_CTX_VALID(ctx); + + h = duk_get_hstring(ctx, idx); + if (h != NULL) { + len = DUK_HSTRING_GET_BYTELEN(h); ret = (const char *) DUK_HSTRING_GET_DATA(h); - if (out_len) { - *out_len = DUK_HSTRING_GET_BYTELEN(h); - } + } else { + len = 0; + ret = NULL; } + if (out_len != NULL) { + *out_len = len; + } return ret; } @@ -17547,9 +18169,72 @@ DUK_INTERNAL const char *duk_require_lstring_notsymbol(duk_context *ctx, duk_idx } DUK_EXTERNAL const char *duk_get_string(duk_context *ctx, duk_idx_t idx) { + duk_hstring *h; + + DUK_ASSERT_CTX_VALID(ctx); + + h = duk_get_hstring(ctx, idx); + if (h != NULL) { + return (const char *) DUK_HSTRING_GET_DATA(h); + } else { + return NULL; + } +} + +DUK_EXTERNAL const char *duk_opt_lstring(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_len != NULL) { + *out_len = def_len; + } + return def_ptr; + } + return duk_require_lstring(ctx, idx, out_len); +} + +DUK_EXTERNAL const char *duk_opt_string(duk_context *ctx, duk_idx_t idx, const char *def_ptr) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_ptr; + } + return duk_require_string(ctx, idx); +} + +DUK_EXTERNAL const char *duk_get_lstring_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_len, const char *def_ptr, duk_size_t def_len) { + duk_hstring *h; + const char *ret; + duk_size_t len; + + DUK_ASSERT_CTX_VALID(ctx); + + h = duk_get_hstring(ctx, idx); + if (h != NULL) { + len = DUK_HSTRING_GET_BYTELEN(h); + ret = (const char *) DUK_HSTRING_GET_DATA(h); + } else { + len = def_len; + ret = def_ptr; + } + + if (out_len != NULL) { + *out_len = len; + } + return ret; +} + +DUK_EXTERNAL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, const char *def_value) { + duk_hstring *h; + DUK_ASSERT_CTX_VALID(ctx); - return duk_get_lstring(ctx, idx, NULL); + h = duk_get_hstring(ctx, idx); + if (h != NULL) { + return (const char *) DUK_HSTRING_GET_DATA(h); + } else { + return def_value; + } } DUK_INTERNAL const char *duk_get_string_notsymbol(duk_context *ctx, duk_idx_t idx) { @@ -17581,7 +18266,7 @@ DUK_INTERNAL const char *duk_require_string_notsymbol(duk_context *ctx, duk_idx_ return (const char *) DUK_HSTRING_GET_DATA(h); } -DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx) { +DUK_LOCAL void *duk__get_pointer_raw(duk_context *ctx, duk_idx_t idx, void *def_value) { duk_tval *tv; void *p; @@ -17590,13 +18275,30 @@ DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); if (!DUK_TVAL_IS_POINTER(tv)) { - return NULL; + return def_value; } p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ return p; } +DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx) { + return duk__get_pointer_raw(ctx, idx, NULL /*def_value*/); +} + +DUK_EXTERNAL void *duk_opt_pointer(duk_context *ctx, duk_idx_t idx, void *def_value) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_pointer(ctx, idx); +} + +DUK_EXTERNAL void *duk_get_pointer_default(duk_context *ctx, duk_idx_t idx, void *def_value) { + return duk__get_pointer_raw(ctx, idx, def_value); +} + DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; @@ -17609,7 +18311,7 @@ DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t idx) { */ tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_POINTER(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_POINTER(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "pointer", DUK_STR_NOT_POINTER); } p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */ @@ -17635,10 +18337,12 @@ 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, duk_bool_t throw_flag) { +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_tval *tv; duk_hbuffer *h; + void *ret; + duk_size_t len; + duk_tval *tv; DUK_ASSERT_CTX_VALID(ctx); DUK_UNREF(thr); @@ -17649,27 +18353,54 @@ DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t idx, duk_size tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_BUFFER(tv)) { + if (DUK_LIKELY(DUK_TVAL_IS_BUFFER(tv))) { + h = DUK_TVAL_GET_BUFFER(tv); + DUK_ASSERT(h != NULL); + + len = DUK_HBUFFER_GET_SIZE(h); + ret = DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); + } else { if (throw_flag) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); } - return NULL; + len = def_size; + ret = def_ptr; } - h = DUK_TVAL_GET_BUFFER(tv); - DUK_ASSERT(h != NULL); - if (out_size) { - *out_size = DUK_HBUFFER_GET_SIZE(h); + if (out_size != NULL) { + *out_size = len; } - return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */ + return ret; } DUK_EXTERNAL void *duk_get_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) { - return duk__get_buffer_helper(ctx, idx, out_size, 0 /*throw_flag*/); + DUK_ASSERT_CTX_VALID(ctx); + + return duk__get_buffer_helper(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/); +} + +DUK_EXTERNAL void *duk_opt_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_size != NULL) { + *out_size = def_size; + } + return def_ptr; + } + return duk_require_buffer(ctx, idx, out_size); +} + +DUK_EXTERNAL void *duk_get_buffer_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_len) { + DUK_ASSERT_CTX_VALID(ctx); + + return duk__get_buffer_helper(ctx, idx, out_size, def_ptr, def_len, 0 /*throw_flag*/); } DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) { - return duk__get_buffer_helper(ctx, idx, out_size, 1 /*throw_flag*/); + DUK_ASSERT_CTX_VALID(ctx); + + return duk__get_buffer_helper(ctx, 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. @@ -17677,7 +18408,7 @@ 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, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) { +DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size, duk_bool_t throw_flag, duk_bool_t *out_isbuffer) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; @@ -17688,7 +18419,7 @@ DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_ *out_isbuffer = 0; } if (out_size != NULL) { - *out_size = 0; + *out_size = def_size; } tv = duk_get_tval_or_unused(ctx, idx); @@ -17737,15 +18468,31 @@ DUK_INTERNAL void *duk_get_buffer_data_raw(duk_context *ctx, duk_idx_t idx, duk_ if (throw_flag) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "buffer", DUK_STR_NOT_BUFFER); } - return NULL; + 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, 0 /*throw_flag*/, NULL); + return duk_get_buffer_data_raw(ctx, idx, out_size, NULL /*def_ptr*/, 0 /*def_size*/, 0 /*throw_flag*/, NULL); +} + +DUK_EXTERNAL void *duk_get_buffer_data_default(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + return duk_get_buffer_data_raw(ctx, idx, out_size, def_ptr, def_size, 0 /*throw_flag*/, NULL); +} + +DUK_EXTERNAL void *duk_opt_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size, void *def_ptr, duk_size_t def_size) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + if (out_size != NULL) { + *out_size = def_size; + } + return def_ptr; + } + return duk_require_buffer_data(ctx, idx, out_size); } DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t idx, duk_size_t *out_size) { - return duk_get_buffer_data_raw(ctx, idx, out_size, 1 /*throw_flag*/, NULL); + return duk_get_buffer_data_raw(ctx, 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. @@ -17777,7 +18524,7 @@ DUK_INTERNAL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t idx) { 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 (res && DUK_HSTRING_HAS_SYMBOL(res)) { + if (DUK_UNLIKELY(res && DUK_HSTRING_HAS_SYMBOL(res))) { return NULL; } return res; @@ -17786,7 +18533,7 @@ DUK_INTERNAL duk_hstring *duk_get_hstring_notsymbol(duk_context *ctx, duk_idx_t DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t idx) { duk_hstring *h; h = (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING); - if (h == NULL) { + if (DUK_UNLIKELY(h == NULL)) { DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "string", DUK_STR_NOT_STRING); } return h; @@ -17795,7 +18542,7 @@ DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t idx) { DUK_INTERNAL duk_hstring *duk_require_hstring_notsymbol(duk_context *ctx, duk_idx_t idx) { duk_hstring *h; h = (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_STRING); - if (h == NULL || DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(h == NULL || DUK_HSTRING_HAS_SYMBOL(h))) { DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "string", DUK_STR_NOT_STRING); } return h; @@ -17808,7 +18555,7 @@ DUK_INTERNAL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t idx) { DUK_INTERNAL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t idx) { duk_hobject *h; h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT); - if (h == NULL) { + if (DUK_UNLIKELY(h == NULL)) { DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "object", DUK_STR_NOT_OBJECT); } return h; @@ -17821,7 +18568,7 @@ DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t idx) { DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t idx) { duk_hbuffer *h; h = (duk_hbuffer *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_BUFFER); - if (h == NULL) { + if (DUK_UNLIKELY(h == NULL)) { DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, idx, "buffer", DUK_STR_NOT_BUFFER); } return h; @@ -17829,7 +18576,7 @@ DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t idx) { 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); - if (h != NULL && !DUK_HOBJECT_IS_THREAD(h)) { + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_THREAD(h))) { h = NULL; } return (duk_hthread *) h; @@ -17838,7 +18585,7 @@ DUK_INTERNAL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t idx) { 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); - if (!(h != NULL && DUK_HOBJECT_IS_THREAD(h))) { + 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; @@ -17846,7 +18593,7 @@ DUK_INTERNAL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t idx) { 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); - if (h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h)) { + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_COMPFUNC(h))) { h = NULL; } return (duk_hcompfunc *) h; @@ -17855,7 +18602,7 @@ DUK_INTERNAL duk_hcompfunc *duk_get_hcompfunc(duk_context *ctx, duk_idx_t idx) { 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); - if (!(h != NULL && DUK_HOBJECT_IS_COMPFUNC(h))) { + 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; @@ -17863,7 +18610,7 @@ DUK_INTERNAL duk_hcompfunc *duk_require_hcompfunc(duk_context *ctx, duk_idx_t id 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); - if (h != NULL && !DUK_HOBJECT_IS_NATFUNC(h)) { + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_IS_NATFUNC(h))) { h = NULL; } return (duk_hnatfunc *) h; @@ -17872,7 +18619,7 @@ DUK_INTERNAL duk_hnatfunc *duk_get_hnatfunc(duk_context *ctx, duk_idx_t idx) { 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); - if (!(h != NULL && DUK_HOBJECT_IS_NATFUNC(h))) { + 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; @@ -17887,13 +18634,13 @@ DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx) tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_OBJECT(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_OBJECT(tv))) { return NULL; } h = DUK_TVAL_GET_OBJECT(tv); DUK_ASSERT(h != NULL); - if (!DUK_HOBJECT_IS_NATFUNC(h)) { + if (DUK_UNLIKELY(!DUK_HOBJECT_IS_NATFUNC(h))) { return NULL; } DUK_ASSERT(DUK_HOBJECT_HAS_NATFUNC(h)); @@ -17902,6 +18649,28 @@ 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); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_c_function(ctx, idx); +} + +DUK_EXTERNAL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value) { + duk_c_function ret; + + DUK_ASSERT_CTX_VALID(ctx); + + ret = duk_get_c_function(ctx, idx); + if (ret != NULL) { + return ret; + } + + 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_c_function ret; @@ -17909,14 +18678,14 @@ DUK_EXTERNAL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t i DUK_ASSERT_CTX_VALID(ctx); ret = duk_get_c_function(ctx, idx); - if (!ret) { + 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_is_function(ctx, idx)) { + if (DUK_UNLIKELY(!duk_is_function(ctx, idx))) { DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, idx, "function", DUK_STR_NOT_FUNCTION); } } @@ -17925,7 +18694,7 @@ DUK_INTERNAL_DECL void duk_require_constructable(duk_context *ctx, duk_idx_t idx duk_hobject *h; h = duk_require_hobject_accept_mask(ctx, idx, DUK_TYPE_MASK_LIGHTFUNC); - if (h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) { + if (DUK_UNLIKELY(h != NULL && !DUK_HOBJECT_HAS_CONSTRUCTABLE(h))) { DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, idx, "constructable", DUK_STR_NOT_CONSTRUCTABLE); } /* Lightfuncs (h == NULL) are constructable. */ @@ -17943,6 +18712,28 @@ DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t idx) { return (duk_context *) duk_require_hthread(ctx, idx); } +DUK_EXTERNAL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value) { + DUK_ASSERT_CTX_VALID(ctx); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_context(ctx, idx); +} + +DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx_t idx, duk_context *def_value) { + duk_context *ret; + + DUK_ASSERT_CTX_VALID(ctx); + + ret = duk_get_context(ctx, idx); + if (ret != NULL) { + return ret; + } + + return def_value; +} + DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx) { duk_tval *tv; void *ret; @@ -17951,7 +18742,7 @@ DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { return (void *) NULL; } @@ -17960,6 +18751,28 @@ 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); + + if (duk_check_type_mask(ctx, idx, DUK_TYPE_MASK_NONE | DUK_TYPE_MASK_UNDEFINED)) { + return def_value; + } + return duk_require_heapptr(ctx, idx); +} + +DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value) { + void *ret; + + DUK_ASSERT_CTX_VALID(ctx); + + ret = duk_get_heapptr(ctx, idx); + if (ret != NULL) { + return ret; + } + + return def_value; +} + DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; @@ -17969,7 +18782,7 @@ DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t idx) { tv = duk_get_tval_or_unused(ctx, idx); DUK_ASSERT(tv != NULL); - if (!DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + if (DUK_UNLIKELY(!DUK_TVAL_IS_HEAP_ALLOCATED(tv))) { DUK_ERROR_REQUIRE_TYPE_INDEX(thr, idx, "heapobject", DUK_STR_UNEXPECTED_TYPE); } @@ -17986,7 +18799,7 @@ DUK_LOCAL duk_hobject *duk__get_hobject_promote_mask_raw(duk_context *ctx, duk_i DUK_ASSERT_CTX_VALID(ctx); res = duk_get_hobject(ctx, idx); /* common case, not promoted */ - if (res != NULL) { + if (DUK_LIKELY(res != NULL)) { DUK_ASSERT(res != NULL); return res; } @@ -18041,7 +18854,7 @@ DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX); h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT); - if (h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum) { + if (DUK_UNLIKELY(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum)) { h = NULL; } return h; @@ -18057,7 +18870,7 @@ DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_i thr = (duk_hthread *) ctx; h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, idx, DUK_TAG_OBJECT); - if (!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum)) { + 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)); DUK_UNREF(h_class); @@ -18099,7 +18912,7 @@ DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx) { case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { return 0; } return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h); @@ -18401,6 +19214,16 @@ DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t idx, tv = duk_require_tval(ctx, idx); DUK_ASSERT(tv != NULL); + +#if defined(DUK_USE_FASTINT) + /* If argument is a fastint, guarantee that it remains one. + * There's no downgrade check for other cases. + */ + if (DUK_TVAL_IS_FASTINT(tv)) { + /* XXX: Unnecessary conversion back and forth. */ + return (duk_double_t) DUK_TVAL_GET_FASTINT(tv); + } +#endif d = coerce_func(thr, tv); /* XXX: fastint? */ @@ -18412,21 +19235,21 @@ DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t idx, } DUK_EXTERNAL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t idx) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4 - * API return value coercion: custom + /* 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 /*require*/); + return (duk_int_t) duk__api_coerce_d2i(ctx, idx, 0 /*def_value*/, 0 /*require*/); } DUK_EXTERNAL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t idx) { - /* Value coercion (in stack): ToInteger(), E5 Section 9.4 - * API return value coercion: custom + /* 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 /*require*/); + return (duk_uint_t) duk__api_coerce_d2ui(ctx, idx, 0 /*def_value*/, 0 /*require*/); } DUK_EXTERNAL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t idx) { @@ -18630,7 +19453,7 @@ DUK_INTERNAL void duk_push_class_string_tval(duk_context *ctx, duk_tval *tv) { duk_hstring *h; h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { stridx = DUK_STRIDX_UC_SYMBOL; } else { stridx = DUK_STRIDX_UC_STRING; @@ -18788,7 +19611,7 @@ DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t idx) { duk_hstring *h; h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CANNOT_STRING_COERCE_SYMBOL); } else { goto skip_replace; @@ -18868,7 +19691,7 @@ DUK_INTERNAL duk_hstring *duk_to_hstring_acceptsymbol(duk_context *ctx, duk_idx_ duk_hstring *ret; DUK_ASSERT_CTX_VALID(ctx); ret = duk_get_hstring(ctx, idx); - if (ret && DUK_HSTRING_HAS_SYMBOL(ret)) { + if (DUK_UNLIKELY(ret && DUK_HSTRING_HAS_SYMBOL(ret))) { return ret; } return duk_to_hstring(ctx, idx); @@ -19021,6 +19844,7 @@ 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_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | @@ -19071,6 +19895,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { } case DUK_TAG_BOOLEAN: { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN); proto = DUK_BIDX_BOOLEAN_PROTOTYPE; goto create_object; @@ -19079,12 +19904,14 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { duk_hstring *h; h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_SYMBOL); proto = DUK_BIDX_SYMBOL_PROTOTYPE; } else { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING); proto = DUK_BIDX_STRING_PROTOTYPE; @@ -19114,6 +19941,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ case DUK_TAG_POINTER: { flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER); proto = DUK_BIDX_POINTER_PROTOTYPE; goto create_object; @@ -19142,7 +19970,8 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t idx) { DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER); + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER); proto = DUK_BIDX_NUMBER_PROTOTYPE; goto create_object; } @@ -19360,7 +20189,7 @@ DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t idx, duk DUK_ASSERT_CTX_VALID(ctx); - if (duk_get_type_mask(ctx, idx) & mask) { + if (DUK_LIKELY(duk_get_type_mask(ctx, idx) & mask)) { return 1; } if (mask & DUK_TYPE_MASK_THROW) { @@ -19486,7 +20315,10 @@ DUK_EXTERNAL duk_bool_t duk_is_symbol(duk_context *ctx, duk_idx_t idx) { DUK_ASSERT_CTX_VALID(ctx); h = duk_get_hstring(ctx, idx); - if (h != NULL && DUK_HSTRING_HAS_SYMBOL(h)) { + /* Use DUK_LIKELY() here because caller may be more likely to type + * check an expected symbol than not. + */ + if (DUK_LIKELY(h != NULL && DUK_HSTRING_HAS_SYMBOL(h))) { return 1; } return 0; @@ -19542,10 +20374,15 @@ DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t idx) { } DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t idx) { + duk_hobject *obj; + DUK_ASSERT_CTX_VALID(ctx); - return duk__obj_flag_any_default_false(ctx, - idx, - DUK_HOBJECT_FLAG_THREAD); + + obj = duk_get_hobject(ctx, 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) { @@ -19816,9 +20653,7 @@ DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk DUK_ASSERT_CTX_VALID(ctx); /* check stack before interning (avoid hanging temp) */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); /* NULL with zero length represents an empty string; NULL with higher * length is also now trated like an empty string although it is @@ -19830,11 +20665,11 @@ DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk } /* Check for maximum string length */ - if (len > DUK_HSTRING_MAX_BYTELEN) { + if (DUK_UNLIKELY(len > DUK_HSTRING_MAX_BYTELEN)) { DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG); } - h = duk_heap_string_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); + h = duk_heap_strtable_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len); DUK_ASSERT(h != NULL); tv_slot = thr->valstack_top++; @@ -19952,6 +20787,7 @@ DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx) { thr = (duk_hthread *) ctx; DUK_ASSERT(thr->callstack_top > 0); /* caller required to know */ + DUK_ASSERT(thr->callstack_curr != NULL); DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* consequence of above */ DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack); /* 'this' binding exists */ @@ -19967,8 +20803,8 @@ DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) { DUK_ASSERT_DISABLE(thr->callstack_top >= 0); DUK_ASSERT(thr->callstack_top <= thr->callstack_size); - act = duk_hthread_get_current_activation(thr); - if (act) { + act = thr->callstack_curr; + if (act != NULL) { duk_push_tval(ctx, &act->tv_func); } else { duk_push_undefined(ctx); @@ -20026,7 +20862,7 @@ DUK_EXTERNAL void duk_push_global_stash(duk_context *ctx) { DUK_EXTERNAL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx) { duk_hthread *thr = (duk_hthread *) ctx; DUK_ASSERT_CTX_VALID(ctx); - if (!target_ctx) { + if (DUK_UNLIKELY(target_ctx == NULL)) { DUK_ERROR_TYPE_INVALID_ARGS(thr); return; /* not reached */ } @@ -20103,7 +20939,7 @@ DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va /* failed, resize and try again */ sz = sz * 2; - if (sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT) { + if (DUK_UNLIKELY(sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT)) { DUK_ERROR_RANGE(thr, DUK_STR_RESULT_TOO_LONG); } } @@ -20141,15 +20977,10 @@ DUK_INTERNAL duk_hobject *duk_push_object_helper(duk_context *ctx, duk_uint_t ho DUK_ASSERT(prototype_bidx == -1 || (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS)); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); - h = duk_hobject_alloc(thr->heap, hobject_flags_and_class); - if (!h) { - DUK_ERROR_ALLOC_FAILED(thr); - } + h = duk_hobject_alloc(thr, hobject_flags_and_class); + DUK_ASSERT(h != NULL); DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags)); @@ -20188,6 +21019,7 @@ DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) { (void) duk_push_object_helper(ctx, 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); @@ -20203,14 +21035,13 @@ DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) { DUK_ASSERT_CTX_VALID(ctx); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_ARRAY_PART | DUK_HOBJECT_FLAG_EXOTIC_ARRAY | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY); - obj = duk_harray_alloc(thr->heap, flags); - if (!obj) { - DUK_ERROR_ALLOC_FAILED(thr); - } + 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]); @@ -20264,18 +21095,12 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { DUK_ASSERT_CTX_VALID(ctx); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); - obj = duk_hthread_alloc(thr->heap, + obj = duk_hthread_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_THREAD | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); - if (!obj) { - DUK_ERROR_ALLOC_FAILED(thr); - } + DUK_ASSERT(obj != NULL); obj->state = DUK_HTHREAD_STATE_INACTIVE; #if defined(DUK_USE_ROM_STRINGS) /* Nothing to initialize, strs[] is in ROM. */ @@ -20296,7 +21121,7 @@ DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) { thr->valstack_top++; /* important to do this *after* pushing, to make the thread reachable for gc */ - if (!duk_hthread_init_stacks(thr->heap, obj)) { + if (DUK_UNLIKELY(!duk_hthread_init_stacks(thr->heap, obj))) { DUK_ERROR_ALLOC_FAILED(thr); } @@ -20327,21 +21152,18 @@ DUK_INTERNAL duk_hcompfunc *duk_push_hcompfunc(duk_context *ctx) { DUK_ASSERT_CTX_VALID(ctx); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); /* Template functions are not strictly constructable (they don't * have a "prototype" property for instance), so leave the * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here. */ - obj = duk_hcompfunc_alloc(thr->heap, + obj = duk_hcompfunc_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_COMPFUNC | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION)); - if (!obj) { + if (DUK_UNLIKELY(obj == NULL)) { DUK_ERROR_ALLOC_FAILED(thr); } @@ -20366,11 +21188,9 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu DUK_ASSERT_CTX_VALID(ctx); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } - if (func == NULL) { + DUK__CHECK_SPACE(); + + if (DUK_UNLIKELY(func == NULL)) { goto api_error; } if (nargs >= 0 && nargs < DUK_HNATFUNC_NARGS_MAX) { @@ -20381,10 +21201,8 @@ DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function fu goto api_error; } - obj = duk_hnatfunc_alloc(thr->heap, flags); - if (!obj) { - DUK_ERROR_ALLOC_FAILED(thr); - } + obj = duk_hnatfunc_alloc(thr, flags); + DUK_ASSERT(obj != NULL); obj->func = func; obj->nargs = func_nargs; @@ -20415,6 +21233,7 @@ DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | @@ -20432,6 +21251,7 @@ DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function flags = DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_CONSTRUCTABLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | @@ -20447,6 +21267,7 @@ DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk DUK_ASSERT_CTX_VALID(ctx); flags = DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_NATFUNC | DUK_HOBJECT_FLAG_NEWENV | DUK_HOBJECT_FLAG_STRICT | @@ -20463,10 +21284,7 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun DUK_ASSERT_CTX_VALID(ctx); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) { /* as is */ @@ -20475,10 +21293,10 @@ DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function fun } else { goto api_error; } - if (!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX)) { + if (DUK_UNLIKELY(!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX))) { goto api_error; } - if (!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX)) { + if (DUK_UNLIKELY(!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX))) { goto api_error; } @@ -20502,15 +21320,10 @@ DUK_INTERNAL duk_hbufobj *duk_push_bufobj_raw(duk_context *ctx, duk_uint_t hobje DUK_ASSERT(ctx != NULL); DUK_ASSERT(prototype_bidx >= 0); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); - obj = duk_hbufobj_alloc(thr->heap, hobject_flags_and_class); - if (!obj) { - DUK_ERROR_ALLOC_FAILED(thr); - } + 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_ASSERT_HBUFOBJ_VALID(obj); @@ -20570,19 +21383,19 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, uint_offset = (duk_uint_t) byte_offset; uint_length = (duk_uint_t) byte_length; if (sizeof(duk_size_t) != sizeof(duk_uint_t)) { - if ((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length) { + if (DUK_UNLIKELY((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length)) { goto range_error; } } uint_added = uint_offset + uint_length; - if (uint_added < uint_offset) { + 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 & 0x0f; /* 4 low bits */ - if (lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t)) { + lookupidx = flags; + if (DUK_UNLIKELY(lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t))) { goto arg_error; } tmp = duk__bufobj_flags_lookup[lookupidx]; @@ -20611,39 +21424,9 @@ DUK_EXTERNAL void duk_push_buffer_object(duk_context *ctx, duk_idx_t idx_buffer, /* TypedArray views need an automatic ArrayBuffer which must be * 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 offset is always set to zero, so that if one - * accesses the ArrayBuffer at the view's .byteOffset, the value - * matches the view at index 0. + * accessor returns it. The ArrayBuffer is created lazily on first + * access so we don't need to do anything more here. */ - if (flags & DUK_BUFOBJ_CREATE_ARRBUF) { - duk_hbufobj *h_arrbuf; - - h_arrbuf = duk_push_bufobj_raw(ctx, - 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); - - h_arrbuf->buf = h_val; - DUK_HBUFFER_INCREF(thr, h_val); - h_arrbuf->offset = 0; - h_arrbuf->length = uint_offset + uint_length; /* Wrap checked above. */ - DUK_ASSERT(h_arrbuf->shift == 0); - h_arrbuf->elem_type = DUK_HBUFOBJ_ELEM_UINT8; - DUK_ASSERT(h_arrbuf->is_typedarray == 0); - DUK_ASSERT_HBUFOBJ_VALID(h_arrbuf); - DUK_ASSERT(h_arrbuf->buf_prop == NULL); - - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; - DUK_HBUFOBJ_INCREF(thr, h_arrbuf); /* Now reachable and accounted for. */ - - duk_pop(ctx); - } - return; range_error: @@ -20686,6 +21469,7 @@ DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcod proto = duk_error_prototype_from_code(thr, err_code); (void) duk_push_object_helper_proto(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR), proto); @@ -20753,18 +21537,15 @@ DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_sm DUK_ASSERT_CTX_VALID(ctx); - /* check stack first */ - if (thr->valstack_top >= thr->valstack_end) { - DUK_ERROR_RANGE_PUSH_BEYOND(thr); - } + DUK__CHECK_SPACE(); /* Check for maximum buffer length. */ - if (size > DUK_HBUFFER_MAX_BYTELEN) { + if (DUK_UNLIKELY(size > DUK_HBUFFER_MAX_BYTELEN)) { DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); } h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data); - if (!h) { + if (DUK_UNLIKELY(h == NULL)) { DUK_ERROR_ALLOC_FAILED(thr); } @@ -20818,46 +21599,66 @@ DUK_LOCAL void duk__validate_push_heapptr(duk_context *ctx, void *ptr) { * by seeing that X's FINALIZED flag is set (which is done before * the finalizer starts executing). */ +#if defined(DUK_USE_FINALIZER_SUPPORT) for (curr = thr->heap->finalize_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { + /* FINALIZABLE is set for all objects on finalize_list + * except for an object being finalized right now. So + * can't assert here. + */ +#if 0 + DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr)); +#endif + if (curr == h) { if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) { /* Object is currently being finalized. */ DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ found = 1; } else { - DUK_ASSERT(0); - } - } - } - - /* Also check for the refzero_list; must not be there unless it is - * being finalized when duk_push_heapptr() is called. - * - * Corner case: similar to finalize_list. - */ -#if defined(DUK_USE_REFERENCE_COUNTING) - for (curr = thr->heap->refzero_list; - curr != NULL; - curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) { - if (curr == h) { - if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) { - /* Object is currently being finalized. */ + /* Not being finalized but on finalize_list, + * allowed since Duktape 2.1. + */ DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ found = 1; - } else { - DUK_ASSERT(0); } } } +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +#if defined(DUK_USE_REFERENCE_COUNTING) + /* Because refzero_list is now processed to completion inline with + * no side effects, it's always empty here. + */ + DUK_ASSERT(thr->heap->refzero_list == NULL); #endif - /* If not present in finalize_list or refzero_list, the pointer + /* If not present in finalize_list (or refzero_list), it * must be either in heap_allocated or the string table. */ - if (DUK_HEAPHDR_GET_TYPE(h) == DUK_HTYPE_STRING) { - /* Omitted from Duktape 2.0.x maintenance backport. */ + if (DUK_HEAPHDR_IS_STRING(h)) { + duk_uint32_t i; + duk_hstring *str; + duk_heap *heap = thr->heap; + + DUK_ASSERT(found == 0); + for (i = 0; i < heap->st_size; i++) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + str = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]); +#else + str = heap->strtable[i]; +#endif + while (str != NULL) { + if (str == (duk_hstring *) h) { + DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */ + found = 1; + break; + } + str = str->hdr.h_next; + } + } + DUK_ASSERT(found != 0); } else { for (curr = thr->heap->heap_allocated; curr != NULL; @@ -20875,6 +21676,7 @@ DUK_LOCAL void duk__validate_push_heapptr(duk_context *ctx, void *ptr) { DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { duk_hthread *thr = (duk_hthread *) ctx; duk_idx_t ret; + duk_tval *tv; DUK_ASSERT_CTX_VALID(ctx); @@ -20889,29 +21691,77 @@ DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { duk__validate_push_heapptr(ctx, ptr); #endif + DUK__CHECK_SPACE(); + ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); + tv = thr->valstack_top++; if (ptr == NULL) { - goto push_undefined; + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv)); + return ret; + } + + DUK_ASSERT_HEAPHDR_VALID((duk_heaphdr *) ptr); + + /* If the argument is on finalize_list it has technically been + * unreachable before duk_push_heapptr() but it's still safe to + * push it. Starting from Duktape 2.1 allow application code to + * do so. There are two main cases: + * + * (1) The object is on the finalize_list and we're called by + * the finalizer for the object being finalized. In this + * case do nothing: finalize_list handling will deal with + * the object queueing. This is detected by the object not + * having a FINALIZABLE flag despite being on the finalize_list; + * the flag is cleared for the object being finalized only. + * + * (2) The object is on the finalize_list but is not currently + * being processed. In this case the object can be queued + * back to heap_allocated with a few flags cleared, in effect + * cancelling the finalizer. + */ + if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE((duk_heaphdr *) ptr))) { + duk_heaphdr *curr; + + DUK_D(DUK_DPRINT("duk_push_heapptr() with a pointer on finalize_list, autorescue")); + + curr = (duk_heaphdr *) ptr; + DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); + + /* Because FINALIZED is set prior to finalizer call, it will + * be set for the object being currently finalized, but not + * for other objects on finalize_list. + */ + DUK_HEAPHDR_CLEAR_FINALIZED(curr); + + /* Dequeue object from finalize_list and queue it back to + * heap_allocated. + */ +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); /* Preincremented on finalize_list insert. */ + DUK_HEAPHDR_PREDEC_REFCOUNT(curr); +#endif + DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(thr->heap, curr); + DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(thr->heap, curr); + + /* Continue with the rest. */ } switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) { case DUK_HTYPE_STRING: - duk_push_hstring(ctx, (duk_hstring *) ptr); + DUK_TVAL_SET_STRING(tv, (duk_hstring *) ptr); break; case DUK_HTYPE_OBJECT: - duk_push_hobject(ctx, (duk_hobject *) ptr); - break; - case DUK_HTYPE_BUFFER: - duk_push_hbuffer(ctx, (duk_hbuffer *) ptr); + DUK_TVAL_SET_OBJECT(tv, (duk_hobject *) ptr); break; default: - goto push_undefined; + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr) == DUK_HTYPE_BUFFER); + DUK_TVAL_SET_BUFFER(tv, (duk_hbuffer *) ptr); + break; } - return ret; - push_undefined: - duk_push_undefined(ctx); + DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) ptr); + return ret; } @@ -20919,6 +21769,7 @@ DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) { DUK_EXTERNAL duk_idx_t duk_push_bare_object(duk_context *ctx) { (void) duk_push_object_helper(ctx, 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); @@ -20982,28 +21833,52 @@ DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) { #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; } - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); if (DUK_UNLIKELY((duk_size_t) (thr->valstack_top - thr->valstack_bottom) < (duk_size_t) count)) { DUK_ERROR_RANGE_INVALID_COUNT(thr); } - /* - * Must be very careful here, every DECREF may cause reallocation - * of our valstack. - */ +#if defined(DUK_USE_REFERENCE_COUNTING) + tv = thr->valstack_top; + tv_end = tv - count; + while (tv != tv_end) { + tv--; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED_UPDREF_NORZ(thr, tv); + } + thr->valstack_top = tv; + DUK_REFZERO_CHECK_FAST(thr); +#else + tv = thr->valstack_top; + while (count > 0) { + count--; + tv--; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED(tv); + } + thr->valstack_top = tv; +#endif - /* XXX: inlined DECREF macro would be nice here: no NULL check, - * refzero queueing but no refzero algorithm run (= no pointer - * instability), inline code. - */ + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); +} - /* XXX: optimize loops */ +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_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; @@ -21029,6 +21904,34 @@ DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) { DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); } +/* Pop N elements without DECREF (in effect "stealing" the refcounts). */ +#if defined(DUK_USE_REFERENCE_COUNTING) +DUK_INTERNAL void duk_pop_n_nodecref_unsafe(duk_context *ctx, duk_idx_t count) { + duk_hthread *thr = (duk_hthread *) ctx; + duk_tval *tv; + + DUK_ASSERT_CTX_VALID(ctx); + DUK_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); + + tv = thr->valstack_top; + while (count > 0) { + count--; + tv--; + DUK_ASSERT(tv >= thr->valstack_bottom); + DUK_TVAL_SET_UNDEFINED(tv); + } + thr->valstack_top = tv; + + 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); +} +#endif /* DUK_USE_REFERENCE_COUNTING */ + /* Popping one element is called so often that when footprint is not an issue, * compile a specialized function for it. */ @@ -21065,16 +21968,17 @@ DUK_EXTERNAL void duk_pop(duk_context *ctx) { #if defined(DUK_USE_PREFER_SIZE) DUK_INTERNAL void duk_pop_unsafe(duk_context *ctx) { DUK_ASSERT_CTX_VALID(ctx); - duk_pop_n(ctx, 1); + duk_pop_n_unsafe(ctx, 1); } #else DUK_INTERNAL void duk_pop_unsafe(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_tval *tv; - DUK_ASSERT_CTX_VALID(ctx); - DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT_CTX_VALID(ctx); DUK_ASSERT(thr->valstack_top != thr->valstack_bottom); + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); + DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) >= (duk_size_t) 1); tv = --thr->valstack_top; /* tv points to element just below prev top */ DUK_ASSERT(tv >= thr->valstack_bottom); @@ -21083,6 +21987,7 @@ DUK_INTERNAL void duk_pop_unsafe(duk_context *ctx) { #else DUK_TVAL_SET_UNDEFINED(tv); #endif + DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); } #endif /* !DUK_USE_PREFER_SIZE */ @@ -21115,7 +22020,7 @@ DUK_INTERNAL void duk_pack(duk_context *ctx, duk_idx_t count) { thr = (duk_hthread *) ctx; top = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom); - if (count < 0 || count > top) { + if (DUK_UNLIKELY(count < 0 || count > top)) { DUK_ERROR_RANGE_INVALID_COUNT(thr); return; } @@ -21178,12 +22083,13 @@ DUK_INTERNAL void duk_unpack(duk_context *ctx) { DUK_EXTERNAL void duk_throw_raw(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; + duk_tval *tv_val; DUK_ASSERT(thr->valstack_bottom >= thr->valstack); DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom); DUK_ASSERT(thr->valstack_end >= thr->valstack_top); - if (thr->valstack_top == thr->valstack_bottom) { + if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) { DUK_ERROR_TYPE_INVALID_ARGS(thr); } @@ -21204,7 +22110,11 @@ DUK_EXTERNAL void duk_throw_raw(duk_context *ctx) { #endif DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(ctx, -1))); - duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); + tv_val = DUK_GET_TVAL_NEGIDX(ctx, -1); + duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, tv_val); +#if defined(DUK_USE_DEBUGGER_SUPPORT) + duk_err_check_debugger_integration(thr); +#endif /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't * need to check that here. If the value is NULL, a fatal error occurs @@ -21348,7 +22258,7 @@ 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_DECL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) { +DUK_EXTERNAL duk_bool_t duk_samevalue(duk_context *ctx, duk_idx_t idx1, duk_idx_t idx2) { duk_tval *tv1, *tv2; DUK_ASSERT_CTX_VALID(ctx); @@ -21846,6 +22756,7 @@ DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t star duk_hstring *res; duk_size_t start_byte_offset; duk_size_t end_byte_offset; + duk_size_t charlen; DUK_ASSERT_CTX_VALID(ctx); @@ -21853,8 +22764,9 @@ DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t star h = duk_require_hstring(ctx, idx); DUK_ASSERT(h != NULL); - if (end_offset >= DUK_HSTRING_GET_CHARLEN(h)) { - end_offset = DUK_HSTRING_GET_CHARLEN(h); + charlen = DUK_HSTRING_GET_CHARLEN(h); + if (end_offset >= charlen) { + end_offset = charlen; } if (start_offset > end_offset) { start_offset = end_offset; @@ -21876,9 +22788,9 @@ DUK_EXTERNAL void duk_substring(duk_context *ctx, duk_idx_t idx, duk_size_t star DUK_ASSERT(end_byte_offset - start_byte_offset <= DUK_UINT32_MAX); /* Guaranteed by string limits. */ /* No size check is necessary. */ - res = duk_heap_string_intern_checked(thr, - DUK_HSTRING_GET_DATA(h) + start_byte_offset, - (duk_uint32_t) (end_byte_offset - start_byte_offset)); + res = duk_heap_strtable_intern_checked(thr, + 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); @@ -23996,26 +24908,6 @@ DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufobj *h_bufobj, d DUK_ASSERT_HBUFOBJ_VALID(h_bufobj); } -DUK_LOCAL duk_hbufobj *duk__push_arraybuffer_with_length(duk_context *ctx, duk_uint_t len) { - duk_hbuffer *h_val; - duk_hbufobj *h_bufobj; - - (void) duk_push_fixed_buffer_zero(ctx, (duk_size_t) len); - h_val = (duk_hbuffer *) duk_known_hbuffer(ctx, -1); - - h_bufobj = duk_push_bufobj_raw(ctx, - 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_ASSERT_HBUFOBJ_VALID(h_bufobj); - - return h_bufobj; -} - /* Shared offset/length coercion helper. */ DUK_LOCAL void duk__resolve_offset_opt_length(duk_context *ctx, duk_hbufobj *h_bufarg, @@ -24469,7 +25361,6 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { duk_tval *tv; duk_hobject *h_obj; duk_hbufobj *h_bufobj = NULL; - duk_hbufobj *h_bufarr = NULL; duk_hbufobj *h_bufarg = NULL; duk_hbuffer *h_val; duk_small_uint_t magic; @@ -24682,15 +25573,17 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { /* ArrayBuffer argument is handled specially above; the rest of the * argument variants are handled by shared code below. + * + * ArrayBuffer in h_bufobj->buf_prop is intentionally left unset. + * It will be automatically created by the .buffer accessor on + * first access. */ - /* Push a new ArrayBuffer (becomes view .buffer) */ - h_bufarr = duk__push_arraybuffer_with_length(ctx, byte_length); - DUK_ASSERT(h_bufarr != NULL); - h_val = h_bufarr->buf; + /* 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); DUK_ASSERT(h_val != NULL); - /* Push the resulting view object and attach the ArrayBuffer. */ h_bufobj = duk_push_bufobj_raw(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_FLAG_BUFOBJ | @@ -24706,12 +25599,6 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { h_bufobj->is_typedarray = 1; DUK_ASSERT_HBUFOBJ_VALID(h_bufobj); - /* Set .buffer */ - DUK_ASSERT(h_bufobj->buf_prop == NULL); - h_bufobj->buf_prop = (duk_hobject *) h_bufarr; - DUK_ASSERT(h_bufarr != NULL); - DUK_HBUFOBJ_INCREF(thr, h_bufarr); - /* Copy values, the copy method depends on the arguments. * * Copy mode decision may depend on the validity of the underlying @@ -26626,30 +27513,63 @@ 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_hbufobj *h_res; + + h_res = duk_push_bufobj_raw(ctx, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_BUFOBJ | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), + DUK_BIDX_ARRAYBUFFER_PROTOTYPE); + DUK_ASSERT(h_res != NULL); + DUK_UNREF(h_res); + + duk__set_bufobj_buffer(ctx, 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_hbufobj *h_bufobj; h_bufobj = (duk_hbufobj *) duk__getrequire_bufobj_this(ctx, DUK__BUFOBJ_FLAG_THROW /*flags*/); DUK_ASSERT(h_bufobj != NULL); if (DUK_HEAPHDR_IS_BUFFER((duk_heaphdr *) h_bufobj)) { - duk_hbufobj *h_res; - duk_hbuffer *h_buf; + DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for plain buffer")); + (void) duk__autospawn_arraybuffer(ctx, (duk_hbuffer *) h_bufobj); + return 1; + } else { + if (h_bufobj->buf_prop == NULL && + DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) h_bufobj) != DUK_HOBJECT_CLASS_ARRAYBUFFER && + h_bufobj->buf != NULL) { + duk_hbufobj *h_arrbuf; + + DUK_DD(DUK_DDPRINT("autospawn ArrayBuffer for typed array or DataView")); + h_arrbuf = duk__autospawn_arraybuffer(ctx, h_bufobj->buf); + + if (h_bufobj->buf_prop == NULL) { + /* Must recheck buf_prop, in case ArrayBuffer + * alloc had a side effect which already filled + * it! + */ - h_buf = (duk_hbuffer *) h_bufobj; - h_res = duk_push_bufobj_raw(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_BUFOBJ | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER), - DUK_BIDX_ARRAYBUFFER_PROTOTYPE); - DUK_ASSERT(h_res != NULL); - DUK_UNREF(h_res); + /* Set ArrayBuffer's .byteOffset and .byteLength based + * on the view so that Arraybuffer[view.byteOffset] + * matches view[0]. + */ + h_arrbuf->offset = 0; + DUK_ASSERT(h_bufobj->offset + h_bufobj->length >= h_bufobj->offset); /* Wrap check on creation. */ + h_arrbuf->length = h_bufobj->offset + h_bufobj->length; + DUK_ASSERT(h_arrbuf->buf_prop == NULL); - duk__set_bufobj_buffer(ctx, h_res, h_buf); - DUK_ASSERT_HBUFOBJ_VALID(h_res); + DUK_ASSERT(h_bufobj->buf_prop == NULL); + h_bufobj->buf_prop = (duk_hobject *) h_arrbuf; + DUK_HBUFOBJ_INCREF(thr, h_arrbuf); /* Now reachable and accounted for. */ + } - DUK_DD(DUK_DDPRINT("autospawned .buffer ArrayBuffer: %!iT", duk_get_tval(ctx, -1))); - return 1; - } else { + /* Left on stack; pushed for the second time below (OK). */ + } if (h_bufobj->buf_prop) { duk_push_hobject(ctx, h_bufobj->buf_prop); return 1; @@ -28152,6 +29072,7 @@ DUK_INTERNAL duk_ret_t duk_bi_date_constructor(duk_context *ctx) { (void) duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DATE), DUK_BIDX_DATE_PROTOTYPE); @@ -28664,7 +29585,7 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { * an mktime() error return is the cast above. See e.g.: * http://pubs.opengroup.org/onlinepubs/009695299/functions/mktime.html */ - goto error; + goto mktime_error; } DUK_DDD(DUK_DDDPRINT("t1=%ld (utc), t2=%ld (local)", (long) t1, (long) t2)); @@ -28680,7 +29601,7 @@ DUK_INTERNAL duk_int_t duk_bi_date_get_local_tzoffset_gmtime(duk_double_t d) { #endif return (duk_int_t) difftime(t2, t1); - error: + mktime_error: /* XXX: return something more useful, so that caller can throw? */ DUK_D(DUK_DPRINT("mktime() failed, d=%lf", (double) d)); return 0; @@ -28902,6 +29823,38 @@ DUK_INTERNAL_DECL duk_int_t duk_bi_date_get_local_tzoffset_windows(duk_double_t return (duk_int_t) (((LONGLONG) tmp3.QuadPart - (LONGLONG) tmp2.QuadPart) / 10000000LL); /* 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) { + SYSTEMTIME st1; + SYSTEMTIME st2; + FILETIME ft1; + FILETIME ft2; + ULARGE_INTEGER tmp1; + ULARGE_INTEGER tmp2; + + /* Do a similar computation to duk_bi_date_get_local_tzoffset_windows + * but without accounting for daylight savings time. Use this on + * Windows platforms (like Durango) that don't support the + * SystemTimeToTzSpecificLocalTime() call. + */ + + /* current time not needed for this computation */ + DUK_UNREF(d); + + duk__set_systime_jan1970(&st1); + duk__convert_systime_to_ularge((const SYSTEMTIME *) &st1, &tmp1); + + ft1.dwLowDateTime = tmp1.LowPart; + ft1.dwHighDateTime = tmp1.HighPart; + FileTimeToLocalFileTime((const FILETIME *) &ft1, &ft2); + + 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 */ +} +#endif /* DUK_USE_DATE_TZO_WINDOWS_NO_DST */ /* * Duktape built-ins * @@ -28933,15 +29886,14 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_act(duk_context *ctx) { DUK_INTERNAL duk_ret_t duk_bi_duktape_object_gc(duk_context *ctx) { duk_hthread *thr = (duk_hthread *) ctx; duk_small_uint_t flags; - duk_bool_t rc; flags = (duk_small_uint_t) duk_get_uint(ctx, 0); - rc = duk_heap_mark_and_sweep(thr->heap, flags); + duk_heap_mark_and_sweep(thr->heap, flags); /* XXX: Not sure what the best return value would be in the API. - * Return a boolean for now. Note that rc == 0 is success (true). + * Return true for now. */ - duk_push_boolean(ctx, !rc); + duk_push_true(ctx); return 1; } @@ -28953,15 +29905,16 @@ DUK_INTERNAL duk_ret_t duk_bi_duktape_object_fin(duk_context *ctx) { * undefined; this does not remove the property at the moment. * The value could be type checked to be either a function * or something else; if something else, the property could - * be deleted. + * be deleted. Must use duk_set_finalizer() to keep + * DUK_HOBJECT_FLAG_HAVE_FINALIZER in sync. */ duk_set_top(ctx, 2); - (void) duk_put_prop_stridx_short(ctx, 0, DUK_STRIDX_INT_FINALIZER); + duk_set_finalizer(ctx, 0); return 0; } else { /* Get. */ DUK_ASSERT(duk_get_top(ctx) == 1); - duk_get_prop_stridx_short(ctx, 0, DUK_STRIDX_INT_FINALIZER); + duk_get_finalizer(ctx, 0); return 1; } } @@ -29212,6 +30165,7 @@ DUK_LOCAL duk_codepoint_t duk__utf8_decode_next(duk__decode_context *dec_ctx, du } } +#if defined(DUK_USE_ENCODING_BUILTINS) DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { duk__encode_context *enc_ctx; @@ -29266,6 +30220,7 @@ DUK_LOCAL void duk__utf8_encode_char(void *udata, duk_codepoint_t codepoint) { */ enc_ctx->out += duk_unicode_encode_xutf8(codepoint, enc_ctx->out); } +#endif /* DUK_USE_ENCODING_BUILTINS */ /* Shared helper for buffer-to-string using a TextDecoder() compatible UTF-8 * decoder. @@ -29528,8 +30483,8 @@ DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx) { * initialized explicitly. */ dec_ctx = (duk__decode_context *) duk_push_fixed_buffer(ctx, sizeof(duk__decode_context)); - dec_ctx->fatal = fatal; - dec_ctx->ignore_bom = ignore_bom; + 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"); @@ -29620,6 +30575,7 @@ DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) { /* 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); @@ -30060,7 +31016,7 @@ DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { DUK_ASSERT_TOP(ctx, 3); /* strictness is not inherited, intentional */ - comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; + 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 */ @@ -30331,6 +31287,7 @@ DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) { /* 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), @@ -30851,7 +31808,8 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { DUK_ASSERT(duk_get_top(ctx) == 1 || duk_get_top(ctx) == 2); /* 2 when called by debugger */ DUK_ASSERT(thr->callstack_top >= 1); /* at least this function exists */ - DUK_ASSERT(((thr->callstack + thr->callstack_top - 1)->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ + DUK_ASSERT(thr->callstack_curr != NULL); + DUK_ASSERT((thr->callstack_curr->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0 || /* indirect eval */ (thr->callstack_top >= 2)); /* if direct eval, calling activation must exist */ /* @@ -30881,8 +31839,9 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { /* [ source ] */ - comp_flags = DUK_JS_COMPILE_FLAG_EVAL; - act_eval = thr->callstack + thr->callstack_top - 1; /* this function */ + 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) { /* Have a calling activation, check for direct eval (otherwise * assume indirect eval. @@ -30893,7 +31852,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { /* Only direct eval inherits strictness from calling code * (E5.1 Section 10.1.1). */ - comp_flags |= DUK_JS_COMPILE_FLAG_STRICT; + comp_flags |= DUK_COMPILE_STRICT; } } else { DUK_ASSERT((act_eval->flags & DUK_ACT_FLAG_DIRECT_EVAL) == 0); @@ -30913,7 +31872,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { /* E5 Section 10.4.2 */ DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack + thr->callstack_top - 1; /* this function */ + act = thr->callstack_curr; /* this function */ if (act->flags & DUK_ACT_FLAG_DIRECT_EVAL) { DUK_ASSERT(thr->callstack_top >= 2); act = thr->callstack + thr->callstack_top + level; /* caller */ @@ -30931,7 +31890,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { this_to_global = 0; if (DUK_HOBJECT_HAS_STRICT((duk_hobject *) func)) { - duk_hobject *new_env; + duk_hdecenv *new_env; duk_hobject *act_lex_env; DUK_DDD(DUK_DDDPRINT("direct eval call to a strict function -> " @@ -30941,15 +31900,19 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { act_lex_env = act->lex_env; act = NULL; /* invalidated */ - new_env = duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - act_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_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); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, act_lex_env); DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); - outer_lex_env = new_env; - outer_var_env = new_env; + 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 */ @@ -30979,7 +31942,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { /* 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*/); - /* [ source template closure ] */ + /* [ env? source template closure ] */ if (this_to_global) { DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); @@ -30998,11 +31961,11 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) { (duk_heaphdr *) outer_var_env, duk_get_tval(ctx, -1))); - /* [ source template closure this ] */ + /* [ env? source template closure this ] */ duk_call_method(ctx, 0); - /* [ source template result ] */ + /* [ env? source template result ] */ return 1; } @@ -31216,12 +32179,15 @@ DUK_LOCAL_DECL void duk__enc_double(duk_json_enc_ctx *js_ctx); DUK_LOCAL_DECL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv); #endif #if defined(DUK_USE_JX) || defined(DUK_USE_JC) -DUK_LOCAL_DECL void duk__enc_buffer(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); +DUK_LOCAL_DECL void duk__enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h); DUK_LOCAL_DECL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) DUK_LOCAL_DECL void duk__enc_bufobj(duk_json_enc_ctx *js_ctx, duk_hbufobj *h_bufobj); #endif #endif +#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); /* @@ -32701,13 +33667,60 @@ DUK_LOCAL void duk__enc_buffer_data(duk_json_enc_ctx *js_ctx, duk_uint8_t *buf_d DUK_BW_SET_PTR(thr, &js_ctx->bw, q); } -DUK_LOCAL void duk__enc_buffer(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { +DUK_LOCAL void duk__enc_buffer_jx_jc(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { duk__enc_buffer_data(js_ctx, (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h), (duk_size_t) DUK_HBUFFER_GET_SIZE(h)); } #endif /* DUK_USE_JX || DUK_USE_JC */ +#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH) +DUK_LOCAL void duk__enc_buffer_json_fastpath(duk_json_enc_ctx *js_ctx, duk_hbuffer *h) { + duk_size_t i, n; + const duk_uint8_t *buf; + duk_uint8_t *q; + + n = DUK_HBUFFER_GET_SIZE(h); + if (n == 0) { + DUK__EMIT_2(js_ctx, DUK_ASC_LCURLY, DUK_ASC_RCURLY); + return; + } + + 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. + * + * Note that because the output buffer is reallocated from time to time, + * side effects (such as finalizers) affecting the buffer 'h' must be + * disabled. This is the case in the JSON.stringify() fast path. + */ + + buf = (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(js_ctx->thr->heap, h); + if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { + for (i = 0; i < n; i++) { + duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth + 1); + q = DUK_BW_ENSURE_GETPTR(js_ctx->thr, &js_ctx->bw, 32); + q += DUK_SPRINTF((char *) q, "\"%lu\": %u,", (unsigned long) i, (unsigned int) buf[i]); + DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); + } + } else { + q = DUK_BW_GET_PTR(js_ctx->thr, &js_ctx->bw); + for (i = 0; i < n; i++) { + q = DUK_BW_ENSURE_RAW(js_ctx->thr, &js_ctx->bw, 32, q); + q += DUK_SPRINTF((char *) q, "\"%lu\":%u,", (unsigned long) i, (unsigned int) buf[i]); + } + DUK_BW_SET_PTR(js_ctx->thr, &js_ctx->bw, q); + } + DUK__UNEMIT_1(js_ctx); /* eat trailing comma */ + + if (DUK_UNLIKELY(js_ctx->h_gap != NULL)) { + duk__enc_newline_indent(js_ctx, js_ctx->recursion_depth); + } + DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY); +} +#endif /* DUK_USE_JSON_STRINGIFY_FASTPATH */ + #if defined(DUK_USE_JX) || defined(DUK_USE_JC) DUK_LOCAL void duk__enc_pointer(duk_json_enc_ctx *js_ctx, void *ptr) { char buf[64]; /* XXX: how to figure correct size? */ @@ -33257,7 +34270,7 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold case DUK_TAG_STRING: { duk_hstring *h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { goto pop2_undef; } duk__enc_quote_string(js_ctx, h); @@ -33288,12 +34301,13 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold case DUK_TAG_BUFFER: { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) if (js_ctx->flag_ext_custom_or_compatible) { - duk__enc_buffer(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + duk__enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); break; } #endif - /* Could implement a fast path, but object coerce and - * serialize the result for now. + + /* Could implement a fastpath, but the fast path would need + * to handle realloc side effects correctly. */ duk_to_object(ctx, -1); duk__enc_object(js_ctx); @@ -33353,7 +34367,7 @@ DUK_LOCAL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv) { duk_hstring *h; h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { return 0; } return 1; @@ -33422,7 +34436,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du duk_hstring *h; h = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { goto emit_undefined; } duk__enc_quote_string(js_ctx, h); @@ -33587,7 +34601,7 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du DUK_DD(DUK_DDPRINT("property is an accessor, abort fast path")); goto abort_fastpath; } - if (DUK_HSTRING_HAS_SYMBOL(k)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) { continue; } @@ -33781,13 +34795,16 @@ DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, du #if defined(DUK_USE_JX) || defined(DUK_USE_JC) if (js_ctx->flag_ext_custom_or_compatible) { - duk__enc_buffer(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + duk__enc_buffer_jx_jc(js_ctx, DUK_TVAL_GET_BUFFER(tv)); break; } #endif - /* Could implement a fast path, but abort fast path for now. */ - DUK_DD(DUK_DDPRINT("value is a plain buffer and serializing as plain JSON, abort fast path")); - goto abort_fastpath; + + /* Plain buffers mimic Uint8Arrays, and have enumerable index + * properties. + */ + duk__enc_buffer_json_fastpath(js_ctx, DUK_TVAL_GET_BUFFER(tv)); + break; } case DUK_TAG_POINTER: { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) @@ -34184,8 +35201,11 @@ void duk_bi_json_stringify_helper(duk_context *ctx, } if (js_ctx->h_gap != NULL) { - /* if gap is empty, behave as if not given at all */ - if (DUK_HSTRING_GET_CHARLEN(js_ctx->h_gap) == 0) { + /* If gap is empty, behave as if not given at all. Check + * against byte length because character length is more + * expensive. + */ + if (DUK_HSTRING_GET_BYTELEN(js_ctx->h_gap) == 0) { js_ctx->h_gap = NULL; } } @@ -34201,7 +35221,7 @@ void duk_bi_json_stringify_helper(duk_context *ctx, if (js_ctx->h_replacer == NULL && /* replacer is a mutation risk */ js_ctx->idx_proplist == -1) { /* proplist is very rare */ duk_int_t pcall_rc; - duk_small_uint_t prev_mark_and_sweep_base_flags; + duk_small_uint_t prev_ms_base_flags; DUK_DD(DUK_DDPRINT("try JSON.stringify() fast path")); @@ -34223,14 +35243,17 @@ void duk_bi_json_stringify_helper(duk_context *ctx, duk_dup(ctx, idx_value); /* Must prevent finalizers which may have arbitrary side effects. */ - prev_mark_and_sweep_base_flags = thr->heap->mark_and_sweep_base_flags; - thr->heap->mark_and_sweep_base_flags |= - DUK_MS_FLAG_NO_FINALIZERS | /* avoid attempts to add/remove object keys */ - DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid attempt to compact any objects */ + prev_ms_base_flags = thr->heap->ms_base_flags; + thr->heap->ms_base_flags |= + DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* Avoid attempt to compact any objects. */ + 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*/); - thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; + DUK_ASSERT(thr->heap->pf_prevent_count > 0); + thr->heap->pf_prevent_count--; + thr->heap->ms_base_flags = prev_ms_base_flags; if (pcall_rc == DUK_EXEC_SUCCESS) { DUK_DD(DUK_DDPRINT("fast path successful")); @@ -35074,6 +36097,7 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_context *ctx) { (void) duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), DUK_BIDX_OBJECT_PROTOTYPE); return 1; @@ -35136,6 +36160,7 @@ DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx) { (void) duk_push_object_helper_proto(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), proto); @@ -35854,6 +36879,7 @@ DUK_INTERNAL duk_ret_t duk_bi_pointer_constructor(duk_context *ctx) { if (duk_is_constructor_call(ctx)) { (void) duk_push_object_helper(ctx, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER), DUK_BIDX_POINTER_PROTOTYPE); @@ -35950,7 +36976,7 @@ DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h goto skip_key; } } - if (DUK_HSTRING_HAS_SYMBOL(h)) { + 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))); goto skip_key; @@ -36027,6 +37053,7 @@ DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx) { */ (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); @@ -36405,6 +37432,96 @@ DUK_INTERNAL duk_ret_t duk_bi_regexp_prototype_shared_getter(duk_context *ctx) { #if defined(DUK_USE_STRING_BUILTIN) +/* + * Helpers + */ + +DUK_LOCAL duk_hstring *duk__str_tostring_notregexp(duk_context *ctx, duk_idx_t idx) { + duk_hstring *h; + + if (duk_get_class_number(ctx, idx) == DUK_HOBJECT_CLASS_REGEXP) { + DUK_ERROR_TYPE_INVALID_ARGS((duk_hthread *) ctx); + } + h = duk_to_hstring(ctx, idx); + 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_int_t cpos; + duk_int_t bpos; + const duk_uint8_t *p_start, *p_end, *p; + const duk_uint8_t *q_start; + duk_int_t q_blen; + duk_uint8_t firstbyte; + duk_uint8_t t; + + cpos = start_cpos; + + /* Empty searchstring always matches; cpos must be clamped here. + * (If q_blen were < 0 due to clamped coercion, it would also be + * caught here.) + */ + q_start = DUK_HSTRING_GET_DATA(h_search); + q_blen = (duk_int_t) DUK_HSTRING_GET_BYTELEN(h_search); + if (q_blen <= 0) { + return cpos; + } + DUK_ASSERT(q_blen > 0); + + bpos = (duk_int_t) duk_heap_strcache_offset_char2byte((duk_hthread *) ctx, h_this, (duk_uint32_t) cpos); + + p_start = DUK_HSTRING_GET_DATA(h_this); + p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_this); + p = p_start + bpos; + + /* This loop is optimized for size. For speed, there should be + * two separate loops, and we should ensure that memcmp() can be + * used without an extra "will searchstring fit" check. Doing + * the preconditioning for 'p' and 'p_end' is easy but cpos + * must be updated if 'p' is wound back (backward scanning). + */ + + firstbyte = q_start[0]; /* leading byte of match string */ + while (p <= p_end && p >= p_start) { + t = *p; + + /* For Ecmascript strings, this check can only match for + * initial UTF-8 bytes (not continuation bytes). For other + * strings all bets are off. + */ + + if ((t == firstbyte) && ((duk_size_t) (p_end - p) >= (duk_size_t) q_blen)) { + DUK_ASSERT(q_blen > 0); /* no issues with memcmp() zero size, even if broken */ + if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { + return cpos; + } + } + + /* track cpos while scanning */ + if (backwards) { + /* when going backwards, we decrement cpos 'early'; + * 'p' may point to a continuation byte of the char + * at offset 'cpos', but that's OK because we'll + * backtrack all the way to the initial byte. + */ + if ((t & 0xc0) != 0x80) { + cpos--; + } + p--; + } else { + if ((t & 0xc0) != 0x80) { + cpos++; + } + p++; + } + } + + /* Not found. Empty string case is handled specially above. */ + return -1; +} + /* * Constructor */ @@ -36427,7 +37544,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_context *ctx) { duk_push_hstring_empty(ctx); } else { h = duk_to_hstring_acceptsymbol(ctx, 0); - if (DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(ctx)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h) && !duk_is_constructor_call(ctx))) { duk_push_symbol_descriptive_string(ctx, h); duk_replace(ctx, 0); } @@ -36439,6 +37556,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_constructor(duk_context *ctx) { if (duk_is_constructor_call(ctx)) { /* 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); @@ -36588,7 +37706,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_char_code_at(duk_context *ctx) { pos = duk_to_int_clamped_raw(ctx, 0 /*index*/, 0 /*min(incl)*/, - DUK_HSTRING_GET_CHARLEN(h) - 1 /*max(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); @@ -36750,17 +37868,10 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_caseconv_shared(duk_context *ctx) */ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) { - duk_hthread *thr = (duk_hthread *) ctx; duk_hstring *h_this; duk_hstring *h_search; duk_int_t clen_this; duk_int_t cpos; - duk_int_t bpos; - const duk_uint8_t *p_start, *p_end, *p; - const duk_uint8_t *q_start; - duk_int_t q_blen; - duk_uint8_t firstbyte; - duk_uint8_t t; duk_small_int_t is_lastindexof = duk_get_current_magic(ctx); /* 0=indexOf, 1=lastIndexOf */ h_this = duk_push_this_coercible_to_string(ctx); @@ -36769,8 +37880,6 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) h_search = duk_to_hstring(ctx, 0); DUK_ASSERT(h_search != NULL); - q_start = DUK_HSTRING_GET_DATA(h_search); - q_blen = (duk_int_t) DUK_HSTRING_GET_BYTELEN(h_search); duk_to_number(ctx, 1); if (duk_is_nan(ctx, 1) && is_lastindexof) { @@ -36783,67 +37892,8 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_indexof_shared(duk_context *ctx) cpos = duk_to_int_clamped(ctx, 1, 0, clen_this); } - /* Empty searchstring always matches; cpos must be clamped here. - * (If q_blen were < 0 due to clamped coercion, it would also be - * caught here.) - */ - if (q_blen <= 0) { - duk_push_int(ctx, cpos); - return 1; - } - DUK_ASSERT(q_blen > 0); - - 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); - p = p_start + bpos; - - /* This loop is optimized for size. For speed, there should be - * two separate loops, and we should ensure that memcmp() can be - * used without an extra "will searchstring fit" check. Doing - * the preconditioning for 'p' and 'p_end' is easy but cpos - * must be updated if 'p' is wound back (backward scanning). - */ - - firstbyte = q_start[0]; /* leading byte of match string */ - while (p <= p_end && p >= p_start) { - t = *p; - - /* For Ecmascript strings, this check can only match for - * initial UTF-8 bytes (not continuation bytes). For other - * strings all bets are off. - */ - - if ((t == firstbyte) && ((duk_size_t) (p_end - p) >= (duk_size_t) q_blen)) { - DUK_ASSERT(q_blen > 0); /* no issues with memcmp() zero size, even if broken */ - if (DUK_MEMCMP((const void *) p, (const void *) q_start, (size_t) q_blen) == 0) { - duk_push_int(ctx, cpos); - return 1; - } - } - - /* track cpos while scanning */ - if (is_lastindexof) { - /* when going backwards, we decrement cpos 'early'; - * 'p' may point to a continuation byte of the char - * at offset 'cpos', but that's OK because we'll - * backtrack all the way to the initial byte. - */ - if ((t & 0xc0) != 0x80) { - cpos--; - } - p--; - } else { - if ((t & 0xc0) != 0x80) { - cpos++; - } - p++; - } - } - - /* Not found. Empty string case is handled specially above. */ - duk_push_int(ctx, -1); + cpos = duk__str_search_shared(ctx, h_this, h_search, cpos, is_lastindexof /*backwards*/); + duk_push_int(ctx, cpos); return 1; } @@ -37140,9 +38190,10 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_replace(duk_context *ctx) { /* Use match charlen instead of bytelen, just in case the input and * 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_HSTRING_GET_CHARLEN(h_match)); + 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); @@ -37492,7 +38543,7 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_split(duk_context *ctx) { DUK_DDD(DUK_DDDPRINT("split trailer; prev_end b=%ld,c=%ld", (long) prev_match_end_boff, (long) prev_match_end_coff)); - if (DUK_HSTRING_GET_CHARLEN(h_input) > 0 || !matched) { + if (DUK_HSTRING_GET_BYTELEN(h_input) > 0 || !matched) { /* Add trailer if: * a) non-empty input * b) empty input and no (zero size) match found (step 11) @@ -37834,6 +38885,93 @@ DUK_INTERNAL duk_ret_t duk_bi_string_prototype_locale_compare(duk_context *ctx) return 1; } +#if defined(DUK_USE_ES6) +DUK_INTERNAL duk_ret_t duk_bi_string_prototype_startswith_endswith(duk_context *ctx) { + duk_int_t magic; + duk_hstring *h; + duk_hstring *h_search; + duk_size_t blen_search; + const duk_uint8_t *p_cmp_start; + duk_bool_t result; + + h = duk_push_this_coercible_to_string(ctx); + DUK_ASSERT(h != NULL); + + h_search = duk__str_tostring_notregexp(ctx, 0); + DUK_ASSERT(h_search != NULL); + + magic = duk_get_current_magic(ctx); + + 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 (magic) { + p_cmp_start += DUK_HSTRING_GET_BYTELEN(h) - blen_search; + } else { + /* p_cmp_start already OK */ + } + } else { + duk_int_t len; + duk_int_t pos; + + 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); + DUK_ASSERT(pos >= 0 && pos <= len); + + if (magic) { + p_cmp_start -= blen_search; /* Conceptually subtracted last, but do already here. */ + } + DUK_ASSERT(pos >= 0 && pos <= len); + + p_cmp_start += duk_heap_strcache_offset_char2byte((duk_hthread *) ctx, h, pos); + } + + /* The main comparison can be done using a memcmp() rather than + * doing codepoint comparisons: for CESU-8 strings there is a + * canonical representation for every codepoint. But we do need + * to deal with the char/byte offset translation to find the + * comparison range. + */ + + 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)) { + if (DUK_MEMCMP((const void *) p_cmp_start, + (const void *) DUK_HSTRING_GET_DATA(h_search), + (size_t) blen_search) == 0) { + result = 1; + } + } + + duk_push_boolean(ctx, 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_hstring *h; + duk_hstring *h_search; + duk_int_t len; + duk_int_t pos; + + h = duk_push_this_coercible_to_string(ctx); + DUK_ASSERT(h != NULL); + + h_search = duk__str_tostring_notregexp(ctx, 0); + DUK_ASSERT(h_search != NULL); + + len = (duk_int_t) DUK_HSTRING_GET_CHARLEN(h); + pos = duk_to_int_clamped(ctx, 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); + return 1; +} +#endif /* DUK_USE_ES6 */ #endif /* DUK_USE_STRING_BUILTIN */ /* * Symbol built-in @@ -37941,7 +39079,8 @@ DUK_LOCAL duk_hstring *duk__auto_unbox_symbol(duk_context *ctx, duk_tval *tv_arg h_str = DUK_TVAL_GET_STRING(tv); DUK_ASSERT(h_str != NULL); - if (!DUK_HSTRING_HAS_SYMBOL(h_str)) { + /* Here symbol is more expected than not. */ + if (DUK_UNLIKELY(!DUK_HSTRING_HAS_SYMBOL(h_str))) { return NULL; } @@ -37961,6 +39100,7 @@ DUK_INTERNAL duk_ret_t duk_bi_symbol_tostring_shared(duk_context *ctx) { duk_push_symbol_descriptive_string(ctx, h_str); } else { /* .valueOf() */ + duk_push_hstring(ctx, h_str); } return 1; } @@ -38087,11 +39227,12 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_context *ctx) { DUK_DD(DUK_DDPRINT("resume state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.resume)")); goto state_error; } - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */ - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */ + DUK_ASSERT(thr->callstack_curr != NULL); + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); /* us */ + DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL); /* caller */ - caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2); + caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1); if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) { DUK_DD(DUK_DDPRINT("resume state invalid: caller must be Ecmascript code")); goto state_error; @@ -38241,11 +39382,12 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_context *ctx) { DUK_DD(DUK_DDPRINT("yield state invalid: callstack should contain at least 2 entries (caller and Duktape.Thread.yield)")); goto state_error; } - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); /* us */ - DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL); /* caller */ + DUK_ASSERT(thr->callstack_curr != NULL); + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL); /* us */ + DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr))); + DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL); /* caller */ - caller_func = DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2); + caller_func = DUK_ACT_GET_FUNC(thr->callstack_curr - 1); if (!DUK_HOBJECT_IS_COMPFUNC(caller_func)) { DUK_DD(DUK_DDPRINT("yield state invalid: caller must be Ecmascript code")); goto state_error; @@ -38820,6 +39962,9 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { } } if (st->internal) { + if (DUK_HOBJECT_IS_ARRAY(h)) { + DUK__COMMA(); duk_fb_sprintf(fb, "__array:true"); + } if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true"); } @@ -38838,7 +39983,7 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { if (DUK_HOBJECT_HAS_BUFOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__bufobj:true"); } - if (DUK_HOBJECT_HAS_THREAD(h)) { + if (DUK_HOBJECT_IS_THREAD(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true"); } if (DUK_HOBJECT_HAS_ARRAY_PART(h)) { @@ -38859,9 +40004,6 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { if (DUK_HOBJECT_HAS_CREATEARGS(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true"); } - if (DUK_HOBJECT_HAS_ENVRECCLOSED(h)) { - DUK__COMMA(); duk_fb_sprintf(fb, "__envrecclosed:true"); - } if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_array:true"); } @@ -38906,6 +40048,15 @@ DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func)); DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic); + } else if (st->internal && DUK_HOBJECT_IS_DECENV(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); + } 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); + DUK__COMMA(); duk_fb_sprintf(fb, "__has_this:%ld", (long) e->has_this); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; @@ -39139,6 +40290,10 @@ DUK_LOCAL void duk__print_tval(duk__dprint_state *st, duk_tval *tv) { } #if defined(DUK_USE_FASTINT) 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)); + break; #endif default: { /* IEEE double is approximately 16 decimal digits; print a couple extra */ @@ -39232,7 +40387,7 @@ DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const cha if (ch == DUK_ASC_STAR) { /* unsupported: would consume multiple args */ - goto error; + goto format_error; } else if (ch == DUK_ASC_PERCENT) { duk_fb_put_byte(&fb, (duk_uint8_t) DUK_ASC_PERCENT); break; @@ -39284,7 +40439,7 @@ DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const cha fmtlen = (duk_size_t) (p - p_begfmt); if (fmtlen >= sizeof(fmtbuf)) { /* format is too large, abort */ - goto error; + goto format_error; } DUK_MEMZERO(fmtbuf, sizeof(fmtbuf)); DUK_MEMCPY(fmtbuf, p_begfmt, fmtlen); @@ -39359,7 +40514,7 @@ DUK_INTERNAL duk_int_t duk_debug_vsnprintf(char *str, duk_size_t size, const cha } goto done; - error: + format_error: duk_fb_put_cstring(&fb, "FMTERR"); /* fall through */ @@ -39487,7 +40642,6 @@ DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) { /* heap->dbg_detached_cb: keep */ /* heap->dbg_udata: keep */ /* heap->dbg_processing: keep on purpose to avoid debugger re-entry in detaching state */ - DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap); heap->dbg_state_dirty = 0; heap->dbg_force_restart = 0; heap->dbg_step_type = 0; @@ -39495,6 +40649,8 @@ DUK_LOCAL void duk__debug_do_detach1(duk_heap *heap, duk_int_t reason) { heap->dbg_step_csindex = 0; heap->dbg_step_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? */ /* Ensure there are no stale active breakpoint pointers. * Breakpoint list is currently kept - we could empty it @@ -39513,7 +40669,10 @@ DUK_LOCAL void duk__debug_do_detach2(duk_heap *heap) { duk_context *ctx; thr = heap->heap_thread; - DUK_ASSERT(thr != NULL); + if (thr == NULL) { + DUK_ASSERT(heap->dbg_detached_cb == NULL); + return; + } ctx = (duk_context *) thr; /* Safe to call multiple times. */ @@ -39547,6 +40706,9 @@ DUK_INTERNAL void duk_debug_do_detach(duk_heap *heap) { */ DUK_LOCAL void duk__debug_null_most_callbacks(duk_hthread *thr) { duk_heap *heap; + + DUK_ASSERT(thr != NULL); + heap = thr->heap; DUK_D(DUK_DPRINT("transport read/write error, NULL all callbacks expected detached")); heap->dbg_read_cb = NULL; @@ -40394,7 +41556,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_debug_curr_line(duk_hthread *thr) { duk_uint_fast32_t line; duk_uint_fast32_t pc; - act = duk_hthread_get_current_activation(thr); /* may be NULL */ + act = thr->callstack_curr; if (act == NULL) { return 0; } @@ -40425,13 +41587,13 @@ DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) { duk_debug_write_int(thr, (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) ? 1 : 0)); DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* unsigned */ - if (thr->callstack_top == 0) { + act = thr->callstack_curr; + if (act == NULL) { duk_debug_write_undefined(thr); duk_debug_write_undefined(thr); duk_debug_write_int(thr, 0); duk_debug_write_int(thr, 0); } else { - act = thr->callstack + thr->callstack_top - 1; duk_push_tval(ctx, &act->tv_func); duk_get_prop_string(ctx, -1, "fileName"); duk__debug_write_hstring_safe_top(thr); @@ -40440,7 +41602,7 @@ DUK_INTERNAL void duk_debug_send_status(duk_hthread *thr) { duk_pop_3(ctx); /* Report next pc/line to be executed. */ duk_debug_write_uint(thr, (duk_uint32_t) duk_debug_curr_line(thr)); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; duk_debug_write_uint(thr, (duk_uint32_t) duk_hthread_get_act_curr_pc(thr, act)); } @@ -40479,12 +41641,12 @@ DUK_INTERNAL void duk_debug_send_throw(duk_hthread *thr, duk_bool_t fatal) { * error location directly from the current activation if one * exists. */ - if (thr->callstack_top > 0) { - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + if (act != NULL) { duk_push_tval(ctx, &act->tv_func); duk_get_prop_string(ctx, -1, "fileName"); duk__debug_write_hstring_safe_top(thr); - act = thr->callstack + thr->callstack_top - 1; + 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); @@ -40635,7 +41797,11 @@ 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")); - DUK_HEAP_SET_PAUSED(heap); + if (duk_debug_is_paused(heap)) { + DUK_D(DUK_DPRINT("Pause requested when already paused, ignore")); + } else { + duk_debug_set_paused(heap); + } duk_debug_write_reply(thr); duk_debug_write_eom(thr); } @@ -40643,7 +41809,7 @@ DUK_LOCAL void duk__debug_handle_pause(duk_hthread *thr, duk_heap *heap) { DUK_LOCAL void duk__debug_handle_resume(duk_hthread *thr, duk_heap *heap) { DUK_D(DUK_DPRINT("debug command Resume")); - DUK_HEAP_CLEAR_PAUSED(heap); + duk_debug_clear_paused(heap); duk_debug_write_reply(thr); duk_debug_write_eom(thr); } @@ -40665,7 +41831,7 @@ DUK_LOCAL void duk__debug_handle_step(duk_hthread *thr, duk_heap *heap, duk_int3 line = duk_debug_curr_line(thr); if (line > 0) { - DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap); + 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; @@ -40927,6 +42093,7 @@ DUK_LOCAL void duk__debug_handle_eval(duk_hthread *thr, duk_heap *heap) { /* Read callstack index, if non-null. */ if (duk_debug_peek_byte(thr) == DUK_DBG_IB_NULL) { direct_eval = 0; + level = -1; /* Not needed, but silences warning. */ (void) duk_debug_read_byte(thr); } else { direct_eval = 1; @@ -41169,82 +42336,29 @@ DUK_LOCAL void duk__debug_dump_heap_allocated(duk_hthread *thr, duk_heap *heap) } } -#if defined(DUK_USE_STRTAB_CHAIN) -DUK_LOCAL void duk__debug_dump_strtab_chain(duk_hthread *thr, duk_heap *heap) { - duk_uint_fast32_t i, j; - duk_strtab_entry *e; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *lst; -#else - duk_hstring **lst; -#endif - duk_hstring *h; - - for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { - e = heap->strtable + i; - if (e->listlen > 0) { -#if defined(DUK_USE_HEAPPTR16) - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); -#else - lst = e->u.strlist; -#endif - DUK_ASSERT(lst != NULL); - - for (j = 0; j < e->listlen; j++) { -#if defined(DUK_USE_HEAPPTR16) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[j]); -#else - h = lst[j]; -#endif - if (h != NULL) { - duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); - } - } - } else { -#if defined(DUK_USE_HEAPPTR16) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); -#else - h = e->u.str; -#endif - if (h != NULL) { - duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); - } - } - } -} -#endif /* DUK_USE_STRTAB_CHAIN */ - -#if defined(DUK_USE_STRTAB_PROBE) -DUK_LOCAL void duk__debug_dump_strtab_probe(duk_hthread *thr, duk_heap *heap) { +DUK_LOCAL void duk__debug_dump_strtab(duk_hthread *thr, duk_heap *heap) { duk_uint32_t i; duk_hstring *h; for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_HEAPPTR16) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); +#if defined(DUK_USE_STRTAB_PTRCOMP) + h = DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif - if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { - continue; + while (h != NULL) { + duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); + h = h->hdr.h_next; } - - duk__debug_dump_heaphdr(thr, heap, (duk_heaphdr *) h); } } -#endif /* DUK_USE_STRTAB_PROBE */ DUK_LOCAL void duk__debug_handle_dump_heap(duk_hthread *thr, duk_heap *heap) { DUK_D(DUK_DPRINT("debug command DumpHeap")); duk_debug_write_reply(thr); duk__debug_dump_heap_allocated(thr, heap); -#if defined(DUK_USE_STRTAB_CHAIN) - duk__debug_dump_strtab_chain(thr, heap); -#endif -#if defined(DUK_USE_STRTAB_PROBE) - duk__debug_dump_strtab_probe(thr, heap); -#endif + duk__debug_dump_strtab(thr, heap); duk_debug_write_eom(thr); } #endif /* DUK_USE_DEBUGGER_DUMPHEAP */ @@ -41382,14 +42496,14 @@ DUK_LOCAL const char * const duk__debug_getinfo_hobject_keys[] = { "compfunc", "natfunc", "bufobj", - "thread", + "fastrefs", "array_part", "strict", "notail", "newenv", "namebinding", "createargs", - "envrecclosed", + "have_finalizer" "exotic_array", "exotic_stringobj", "exotic_arguments", @@ -41404,14 +42518,14 @@ DUK_LOCAL duk_uint_t duk__debug_getinfo_hobject_masks[] = { DUK_HOBJECT_FLAG_COMPFUNC, DUK_HOBJECT_FLAG_NATFUNC, DUK_HOBJECT_FLAG_BUFOBJ, - DUK_HOBJECT_FLAG_THREAD, + DUK_HOBJECT_FLAG_FASTREFS, DUK_HOBJECT_FLAG_ARRAY_PART, DUK_HOBJECT_FLAG_STRICT, DUK_HOBJECT_FLAG_NOTAIL, DUK_HOBJECT_FLAG_NEWENV, DUK_HOBJECT_FLAG_NAMEBINDING, DUK_HOBJECT_FLAG_CREATEARGS, - DUK_HOBJECT_FLAG_ENVRECCLOSED, + DUK_HOBJECT_FLAG_HAVE_FINALIZER, DUK_HOBJECT_FLAG_EXOTIC_ARRAY, DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ, DUK_HOBJECT_FLAG_EXOTIC_ARGUMENTS, @@ -41579,9 +42693,9 @@ DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h duk__debug_getinfo_hstring_keys, duk__debug_getinfo_hstring_masks, DUK_HEAPHDR_GET_FLAGS_RAW(h)); - duk__debug_getinfo_prop_uint(thr, "bytelen", DUK_HSTRING_GET_BYTELEN(h_str)); - duk__debug_getinfo_prop_uint(thr, "charlen", DUK_HSTRING_GET_CHARLEN(h_str)); - duk__debug_getinfo_prop_uint(thr, "hash", DUK_HSTRING_GET_HASH(h_str)); + duk__debug_getinfo_prop_uint(thr, "bytelen", (duk_uint_t) DUK_HSTRING_GET_BYTELEN(h_str)); + duk__debug_getinfo_prop_uint(thr, "charlen", (duk_uint_t) DUK_HSTRING_GET_CHARLEN(h_str)); + duk__debug_getinfo_prop_uint(thr, "hash", (duk_uint_t) DUK_HSTRING_GET_HASH(h_str)); duk__debug_getinfo_flags_key(thr, "data"); duk_debug_write_hstring(thr, h_str); break; @@ -41679,6 +42793,26 @@ DUK_LOCAL void duk__debug_handle_get_heap_obj_info(duk_hthread *thr, duk_heap *h DUK_UNREF(h_thr); } + if (DUK_HOBJECT_IS_DECENV(h_obj)) { + duk_hdecenv *h_env; + h_env = (duk_hdecenv *) h_obj; + + duk__debug_getinfo_flags_key(thr, "thread"); + 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); + } + + if (DUK_HOBJECT_IS_OBJENV(h_obj)) { + duk_hobjenv *h_env; + h_env = (duk_hobjenv *) h_obj; + + duk__debug_getinfo_flags_key(thr, "target"); + duk_debug_write_heapptr(thr, (duk_heaphdr *) (h_env->target)); + duk__debug_getinfo_prop_bool(thr, "has_this", h_env->has_this); + } + #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) if (DUK_HOBJECT_IS_BUFOBJ(h_obj)) { duk_hbufobj *h_bufobj; @@ -42101,12 +43235,13 @@ DUK_INTERNAL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); - DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)); + DUK_ASSERT(duk_debug_is_attached(thr->heap)); DUK_ASSERT(thr->heap->dbg_processing == 0); + DUK_ASSERT(!duk_debug_is_paused(thr->heap)); - DUK_HEAP_SET_PAUSED(thr->heap); + duk_debug_set_paused(thr->heap); - act = duk_hthread_get_current_activation(thr); + act = thr->callstack_curr; /* NOTE: act may be NULL if an error is thrown outside of any activation, * which may happen in the case of, e.g. syntax errors. @@ -42139,8 +43274,8 @@ DUK_INTERNAL void duk_debug_halt_execution(duk_hthread *thr, duk_bool_t use_prev thr->heap->dbg_state_dirty = 1; while (DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap)) { - DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)); - DUK_ASSERT(thr->heap->dbg_processing); + DUK_ASSERT(duk_debug_is_attached(thr->heap)); + DUK_ASSERT(thr->heap->dbg_processing == 0); duk_debug_process_messages(thr, 0 /*no_block*/); } @@ -42202,7 +43337,7 @@ DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_ DUK_ASSERT(thr != NULL); heap = thr->heap; DUK_ASSERT(heap != NULL); - DUK_ASSERT(DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)); + DUK_ASSERT(duk_debug_is_attached(thr->heap)); DUK_ASSERT_DISABLE(breakpoint_index >= 0); /* unsigned */ if (breakpoint_index >= heap->dbg_breakpoint_count) { @@ -42231,6 +43366,55 @@ DUK_INTERNAL duk_bool_t duk_debug_remove_breakpoint(duk_hthread *thr, duk_small_ return 1; } +/* + * Misc state management + */ + +DUK_INTERNAL duk_bool_t duk_debug_is_attached(duk_heap *heap) { + return (heap->dbg_read_cb != NULL); +} + +DUK_INTERNAL duk_bool_t duk_debug_is_paused(duk_heap *heap) { + return (DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) != 0); +} + +DUK_INTERNAL void duk_debug_set_paused(duk_heap *heap) { + if (duk_debug_is_paused(heap)) { + DUK_D(DUK_DPRINT("trying to set paused state when already paused, ignoring")); + } else { + DUK_HEAP_SET_DEBUGGER_PAUSED(heap); + heap->dbg_state_dirty = 1; + duk_debug_clear_step_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++; + DUK_ASSERT(heap->ms_prevent_count != 0); /* Wrap. */ + DUK_ASSERT(heap->heap_thread != NULL); + } +} + +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_ASSERT(heap->ms_running == 1); + DUK_ASSERT(heap->ms_prevent_count > 0); + heap->ms_prevent_count--; + heap->ms_running = 0; + DUK_ASSERT(heap->heap_thread != NULL); + } else { + DUK_D(DUK_DPRINT("trying to clear paused state when not paused, ignoring")); + } +} + +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; +} + #else /* DUK_USE_DEBUGGER_SUPPORT */ /* No debugger support. */ @@ -42868,18 +44052,37 @@ DUK_LOCAL void duk__uncaught_error_aware(duk_hthread *thr) { DUK_INTERNAL void duk_err_longjmp(duk_hthread *thr) { DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); DUK_DD(DUK_DDPRINT("longjmp error: type=%d iserror=%d value1=%!T value2=%!T", (int) thr->heap->lj.type, (int) thr->heap->lj.iserror, &thr->heap->lj.value1, &thr->heap->lj.value2)); - /* Perform a refzero check before throwing: this catches cases where - * some internal code uses no-refzero (NORZ) macro variants but an - * error occurs before it has the chance to DUK_REFZERO_CHECK_xxx() - * explicitly. Refzero'ed objects would otherwise remain pending - * until the next refzero (which is not a big issue but still). + /* Prevent finalizer execution during error handling. All error + * handling sites will process pending finalizers once error handling + * is complete and we're ready for the side effects. Does not prevent + * refzero freeing or mark-and-sweep during error handling. + * + * NOTE: when we come here some calling code may have used DECREF + * NORZ macros without an explicit DUK_REFZERO_CHECK_xxx() call. + * We don't want to do it here because it would just check for + * pending finalizers and we prevent that explicitly. Instead, + * the error catcher will run the finalizers once error handling + * is complete. */ - DUK_REFZERO_CHECK_SLOW(thr); + + DUK_ASSERT_LJSTATE_SET(thr->heap); + + thr->heap->pf_prevent_count++; + DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ + +#if defined(DUK_USE_ASSERTIONS) + /* XXX: set this immediately when longjmp state is set */ + DUK_ASSERT(thr->heap->error_not_allowed == 0); /* Detect error within critical section. */ + thr->heap->error_not_allowed = 1; +#endif + + DUK_DD(DUK_DDPRINT("about to longjmp, pf_prevent_count=%ld", (long) thr->heap->pf_prevent_count)); #if !defined(DUK_USE_CPP_EXCEPTIONS) /* If we don't have a jmpbuf_ptr, there is little we can do except @@ -42978,11 +44181,16 @@ DUK_INTERNAL duk_hobject *duk_error_prototype_from_code(duk_hthread *thr, duk_er } /* - * Exposed helper for setting up heap longjmp state. + * Helper for debugger throw notify and pause-on-uncaught integration. */ -DUK_INTERNAL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t lj_type) { #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_tval *tv_obj; + /* If something is thrown with the debugger attached and nobody will * catch it, execution is paused before the longjmp, turning over * control to the debug client. This allows local state to be examined @@ -42990,55 +44198,102 @@ DUK_INTERNAL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t l * message loop is active (e.g. for Eval). */ + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + /* XXX: Allow customizing the pause and notify behavior at runtime * using debugger runtime flags. For now the behavior is fixed using * config options. */ -#if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) || defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && - !thr->heap->dbg_processing && - lj_type == DUK_LJ_TYPE_THROW) { - duk_context *ctx = (duk_context *) thr; - duk_bool_t fatal; - duk_hobject *h_obj; - /* Don't intercept a DoubleError, we may have caused the initial double - * fault and attempting to intercept it will cause us to be called - * recursively and exhaust the C stack. - */ - h_obj = duk_get_hobject(ctx, -1); - if (h_obj == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) { - DUK_D(DUK_DPRINT("built-in DoubleError instance thrown, not intercepting")); - goto skip_throw_intercept; - } + if (!duk_debug_is_attached(thr->heap) || + thr->heap->dbg_processing || + thr->heap->lj.type != DUK_LJ_TYPE_THROW || + thr->heap->creating_error) { + DUK_D(DUK_DPRINT("skip debugger error integration; not attached, debugger processing, not THROW, or error thrown while creating error")); + return; + } - DUK_D(DUK_DPRINT("throw with debugger attached, report to client")); + /* Don't intercept a DoubleError, we may have caused the initial double + * fault and attempting to intercept it will cause us to be called + * recursively and exhaust the C stack. (This should no longer happen + * for the initial throw because DoubleError path doesn't do a debugger + * integration check, but it might happen for rethrows.) + */ + tv_obj = &thr->heap->lj.value1; + if (DUK_TVAL_IS_OBJECT(tv_obj) && DUK_TVAL_GET_OBJECT(tv_obj) == thr->builtins[DUK_BIDX_DOUBLE_ERROR]) { + DUK_D(DUK_DPRINT("built-in DoubleError instance (re)thrown, not intercepting")); + return; + } + + fatal = !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); - fatal = !duk__have_active_catcher(thr); + /* Store and reset longjmp state. */ + DUK_ASSERT_LJSTATE_SET(thr->heap); + DUK_TVAL_DECREF_NORZ(thr, tv_obj); + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); /* Always for THROW type. */ + DUK_TVAL_SET_UNDEFINED(tv_obj); + thr->heap->lj.type = DUK_LJ_TYPE_UNKNOWN; + DUK_ASSERT_LJSTATE_UNSET(thr->heap); #if defined(DUK_USE_DEBUGGER_THROW_NOTIFY) - /* Report it to the debug client */ - duk_debug_send_throw(thr, fatal); + /* Report it to the debug client */ + DUK_D(DUK_DPRINT("throw with debugger attached, report to client")); + duk_debug_send_throw(thr, fatal); #endif #if defined(DUK_USE_DEBUGGER_PAUSE_UNCAUGHT) - if (fatal) { - DUK_D(DUK_DPRINT("throw will be fatal, halt before longjmp")); - duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); - } -#endif + if (fatal) { + DUK_D(DUK_DPRINT("throw will be fatal, halt before longjmp")); + 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); + 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); - skip_throw_intercept: + duk_pop(ctx); +} +#else /* DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT */ +DUK_INTERNAL void duk_err_check_debugger_integration(duk_hthread *thr) { + DUK_UNREF(thr); +} #endif /* DUK_USE_DEBUGGER_THROW_NOTIFY || DUK_USE_DEBUGGER_PAUSE_UNCAUGHT */ #endif /* DUK_USE_DEBUGGER_SUPPORT */ - thr->heap->lj.type = lj_type; +/* + * Helpers for setting up heap longjmp state. + */ - DUK_ASSERT(thr->valstack_top > thr->valstack); - DUK_TVAL_SET_TVAL_UPDREF(thr, &thr->heap->lj.value1, thr->valstack_top - 1); /* side effects */ +DUK_INTERNAL void duk_err_setup_ljstate1(duk_hthread *thr, duk_small_uint_t lj_type, duk_tval *tv_val) { + duk_heap *heap; - duk_pop((duk_context *) thr); + DUK_ASSERT(thr != NULL); + heap = thr->heap; + DUK_ASSERT(heap != NULL); + DUK_ASSERT(tv_val != NULL); + + DUK_ASSERT_LJSTATE_UNSET(heap); + + heap->lj.type = lj_type; + DUK_TVAL_SET_TVAL(&heap->lj.value1, tv_val); + DUK_TVAL_INCREF(thr, tv_val); + + DUK_ASSERT_LJSTATE_SET(heap); } /* * Create and throw an Ecmascript error object based on a code and a message. @@ -43058,7 +44313,7 @@ DUK_INTERNAL void duk_err_setup_heap_ljstate(duk_hthread *thr, duk_small_int_t l * * If an error occurs while we're dealing with the current error, we might * enter an infinite recursion loop. This is prevented by detecting a - * "double fault" through the heap->handling_error flag; the recursion + * "double fault" through the heap->creating_error flag; the recursion * then stops at the second level. */ @@ -43068,7 +44323,6 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) { #endif duk_context *ctx = (duk_context *) thr; - duk_bool_t double_error = thr->heap->handling_error; #if defined(DUK_USE_VERBOSE_ERRORS) DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld", @@ -43081,18 +44335,11 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); - thr->heap->handling_error = 1; - - if (!double_error) { - /* 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. - */ - DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX); - thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11; - } - - DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11); /* just making sure */ + /* Even though nested call is possible because we throw an error when + * trying to create an error, the potential errors must happen before + * the longjmp state is configured. + */ + DUK_ASSERT_LJSTATE_UNSET(thr->heap); /* Sync so that augmentation sees up-to-date activations, NULL * thr->ptr_curr_pc so that it's not used if side effects occur @@ -43102,29 +44349,50 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) /* * Create and push an error object onto the top of stack. + * The error is potentially augmented before throwing. + * * If a "double error" occurs, use a fixed error instance * to avoid further trouble. */ - /* XXX: if attempt to push beyond allocated valstack, this double fault - * handling fails miserably. We should really write the double error - * directly to thr->heap->lj.value1 and avoid valstack use entirely. - */ + if (thr->heap->creating_error) { + 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; - if (double_error) { - if (thr->builtins[DUK_BIDX_DOUBLE_ERROR]) { - DUK_D(DUK_DPRINT("double fault detected -> push built-in fixed 'double error' instance")); - duk_push_hobject_bidx(ctx, DUK_BIDX_DOUBLE_ERROR); + h_err = thr->builtins[DUK_BIDX_DOUBLE_ERROR]; + if (h_err != NULL) { + DUK_D(DUK_DPRINT("double fault detected -> use built-in fixed 'double error' instance")); + DUK_TVAL_SET_OBJECT(&tv_val, h_err); } else { DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance " - "-> push the error code as a number")); - duk_push_int(ctx, (duk_int_t) code); + "-> use the error code as a number")); + DUK_TVAL_SET_I32(&tv_val, (duk_int32_t) code); } + + duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, &tv_val); + + /* No augmentation to avoid any allocations or side effects. */ } else { - /* Error object is augmented at its creation here. */ + /* Allow headroom for calls during error handling (see GH-191). + * We allow space for 10 additional recursions, with one extra + * for, e.g. a print() call at the deepest level. + */ +#if 0 /* XXX: not always true, second throw may come from a different coroutine */ + DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX); +#endif + thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11; + thr->heap->creating_error = 1; + duk_require_stack(ctx, 1); - /* XXX: unnecessary '%s' formatting here, but cannot use - * 'msg' as a format string directly. + + /* 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, @@ -43140,37 +44408,38 @@ DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) 0, NULL); #endif - } - - /* - * Augment error (throw time), unless double error - * - * Note that an alloc error may happen during error augmentation. - * This may happen both when the original error is an alloc error - * and when it's something else. Because any error in augmentation - * must be handled correctly anyway, there's no special check for - * avoiding it for alloc errors (this differs from Duktape 1.x). - */ - if (double_error) { - DUK_D(DUK_DPRINT("double error: skip throw augmenting to avoid further trouble")); - } else { + /* Note that an alloc error may happen during error augmentation. + * This may happen both when the original error is an alloc error + * and when it's something else. Because any error in augmentation + * must be handled correctly anyway, there's no special check for + * avoiding it for alloc errors (this differs from Duktape 1.x). + */ #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_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; + thr->heap->creating_error = 0; + + /* Error is now created and we assume no errors can occur any + * more. Check for debugger Throw integration only when the + * error is complete. If we enter debugger message loop, + * creating_error must be 0 so that errors can be thrown in + * the paused state, e.g. in Eval commands. + */ +#if defined(DUK_USE_DEBUGGER_SUPPORT) + duk_err_check_debugger_integration(thr); +#endif } /* * Finally, longjmp */ - duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); - - thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; /* reset callstack limit */ - thr->heap->handling_error = 0; - DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT, %!iT (after throw augment)", (duk_tval *) &thr->heap->lj.value1, (duk_tval *) &thr->heap->lj.value2)); @@ -43244,8 +44513,8 @@ DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk } res = (duk_hbuffer *) DUK_ALLOC(heap, alloc_size); - if (!res) { - goto error; + if (DUK_UNLIKELY(res == NULL)) { + goto alloc_error; } /* zero everything unless requested not to do so */ @@ -43281,9 +44550,9 @@ DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk #else ptr = DUK_ALLOC(heap, size); #endif - if (!ptr) { + if (DUK_UNLIKELY(ptr == NULL)) { /* Because size > 0, NULL check is correct */ - goto error; + goto alloc_error; } *out_bufdata = ptr; @@ -43319,7 +44588,7 @@ DUK_INTERNAL duk_hbuffer *duk_hbuffer_alloc(duk_heap *heap, duk_size_t size, duk DUK_DDD(DUK_DDDPRINT("allocated hbuffer: %p", (void *) res)); return res; - error: + alloc_error: DUK_DD(DUK_DDPRINT("hbuffer allocation failed")); DUK_FREE(heap, res); @@ -43374,7 +44643,7 @@ DUK_INTERNAL void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, */ res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_size); - if (res != NULL || new_size == 0) { + if (DUK_LIKELY(res != NULL || new_size == 0)) { /* 'res' may be NULL if new allocation size is 0. */ DUK_DDD(DUK_DDDPRINT("resized dynamic buffer %p:%ld -> %p:%ld", @@ -43529,11 +44798,9 @@ DUK_INTERNAL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr) { case DUK_HTYPE_OBJECT: duk_free_hobject(heap, (duk_hobject *) hdr); break; - case DUK_HTYPE_BUFFER: - duk_free_hbuffer(heap, (duk_hbuffer *) hdr); - break; default: - DUK_UNREACHABLE(); + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_BUFFER); + duk_free_hbuffer(heap, (duk_hbuffer *) hdr); } } @@ -43569,23 +44836,8 @@ DUK_LOCAL void duk__free_allocated(duk_heap *heap) { } } -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_LOCAL void duk__free_refzero_list(duk_heap *heap) { - duk_heaphdr *curr; - duk_heaphdr *next; - - curr = heap->refzero_list; - while (curr) { - DUK_DDD(DUK_DDDPRINT("FINALFREE (refzero_list): %!iO", - (duk_heaphdr *) curr)); - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - duk_heap_free_heaphdr_raw(heap, curr); - curr = next; - } -} -#endif - -DUK_LOCAL void duk__free_markandsweep_finalize_list(duk_heap *heap) { +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_LOCAL void duk__free_finalize_list(duk_heap *heap) { duk_heaphdr *curr; duk_heaphdr *next; @@ -43598,15 +44850,15 @@ DUK_LOCAL void duk__free_markandsweep_finalize_list(duk_heap *heap) { curr = next; } } +#endif /* DUK_USE_FINALIZER_SUPPORT */ DUK_LOCAL void duk__free_stringtable(duk_heap *heap) { /* strings are only tracked by stringtable */ - duk_heap_free_strtab(heap); + duk_heap_strtable_free(heap); } #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { - duk_hthread *thr; duk_heaphdr *curr; duk_uint_t round_no; duk_size_t count_all; @@ -43614,25 +44866,31 @@ DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { duk_size_t curr_limit; DUK_ASSERT(heap != NULL); - DUK_ASSERT(heap->heap_thread != NULL); #if defined(DUK_USE_REFERENCE_COUNTING) DUK_ASSERT(heap->refzero_list == NULL); /* refzero not running -> must be empty */ #endif - DUK_ASSERT(heap->finalize_list == NULL); /* mark-and-sweep not running -> must be empty */ + DUK_ASSERT(heap->finalize_list == NULL); /* mark-and-sweep last pass */ - /* XXX: here again finalizer thread is the heap_thread which needs - * to be coordinated with finalizer thread fixes. - */ - thr = heap->heap_thread; - DUK_ASSERT(thr != NULL); + if (heap->heap_thread == NULL) { + /* May happen when heap allocation fails right off. There + * cannot be any finalizable objects in this case. + */ + DUK_D(DUK_DPRINT("no heap_thread in heap destruct, assume no finalizable objects")); + return; + } - /* Prevent mark-and-sweep for the pending finalizers, also prevents - * refzero handling from moving objects away from the heap_allocated - * list. (The flag meaning is slightly abused here.) + /* Prevent finalize_list processing and mark-and-sweep entirely. + * Setting ms_running = 1 also prevents refzero handling from moving + * objects away from the heap_allocated list (the flag name is a bit + * misleading here). */ - DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)); - DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap); + DUK_ASSERT(heap->pf_prevent_count == 0); + heap->pf_prevent_count = 1; + DUK_ASSERT(heap->ms_running == 0); + heap->ms_running = 1; + DUK_ASSERT(heap->ms_prevent_count == 0); + heap->ms_prevent_count = 1; /* Bump, because mark-and-sweep assumes it's bumped when ms_running is set. */ curr_limit = 0; /* suppress warning, not used */ for (round_no = 0; ; round_no++) { @@ -43641,18 +44899,17 @@ DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { count_finalized = 0; while (curr) { count_all++; - if (DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT) { + if (DUK_HEAPHDR_IS_OBJECT(curr)) { /* Only objects in heap_allocated may have finalizers. Check that * the object itself has a _Finalizer property (own or inherited) * so that we don't execute finalizers for e.g. Proxy objects. */ - DUK_ASSERT(thr != NULL); DUK_ASSERT(curr != NULL); - if (duk_hobject_hasprop_raw(thr, (duk_hobject *) curr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) { + if (DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) curr)) { if (!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) curr)) { DUK_ASSERT(DUK_HEAP_HAS_FINALIZER_NORESCUE(heap)); /* maps to finalizer 2nd argument */ - duk_hobject_run_finalizer(thr, (duk_hobject *) curr); + duk_heap_run_finalizer(heap, (duk_hobject *) curr); count_finalized++; } } @@ -43693,8 +44950,10 @@ DUK_LOCAL void duk__free_run_finalizers(duk_heap *heap) { } } - DUK_ASSERT(DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)); - DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap); + DUK_ASSERT(heap->ms_running == 1); + heap->ms_running = 0; + DUK_ASSERT(heap->pf_prevent_count == 1); + heap->pf_prevent_count = 0; } #endif /* DUK_USE_FINALIZER_SUPPORT */ @@ -43702,7 +44961,7 @@ DUK_INTERNAL void duk_heap_free(duk_heap *heap) { DUK_D(DUK_DPRINT("free heap: %p", (void *) heap)); #if defined(DUK_USE_DEBUG) - duk_heap_dump_strtab(heap); + duk_heap_strtable_dump(heap); #endif #if defined(DUK_USE_DEBUGGER_SUPPORT) @@ -43716,32 +44975,47 @@ DUK_INTERNAL void duk_heap_free(duk_heap *heap) { #endif /* Execute finalizers before freeing the heap, even for reachable - * objects, and regardless of whether or not mark-and-sweep is - * enabled. This gives finalizers the chance to free any native + * objects. This gives finalizers the chance to free any native * resources like file handles, allocations made outside Duktape, * etc. This is quite tricky to get right, so that all finalizer * guarantees are honored. * - * XXX: this perhaps requires an execution time limit. - */ - DUK_D(DUK_DPRINT("execute finalizers before freeing heap")); - /* Run mark-and-sweep a few times just in case (unreachable object + * Run mark-and-sweep a few times just in case (unreachable object * finalizers run already here). The last round must rescue objects * from the previous round without running any more finalizers. This * ensures rescued objects get their FINALIZED flag cleared so that * their finalizer is called once more in forced finalization to * satisfy finalizer guarantees. However, we don't want to run any - * more finalizer because that'd required one more loop, and so on. + * more finalizers because that'd required one more loop, and so on. + * + * XXX: this perhaps requires an execution time limit. */ + DUK_D(DUK_DPRINT("execute finalizers before freeing heap")); + DUK_ASSERT(heap->pf_skip_finalizers == 0); DUK_D(DUK_DPRINT("forced gc #1 in heap destruction")); duk_heap_mark_and_sweep(heap, 0); DUK_D(DUK_DPRINT("forced gc #2 in heap destruction")); duk_heap_mark_and_sweep(heap, 0); DUK_D(DUK_DPRINT("forced gc #3 in heap destruction (don't run finalizers)")); - duk_heap_mark_and_sweep(heap, DUK_MS_FLAG_SKIP_FINALIZERS); /* skip finalizers; queue finalizable objects to heap_allocated */ + heap->pf_skip_finalizers = 1; + duk_heap_mark_and_sweep(heap, 0); /* Skip finalizers; queue finalizable objects to heap_allocated. */ + /* There are never objects in refzero_list at this point, or at any + * point beyond a DECREF (even a DECREF_NORZ). Since Duktape 2.1 + * refzero_list processing is side effect free, so it is always + * processed to completion by a DECREF initially triggering a zero + * refcount. + */ +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_ASSERT(heap->refzero_list == NULL); /* Always processed to completion inline. */ +#endif #if defined(DUK_USE_FINALIZER_SUPPORT) - DUK_HEAP_SET_FINALIZER_NORESCUE(heap); /* rescue no longer supported */ + DUK_ASSERT(heap->finalize_list == NULL); /* Last mark-and-sweep with skip_finalizers. */ +#endif + +#if defined(DUK_USE_FINALIZER_SUPPORT) + DUK_D(DUK_DPRINT("run finalizers for remaining finalizable objects")); + DUK_HEAP_SET_FINALIZER_NORESCUE(heap); /* Rescue no longer supported. */ duk__free_run_finalizers(heap); #endif /* DUK_USE_FINALIZER_SUPPORT */ @@ -43749,16 +45023,17 @@ DUK_INTERNAL void duk_heap_free(duk_heap *heap) { * are on the heap allocated list. */ - DUK_D(DUK_DPRINT("freeing heap objects of heap: %p", (void *) heap)); + DUK_D(DUK_DPRINT("freeing heap_allocated of heap: %p", (void *) heap)); duk__free_allocated(heap); #if defined(DUK_USE_REFERENCE_COUNTING) - DUK_D(DUK_DPRINT("freeing refzero list of heap: %p", (void *) heap)); - duk__free_refzero_list(heap); + DUK_ASSERT(heap->refzero_list == NULL); /* Always processed to completion inline. */ #endif - DUK_D(DUK_DPRINT("freeing mark-and-sweep finalize list of heap: %p", (void *) heap)); - duk__free_markandsweep_finalize_list(heap); +#if defined(DUK_USE_FINALIZER_SUPPORT) + DUK_D(DUK_DPRINT("freeing finalize_list of heap: %p", (void *) heap)); + duk__free_finalize_list(heap); +#endif DUK_D(DUK_DPRINT("freeing string table of heap: %p", (void *) heap)); duk__free_stringtable(heap); @@ -43780,20 +45055,26 @@ DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { duk_small_uint_t i; #endif + DUK_UNREF(heap); + /* With ROM-based strings, heap->strs[] and thr->strs[] are omitted * so nothing to initialize for strs[]. */ #if defined(DUK_USE_ASSERTIONS) - for (i = 0; i < sizeof(duk_rom_strings) / sizeof(const duk_hstring *); i++) { - duk_uint32_t hash; + for (i = 0; i < sizeof(duk_rom_strings_lookup) / sizeof(const duk_hstring *); i++) { const duk_hstring *h; - h = duk_rom_strings[i]; - DUK_ASSERT(h != NULL); - hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); - DUK_DD(DUK_DDPRINT("duk_rom_strings[%d] -> hash 0x%08lx, computed 0x%08lx", - (int) i, (unsigned long) DUK_HSTRING_GET_HASH(h), (unsigned long) hash)); - DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h)); + duk_uint32_t hash; + + h = duk_rom_strings_lookup[i]; + while (h != NULL) { + hash = duk_heap_hashstring(heap, (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); + DUK_DD(DUK_DDPRINT("duk_rom_strings_lookup[%d] -> hash 0x%08lx, computed 0x%08lx", + (int) i, (unsigned long) DUK_HSTRING_GET_HASH(h), (unsigned long) hash)); + DUK_ASSERT(hash == (duk_uint32_t) DUK_HSTRING_GET_HASH(h)); + + h = (const duk_hstring *) h->hdr.h_next; + } } #endif return 1; @@ -43821,9 +45102,9 @@ DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { */ DUK_ASSERT(len <= 0xffffUL); DUK_DDD(DUK_DDDPRINT("intern built-in string %ld", (long) i)); - h = duk_heap_string_intern(heap, tmp, len); + h = duk_heap_strtable_intern(heap, tmp, len); if (!h) { - goto error; + goto failed; } DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)); @@ -43858,7 +45139,7 @@ DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { return 1; - error: + failed: return 0; } #endif /* DUK_USE_ROM_STRINGS */ @@ -43866,12 +45147,11 @@ DUK_LOCAL duk_bool_t duk__init_heap_strings(duk_heap *heap) { DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) { duk_hthread *thr; - DUK_DD(DUK_DDPRINT("heap init: alloc heap thread")); - thr = duk_hthread_alloc(heap, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_FLAG_THREAD | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); - if (!thr) { + DUK_D(DUK_DPRINT("heap init: alloc heap thread")); + thr = duk_hthread_alloc_unchecked(heap, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD)); + if (thr == NULL) { DUK_D(DUK_DPRINT("failed to alloc heap_thread")); return 0; } @@ -43891,6 +45171,7 @@ DUK_LOCAL duk_bool_t duk__init_heap_thread(duk_heap *heap) { /* 'thr' is now reachable */ + DUK_D(DUK_DPRINT("heap init: init heap thread stacks")); if (!duk_hthread_init_stacks(heap, thr)) { return 0; } @@ -44009,6 +45290,8 @@ DUK_LOCAL void duk__dump_type_sizes(void) { DUK__DUMPSZ(duk_harray); DUK__DUMPSZ(duk_hcompfunc); DUK__DUMPSZ(duk_hnatfunc); + DUK__DUMPSZ(duk_hdecenv); + DUK__DUMPSZ(duk_hobjenv); DUK__DUMPSZ(duk_hthread); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) DUK__DUMPSZ(duk_hbufobj); @@ -44021,9 +45304,6 @@ DUK_LOCAL void duk__dump_type_sizes(void) { DUK__DUMPSZ(duk_propvalue); DUK__DUMPSZ(duk_propdesc); DUK__DUMPSZ(duk_heap); -#if defined(DUK_USE_STRTAB_CHAIN) - DUK__DUMPSZ(duk_strtab_entry); -#endif DUK__DUMPSZ(duk_activation); DUK__DUMPSZ(duk_catcher); DUK__DUMPSZ(duk_strcache); @@ -44132,9 +45412,20 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, void *heap_udata, duk_fatal_function fatal_func) { duk_heap *res = NULL; + duk_uint32_t st_initsize; DUK_D(DUK_DPRINT("allocate heap")); + /* + * Random config sanity asserts + */ + + DUK_ASSERT(DUK_USE_STRTAB_MINSIZE >= 64); + + DUK_ASSERT((DUK_HTYPE_STRING & 0x01U) == 0); + DUK_ASSERT((DUK_HTYPE_BUFFER & 0x01U) == 0); + DUK_ASSERT((DUK_HTYPE_OBJECT & 0x01U) == 1); /* DUK_HEAPHDR_IS_OBJECT() relies ont his. */ + /* * Debug dump type sizes */ @@ -44150,9 +45441,11 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, */ #if defined(DUK_USE_SELF_TESTS) + DUK_D(DUK_DPRINT("run self tests")); if (duk_selftest_run_tests(alloc_func, realloc_func, free_func, heap_udata) > 0) { fatal_func(heap_udata, "self test(s) failed"); } + DUK_D(DUK_DPRINT("self tests passed")); #endif /* @@ -44209,9 +45502,13 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, * Use a raw call, all macros expect the heap to be initialized */ +#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 1) + goto failed; +#endif + DUK_D(DUK_DPRINT("alloc duk_heap object")); res = (duk_heap *) alloc_func(heap_udata, sizeof(duk_heap)); if (!res) { - goto error; + goto failed; } /* @@ -44219,6 +45516,9 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, */ DUK_MEMZERO(res, sizeof(*res)); +#if defined(DUK_USE_ASSERTIONS) + res->heap_initializing = 1; +#endif /* explicit NULL inits */ #if defined(DUK_USE_EXPLICIT_NULL_INIT) @@ -44226,20 +45526,20 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, res->heap_allocated = NULL; #if defined(DUK_USE_REFERENCE_COUNTING) res->refzero_list = NULL; - res->refzero_list_tail = NULL; #endif +#if defined(DUK_USE_FINALIZER_SUPPORT) res->finalize_list = NULL; +#if defined(DUK_USE_ASSERTIONS) + res->currently_finalizing = NULL; +#endif +#endif res->heap_thread = NULL; res->curr_thread = NULL; res->heap_object = NULL; -#if defined(DUK_USE_STRTAB_CHAIN) - /* nothing to NULL */ -#elif defined(DUK_USE_STRTAB_PROBE) -#if defined(DUK_USE_HEAPPTR16) - res->strtable16 = (duk_uint16_t *) NULL; +#if defined(DUK_USE_STRTAB_PTRCOMP) + res->strtable16 = NULL; #else - res->strtable = (duk_hstring **) NULL; -#endif + res->strtable = NULL; #endif #if defined(DUK_USE_ROM_STRINGS) /* no res->strs[] */ @@ -44273,13 +45573,21 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, res->heap_udata = heap_udata; res->fatal_func = fatal_func; -#if defined(DUK_USE_HEAPPTR16) - /* XXX: zero assumption */ - res->heapptr_null16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) NULL); - res->heapptr_deleted16 = DUK_USE_HEAPPTR_ENC16(res->heap_udata, (void *) DUK_STRTAB_DELETED_MARKER(res)); -#endif + /* XXX: for now there's a pointer packing zero assumption, i.e. + * NULL <=> compressed pointer 0. If this is removed, may need + * to precompute e.g. null16 here. + */ + + /* res->ms_trigger_counter == 0 -> now causes immediate GC; which is OK */ - /* res->mark_and_sweep_trigger_counter == 0 -> now causes immediate GC; which is OK */ + /* Prevent mark-and-sweep and finalizer execution until heap is completely + * initialized. + */ + DUK_ASSERT(res->ms_prevent_count == 0); + DUK_ASSERT(res->pf_prevent_count == 0); + res->ms_prevent_count = 1; + res->pf_prevent_count = 1; + DUK_ASSERT(res->ms_running == 0); res->call_recursion_depth = 0; res->call_recursion_limit = DUK_USE_NATIVE_CALL_RECLIMIT; @@ -44307,71 +45615,49 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, res->lj.jmpbuf_ptr = NULL; #endif DUK_ASSERT(res->lj.type == DUK_LJ_TYPE_UNKNOWN); /* zero */ - + DUK_ASSERT(res->lj.iserror == 0); DUK_TVAL_SET_UNDEFINED(&res->lj.value1); DUK_TVAL_SET_UNDEFINED(&res->lj.value2); -#if (DUK_STRTAB_INITIAL_SIZE < DUK_UTIL_MIN_HASH_PRIME) -#error initial heap stringtable size is defined incorrectly -#endif + DUK_ASSERT_LJSTATE_UNSET(res); /* * Init stringtable: fixed variant */ -#if defined(DUK_USE_STRTAB_CHAIN) - DUK_MEMZERO(res->strtable, sizeof(duk_strtab_entry) * DUK_STRTAB_CHAIN_SIZE); -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - { - duk_small_uint_t i; - for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { -#if defined(DUK_USE_HEAPPTR16) - res->strtable[i].u.str16 = res->heapptr_null16; + st_initsize = DUK_USE_STRTAB_MINSIZE; +#if defined(DUK_USE_STRTAB_PTRCOMP) + res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * st_initsize); + if (res->strtable16 == NULL) { + goto failed; + } #else - res->strtable[i].u.str = NULL; -#endif - } + res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * st_initsize); + if (res->strtable == NULL) { + goto failed; } -#endif /* DUK_USE_EXPLICIT_NULL_INIT */ -#endif /* DUK_USE_STRTAB_CHAIN */ - - /* - * Init stringtable: probe variant - */ +#endif + res->st_size = st_initsize; + res->st_mask = st_initsize - 1; +#if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) + DUK_ASSERT(res->st_count == 0); +#endif -#if defined(DUK_USE_STRTAB_PROBE) -#if defined(DUK_USE_HEAPPTR16) - res->strtable16 = (duk_uint16_t *) alloc_func(heap_udata, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); - if (!res->strtable16) { - goto error; - } -#else /* DUK_USE_HEAPPTR16 */ - res->strtable = (duk_hstring **) alloc_func(heap_udata, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); - if (!res->strtable) { - goto error; - } -#endif /* DUK_USE_HEAPPTR16 */ - res->st_size = DUK_STRTAB_INITIAL_SIZE; +#if defined(DUK_USE_STRTAB_PTRCOMP) + /* zero assumption */ + DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * st_initsize); +#else #if defined(DUK_USE_EXPLICIT_NULL_INIT) { duk_small_uint_t i; - DUK_ASSERT(res->st_size == DUK_STRTAB_INITIAL_SIZE); - for (i = 0; i < DUK_STRTAB_INITIAL_SIZE; i++) { -#if defined(DUK_USE_HEAPPTR16) - res->strtable16[i] = res->heapptr_null16; -#else + for (i = 0; i < st_initsize; i++) { res->strtable[i] = NULL; -#endif } } -#else /* DUK_USE_EXPLICIT_NULL_INIT */ -#if defined(DUK_USE_HEAPPTR16) - DUK_MEMZERO(res->strtable16, sizeof(duk_uint16_t) * DUK_STRTAB_INITIAL_SIZE); #else - DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * DUK_STRTAB_INITIAL_SIZE); -#endif + DUK_MEMZERO(res->strtable, sizeof(duk_hstring *) * st_initsize); #endif /* DUK_USE_EXPLICIT_NULL_INIT */ -#endif /* DUK_USE_STRTAB_PROBE */ +#endif /* DUK_USE_STRTAB_PTRCOMP */ /* * Init stringcache @@ -44396,30 +45682,40 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, * Init built-in strings */ - DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS")); +#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 2) + goto failed; +#endif + DUK_D(DUK_DPRINT("heap init: initialize heap strings")); if (!duk__init_heap_strings(res)) { - goto error; + goto failed; } /* * Init the heap thread */ - DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD")); +#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 3) + goto failed; +#endif + DUK_D(DUK_DPRINT("heap init: initialize heap thread")); if (!duk__init_heap_thread(res)) { - goto error; + goto failed; } /* * Init the heap object */ - DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT")); +#if defined(DUK_USE_INJECT_HEAP_ALLOC_ERROR) && (DUK_USE_INJECT_HEAP_ALLOC_ERROR == 4) + goto failed; +#endif + DUK_D(DUK_DPRINT("heap init: initialize heap object")); DUK_ASSERT(res->heap_thread != NULL); - res->heap_object = duk_hobject_alloc(res, DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); - if (!res->heap_object) { - goto error; + res->heap_object = duk_hobject_alloc_unchecked(res, DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT)); + if (res->heap_object == NULL) { + goto failed; } DUK_HOBJECT_INCREF(res->heap_thread, res->heap_object); @@ -44466,24 +45762,50 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #endif /* - * All done + * Allow finalizer and mark-and-sweep processing. + */ + + DUK_D(DUK_DPRINT("heap init: allow finalizer/mark-and-sweep processing")); + DUK_ASSERT(res->ms_prevent_count == 1); + DUK_ASSERT(res->pf_prevent_count == 1); + res->ms_prevent_count = 0; + res->pf_prevent_count = 0; + DUK_ASSERT(res->ms_running == 0); +#if defined(DUK_USE_ASSERTIONS) + res->heap_initializing = 0; +#endif + + /* + * All done. */ DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res)); return res; - error: + failed: DUK_D(DUK_DPRINT("heap allocation failed")); - if (res) { - /* assumes that allocated pointers and alloc funcs are valid - * if res exists + if (res != NULL) { + /* Assumes that allocated pointers and alloc funcs are valid + * if res exists. */ + DUK_ASSERT(res->ms_prevent_count == 1); + DUK_ASSERT(res->pf_prevent_count == 1); + DUK_ASSERT(res->ms_running == 0); + if (res->heap_thread != NULL) { + res->ms_prevent_count = 0; + res->pf_prevent_count = 0; + } +#if defined(DUK_USE_ASSERTIONS) + res->heap_initializing = 0; +#endif + DUK_ASSERT(res->alloc_func != NULL); DUK_ASSERT(res->realloc_func != NULL); DUK_ASSERT(res->free_func != NULL); duk_heap_free(res); } + return NULL; } @@ -44494,6 +45816,455 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #undef DUK__DUMPLM_UNSIGNED_RAW #undef DUK__DUMPSZ #undef DUK__FIXED_HASH_SEED +/* + * Finalizer handling. + */ + +/* #include duk_internal.h -> already included */ + +#if defined(DUK_USE_FINALIZER_SUPPORT) + +/* + * Fake torture finalizer. + */ + +#if defined(DUK_USE_FINALIZER_TORTURE) +DUK_LOCAL duk_ret_t duk__fake_global_finalizer(duk_context *ctx) { + DUK_DD(DUK_DDPRINT("fake global torture finalizer executed")); + + /* Require a lot of stack to force a value stack grow/shrink. */ + duk_require_stack(ctx, 100000); + + /* Force a reallocation with pointer change for value, call, and + * catch stacks to maximize side effects. + */ + duk_hthread_valstack_torture_realloc((duk_hthread *) ctx); + duk_hthread_callstack_torture_realloc((duk_hthread *) ctx); + duk_hthread_catchstack_torture_realloc((duk_hthread *) ctx); + + /* Inner function call, error throw. */ + duk_eval_string_noresult(ctx, + "(function dummy() {\n" + " dummy.prototype = null; /* break reference loop */\n" + " try {\n" + " throw 'fake-finalizer-dummy-error';\n" + " } catch (e) {\n" + " void e;\n" + " }\n" + "})()"); + + /* The above creates garbage (e.g. a function instance). Because + * the function/prototype reference loop is broken, it gets collected + * immediately by DECREF. If Function.prototype has a _Finalizer + * property (happens in some test cases), the garbage gets queued to + * finalize_list. This still won't cause an infinite loop because + * the torture finalizer is called once per finalize_list run and + * the garbage gets handled in the same run. (If the garbage needs + * mark-and-sweep collection, an infinite loop might ensue.) + */ + return 0; +} + +DUK_LOCAL void duk__run_global_torture_finalizer(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + + /* Avoid fake finalization when callstack limit has been reached. + * Otherwise a callstack limit error will be created, then refzero'ed. + */ + if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit || + thr->callstack_size + 2 * DUK_CALLSTACK_GROW_STEP >= thr->callstack_max /*approximate*/) { + DUK_D(DUK_DPRINT("skip global torture finalizer because of call recursion or call stack size limit")); + 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); +} +#endif /* DUK_USE_FINALIZER_TORTURE */ + +/* + * Process the finalize_list to completion. + * + * An object may be placed on finalize_list by either refcounting or + * mark-and-sweep. The refcount of objects placed by refcounting will be + * zero; the refcount of objects placed by mark-and-sweep is > 0. In both + * cases the refcount is bumped by 1 artificially so that a REFZERO event + * can never happen while an object is waiting for finalization. Without + * this bump a REFZERO could now happen because user code may call + * duk_push_heapptr() and then pop a value even when it's on finalize_list. + * + * List processing assumes refcounts are kept up-to-date at all times, so + * that once the finalizer returns, a zero refcount is a reliable reason to + * free the object immediately rather than place it back to the heap. This + * is the case because we run outside of refzero_list processing so that + * DECREF cascades are handled fully inline. + * + * For mark-and-sweep queued objects (had_zero_refcount false) the object + * may be freed immediately if its refcount is zero after the finalizer call + * (i.e. finalizer removed the reference loop for the object). If not, the + * next mark-and-sweep will collect the object unless it has become reachable + * (i.e. rescued) by that time and its refcount hasn't fallen to zero before + * that. Mark-and-sweep detects these objects because their FINALIZED flag + * is set. + * + * There's an inherent limitation for mark-and-sweep finalizer rescuing: an + * object won't get refinalized if (1) it's rescued, but (2) becomes + * unreachable before mark-and-sweep has had time to notice it. The next + * mark-and-sweep round simply doesn't have any information of whether the + * object has been unreachable the whole time or not (the only way to get + * that information would be a mark-and-sweep pass for *every finalized + * object*). This is awkward for the application because the mark-and-sweep + * round is not generally visible or under full application control. + * + * For refcount queued objects (had_zero_refcount true) the object is either + * immediately freed or rescued, and waiting for a mark-and-sweep round is not + * necessary (or desirable); FINALIZED is cleared when a rescued object is + * queued back to heap_allocated. The object is eligible for finalization + * again (either via refcounting or mark-and-sweep) immediately after being + * rescued. If a refcount finalized object is placed into an unreachable + * reference loop by its finalizer, it will get collected by mark-and-sweep + * and currently the finalizer will execute again. + * + * There's a special case where: + * + * - Mark-and-sweep queues an object to finalize_list for finalization. + * - The finalizer is executed, FINALIZED is set, and object is queued + * back to heap_allocated, waiting for a new mark-and-sweep round. + * - The object's refcount drops to zero before mark-and-sweep has a + * chance to run another round and make a rescue/free decision. + * + * This is now handled by refzero code: if an object has a finalizer but + * FINALIZED is already set, the object is freed without finalizer processing. + * The outcome is the same as if mark-and-sweep was executed at that point; + * mark-and-sweep would also free the object without another finalizer run. + * This could also be changed so that the refzero-triggered finalizer *IS* + * executed: being refzero collected implies someone has operated on the + * object so it hasn't been totally unreachable the whole time. This would + * risk a finalizer loop however. + */ + +DUK_INTERNAL void duk_heap_process_finalize_list(duk_heap *heap) { + duk_heaphdr *curr; +#if defined(DUK_USE_DEBUG) + duk_size_t count = 0; +#endif + + DUK_DDD(DUK_DDDPRINT("duk_heap_process_finalize_list: %p", (void *) heap)); + + if (heap->pf_prevent_count != 0) { + DUK_DDD(DUK_DDDPRINT("skip finalize_list processing: pf_prevent_count != 0")); + return; + } + + /* Heap alloc prevents mark-and-sweep before heap_thread is ready. */ + 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 + + DUK_ASSERT(heap->pf_prevent_count == 0); + heap->pf_prevent_count = 1; + + /* Mark-and-sweep no longer needs to be prevented when running + * finalizers: mark-and-sweep skips any rescue decisions if there + * are any objects in finalize_list when mark-and-sweep is entered. + * This protects finalized objects from incorrect rescue decisions + * caused by finalize_list being a reachability root and only + * partially processed. Freeing decisions are not postponed. + */ + + /* When finalizer torture is enabled, make a fake finalizer call with + * maximum side effects regardless of whether finalize_list is empty. + */ +#if defined(DUK_USE_FINALIZER_TORTURE) + duk__run_global_torture_finalizer(heap->heap_thread); +#endif + + /* Process finalize_list until it becomes empty. There's currently no + * protection against a finalizer always creating more garbage. + */ + while ((curr = heap->finalize_list) != NULL) { +#if defined(DUK_USE_REFERENCE_COUNTING) + duk_bool_t queue_back; +#endif + + DUK_DD(DUK_DDPRINT("processing finalize_list entry: %p -> %!iO", (void *) curr, curr)); + + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* Only objects have finalizers. */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); + DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr)); + DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(curr)); /* All objects on finalize_list will have this flag (except object being finalized right now). */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); /* Queueing code ensures. */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); /* ROM objects never get freed (or finalized). */ + +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(heap->currently_finalizing == NULL); + heap->currently_finalizing = curr; +#endif + + /* Clear FINALIZABLE for object being finalized, so that + * duk_push_heapptr() can properly ignore the object. + */ + DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); + + if (DUK_LIKELY(!heap->pf_skip_finalizers)) { + /* Run the finalizer, duk_heap_run_finalizer() sets + * and checks for FINALIZED to prevent the finalizer + * from executing multiple times per finalization cycle. + * (This safeguard shouldn't be actually needed anymore). + */ + +#if defined(DUK_USE_REFERENCE_COUNTING) + duk_bool_t had_zero_refcount; +#endif + + /* The object's refcount is >0 throughout so it won't be + * refzero processed prematurely. + */ +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); + had_zero_refcount = (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1); /* Preincremented on finalize_list insert. */ +#endif + + DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); + duk_heap_run_finalizer(heap, (duk_hobject *) curr); /* must never longjmp */ + DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(curr)); + /* XXX: assert that object is still in finalize_list + * when duk_push_heapptr() allows automatic rescue. + */ + +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_DD(DUK_DDPRINT("refcount after finalizer (includes bump): %ld", (long) DUK_HEAPHDR_GET_REFCOUNT(curr))); + if (DUK_HEAPHDR_GET_REFCOUNT(curr) == 1) { /* Only artificial bump in refcount? */ +#if defined(DUK_USE_DEBUG) + if (had_zero_refcount) { + DUK_DD(DUK_DDPRINT("finalized object's refcount is zero -> free immediately (refcount queued)")); + } else { + DUK_DD(DUK_DDPRINT("finalized object's refcount is zero -> free immediately (mark-and-sweep queued)")); + } +#endif + queue_back = 0; + } else +#endif + { +#if defined(DUK_USE_REFERENCE_COUNTING) + queue_back = 1; + if (had_zero_refcount) { + /* When finalization is triggered + * by refzero and we queue the object + * back, clear FINALIZED right away + * so that the object can be refinalized + * immediately if necessary. + */ + DUK_HEAPHDR_CLEAR_FINALIZED(curr); + } +#endif + } + } else { + /* Used during heap destruction: don't actually run finalizers + * because we're heading into forced finalization. Instead, + * queue finalizable objects back to the heap_allocated list. + */ + DUK_D(DUK_DPRINT("skip finalizers flag set, queue object to heap_allocated without finalizing")); + DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); +#if defined(DUK_USE_REFERENCE_COUNTING) + queue_back = 1; +#endif + } + + /* Dequeue object from finalize_list. Note that 'curr' may no + * longer be finalize_list head because new objects may have + * been queued to the list. As a result we can't optimize for + * the single-linked heap case and must scan the list for + * removal, typically the scan is very short however. + */ + DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap, curr); + + /* Queue back to heap_allocated or free immediately. */ +#if defined(DUK_USE_REFERENCE_COUNTING) + if (queue_back) { + /* FINALIZED is only cleared if object originally + * queued for finalization by refcounting. For + * mark-and-sweep FINALIZED is left set, so that + * next mark-and-sweep round can make a rescue/free + * decision. + */ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) >= 1); + DUK_HEAPHDR_PREDEC_REFCOUNT(curr); /* Remove artificial refcount bump. */ + DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); + DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); + } else { + /* No need to remove the refcount bump here. */ + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* currently, always the case */ + DUK_DD(DUK_DDPRINT("refcount finalize after finalizer call: %!O", curr)); + duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr); + duk_free_hobject(heap, (duk_hobject *) curr); + DUK_DD(DUK_DDPRINT("freed hobject after finalization: %p", (void *) curr)); + } +#else /* DUK_USE_REFERENCE_COUNTING */ + DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); + DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); +#endif /* DUK_USE_REFERENCE_COUNTING */ + +#if defined(DUK_USE_DEBUG) + count++; +#endif + +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(heap->currently_finalizing != NULL); + heap->currently_finalizing = NULL; +#endif + } + + /* finalize_list will always be processed completely. */ + DUK_ASSERT(heap->finalize_list == NULL); + +#if 0 + /* While NORZ macros are used above, this is unnecessary because the + * only pending side effects are now finalizers, and finalize_list is + * empty. + */ + DUK_REFZERO_CHECK_SLOW(heap->heap_thread); +#endif + + /* Prevent count may be bumped while finalizers run, but should always + * be reliably unbumped by the time we get here. + */ + DUK_ASSERT(heap->pf_prevent_count == 1); + heap->pf_prevent_count = 0; + +#if defined(DUK_USE_DEBUG) + DUK_DD(DUK_DDPRINT("duk_heap_process_finalize_list: %ld finalizers called", (long) count)); +#endif +} + +/* + * Run an duk_hobject finalizer. Must never throw an uncaught error + * (but may throw caught errors). + * + * There is no return value. Any return value or error thrown by + * the finalizer is ignored (although errors are debug logged). + * + * Notes: + * + * - The finalizer thread 'top' assertions are there because it is + * critical that strict stack policy is observed (i.e. no cruft + * 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_UNREF(udata); + + DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); + + /* [... obj] */ + + /* _Finalizer property is read without checking if the value is + * callable or even exists. This is intentional, and handled + * by throwing an error which is caught by the safe call wrapper. + * + * XXX: Finalizer lookup should traverse the prototype chain (to allow + * inherited finalizers) but should not invoke accessors or proxy object + * behavior. At the moment this lookup will invoke proxy behavior, so + * 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_DDD(DUK_DDDPRINT("calling finalizer")); + duk_call(ctx, 2); /* [ ... obj finalizer obj heapDestruct ] -> [ ... obj retval ] */ + DUK_DDD(DUK_DDDPRINT("finalizer returned successfully")); + return 0; + + /* Note: we rely on duk_safe_call() to fix up the stack for the caller, + * so we don't need to pop stuff here. There is no return value; + * caller determines rescued status based on object refcount. + */ +} + +DUK_INTERNAL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj) { + duk_context *ctx; + duk_ret_t rc; +#if defined(DUK_USE_ASSERTIONS) + duk_idx_t entry_top; +#endif + + DUK_DD(DUK_DDPRINT("running duk_hobject finalizer for object: %p", (void *) obj)); + + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); + ctx = (duk_context *) 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); +#endif + /* + * Get and call the finalizer. All of this must be wrapped + * in a protected call, because even getting the finalizer + * may trigger an error (getter may throw one, for instance). + */ + + /* ROM objects could inherit a finalizer, but they are never deemed + * unreachable by mark-and-sweep, and their refcount never falls to 0. + */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); + + /* Duktape 2.1: finalize_list never contains objects with FINALIZED + * set, so no need to check here. + */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)); +#if 0 + if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { + DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); + return; + } +#endif + DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ + + if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { + /* This may happen if duk_set_finalizer() or Duktape.fin() is + * called for a Proxy object. In such cases the fast finalizer + * flag will be set on the Proxy, not the target, and neither + * will be finalized. + */ + DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call")); + return; + } + + duk_push_hobject(ctx, obj); /* this also increases refcount by one */ + rc = duk_safe_call(ctx, duk__finalize_helper, NULL /*udata*/, 0 /*nargs*/, 1 /*nrets*/); /* -> [... obj retval/error] */ + DUK_ASSERT_TOP(ctx, entry_top + 2); /* duk_safe_call discipline */ + + if (rc != DUK_EXEC_SUCCESS) { + /* Note: we ask for one return value from duk_safe_call to get this + * error debugging here. + */ + DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", + (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); + } + duk_pop_2(ctx); /* -> [...] */ + + DUK_ASSERT_TOP(ctx, entry_top); +} + +#else /* DUK_USE_FINALIZER_SUPPORT */ + +/* nothing */ + +#endif /* DUK_USE_FINALIZER_SUPPORT */ /* * String hash computation (interning). * @@ -44625,22 +46396,7 @@ DUK_LOCAL_DECL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h); DUK_LOCAL_DECL void duk__mark_tval(duk_heap *heap, duk_tval *tv); /* - * Misc - */ - -/* Select a thread for mark-and-sweep use. - * - * XXX: This needs to change later. - */ -DUK_LOCAL duk_hthread *duk__get_temp_hthread(duk_heap *heap) { - if (heap->curr_thread) { - return heap->curr_thread; - } - return heap->heap_thread; /* may be NULL, too */ -} - -/* - * Marking functions for heap types: mark children recursively + * Marking functions for heap types: mark children recursively. */ DUK_LOCAL void duk__mark_hstring(duk_heap *heap, duk_hstring *h) { @@ -44664,7 +46420,7 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) { duk_hstring *key = DUK_HOBJECT_E_GET_KEY(heap, h, i); - if (!key) { + if (key == NULL) { continue; } duk__mark_heaphdr(heap, (duk_heaphdr *) key); @@ -44680,15 +46436,19 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { duk__mark_tval(heap, DUK_HOBJECT_A_GET_VALUE_PTR(heap, h, i)); } - /* hash part is a 'weak reference' and does not contribute */ + /* Hash part is a 'weak reference' and does not contribute. */ duk__mark_heaphdr(heap, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(heap, h)); - /* XXX: rearrange bits to allow a switch case to be used here? */ - /* XXX: add a fast path for objects (and arrays)? */ - /* DUK_HOBJECT_IS_ARRAY(h): needs no special handling now as there are - * no extra fields in need of marking. + /* Fast path for objects which don't have a subclass struct, or have a + * subclass struct but nothing that needs marking in the subclass struct. */ + if (DUK_HOBJECT_HAS_FASTREFS(h)) { + DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h)); + return; + } + DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h)); + if (DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; duk_tval *tv, *tv_end; @@ -44720,16 +46480,21 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { /* May happen in some out-of-memory corner cases. */ DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping marking")); } - } else if (DUK_HOBJECT_IS_NATFUNC(h)) { - duk_hnatfunc *f = (duk_hnatfunc *) h; - DUK_UNREF(f); - /* nothing to mark */ #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); + duk__mark_heaphdr(heap, (duk_heaphdr *) e->thread); + duk__mark_heaphdr(heap, (duk_heaphdr *) e->varmap); + } 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); } else if (DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; duk_tval *tv; @@ -44758,19 +46523,25 @@ DUK_LOCAL void duk__mark_hobject(duk_heap *heap, duk_hobject *h) { duk__mark_heaphdr(heap, (duk_heaphdr *) t->resumer); - /* XXX: duk_small_uint_t would be enough for this loop */ for (i = 0; i < DUK_NUM_BUILTINS; i++) { duk__mark_heaphdr(heap, (duk_heaphdr *) t->builtins[i]); } + } else { + /* We may come here if the object should have a FASTREFS flag + * but it's missing for some reason. Assert for never getting + * here; however, other than performance, this is harmless. + */ + DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h)); + DUK_ASSERT(0); } } -/* recursion tracking happens here only */ +/* Mark any duk_heaphdr type. Recursion tracking happens only here. */ 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))); - if (!h) { + if (h == NULL) { return; } #if defined(DUK_USE_ROM_OBJECTS) @@ -44778,6 +46549,9 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { DUK_DDD(DUK_DDDPRINT("readonly object %p, skip", (void *) h)); return; } +#endif +#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) + h->h_assert_refcount++; /* Comparison refcount: bump even if already reachable. */ #endif if (DUK_HEAPHDR_HAS_REACHABLE(h)) { DUK_DDD(DUK_DDDPRINT("already marked reachable, skip")); @@ -44785,15 +46559,15 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { } DUK_HEAPHDR_SET_REACHABLE(h); - if (heap->mark_and_sweep_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) { - /* log this with a normal debug level because this should be relatively rare */ + if (heap->ms_recursion_depth >= DUK_USE_MARK_AND_SWEEP_RECLIMIT) { DUK_D(DUK_DPRINT("mark-and-sweep recursion limit reached, marking as temproot: %p", (void *) h)); DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap); DUK_HEAPHDR_SET_TEMPROOT(h); return; } - heap->mark_and_sweep_recursion_depth++; + heap->ms_recursion_depth++; + DUK_ASSERT(heap->ms_recursion_depth != 0); /* Wrap. */ switch (DUK_HEAPHDR_GET_TYPE(h)) { case DUK_HTYPE_STRING: @@ -44810,12 +46584,13 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { DUK_UNREACHABLE(); } - heap->mark_and_sweep_recursion_depth--; + DUK_ASSERT(heap->ms_recursion_depth > 0); + heap->ms_recursion_depth--; } DUK_LOCAL void duk__mark_tval(duk_heap *heap, duk_tval *tv) { DUK_DDD(DUK_DDDPRINT("duk__mark_tval %p", (void *) tv)); - if (!tv) { + if (tv == NULL) { return; } if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { @@ -44850,38 +46625,13 @@ DUK_LOCAL void duk__mark_roots_heap(duk_heap *heap) { #endif } -/* - * Mark refzero_list objects. - * - * Objects on the refzero_list have no inbound references. They might have - * outbound references to objects that we might free, which would invalidate - * any references held by the refzero objects. A refzero object might also - * be rescued by refcount finalization. Refzero objects are treated as - * reachability roots to ensure they (or anything they point to) are not - * freed in mark-and-sweep. - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_LOCAL void duk__mark_refzero_list(duk_heap *heap) { - duk_heaphdr *hdr; - - DUK_DD(DUK_DDPRINT("duk__mark_refzero_list: %p", (void *) heap)); - - hdr = heap->refzero_list; - while (hdr) { - duk__mark_heaphdr(heap, hdr); - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -} -#endif - /* * Mark unreachable, finalizable objects. * - * Such objects will be moved aside and their finalizers run later. They have - * to be treated as reachability roots for their properties etc to remain - * allocated. This marking is only done for unreachable values which would - * be swept later (refzero_list is thus excluded). + * Such objects will be moved aside and their finalizers run later. They + * have to be treated as reachability roots for their properties etc to + * remain allocated. This marking is only done for unreachable values which + * would be swept later. * * Objects are first marked FINALIZABLE and only then marked as reachability * roots; otherwise circular references might be handled inconsistently. @@ -44889,32 +46639,30 @@ DUK_LOCAL void duk__mark_refzero_list(duk_heap *heap) { #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { - duk_hthread *thr; duk_heaphdr *hdr; duk_size_t count_finalizable = 0; DUK_DD(DUK_DDPRINT("duk__mark_finalizable: %p", (void *) heap)); - thr = duk__get_temp_hthread(heap); - DUK_ASSERT(thr != NULL); + DUK_ASSERT(heap->heap_thread != NULL); hdr = heap->heap_allocated; - while (hdr) { - /* A finalizer is looked up from the object and up its prototype chain - * (which allows inherited finalizers). A prototype loop must not cause - * an error to be thrown here; duk_hobject_hasprop_raw() will ignore a - * prototype loop silently and indicate that the property doesn't exist. + while (hdr != NULL) { + /* A finalizer is looked up from the object and up its + * prototype chain (which allows inherited finalizers). + * The finalizer is checked for using a duk_hobject flag + * which is kept in sync with the presence and callability + * of a _Finalizer hidden symbol. */ if (!DUK_HEAPHDR_HAS_REACHABLE(hdr) && - DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_OBJECT && + DUK_HEAPHDR_IS_OBJECT(hdr) && !DUK_HEAPHDR_HAS_FINALIZED(hdr) && - duk_hobject_hasprop_raw(thr, (duk_hobject *) hdr, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) { - + DUK_HOBJECT_HAS_FINALIZER_FAST(heap, (duk_hobject *) hdr)) { /* heaphdr: * - is not reachable * - is an object - * - is not a finalized object + * - is not a finalized object waiting for rescue/keep decision * - has a finalizer */ @@ -44924,7 +46672,7 @@ DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { (void *) hdr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr)); DUK_HEAPHDR_SET_FINALIZABLE(hdr); - count_finalizable ++; + count_finalizable++; } hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); @@ -44938,7 +46686,7 @@ DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { (long) count_finalizable)); hdr = heap->heap_allocated; - while (hdr) { + while (hdr != NULL) { if (DUK_HEAPHDR_HAS_FINALIZABLE(hdr)) { duk__mark_heaphdr(heap, hdr); } @@ -44952,7 +46700,6 @@ DUK_LOCAL void duk__mark_finalizable(duk_heap *heap) { /* * Mark objects on finalize_list. - * */ #if defined(DUK_USE_FINALIZER_SUPPORT) @@ -44965,7 +46712,7 @@ DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) { DUK_DD(DUK_DDPRINT("duk__mark_finalize_list: %p", (void *) heap)); hdr = heap->finalize_list; - while (hdr) { + while (hdr != NULL) { duk__mark_heaphdr(heap, hdr); hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); #if defined(DUK_USE_DEBUG) @@ -44985,15 +46732,18 @@ DUK_LOCAL void duk__mark_finalize_list(duk_heap *heap) { /* * Fallback marking handler if recursion limit is reached. * - * Iterates 'temproots' until recursion limit is no longer hit. Note - * that temproots may reside either in heap allocated list or the - * refzero work list. This is a slow scan, but guarantees that we - * finish with a bounded C stack. + * Iterates 'temproots' until recursion limit is no longer hit. Temproots + * can be in heap_allocated or finalize_list; refzero_list is now always + * empty for mark-and-sweep. A temproot may occur in finalize_list now if + * there are objects on the finalize_list and user code creates a reference + * from an object in heap_allocated to the object in finalize_list (which is + * now allowed), and it happened to coincide with the recursion depth limit. + * + * This is a slow scan, but guarantees that we finish with a bounded C stack. * - * Note that nodes may have been marked as temproots before this - * scan begun, OR they may have been marked during the scan (as - * we process nodes recursively also during the scan). This is - * intended behavior. + * Note that nodes may have been marked as temproots before this scan begun, + * OR they may have been marked during the scan (as we process nodes + * recursively also during the scan). This is intended behavior. */ #if defined(DUK_USE_DEBUG) @@ -45008,7 +46758,10 @@ DUK_LOCAL void duk__handle_temproot(duk_heap *heap, duk_heaphdr *hdr) { DUK_DDD(DUK_DDDPRINT("found a temp root: %p", (void *) hdr)); DUK_HEAPHDR_CLEAR_TEMPROOT(hdr); - DUK_HEAPHDR_CLEAR_REACHABLE(hdr); /* done so that duk__mark_heaphdr() works correctly */ + DUK_HEAPHDR_CLEAR_REACHABLE(hdr); /* Done so that duk__mark_heaphdr() works correctly. */ +#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) + hdr->h_assert_refcount--; /* Same node visited twice. */ +#endif duk__mark_heaphdr(heap, hdr); #if defined(DUK_USE_DEBUG) @@ -45042,9 +46795,8 @@ DUK_LOCAL void duk__mark_temproots_by_heap_scan(duk_heap *heap) { hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } - /* must also check refzero_list */ -#if defined(DUK_USE_REFERENCE_COUNTING) - hdr = heap->refzero_list; +#if defined(DUK_USE_FINALIZER_SUPPORT) + hdr = heap->finalize_list; while (hdr) { #if defined(DUK_USE_DEBUG) duk__handle_temproot(heap, hdr, &count); @@ -45053,7 +46805,7 @@ DUK_LOCAL void duk__mark_temproots_by_heap_scan(duk_heap *heap) { #endif hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } -#endif /* DUK_USE_REFERENCE_COUNTING */ +#endif #if defined(DUK_USE_DEBUG) DUK_DD(DUK_DDPRINT("temproot mark heap scan processed %ld temp roots", (long) count)); @@ -45072,14 +46824,11 @@ DUK_LOCAL void duk__mark_temproots_by_heap_scan(duk_heap *heap) { #if defined(DUK_USE_REFERENCE_COUNTING) DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) { - duk_hthread *thr; duk_heaphdr *hdr; - thr = duk__get_temp_hthread(heap); - DUK_ASSERT(thr != NULL); + DUK_ASSERT(heap->heap_thread != NULL); - DUK_DD(DUK_DDPRINT("duk__finalize_refcounts: heap=%p, hthread=%p", - (void *) heap, (void *) thr)); + DUK_DD(DUK_DDPRINT("duk__finalize_refcounts: heap=%p", (void *) heap)); hdr = heap->heap_allocated; while (hdr) { @@ -45095,37 +46844,21 @@ DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) { */ DUK_DDD(DUK_DDDPRINT("unreachable object, refcount finalize before sweeping: %p", (void *) hdr)); - duk_heaphdr_refcount_finalize(thr, hdr); - } - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -} -#endif /* DUK_USE_REFERENCE_COUNTING */ - -/* - * Clear (reachable) flags of refzero work list. - */ - -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_LOCAL void duk__clear_refzero_list_flags(duk_heap *heap) { - duk_heaphdr *hdr; - - DUK_DD(DUK_DDPRINT("duk__clear_refzero_list_flags: %p", (void *) heap)); + /* Finalize using heap->heap_thread; DECREF has a + * suppress check for mark-and-sweep which is based + * on heap->ms_running. + */ + duk_heaphdr_refcount_finalize_norz(heap, hdr); + } - hdr = heap->refzero_list; - while (hdr) { - DUK_HEAPHDR_CLEAR_REACHABLE(hdr); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(hdr)); - /* DUK_HEAPHDR_HAS_FINALIZED may or may not be set. */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr)); hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } } #endif /* DUK_USE_REFERENCE_COUNTING */ /* - * Clear (reachable) flags of finalize_list + * Clear (reachable) flags of finalize_list. * * We could mostly do in the sweep phase when we move objects from the * heap into the finalize_list. However, if a finalizer run is skipped @@ -45144,8 +46877,11 @@ DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) { hdr = heap->finalize_list; while (hdr) { DUK_HEAPHDR_CLEAR_REACHABLE(hdr); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(hdr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(hdr)); +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZABLE(hdr) || \ + (heap->currently_finalizing == hdr)); +#endif + /* DUK_HEAPHDR_FLAG_FINALIZED may be set. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr)); hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } @@ -45153,191 +46889,85 @@ DUK_LOCAL void duk__clear_finalize_list_flags(duk_heap *heap) { #endif /* DUK_USE_FINALIZER_SUPPORT */ /* - * Sweep stringtable + * Sweep stringtable. */ -#if defined(DUK_USE_STRTAB_CHAIN) - -/* XXX: skip count_free w/o debug? */ -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL void duk__sweep_string_chain16(duk_heap *heap, duk_uint16_t *slot, duk_size_t *count_keep, duk_size_t *count_free) { - duk_uint16_t h16 = *slot; +DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; - duk_uint16_t null16 = heap->heapptr_null16; - - if (h16 == null16) { - /* nop */ - return; - } - h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, h16); - DUK_ASSERT(h != NULL); - - if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { - DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); - (*count_keep)++; - } else { -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); -#endif - /* deal with weak references first */ - duk_heap_strcache_string_remove(heap, (duk_hstring *) h); - *slot = null16; - - /* free inner references (these exist e.g. when external - * strings are enabled) - */ - duk_free_hstring(heap, h); - (*count_free)++; - } -} -#else /* DUK_USE_HEAPPTR16 */ -DUK_LOCAL void duk__sweep_string_chain(duk_heap *heap, duk_hstring **slot, duk_size_t *count_keep, duk_size_t *count_free) { - duk_hstring *h = *slot; - - if (h == NULL) { - /* nop */ - return; - } - - if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { - DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); - (*count_keep)++; - } else { -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); -#endif - /* deal with weak references first */ - duk_heap_strcache_string_remove(heap, (duk_hstring *) h); - *slot = NULL; - - /* free inner references (these exist e.g. when external - * strings are enabled) - */ - duk_free_hstring(heap, h); - (*count_free)++; - } -} -#endif /* DUK_USE_HEAPPTR16 */ - -DUK_LOCAL void duk__sweep_stringtable_chain(duk_heap *heap, duk_size_t *out_count_keep) { - duk_strtab_entry *e; - duk_uint_fast32_t i; + duk_hstring *prev; + duk_uint32_t i; +#if defined(DUK_USE_DEBUG) duk_size_t count_free = 0; - duk_size_t count_keep = 0; - duk_size_t j, n; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *lst; -#else - duk_hstring **lst; #endif + duk_size_t count_keep = 0; DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); - /* Non-zero refcounts should not happen for unreachable strings, - * because we refcount finalize all unreachable objects which - * should have decreased unreachable string refcounts to zero - * (even for cycles). - */ - - for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { - e = heap->strtable + i; - if (e->listlen == 0) { -#if defined(DUK_USE_HEAPPTR16) - duk__sweep_string_chain16(heap, &e->u.str16, &count_keep, &count_free); -#else - duk__sweep_string_chain(heap, &e->u.str, &count_keep, &count_free); -#endif - } else { -#if defined(DUK_USE_HEAPPTR16) - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); -#else - lst = e->u.strlist; -#endif - for (j = 0, n = e->listlen; j < n; j++) { -#if defined(DUK_USE_HEAPPTR16) - duk__sweep_string_chain16(heap, lst + j, &count_keep, &count_free); +#if defined(DUK_USE_STRTAB_PTRCOMP) + if (heap->strtable16 == NULL) { #else - duk__sweep_string_chain(heap, lst + j, &count_keep, &count_free); + if (heap->strtable == NULL) { #endif - } - } + goto done; } - DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", - (long) count_free, (long) count_keep)); - *out_count_keep = count_keep; -} -#endif /* DUK_USE_STRTAB_CHAIN */ - -#if defined(DUK_USE_STRTAB_PROBE) -DUK_LOCAL void duk__sweep_stringtable_probe(duk_heap *heap, duk_size_t *out_count_keep) { - duk_hstring *h; - duk_uint_fast32_t i; -#if defined(DUK_USE_DEBUG) - duk_size_t count_free = 0; -#endif - duk_size_t count_keep = 0; - - DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); - for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_HEAPPTR16) - h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); +#if defined(DUK_USE_STRTAB_PTRCOMP) + h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif - if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { - continue; - } else if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { - DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); - count_keep++; - continue; - } + prev = NULL; + while (h != NULL) { + duk_hstring *next; + next = h->hdr.h_next; + if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { + DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); + count_keep++; + prev = h; + } else { #if defined(DUK_USE_DEBUG) - count_free++; + count_free++; #endif #if defined(DUK_USE_REFERENCE_COUNTING) - /* Non-zero refcounts should not happen for unreachable strings, - * because we refcount finalize all unreachable objects which - * should have decreased unreachable string refcounts to zero - * (even for cycles). - */ - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); + /* Non-zero refcounts should not happen for unreachable strings, + * because we refcount finalize all unreachable objects which + * should have decreased unreachable string refcounts to zero + * (even for cycles). + */ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif - DUK_DDD(DUK_DDDPRINT("sweep string, not reachable: %p", (void *) h)); + /* Deal with weak references first. */ + duk_heap_strcache_string_remove(heap, (duk_hstring *) h); - /* deal with weak references first */ - duk_heap_strcache_string_remove(heap, (duk_hstring *) h); + /* Remove the string from the string table. */ + duk_heap_strtable_unlink_prev(heap, (duk_hstring *) h, (duk_hstring *) prev); - /* remove the string (mark DELETED), could also call - * duk_heap_string_remove() but that would be slow and - * pointless because we already know the slot. - */ -#if defined(DUK_USE_HEAPPTR16) - heap->strtable16[i] = heap->heapptr_deleted16; -#else - heap->strtable[i] = DUK_STRTAB_DELETED_MARKER(heap); -#endif + /* Free inner references (these exist e.g. when external + * strings are enabled) and the struct itself. + */ + duk_free_hstring(heap, (duk_hstring *) h); - /* free inner references (these exist e.g. when external - * strings are enabled) and the struct itself. - */ - duk_free_hstring(heap, (duk_hstring *) h); + /* Don't update 'prev'; it should be last string kept. */ + } + + h = next; + } } + done: #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", (long) count_free, (long) count_keep)); #endif *out_count_keep = count_keep; } -#endif /* DUK_USE_STRTAB_PROBE */ /* - * Sweep heap + * Sweep heap. */ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_count_keep) { @@ -45366,65 +46996,62 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ if (DUK_HEAPHDR_HAS_REACHABLE(curr)) { /* - * Reachable object, keep + * Reachable object: + * - If FINALIZABLE -> actually unreachable (but marked + * artificially reachable), queue to finalize_list. + * - If !FINALIZABLE but FINALIZED -> rescued after + * finalizer execution. + * - Otherwise just a normal, reachable object. + * + * Objects which are kept are queued to heap_allocated + * tail (we're essentially filtering heap_allocated in + * practice). */ - DUK_DDD(DUK_DDDPRINT("sweep, reachable: %p", (void *) curr)); - - if (DUK_HEAPHDR_HAS_FINALIZABLE(curr)) { - /* - * If object has been marked finalizable, move it to the - * "to be finalized" work list. It will be collected on - * the next mark-and-sweep if it is still unreachable - * after running the finalizer. - */ - +#if defined(DUK_USE_FINALIZER_SUPPORT) + if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZABLE(curr))) { DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); - DUK_DDD(DUK_DDDPRINT("object has finalizer, move to finalization work list: %p", (void *) curr)); + DUK_DD(DUK_DDPRINT("sweep; reachable, finalizable --> move to finalize_list: %p", (void *) curr)); -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) - if (heap->finalize_list) { - DUK_HEAPHDR_SET_PREV(heap, heap->finalize_list, curr); - } - DUK_HEAPHDR_SET_PREV(heap, curr, NULL); +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_HEAPHDR_PREINC_REFCOUNT(curr); /* Bump refcount so that refzero never occurs when pending a finalizer call. */ #endif - DUK_HEAPHDR_SET_NEXT(heap, curr, heap->finalize_list); - DUK_ASSERT_HEAPHDR_LINKS(heap, curr); - heap->finalize_list = curr; + DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, curr); #if defined(DUK_USE_DEBUG) count_finalize++; #endif - } else { - /* - * Object will be kept; queue object back to heap_allocated (to tail) - */ - - if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { - /* - * Object's finalizer was executed on last round, and - * object has been happily rescued. - */ - + } + else +#endif /* DUK_USE_FINALIZER_SUPPORT */ + { + if (DUK_UNLIKELY(DUK_HEAPHDR_HAS_FINALIZED(curr))) { DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); - DUK_DD(DUK_DDPRINT("object rescued during mark-and-sweep finalization: %p", (void *) curr)); + + if (flags & DUK_MS_FLAG_POSTPONE_RESCUE) { + DUK_DD(DUK_DDPRINT("sweep; reachable, finalized, but postponing rescue decisions --> keep object (with FINALIZED set): %!iO", curr)); + count_keep++; + } else { + DUK_DD(DUK_DDPRINT("sweep; reachable, finalized --> rescued after finalization: %p", (void *) curr)); +#if defined(DUK_USE_FINALIZER_SUPPORT) + DUK_HEAPHDR_CLEAR_FINALIZED(curr); +#endif #if defined(DUK_USE_DEBUG) - count_rescue++; + count_rescue++; #endif + } } else { - /* - * Plain, boring reachable object. - */ - DUK_DD(DUK_DDPRINT("keep object: %!iO", curr)); + DUK_DD(DUK_DDPRINT("sweep; reachable --> keep: %!iO", curr)); count_keep++; } - if (!heap->heap_allocated) { - heap->heap_allocated = curr; - } - if (prev) { + if (prev != NULL) { + DUK_ASSERT(heap->heap_allocated != NULL); DUK_HEAPHDR_SET_NEXT(heap, prev, curr); + } else { + DUK_ASSERT(heap->heap_allocated == NULL); + heap->heap_allocated = curr; } #if defined(DUK_USE_DOUBLE_LINKED_HEAP) DUK_HEAPHDR_SET_PREV(heap, curr, prev); @@ -45435,21 +47062,23 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ } DUK_HEAPHDR_CLEAR_REACHABLE(curr); - DUK_HEAPHDR_CLEAR_FINALIZED(curr); - DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); - + /* Keep FINALIZED if set, used if rescue decisions are postponed. */ + /* Keep FINALIZABLE for objects on finalize_list. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); - - curr = next; } else { /* - * Unreachable object, free + * Unreachable object: + * - If FINALIZED, object was finalized but not + * rescued. This doesn't affect freeing. + * - Otherwise normal unreachable object. + * + * There's no guard preventing a FINALIZED object + * from being freed while finalizers execute: the + * artificial finalize_list reachability roots can't + * cause an incorrect free decision (but can cause + * an incorrect rescue decision). */ - DUK_DDD(DUK_DDDPRINT("sweep, not reachable: %p", (void *) curr)); - #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen because we refcount * finalize all unreachable objects which should cancel out @@ -45459,10 +47088,15 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ #endif DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); +#if defined(DUK_USE_DEBUG) if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { - DUK_DDD(DUK_DDDPRINT("finalized object not rescued: %p", (void *) curr)); + DUK_DD(DUK_DDPRINT("sweep; unreachable, finalized --> finalized object not rescued: %p", (void *) curr)); + } else { + DUK_DD(DUK_DDPRINT("sweep; not reachable --> free: %p", (void *) curr)); } +#endif + /* Note: object cannot be a finalizable unreachable object, as * they have been marked temporarily reachable for this round, * and are handled above. @@ -45472,17 +47106,18 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ count_free++; #endif - /* weak refs should be handled here, but no weak refs for + /* Weak refs should be handled here, but no weak refs for * any non-string objects exist right now. */ - /* free object and all auxiliary (non-heap) allocs */ + /* Free object and all auxiliary (non-heap) allocs. */ duk_heap_free_heaphdr_raw(heap, curr); - - curr = next; } + + curr = next; } - if (prev) { + + if (prev != NULL) { DUK_HEAPHDR_SET_NEXT(heap, prev, NULL); } DUK_ASSERT_HEAPHDR_LINKS(heap, prev); @@ -45494,81 +47129,6 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ *out_count_keep = count_keep; } -/* - * Run (object) finalizers in the "to be finalized" work list. - */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -DUK_LOCAL void duk__run_object_finalizers(duk_heap *heap, duk_small_uint_t flags) { - duk_heaphdr *curr; - duk_heaphdr *next; -#if defined(DUK_USE_DEBUG) - duk_size_t count = 0; -#endif - duk_hthread *thr; - - DUK_DD(DUK_DDPRINT("duk__run_object_finalizers: %p", (void *) heap)); - - thr = duk__get_temp_hthread(heap); - DUK_ASSERT(thr != NULL); - - curr = heap->finalize_list; - while (curr) { - DUK_DDD(DUK_DDDPRINT("mark-and-sweep finalize: %p", (void *) curr)); - - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* only objects have finalizers */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); /* flags have been already cleared */ - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); /* No finalizers for ROM objects */ - - /* Keep heap->finalize_list up-to-date during the list walk. - * This has no functional impact, but does matter e.g. for - * duk_push_heapptr() asserts when assertions are enabled. - */ - heap->finalize_list = curr; - - if (DUK_LIKELY((flags & DUK_MS_FLAG_SKIP_FINALIZERS) == 0)) { - /* Run the finalizer, duk_hobject_run_finalizer() sets FINALIZED. - * Next mark-and-sweep will collect the object unless it has - * become reachable (i.e. rescued). FINALIZED prevents the - * finalizer from being executed again before that. - */ - duk_hobject_run_finalizer(thr, (duk_hobject *) curr); /* must never longjmp */ - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(curr)); - - /* XXX: could clear FINALIZED already here; now cleared in - * next mark-and-sweep. - */ - } else { - /* Used during heap destruction: don't actually run finalizers - * because we're heading into forced finalization. Instead, - * queue finalizable objects back to the heap_allocated list. - */ - DUK_D(DUK_DPRINT("skip finalizers flag set, queue object to heap_allocated without finalizing")); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); - } - - /* queue back to heap_allocated */ - next = DUK_HEAPHDR_GET_NEXT(heap, curr); - DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, curr); - - curr = next; -#if defined(DUK_USE_DEBUG) - count++; -#endif - } - - /* finalize_list will always be processed completely */ - heap->finalize_list = NULL; - -#if defined(DUK_USE_DEBUG) - DUK_D(DUK_DPRINT("mark-and-sweep finalize objects: %ld finalizers called", (long) count)); -#endif -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ - /* * Object compaction. * @@ -45644,26 +47204,25 @@ DUK_LOCAL void duk__compact_objects(duk_heap *heap) { duk_size_t count_compact = 0; duk_size_t count_bytes_saved = 0; #endif - duk_hthread *thr; DUK_DD(DUK_DDPRINT("duk__compact_objects: %p", (void *) heap)); - thr = duk__get_temp_hthread(heap); - DUK_ASSERT(thr != NULL); + DUK_ASSERT(heap->heap_thread != NULL); #if defined(DUK_USE_DEBUG) - duk__compact_object_list(heap, thr, heap->heap_allocated, &count_check, &count_compact, &count_bytes_saved); - duk__compact_object_list(heap, thr, heap->finalize_list, &count_check, &count_compact, &count_bytes_saved); -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__compact_object_list(heap, thr, heap->refzero_list, &count_check, &count_compact, &count_bytes_saved); + duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated, &count_check, &count_compact, &count_bytes_saved); +#if defined(DUK_USE_FINALIZER_SUPPORT) + duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list, &count_check, &count_compact, &count_bytes_saved); #endif #else - duk__compact_object_list(heap, thr, heap->heap_allocated); - duk__compact_object_list(heap, thr, heap->finalize_list); -#if defined(DUK_USE_REFERENCE_COUNTING) - duk__compact_object_list(heap, thr, heap->refzero_list); + duk__compact_object_list(heap, heap->heap_thread, heap->heap_allocated); +#if defined(DUK_USE_FINALIZER_SUPPORT) + duk__compact_object_list(heap, heap->heap_thread, heap->finalize_list); #endif #endif +#if defined(DUK_USE_REFERENCE_COUNTING) + DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ +#endif #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep compact objects: %ld checked, %ld compaction attempts, %ld bytes saved by compaction", @@ -45689,175 +47248,189 @@ DUK_LOCAL void duk__assert_heaphdr_flags(duk_heap *heap) { } #if defined(DUK_USE_REFERENCE_COUNTING) - hdr = heap->refzero_list; - while (hdr) { - DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(hdr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_TEMPROOT(hdr)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(hdr)); - /* DUK_HEAPHDR_HAS_FINALIZED may be set if we're doing a - * refzero finalization and mark-and-sweep gets triggered - * during the finalizer. - */ - /* DUK_HEAPHDR_HAS_FINALIZED may or may not be set. */ - hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); - } -#endif /* DUK_USE_REFERENCE_COUNTING */ + DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ +#endif } #if defined(DUK_USE_REFERENCE_COUNTING) DUK_LOCAL void duk__assert_valid_refcounts(duk_heap *heap) { duk_heaphdr *hdr = heap->heap_allocated; while (hdr) { + /* Cannot really assert much w.r.t. refcounts now. */ + if (DUK_HEAPHDR_GET_REFCOUNT(hdr) == 0 && DUK_HEAPHDR_HAS_FINALIZED(hdr)) { /* An object may be in heap_allocated list with a zero * refcount if it has just been finalized and is waiting * to be collected by the next cycle. + * (This doesn't currently happen however.) */ } else if (DUK_HEAPHDR_GET_REFCOUNT(hdr) == 0) { /* An object may be in heap_allocated list with a zero - * refcount also if it is a temporary object created by - * a finalizer; because finalization now runs inside - * mark-and-sweep, such objects will not be queued to - * refzero_list and will thus appear here with refcount - * zero. + * refcount also if it is a temporary object created + * during debugger paused state. It will get collected + * by mark-and-sweep based on its reachability status + * (presumably not reachable because refcount is 0). */ -#if 0 /* this case can no longer occur because refcount is unsigned */ - } else if (DUK_HEAPHDR_GET_REFCOUNT(hdr) < 0) { - DUK_D(DUK_DPRINT("invalid refcount: %ld, %p -> %!O", - (hdr != NULL ? (long) DUK_HEAPHDR_GET_REFCOUNT(hdr) : (long) 0), - (void *) hdr, (duk_heaphdr *) hdr)); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(hdr) > 0); -#endif } + DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(hdr) >= 0); /* Unsigned. */ hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); } } -#endif /* DUK_USE_REFERENCE_COUNTING */ -#endif /* DUK_USE_ASSERTIONS */ -/* - * Finalizer torture. Do one fake finalizer call which causes side effects - * similar to one or more finalizers on actual objects. - */ +DUK_LOCAL void duk__clear_assert_refcounts(duk_heap *heap) { + duk_heaphdr *curr; + duk_uint32_t i; + for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + curr->h_assert_refcount = 0; + } #if defined(DUK_USE_FINALIZER_SUPPORT) -#if defined(DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE) -DUK_LOCAL duk_ret_t duk__markandsweep_fake_finalizer(duk_context *ctx) { - DUK_D(DUK_DPRINT("fake mark-and-sweep torture finalizer executed")); + for (curr = heap->finalize_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + curr->h_assert_refcount = 0; + } +#endif +#if defined(DUK_USE_REFERENCE_COUNTING) + for (curr = heap->refzero_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + curr->h_assert_refcount = 0; + } +#endif - /* Require a lot of stack to force a value stack grow/shrink. - * Recursive mark-and-sweep is prevented by allocation macros - * so this won't trigger another mark-and-sweep. + for (i = 0; i < heap->st_size; i++) { + duk_hstring *h; + +#if defined(DUK_USE_STRTAB_PTRCOMP) + h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); +#else + h = heap->strtable[i]; +#endif + while (h != NULL) { + ((duk_heaphdr *) h)->h_assert_refcount = 0; + h = h->hdr.h_next; + } + } +} + +DUK_LOCAL void duk__check_refcount_heaphdr(duk_heaphdr *hdr) { + duk_bool_t count_ok; + + /* The refcount check only makes sense for reachable objects on + * heap_allocated or string table, after the sweep phase. Prior to + * sweep phase refcounts will include references that are not visible + * via reachability roots. + * + * Because we're called after the sweep phase, all heap objects on + * heap_allocated are reachable. REACHABLE flags have already been + * cleared so we can't check them. */ - duk_require_stack(ctx, 100000); - /* XXX: do something to force a callstack grow/shrink, perhaps - * just a manual forced resize or a forced relocating realloc? + /* ROM objects have intentionally incorrect refcount (1), but we won't + * check them. */ + DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(hdr)); - return 0; + count_ok = ((duk_size_t) DUK_HEAPHDR_GET_REFCOUNT(hdr) == hdr->h_assert_refcount); + if (!count_ok) { + DUK_D(DUK_DPRINT("refcount mismatch for: %p: header=%ld counted=%ld --> %!iO", + (void *) hdr, (long) DUK_HEAPHDR_GET_REFCOUNT(hdr), + (long) hdr->h_assert_refcount, hdr)); + DUK_ASSERT(0); + } } -DUK_LOCAL void duk__markandsweep_torture_finalizer(duk_hthread *thr) { - duk_context *ctx; - duk_int_t rc; - - DUK_ASSERT(thr != NULL); - ctx = (duk_context *) thr; +DUK_LOCAL void duk__check_assert_refcounts(duk_heap *heap) { + duk_heaphdr *curr; + duk_uint32_t i; - /* Avoid fake finalization when callstack limit has been reached. - * Otherwise a callstack limit error will be created, then refzero'ed. - */ - if (thr->heap->call_recursion_depth >= thr->heap->call_recursion_limit || - thr->callstack_size + 2 * DUK_CALLSTACK_GROW_STEP >= thr->callstack_max /*approximate*/) { - DUK_D(DUK_DPRINT("call recursion depth reached, avoid fake mark-and-sweep torture finalizer")); - return; + for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + duk__check_refcount_heaphdr(curr); } +#if defined(DUK_USE_FINALIZER_SUPPORT) + for (curr = heap->finalize_list; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + duk__check_refcount_heaphdr(curr); + } +#endif - /* Run fake finalizer. Avoid creating unnecessary garbage. */ - duk_push_c_function(ctx, duk__markandsweep_fake_finalizer, 0 /*nargs*/); - rc = duk_pcall(ctx, 0 /*nargs*/); - DUK_UNREF(rc); /* ignored */ - duk_pop(ctx); + for (i = 0; i < heap->st_size; i++) { + duk_hstring *h; + +#if defined(DUK_USE_STRTAB_PTRCOMP) + h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); +#else + h = heap->strtable[i]; +#endif + while (h != NULL) { + duk__check_refcount_heaphdr((duk_heaphdr *) h); + h = h->hdr.h_next; + } + } } -#endif /* DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE */ -#endif /* DUK_USE_FINALIZER_SUPPORT */ +#endif /* DUK_USE_REFERENCE_COUNTING */ +#endif /* DUK_USE_ASSERTIONS */ /* * Main mark-and-sweep function. * * 'flags' represents the features requested by the caller. The current - * heap->mark_and_sweep_base_flags is ORed automatically into the flags; - * the base flags mask typically prevents certain mark-and-sweep operations - * to avoid trouble. + * heap->ms_base_flags is ORed automatically into the flags; the base flags + * mask typically prevents certain mark-and-sweep operation to avoid trouble. */ -DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags) { - duk_hthread *thr; +DUK_INTERNAL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags) { duk_size_t count_keep_obj; duk_size_t count_keep_str; #if defined(DUK_USE_VOLUNTARY_GC) duk_size_t tmp; #endif - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { - DUK_D(DUK_DPRINT("refuse to do a recursive mark-and-sweep")); - return 0; - } - - /* XXX: thread selection for mark-and-sweep is currently a hack. - * If we don't have a thread, the entire mark-and-sweep is now - * skipped (although we could just skip finalizations). + /* If debugger is paused, garbage collection is disabled by default. + * This is achieved by bumping ms_prevent_count when becoming paused. */ + DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) || heap->ms_prevent_count > 0); - /* If thr != NULL, the thr may still be in the middle of - * initialization. - * XXX: Improve the thread viability test. + /* Prevention/recursion check as soon as possible because we may + * be called a number of times when voluntary mark-and-sweep is + * pending. */ - thr = duk__get_temp_hthread(heap); - if (thr == NULL) { - DUK_D(DUK_DPRINT("gc skipped because we don't have a temp thread")); - - /* reset voluntary gc trigger count */ -#if defined(DUK_USE_VOLUNTARY_GC) - heap->mark_and_sweep_trigger_counter = DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP; -#endif - return 0; /* OK */ + if (heap->ms_prevent_count != 0) { + DUK_DD(DUK_DDPRINT("reject recursive mark-and-sweep")); + return; } + DUK_ASSERT(heap->ms_running == 0); /* ms_prevent_count is bumped when ms_running is set */ - /* If debugger is paused, garbage collection is disabled by default. */ - /* XXX: will need a force flag if garbage collection is triggered - * explicitly during paused state. + /* Heap_thread is used during mark-and-sweep for refcount finalization + * (it's also used for finalizer execution once mark-and-sweep is + * complete). Heap allocation code ensures heap_thread is set and + * properly initialized before setting ms_prevent_count to 0. */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_PAUSED(heap)) { - /* Checking this here rather that in memory alloc primitives - * reduces checking code there but means a failed allocation - * will go through a few retries before giving up. That's - * fine because this only happens during debugging. - */ - DUK_D(DUK_DPRINT("gc skipped because debugger is paused")); - return 0; - } -#endif + 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->mark_and_sweep_base_flags))); + (unsigned long) flags, (unsigned long) (flags | heap->ms_base_flags))); - flags |= heap->mark_and_sweep_base_flags; + flags |= heap->ms_base_flags; +#if defined(DUK_USE_FINALIZER_SUPPORT) + if (heap->finalize_list != NULL) { + flags |= DUK_MS_FLAG_POSTPONE_RESCUE; + } +#endif /* * Assertions before */ #if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)); + DUK_ASSERT(heap->ms_prevent_count == 0); + DUK_ASSERT(heap->ms_running == 0); + DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(heap)); DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)); - DUK_ASSERT(heap->mark_and_sweep_recursion_depth == 0); + DUK_ASSERT(heap->ms_recursion_depth == 0); duk__assert_heaphdr_flags(heap); #if defined(DUK_USE_REFERENCE_COUNTING) - /* Note: DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap) may be true; a refcount + /* Note: heap->refzero_free_running may be true; a refcount * finalizer may trigger a mark-and-sweep. */ duk__assert_valid_refcounts(heap); @@ -45868,7 +47441,10 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t * Begin */ - DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap); + DUK_ASSERT(heap->ms_prevent_count == 0); + DUK_ASSERT(heap->ms_running == 0); + heap->ms_prevent_count = 1; + heap->ms_running = 1; /* * Mark roots, hoping that recursion limit is not normally hit. @@ -45886,17 +47462,20 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t * previous run had finalizer skip flag. */ - duk__mark_roots_heap(heap); /* main reachability roots */ +#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) + duk__clear_assert_refcounts(heap); +#endif + duk__mark_roots_heap(heap); /* Mark main reachability roots. */ #if defined(DUK_USE_REFERENCE_COUNTING) - duk__mark_refzero_list(heap); /* refzero_list treated as reachability roots */ + DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ #endif - duk__mark_temproots_by_heap_scan(heap); /* temproots */ + duk__mark_temproots_by_heap_scan(heap); /* Temproots. */ #if defined(DUK_USE_FINALIZER_SUPPORT) - duk__mark_finalizable(heap); /* mark finalizable as reachability roots */ - duk__mark_finalize_list(heap); /* mark finalizer work list as reachability roots */ + duk__mark_finalizable(heap); /* Mark finalizable as reachability roots. */ + duk__mark_finalize_list(heap); /* Mark finalizer work list as reachability roots. */ #endif - duk__mark_temproots_by_heap_scan(heap); /* temproots */ + duk__mark_temproots_by_heap_scan(heap); /* Temproots. */ /* * Sweep garbage and remove marking flags, and move objects with @@ -45918,15 +47497,12 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t duk__finalize_refcounts(heap); #endif duk__sweep_heap(heap, flags, &count_keep_obj); -#if defined(DUK_USE_STRTAB_CHAIN) - duk__sweep_stringtable_chain(heap, &count_keep_str); -#elif defined(DUK_USE_STRTAB_PROBE) - duk__sweep_stringtable_probe(heap, &count_keep_str); -#else -#error internal error, invalid strtab options + duk__sweep_stringtable(heap, &count_keep_str); +#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) + duk__check_assert_refcounts(heap); #endif #if defined(DUK_USE_REFERENCE_COUNTING) - duk__clear_refzero_list_flags(heap); + DUK_ASSERT(heap->refzero_list == NULL); /* Always handled to completion inline in DECREF. */ #endif #if defined(DUK_USE_FINALIZER_SUPPORT) duk__clear_finalize_list_flags(heap); @@ -45957,94 +47533,39 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t /* * String table resize check. * - * Note: this may silently (and safely) fail if GC is caused by an - * allocation call in stringtable resize_hash(). Resize_hash() - * will prevent a recursive call to itself by setting the - * DUK_MS_FLAG_NO_STRINGTABLE_RESIZE in heap->mark_and_sweep_base_flags. - */ - - /* XXX: stringtable emergency compaction? */ - - /* XXX: remove this feature entirely? it would only matter for - * emergency GC. Disable for lowest memory builds. - */ -#if defined(DUK_USE_MS_STRINGTABLE_RESIZE) - if (!(flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE)) { - DUK_DD(DUK_DDPRINT("resize stringtable: %p", (void *) heap)); - duk_heap_force_strtab_resize(heap); - } else { - DUK_D(DUK_DPRINT("stringtable resize skipped because DUK_MS_FLAG_NO_STRINGTABLE_RESIZE is set")); - } -#endif - - /* - * Finalize objects in the finalization work list. Finalized - * objects are queued back to heap_allocated with FINALIZED set. - * - * Since finalizers may cause arbitrary side effects, they are - * prevented during string table and object property allocation - * resizing using the DUK_MS_FLAG_NO_FINALIZERS flag in - * heap->mark_and_sweep_base_flags. In this case the objects - * remain in the finalization work list after mark-and-sweep - * exits and they may be finalized on the next pass. - * - * Finalization currently happens inside "MARKANDSWEEP_RUNNING" - * protection (no mark-and-sweep may be triggered by the - * finalizers). As a side effect: - * - * 1) an out-of-memory error inside a finalizer will not - * cause a mark-and-sweep and may cause the finalizer - * to fail unnecessarily - * - * 2) any temporary objects whose refcount decreases to zero - * during finalization will not be put into refzero_list; - * they can only be collected by another mark-and-sweep - * - * This is not optimal, but since the sweep for this phase has - * already happened, this is probably good enough for now. + * This is mainly useful in emergency GC: if the string table load + * factor is really low for some reason, we can shrink the string + * table to a smaller size and free some memory in the process. + * Only execute in emergency GC. String table has internal flags + * to protect against recursive resizing if this mark-and-sweep pass + * was triggered by a string table resize. */ -#if defined(DUK_USE_FINALIZER_SUPPORT) -#if defined(DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE) - /* Cannot simulate individual finalizers because finalize_list only - * contains objects with actual finalizers. But simulate side effects - * from finalization by doing a bogus function call and resizing the - * stacks. - */ - if (flags & DUK_MS_FLAG_NO_FINALIZERS) { - DUK_D(DUK_DPRINT("skip mark-and-sweep torture finalizer, DUK_MS_FLAG_NO_FINALIZERS is set")); - } else if (!(thr->valstack != NULL && thr->callstack != NULL && thr->catchstack != NULL)) { - DUK_D(DUK_DPRINT("skip mark-and-sweep torture finalizer, thread not yet viable")); - } else { - DUK_D(DUK_DPRINT("run mark-and-sweep torture finalizer")); - duk__markandsweep_torture_finalizer(thr); - } -#endif /* DUK_USE_MARKANDSWEEP_FINALIZER_TORTURE */ - - if (flags & DUK_MS_FLAG_NO_FINALIZERS) { - DUK_D(DUK_DPRINT("finalizer run skipped because DUK_MS_FLAG_NO_FINALIZERS is set")); - } else { - duk__run_object_finalizers(heap, flags); + if (flags & DUK_MS_FLAG_EMERGENCY) { + DUK_D(DUK_DPRINT("stringtable resize check in emergency gc")); + duk_heap_strtable_force_resize(heap); } -#endif /* DUK_USE_FINALIZER_SUPPORT */ /* * Finish */ - DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap); + DUK_ASSERT(heap->ms_prevent_count == 1); + heap->ms_prevent_count = 0; + DUK_ASSERT(heap->ms_running == 1); + heap->ms_running = 0; /* * Assertions after */ #if defined(DUK_USE_ASSERTIONS) - DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)); + DUK_ASSERT(heap->ms_prevent_count == 0); DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)); - DUK_ASSERT(heap->mark_and_sweep_recursion_depth == 0); + DUK_ASSERT(heap->ms_recursion_depth == 0); duk__assert_heaphdr_flags(heap); #if defined(DUK_USE_REFERENCE_COUNTING) - /* Note: DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap) may be true; a refcount + /* Note: heap->refzero_free_running may be true; a refcount * finalizer may trigger a mark-and-sweep. */ duk__assert_valid_refcounts(heap); @@ -46057,17 +47578,49 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t #if defined(DUK_USE_VOLUNTARY_GC) tmp = (count_keep_obj + count_keep_str) / 256; - heap->mark_and_sweep_trigger_counter = (duk_int_t) ( + heap->ms_trigger_counter = (duk_int_t) ( (tmp * DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT) + DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD); DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, trigger reset to %ld", - (long) count_keep_obj, (long) count_keep_str, (long) heap->mark_and_sweep_trigger_counter)); + (long) count_keep_obj, (long) count_keep_str, (long) heap->ms_trigger_counter)); #else DUK_D(DUK_DPRINT("garbage collect (mark-and-sweep) finished: %ld objects kept, %ld strings kept, no voluntary trigger", (long) count_keep_obj, (long) count_keep_str)); #endif - return 0; /* OK */ + /* + * Finalize objects in the finalization work list. Finalized + * objects are queued back to heap_allocated with FINALIZED set. + * + * Since finalizers may cause arbitrary side effects, they are + * prevented e.g. during string table and object property allocation + * resizing using heap->pf_prevent_count. In this case the objects + * remain in the finalization work list after mark-and-sweep exits + * and they may be finalized on the next pass or any DECREF checking + * for finalize_list. + * + * As of Duktape 2.1 finalization happens outside mark-and-sweep + * protection. Mark-and-sweep is allowed while the finalize_list + * is being processed, but no rescue decisions are done while the + * process is on-going. This avoids incorrect rescue decisions + * if an object is considered reachable (and thus rescued) because + * of a reference via finalize_list (which is considered a reachability + * root). When finalize_list is being processed, reachable objects + * with FINALIZED set will just keep their FINALIZED flag for later + * mark-and-sweep processing. + * + * This could also be handled (a bit better) by having a more refined + * notion of reachability for rescue/free decisions. + * + * XXX: avoid finalizer execution when doing emergency GC? + */ + +#if defined(DUK_USE_FINALIZER_SUPPORT) + /* Attempt to process finalize_list, pf_prevent_count check + * is inside the target. + */ + duk_heap_process_finalize_list(heap); +#endif /* DUK_USE_FINALIZER_SUPPORT */ } /* * Memory allocation handling. @@ -46076,34 +47629,28 @@ DUK_INTERNAL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t /* #include duk_internal.h -> already included */ /* - * Helpers - * - * The fast path checks are done within a macro to ensure "inlining" - * while the slow path actions use a helper (which won't typically be - * inlined in size optimized builds). + * Voluntary GC check */ #if defined(DUK_USE_VOLUNTARY_GC) -#define DUK__VOLUNTARY_PERIODIC_GC(heap) do { \ - (heap)->mark_and_sweep_trigger_counter--; \ - if ((heap)->mark_and_sweep_trigger_counter <= 0) { \ - duk__run_voluntary_gc(heap); \ - } \ - } while (0) - -DUK_LOCAL void duk__run_voluntary_gc(duk_heap *heap) { - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { - DUK_DD(DUK_DDPRINT("mark-and-sweep in progress -> skip voluntary mark-and-sweep now")); - } else { - duk_small_uint_t flags; - duk_bool_t rc; +DUK_LOCAL DUK_INLINE void duk__check_voluntary_gc(duk_heap *heap) { + if (DUK_UNLIKELY(--(heap)->ms_trigger_counter < 0)) { +#if defined(DUK_USE_DEBUG) + if (heap->ms_prevent_count == 0) { + DUK_D(DUK_DPRINT("triggering voluntary mark-and-sweep")); + } else { + DUK_DD(DUK_DDPRINT("gc blocked -> skip voluntary mark-and-sweep now")); + } +#endif - DUK_D(DUK_DPRINT("triggering voluntary mark-and-sweep")); - flags = 0; - rc = duk_heap_mark_and_sweep(heap, flags); - DUK_UNREF(rc); + /* Prevention checks in the call target handle cases where + * voluntary GC is not allowed. The voluntary GC trigger + * counter is only rewritten if mark-and-sweep actually runs. + */ + duk_heap_mark_and_sweep(heap, DUK_MS_FLAG_VOLUNTARY /*flags*/); } } +#define DUK__VOLUNTARY_PERIODIC_GC(heap) do { duk__check_voluntary_gc((heap)); } while (0) #else #define DUK__VOLUNTARY_PERIODIC_GC(heap) /* no voluntary gc */ #endif /* DUK_USE_VOLUNTARY_GC */ @@ -46114,7 +47661,6 @@ DUK_LOCAL void duk__run_voluntary_gc(duk_heap *heap) { DUK_INTERNAL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { void *res; - duk_bool_t rc; duk_small_int_t i; DUK_ASSERT(heap != NULL); @@ -46132,7 +47678,7 @@ 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) */ - if (!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count == 0) { DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first alloc attempt fails")); res = NULL; DUK_UNREF(res); @@ -46140,7 +47686,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 (res || size == 0) { + if (DUK_LIKELY(res || size == 0)) { /* for zero size allocations NULL is allowed */ return res; } @@ -46150,16 +47696,22 @@ DUK_INTERNAL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { DUK_D(DUK_DPRINT("first alloc attempt failed, attempt to gc and retry")); +#if 0 /* * Avoid a GC if GC is already running. This can happen at a late * stage in a GC when we try to e.g. resize the stringtable * or compact objects. + * + * NOTE: explicit handling isn't actually be needed: if the GC is + * not allowed, duk_heap_mark_and_sweep() will reject it for every + * attempt in the loop below, resulting in a NULL same as here. */ - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count != 0) { DUK_D(DUK_DPRINT("duk_heap_mem_alloc() failed, gc in progress (gc skipped), alloc size %ld", (long) size)); return NULL; } +#endif /* * Retry with several GC attempts. Initial attempts are made without @@ -46175,8 +47727,7 @@ DUK_INTERNAL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size) { flags |= DUK_MS_FLAG_EMERGENCY; } - rc = duk_heap_mark_and_sweep(heap, flags); - DUK_UNREF(rc); + duk_heap_mark_and_sweep(heap, flags); res = heap->alloc_func(heap->heap_udata, size); if (res) { @@ -46197,20 +47748,43 @@ DUK_INTERNAL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size) { DUK_ASSERT_DISABLE(size >= 0); res = DUK_ALLOC(heap, size); - if (res) { + if (DUK_LIKELY(res != NULL)) { /* assume memset with zero size is OK */ DUK_MEMZERO(res, size); } return res; } +DUK_INTERNAL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size) { + void *res; + + DUK_ASSERT(thr != NULL); + res = duk_heap_mem_alloc(thr->heap, size); + if (DUK_LIKELY(res != NULL || size == 0)) { + return res; + } + DUK_ERROR_ALLOC_FAILED(thr); + return NULL; +} + +DUK_INTERNAL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size) { + void *res; + + DUK_ASSERT(thr != NULL); + res = duk_heap_mem_alloc_zeroed(thr->heap, size); + if (DUK_LIKELY(res != NULL || size == 0)) { + return res; + } + DUK_ERROR_ALLOC_FAILED(thr); + return NULL; +} + /* * Reallocate memory with garbage collection */ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize) { void *res; - duk_bool_t rc; duk_small_int_t i; DUK_ASSERT(heap != NULL); @@ -46229,7 +47803,7 @@ 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) */ - if (!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count == 0) { DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first realloc attempt fails")); res = NULL; DUK_UNREF(res); @@ -46237,7 +47811,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 (res || newsize == 0) { + if (DUK_LIKELY(res || newsize == 0)) { /* for zero size allocations NULL is allowed */ return res; } @@ -46247,14 +47821,16 @@ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t ne DUK_D(DUK_DPRINT("first realloc attempt failed, attempt to gc and retry")); +#if 0 /* * Avoid a GC if GC is already running. See duk_heap_mem_alloc(). */ - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count != 0) { DUK_D(DUK_DPRINT("duk_heap_mem_realloc() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize)); return NULL; } +#endif /* * Retry with several GC attempts. Initial attempts are made without @@ -46270,8 +47846,7 @@ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t ne flags |= DUK_MS_FLAG_EMERGENCY; } - rc = duk_heap_mark_and_sweep(heap, flags); - DUK_UNREF(rc); + duk_heap_mark_and_sweep(heap, flags); res = heap->realloc_func(heap->heap_udata, ptr, newsize); if (res || newsize == 0) { @@ -46293,7 +47868,6 @@ DUK_INTERNAL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t ne DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize) { void *res; - duk_bool_t rc; duk_small_int_t i; DUK_ASSERT(heap != NULL); @@ -46311,7 +47885,7 @@ 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) */ - if (!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count == 0) { DUK_DDD(DUK_DDDPRINT("gc torture enabled, pretend that first indirect realloc attempt fails")); res = NULL; DUK_UNREF(res); @@ -46319,7 +47893,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 (res || newsize == 0) { + if (DUK_LIKELY(res || newsize == 0)) { /* for zero size allocations NULL is allowed */ return res; } @@ -46329,14 +47903,16 @@ DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr DUK_D(DUK_DPRINT("first indirect realloc attempt failed, attempt to gc and retry")); +#if 0 /* * Avoid a GC if GC is already running. See duk_heap_mem_alloc(). */ - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { + if (heap->ms_prevent_count != 0) { DUK_D(DUK_DPRINT("duk_heap_mem_realloc_indirect() failed, gc in progress (gc skipped), alloc size %ld", (long) newsize)); return NULL; } +#endif /* * Retry with several GC attempts. Initial attempts are made without @@ -46360,8 +47936,7 @@ DUK_INTERNAL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr flags |= DUK_MS_FLAG_EMERGENCY; } - rc = duk_heap_mark_and_sweep(heap, flags); - DUK_UNREF(rc); + duk_heap_mark_and_sweep(heap, flags); #if defined(DUK_USE_ASSERTIONS) ptr_post = cb(heap, ud); if (ptr_pre != ptr_post) { @@ -46400,14 +47975,10 @@ DUK_INTERNAL void duk_heap_mem_free(duk_heap *heap, void *ptr) { */ heap->free_func(heap->heap_udata, ptr); - /* Count free operations toward triggering a GC but never actually trigger - * a GC from a free. Otherwise code which frees internal structures would - * need to put in NULLs at every turn to ensure the object is always in - * consistent state for a mark-and-sweep. + /* Never perform a GC (even voluntary) in a memory free, otherwise + * all call sites doing frees would need to deal with the side effects. + * No need to update voluntary GC counter either. */ -#if defined(DUK_USE_VOLUNTARY_GC) - heap->mark_and_sweep_trigger_counter--; -#endif } /* automatic undefs */ @@ -46418,44 +47989,146 @@ DUK_INTERNAL void duk_heap_mem_free(duk_heap *heap, void *ptr) { /* #include duk_internal.h -> already included */ -#if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING) -/* arbitrary remove only works with double linked heap, and is only required by - * reference counting so far. - */ -DUK_INTERNAL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { +DUK_INTERNAL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { + duk_heaphdr *root; + + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); + + root = heap->heap_allocated; +#if defined(DUK_USE_DOUBLE_LINKED_HEAP) + if (root != NULL) { + DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); + DUK_HEAPHDR_SET_PREV(heap, root, hdr); + } + DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); +#endif + DUK_HEAPHDR_SET_NEXT(heap, hdr, root); + DUK_ASSERT_HEAPHDR_LINKS(heap, hdr); + DUK_ASSERT_HEAPHDR_LINKS(heap, root); + heap->heap_allocated = hdr; +} + +#if defined(DUK_USE_REFERENCE_COUNTING) +DUK_INTERNAL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { + duk_heaphdr *prev; + duk_heaphdr *next; + + /* Strings are in string table. */ + DUK_ASSERT(hdr != NULL); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); - if (DUK_HEAPHDR_GET_PREV(heap, hdr)) { - DUK_HEAPHDR_SET_NEXT(heap, DUK_HEAPHDR_GET_PREV(heap, hdr), DUK_HEAPHDR_GET_NEXT(heap, hdr)); + /* Target 'hdr' must be in heap_allocated (not e.g. finalize_list). + * If not, heap lists will become corrupted so assert early for it. + */ +#if defined(DUK_USE_ASSERTIONS) + { + duk_heaphdr *tmp; + for (tmp = heap->heap_allocated; tmp != NULL; tmp = DUK_HEAPHDR_GET_NEXT(heap, tmp)) { + if (tmp == hdr) { + break; + } + } + DUK_ASSERT(tmp == hdr); + } +#endif + + /* Read/write only once to minimize pointer compression calls. */ + prev = DUK_HEAPHDR_GET_PREV(heap, hdr); + next = DUK_HEAPHDR_GET_NEXT(heap, hdr); + + if (prev != NULL) { + DUK_ASSERT(heap->heap_allocated != hdr); + DUK_HEAPHDR_SET_NEXT(heap, prev, next); } else { - heap->heap_allocated = DUK_HEAPHDR_GET_NEXT(heap, hdr); + DUK_ASSERT(heap->heap_allocated == hdr); + heap->heap_allocated = next; } - if (DUK_HEAPHDR_GET_NEXT(heap, hdr)) { - DUK_HEAPHDR_SET_PREV(heap, DUK_HEAPHDR_GET_NEXT(heap, hdr), DUK_HEAPHDR_GET_PREV(heap, hdr)); + if (next != NULL) { + DUK_HEAPHDR_SET_PREV(heap, next, prev); } else { ; } - - /* The prev/next pointers of the removed duk_heaphdr are left as garbage. - * It's up to the caller to ensure they're written before inserting the - * object back. - */ } -#endif +#endif /* DUK_USE_REFERENCE_COUNTING */ -DUK_INTERNAL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { + duk_heaphdr *root; + root = heap->finalize_list; #if defined(DUK_USE_DOUBLE_LINKED_HEAP) - if (heap->heap_allocated) { - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, heap->heap_allocated) == NULL); - DUK_HEAPHDR_SET_PREV(heap, heap->heap_allocated, hdr); - } DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); + if (root != NULL) { + DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); + DUK_HEAPHDR_SET_PREV(heap, root, hdr); + } #endif - DUK_HEAPHDR_SET_NEXT(heap, hdr, heap->heap_allocated); - heap->heap_allocated = hdr; + DUK_HEAPHDR_SET_NEXT(heap, hdr, root); + DUK_ASSERT_HEAPHDR_LINKS(heap, hdr); + DUK_ASSERT_HEAPHDR_LINKS(heap, root); + heap->finalize_list = hdr; +} +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { +#if defined(DUK_USE_DOUBLE_LINKED_HEAP) + duk_heaphdr *next; + duk_heaphdr *prev; + + next = DUK_HEAPHDR_GET_NEXT(heap, hdr); + prev = DUK_HEAPHDR_GET_PREV(heap, hdr); + if (next != NULL) { + DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, next) == hdr); + DUK_HEAPHDR_SET_PREV(heap, next, prev); + } + if (prev == NULL) { + DUK_ASSERT(hdr == heap->finalize_list); + heap->finalize_list = next; + } else { + DUK_ASSERT(hdr != heap->finalize_list); + DUK_HEAPHDR_SET_NEXT(heap, prev, next); + } +#else + duk_heaphdr *next; + duk_heaphdr *curr; + + /* Random removal is expensive: we need to locate the previous element + * because we don't have a 'prev' pointer. + */ + curr = heap->finalize_list; + if (curr == hdr) { + heap->finalize_list = DUK_HEAPHDR_GET_NEXT(heap, curr); + } else { + DUK_ASSERT(hdr != heap->finalize_list); + for (;;) { + DUK_ASSERT(curr != NULL); /* Caller responsibility. */ + + next = DUK_HEAPHDR_GET_NEXT(heap, curr); + if (next == hdr) { + next = DUK_HEAPHDR_GET_NEXT(heap, hdr); + DUK_HEAPHDR_SET_NEXT(heap, curr, next); + break; + } + } + } +#endif +} +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +#if defined(DUK_USE_ASSERTIONS) +DUK_INTERNAL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr) { + duk_heaphdr *curr; + DUK_ASSERT(heap != NULL); + + for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { + if (curr == ptr) { + return 1; + } + } + return 0; } +#endif /* DUK_USE_ASSERTIONS */ #if defined(DUK_USE_INTERRUPT_COUNTER) DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { @@ -46493,6 +48166,10 @@ DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { #endif /* DUK_USE_INTERRUPT_COUNTER */ /* * Reference counting implementation. + * + * INCREF/DECREF, finalization and freeing of objects whose refcount reaches + * zero (refzero). These operations are very performance sensitive, so + * various small tricks are used in an attempt to maximize speed. */ /* #include duk_internal.h -> already included */ @@ -46503,36 +48180,6 @@ DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { #error internal error, reference counting requires a double linked heap #endif -/* - * Misc - */ - -DUK_LOCAL void duk__queue_refzero(duk_heap *heap, duk_heaphdr *hdr) { - /* tail insert: don't disturb head in case refzero is running */ - - if (heap->refzero_list != NULL) { - duk_heaphdr *hdr_prev; - - hdr_prev = heap->refzero_list_tail; - DUK_ASSERT(hdr_prev != NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_NEXT(heap, hdr_prev) == NULL); - - DUK_HEAPHDR_SET_NEXT(heap, hdr, NULL); - DUK_HEAPHDR_SET_PREV(heap, hdr, hdr_prev); - DUK_HEAPHDR_SET_NEXT(heap, hdr_prev, hdr); - DUK_ASSERT_HEAPHDR_LINKS(heap, hdr); - DUK_ASSERT_HEAPHDR_LINKS(heap, hdr_prev); - heap->refzero_list_tail = hdr; - } else { - DUK_ASSERT(heap->refzero_list_tail == NULL); - DUK_HEAPHDR_SET_NEXT(heap, hdr, NULL); - DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); - DUK_ASSERT_HEAPHDR_LINKS(heap, hdr); - heap->refzero_list = hdr; - heap->refzero_list_tail = hdr; - } -} - /* * Heap object refcount finalization. * @@ -46541,41 +48188,47 @@ DUK_LOCAL void duk__queue_refzero(duk_heap *heap, duk_heaphdr *hdr) { * allocations (mark-and-sweep shares these helpers), it just manipulates * the refcounts. * - * Note that any of the decref's may cause a refcount to drop to zero, BUT - * it will not be processed inline. If refcount finalization is triggered - * by refzero processing, the objects will be just queued to the refzero - * list and processed later which eliminates C recursion. If refcount - * finalization is triggered by mark-and-sweep, any refzero situations are - * ignored because mark-and-sweep will deal with them. NORZ variants can - * be used here in both cases. + * Note that any of the DECREFs may cause a refcount to drop to zero. If so, + * the object won't be refzero processed inline, but will just be queued to + * refzero_list and processed by an earlier caller working on refzero_list, + * eliminating C recursion from even long refzero cascades. If refzero + * finalization is triggered by mark-and-sweep, refzero conditions are ignored + * (objects are not even queued to refzero_list) because mark-and-sweep deals + * with them; refcounts are still updated so that they remain in sync with + * actual references. */ -DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) { +DUK_INTERNAL void duk_hobject_refcount_finalize_norz(duk_heap *heap, duk_hobject *h) { + duk_hthread *thr; duk_uint_fast32_t i; duk_uint_fast32_t n; duk_propvalue *p_val; duk_tval *p_tv; duk_hstring **p_key; duk_uint8_t *p_flag; + duk_hobject *h_proto; + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); DUK_ASSERT(h); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h) == DUK_HTYPE_OBJECT); - /* XXX: better to get base and walk forwards? */ + thr = heap->heap_thread; + DUK_ASSERT(thr != NULL); - p_key = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, h); - p_val = DUK_HOBJECT_E_GET_VALUE_BASE(thr->heap, h); - p_flag = DUK_HOBJECT_E_GET_FLAGS_BASE(thr->heap, h); + p_key = DUK_HOBJECT_E_GET_KEY_BASE(heap, h); + p_val = DUK_HOBJECT_E_GET_VALUE_BASE(heap, h); + p_flag = DUK_HOBJECT_E_GET_FLAGS_BASE(heap, h); n = DUK_HOBJECT_GET_ENEXT(h); while (n-- > 0) { duk_hstring *key; key = p_key[n]; - if (!key) { + if (DUK_UNLIKELY(key == NULL)) { continue; } DUK_HSTRING_DECREF_NORZ(thr, key); - if (p_flag[n] & DUK_PROPDESC_FLAG_ACCESSOR) { + if (DUK_UNLIKELY(p_flag[n] & DUK_PROPDESC_FLAG_ACCESSOR)) { duk_hobject *h_getset; h_getset = p_val[n].a.get; DUK_ASSERT(h_getset == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_getset)); @@ -46590,7 +48243,7 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) } } - p_tv = DUK_HOBJECT_A_GET_BASE(thr->heap, h); + p_tv = DUK_HOBJECT_A_GET_BASE(heap, h); n = DUK_HOBJECT_GET_ASIZE(h); while (n-- > 0) { duk_tval *tv_val; @@ -46598,39 +48251,49 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) DUK_TVAL_DECREF_NORZ(thr, tv_val); } - /* hash part is a 'weak reference' and does not contribute */ + /* Hash part is a 'weak reference' and doesn't contribute to refcounts. */ - { - duk_hobject *h_proto; - h_proto = (duk_hobject *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); - DUK_ASSERT(h_proto == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_proto)); - DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_proto); + h_proto = (duk_hobject *) DUK_HOBJECT_GET_PROTOTYPE(heap, h); + DUK_ASSERT(h_proto == NULL || DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_proto)); + DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, h_proto); + + /* XXX: Object subclass tests are quite awkward at present, ideally + * we should be able to switch-case here with a dense index (subtype + * number or something). For now, fast path plain objects and arrays + * and bit test the rest individually. + */ + + if (DUK_HOBJECT_HAS_FASTREFS(h)) { + /* Plain object or array, nothing more to do. While a + * duk_harray has additional fields, none of them need + * DECREF updates. + */ + DUK_ASSERT(DUK_HOBJECT_ALLOWS_FASTREFS(h)); + return; } + DUK_ASSERT(DUK_HOBJECT_PROHIBITS_FASTREFS(h)); - /* XXX: rearrange bits to allow a switch case to be used here? */ - /* XXX: add a fast path for objects (and arrays)? */ + /* Slow path: special object, start bit checks from most likely. */ - /* DUK_HOBJECT_IS_ARRAY(h): needs no special handling now as there are - * no extra fields in need of decref. - */ if (DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; duk_tval *tv, *tv_end; duk_hobject **funcs, **funcs_end; - if (DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL) { - tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f); - tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, 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); while (tv < tv_end) { DUK_TVAL_DECREF_NORZ(thr, tv); tv++; } - funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f); - funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f); + funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(heap, f); + funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(heap, f); while (funcs < funcs_end) { duk_hobject *h_func; h_func = *funcs; + DUK_ASSERT(h_func != NULL); DUK_ASSERT(DUK_HEAPHDR_IS_OBJECT((duk_heaphdr *) h_func)); DUK_HCOMPFUNC_DECREF_NORZ(thr, (duk_hcompfunc *) h_func); funcs++; @@ -46640,13 +48303,19 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) DUK_D(DUK_DPRINT("duk_hcompfunc 'data' is NULL, skipping decref")); } - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f)); - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(thr->heap, f)); - DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(thr->heap, f)); - } else if (DUK_HOBJECT_IS_NATFUNC(h)) { - duk_hnatfunc *f = (duk_hnatfunc *) h; - DUK_UNREF(f); - /* nothing to finalize */ + DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_LEXENV(heap, f)); + DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_heaphdr *) DUK_HCOMPFUNC_GET_VARENV(heap, f)); + DUK_HEAPHDR_DECREF_ALLOWNULL(thr, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(heap, f)); + } else if (DUK_HOBJECT_IS_DECENV(h)) { + duk_hdecenv *e = (duk_hdecenv *) h; + DUK_ASSERT_HDECENV_VALID(e); + DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, e->thread); + DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, e->varmap); + } else if (DUK_HOBJECT_IS_OBJENV(h)) { + duk_hobjenv *e = (duk_hobjenv *) h; + DUK_ASSERT_HOBJENV_VALID(e); + DUK_ASSERT(e->target != NULL); /* Required for object environments. */ + DUK_HOBJECT_DECREF_NORZ(thr, e->target); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; @@ -46684,269 +48353,284 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) } DUK_HTHREAD_DECREF_NORZ_ALLOWNULL(thr, (duk_hthread *) t->resumer); + } else { + /* We may come here if the object should have a FASTREFS flag + * but it's missing for some reason. Assert for never getting + * here; however, other than performance, this is harmless. + */ + DUK_D(DUK_DPRINT("missing FASTREFS flag for: %!iO", h)); + DUK_ASSERT(0); } } -DUK_INTERNAL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr) { - DUK_ASSERT(hdr); +DUK_INTERNAL void duk_heaphdr_refcount_finalize_norz(duk_heap *heap, duk_heaphdr *hdr) { + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); + DUK_ASSERT(hdr != NULL); - if (DUK_HEAPHDR_GET_TYPE(hdr) == DUK_HTYPE_OBJECT) { - duk__refcount_finalize_hobject(thr, (duk_hobject *) hdr); + if (DUK_HEAPHDR_IS_OBJECT(hdr)) { + duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) hdr); } /* DUK_HTYPE_BUFFER: nothing to finalize */ /* DUK_HTYPE_STRING: nothing to finalize */ } -#if defined(DUK_USE_FINALIZER_SUPPORT) -#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE) -DUK_LOCAL duk_ret_t duk__refcount_fake_finalizer(duk_context *ctx) { - DUK_UNREF(ctx); - DUK_D(DUK_DPRINT("fake refcount torture finalizer executed")); -#if 0 - DUK_DD(DUK_DDPRINT("fake torture finalizer for: %!T", duk_get_tval(ctx, 0))); -#endif - /* Require a lot of stack to force a value stack grow/shrink. */ - duk_require_stack(ctx, 100000); - - /* XXX: do something to force a callstack grow/shrink, perhaps - * just a manual forced resize? - */ - return 0; -} - -DUK_LOCAL void duk__refcount_run_torture_finalizer(duk_hthread *thr, duk_hobject *obj) { - duk_context *ctx; - duk_int_t rc; - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(obj != NULL); - ctx = (duk_context *) thr; - - /* Avoid fake finalization for the duk__refcount_fake_finalizer function - * itself, otherwise we're in infinite recursion. - */ - if (DUK_HOBJECT_HAS_NATFUNC(obj)) { - if (((duk_hnatfunc *) obj)->func == duk__refcount_fake_finalizer) { - DUK_DD(DUK_DDPRINT("avoid fake torture finalizer for duk__refcount_fake_finalizer itself")); - return; - } - } - /* Avoid fake finalization when callstack limit has been reached. - * Otherwise a callstack limit error will be created, then refzero'ed, - * and we're in an infinite loop. - */ - 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("call recursion depth reached, avoid fake torture finalizer")); - return; - } - - /* Run fake finalizer. Avoid creating new refzero queue entries - * so that we are not forced into a forever loop. - */ - duk_push_c_function(ctx, duk__refcount_fake_finalizer, 1 /*nargs*/); - duk_push_hobject(ctx, obj); - rc = duk_pcall(ctx, 1); - DUK_UNREF(rc); /* ignored */ - duk_pop(ctx); -} -#endif /* DUK_USE_REFZERO_FINALIZER_TORTURE */ -#endif /* DUK_USE_FINALIZER_SUPPORT */ - /* - * Refcount memory freeing loop. + * Refzero processing for duk_hobject: queue a refzero'ed object to either + * finalize_list or refzero_list and process the relevent list(s) if + * necessary. + * + * Refzero_list is single linked, with only 'prev' pointers set and valid. + * All 'next' pointers are intentionally left as garbage. This doesn't + * matter because refzero_list is processed to completion before any other + * code (like mark-and-sweep) might walk the list. + * + * In more detail: + * + * - On first insert refzero_list is NULL and the new object becomes the + * first and only element on the list; duk__refcount_free_pending() is + * called and it starts processing the list from the initial element, + * i.e. the list tail. + * + * - As each object is refcount finalized, new objects may be queued to + * refzero_list head. Their 'next' pointers are left as garbage, but + * 'prev' points are set correctly, with the element at refzero_list + * having a NULL 'prev' pointer. The fact that refzero_list is non-NULL + * is used to reject (1) recursive duk__refcount_free_pending() and + * (2) finalize_list processing calls. + * + * - When we're done with the current object, read its 'prev' pointer and + * free the object. If 'prev' is NULL, we've reached head of list and are + * done: set refzero_list to NULL and process pending finalizers. Otherwise + * continue processing the list. + * + * A refzero cascade is free of side effects because it only involves + * queueing more objects and freeing memory; finalizer execution is blocked + * in the code path queueing objects to finalize_list. As a result the + * initial refzero call (which triggers duk__refcount_free_pending()) must + * check finalize_list so that finalizers are executed snappily. * - * Frees objects in the refzero_pending list until the list becomes - * empty. When an object is freed, its references get decref'd and - * may cause further objects to be queued for freeing. + * If finalize_list processing starts first, refzero may occur while we're + * processing finalizers. That's fine: that particular refzero cascade is + * handled to completion without side effects. Once the cascade is complete, + * we'll run pending finalizers but notice that we're already doing that and + * return. * * This could be expanded to allow incremental freeing: just bail out - * early and resume at a future alloc/decref/refzero. + * early and resume at a future alloc/decref/refzero. However, if that + * were done, the list structure would need to be kept consistent at all + * times, mark-and-sweep would need to handle refzero_list, etc. */ -DUK_INTERNAL void duk_refzero_free_pending(duk_hthread *thr) { - duk_heaphdr *h1, *h2; - duk_heap *heap; +DUK_LOCAL void duk__refcount_free_pending(duk_heap *heap) { + duk_heaphdr *curr; +#if defined(DUK_USE_DEBUG) duk_int_t count = 0; +#endif - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - heap = thr->heap; DUK_ASSERT(heap != NULL); - /* - * Detect recursive invocation - */ + curr = heap->refzero_list; + DUK_ASSERT(curr != NULL); + DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, curr) == NULL); /* We're called on initial insert only. */ + /* curr->next is GARBAGE. */ - if (DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap)) { - DUK_DDD(DUK_DDDPRINT("refzero free running, skip run")); - return; - } + do { + duk_heaphdr *prev; - /* - * Churn refzero_list until empty - */ + DUK_DDD(DUK_DDDPRINT("refzero processing %p: %!O", (void *) curr, (duk_heaphdr *) curr)); - DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap); - while (heap->refzero_list) { - duk_hobject *obj; -#if defined(DUK_USE_FINALIZER_SUPPORT) - duk_bool_t rescued = 0; -#endif /* DUK_USE_FINALIZER_SUPPORT */ +#if defined(DUK_USE_DEBUG) + count++; +#endif - /* - * Pick an object from the head (don't remove yet). + DUK_ASSERT(curr != NULL); + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); /* currently, always the case */ + /* FINALIZED may be set; don't care about flags here. */ + + /* Refcount finalize 'curr'. Refzero_list must be non-NULL + * here to prevent recursive entry to duk__refcount_free_pending(). */ + DUK_ASSERT(heap->refzero_list != NULL); + duk_hobject_refcount_finalize_norz(heap, (duk_hobject *) curr); - h1 = heap->refzero_list; - obj = (duk_hobject *) h1; - DUK_DD(DUK_DDPRINT("refzero processing %p: %!O", (void *) h1, (duk_heaphdr *) h1)); - DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, h1) == NULL); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h1) == DUK_HTYPE_OBJECT); /* currently, always the case */ + prev = DUK_HEAPHDR_GET_PREV(heap, curr); + DUK_ASSERT((prev == NULL && heap->refzero_list == curr) || \ + (prev != NULL && heap->refzero_list != curr)); + /* prev->next is intentionally not updated and is garbage. */ -#if defined(DUK_USE_FINALIZER_SUPPORT) -#if defined(DUK_USE_REFZERO_FINALIZER_TORTURE) - /* Torture option to shake out finalizer side effect issues: - * make a bogus function call for every finalizable object, - * essentially simulating the case where everything has a - * finalizer. - */ - DUK_DD(DUK_DDPRINT("refzero torture enabled, fake finalizer")); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h1) == 0); - DUK_HEAPHDR_PREINC_REFCOUNT(h1); /* bump refcount to prevent refzero during finalizer processing */ - duk__refcount_run_torture_finalizer(thr, obj); /* must never longjmp */ - DUK_HEAPHDR_PREDEC_REFCOUNT(h1); /* remove artificial bump */ - DUK_ASSERT_DISABLE(h1->h_refcount >= 0); /* refcount is unsigned, so always true */ -#endif /* DUK_USE_REFZERO_FINALIZER_TORTURE */ -#endif /* DUK_USE_FINALIZER_SUPPORT */ + duk_free_hobject(heap, (duk_hobject *) curr); /* Invalidates 'curr'. */ - /* - * Finalizer check. - * - * Note: running a finalizer may have arbitrary side effects, e.g. - * queue more objects on refzero_list (tail), or even trigger a - * mark-and-sweep. - * - * Note: quick reject check should match vast majority of - * objects and must be safe (not throw any errors, ever). - * - * An object may have FINALIZED here if it was finalized by mark-and-sweep - * on a previous run and refcount then decreased to zero. We won't run the - * finalizer again here. - * - * A finalizer is looked up from the object and up its prototype chain - * (which allows inherited finalizers). - */ + curr = prev; + } while (curr != NULL); -#if defined(DUK_USE_FINALIZER_SUPPORT) - if (duk_hobject_hasprop_raw(thr, obj, DUK_HTHREAD_STRING_INT_FINALIZER(thr))) { - DUK_DDD(DUK_DDDPRINT("object has a finalizer, run it")); + heap->refzero_list = NULL; + + DUK_DD(DUK_DDPRINT("refzero processed %ld objects", (long) count)); +} + +DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hobject(duk_heap *heap, duk_hobject *obj, duk_bool_t skip_free_pending) { + duk_heaphdr *hdr; + duk_heaphdr *root; + + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); + DUK_ASSERT(obj != NULL); + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) obj) == DUK_HTYPE_OBJECT); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h1) == 0); - DUK_HEAPHDR_PREINC_REFCOUNT(h1); /* bump refcount to prevent refzero during finalizer processing */ + hdr = (duk_heaphdr *) obj; - duk_hobject_run_finalizer(thr, obj); /* must never longjmp */ - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(h1)); /* duk_hobject_run_finalizer() sets */ + /* Refzero'd objects must be in heap_allocated. They can't be in + * finalize_list because all objects on finalize_list have an + * artificial +1 refcount bump. + */ +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(duk_heap_in_heap_allocated(heap, (duk_heaphdr *) obj)); +#endif - DUK_HEAPHDR_PREDEC_REFCOUNT(h1); /* remove artificial bump */ - DUK_ASSERT_DISABLE(h1->h_refcount >= 0); /* refcount is unsigned, so always true */ + DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, hdr); - if (DUK_HEAPHDR_GET_REFCOUNT(h1) != 0) { - DUK_DDD(DUK_DDDPRINT("-> object refcount after finalization non-zero, object will be rescued")); - rescued = 1; - } else { - DUK_DDD(DUK_DDDPRINT("-> object refcount still zero after finalization, object will be freed")); +#if defined(DUK_USE_FINALIZER_SUPPORT) + /* 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))) { + /* 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 + * object again, but its refcount drops to zero. Free without + * running the finalizer again. + */ + if (DUK_HEAPHDR_HAS_FINALIZED(hdr)) { + DUK_D(DUK_DPRINT("refzero'd object has finalizer and FINALIZED is set -> free")); + } else { + /* Set FINALIZABLE flag so that all objects on finalize_list + * will have it set and are thus detectable based on the + * flag alone. + */ + DUK_HEAPHDR_SET_FINALIZABLE(hdr); + DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(hdr)); + +#if defined(DUK_USE_REFERENCE_COUNTING) + /* Bump refcount on finalize_list insert so that a + * refzero can never occur when an object is waiting + * for its finalizer call. Refzero might otherwise + * now happen because we allow duk_push_heapptr() for + * objects pending finalization. + */ + DUK_HEAPHDR_PREINC_REFCOUNT(hdr); +#endif + DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap, hdr); + + /* Process finalizers unless skipping is explicitly + * requested (NORZ) or refzero_list is being processed + * (avoids side effects during a refzero cascade). + * If refzero_list is processed, the initial refzero + * call will run pending finalizers when refzero_list + * is done. + */ + if (!skip_free_pending && heap->refzero_list == NULL) { + duk_heap_process_finalize_list(heap); } + return; } + } #endif /* DUK_USE_FINALIZER_SUPPORT */ - /* Refzero head is still the same. This is the case even if finalizer - * inserted more refzero objects; they are inserted to the tail. - */ - DUK_ASSERT(h1 == heap->refzero_list); + /* No need to finalize, free object via refzero_list. */ - /* - * Remove the object from the refzero list. This cannot be done - * before a possible finalizer has been executed; the finalizer - * may trigger a mark-and-sweep, and mark-and-sweep must be able - * to traverse a complete refzero_list. - */ + root = heap->refzero_list; - h2 = DUK_HEAPHDR_GET_NEXT(heap, h1); - if (h2) { - DUK_HEAPHDR_SET_PREV(heap, h2, NULL); /* not strictly necessary */ - heap->refzero_list = h2; - } else { - heap->refzero_list = NULL; - heap->refzero_list_tail = NULL; - } + DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); + /* 'next' is left as GARBAGE. */ + heap->refzero_list = hdr; - /* - * Rescue or free. + if (root == NULL) { + /* Object is now queued. Refzero_list was NULL so + * no-one is currently processing it; do it here. + * With refzero processing just doing a cascade of + * free calls, we can process it directly even when + * NORZ macros are used: there are no side effects. */ + duk__refcount_free_pending(heap); + DUK_ASSERT(heap->refzero_list == NULL); + /* Process finalizers only after the entire cascade + * is finished. In most cases there's nothing to + * finalize, so fast path check to avoid a call. + */ #if defined(DUK_USE_FINALIZER_SUPPORT) - if (rescued) { - /* yes -> move back to heap allocated */ - DUK_DD(DUK_DDPRINT("object rescued during refcount finalization: %p", (void *) h1)); - DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(h1)); - DUK_ASSERT(DUK_HEAPHDR_HAS_FINALIZED(h1)); - DUK_HEAPHDR_CLEAR_FINALIZED(h1); - h2 = heap->heap_allocated; - DUK_HEAPHDR_SET_PREV(heap, h1, NULL); - if (h2) { - DUK_HEAPHDR_SET_PREV(heap, h2, h1); - } - DUK_HEAPHDR_SET_NEXT(heap, h1, h2); - DUK_ASSERT_HEAPHDR_LINKS(heap, h1); - DUK_ASSERT_HEAPHDR_LINKS(heap, h2); - heap->heap_allocated = h1; - } else -#endif /* DUK_USE_FINALIZER_SUPPORT */ - { - /* no -> decref members, then free */ - duk__refcount_finalize_hobject(thr, obj); - DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h1) == DUK_HTYPE_OBJECT); /* currently, always the case */ - duk_free_hobject(heap, (duk_hobject *) h1); + if (!skip_free_pending && DUK_UNLIKELY(heap->finalize_list != NULL)) { + duk_heap_process_finalize_list(heap); } +#endif + } else { + DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); + DUK_HEAPHDR_SET_PREV(heap, root, hdr); - count++; + /* Object is now queued. Because refzero_list was + * non-NULL, it's already being processed by someone + * in the C call stack, so we're done. + */ } - DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap); +} - DUK_DDD(DUK_DDDPRINT("refzero processed %ld objects", (long) count)); +#if defined(DUK_USE_FINALIZER_SUPPORT) +DUK_INTERNAL DUK_ALWAYS_INLINE void duk_refzero_check_fast(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(thr->heap->refzero_list == NULL); /* Processed to completion inline. */ - /* - * Once the whole refzero cascade has been freed, check for - * a voluntary mark-and-sweep. - */ + if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) { + duk_heap_process_finalize_list(thr->heap); + } +} -#if defined(DUK_USE_VOLUNTARY_GC) - /* 'count' is more or less comparable to normal trigger counter update - * which happens in memory block (re)allocation. - */ - heap->mark_and_sweep_trigger_counter -= count; - if (heap->mark_and_sweep_trigger_counter <= 0) { - if (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) { - DUK_D(DUK_DPRINT("mark-and-sweep in progress -> skip voluntary mark-and-sweep now")); - } else { - duk_bool_t rc; - duk_small_uint_t flags = 0; /* not emergency */ +DUK_INTERNAL void duk_refzero_check_slow(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(thr->heap->refzero_list == NULL); /* Processed to completion inline. */ - DUK_D(DUK_DPRINT("refcount triggering mark-and-sweep")); - rc = duk_heap_mark_and_sweep(heap, flags); - DUK_UNREF(rc); - DUK_D(DUK_DPRINT("refcount triggered mark-and-sweep => rc %ld", (long) rc)); - } + if (DUK_UNLIKELY(thr->heap->finalize_list != NULL)) { + duk_heap_process_finalize_list(thr->heap); } -#endif /* DUK_USE_VOLUNTARY_GC */ +} +#endif /* DUK_USE_FINALIZER_SUPPORT */ + +/* + * Refzero processing for duk_hstring. + */ + +DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hstring(duk_heap *heap, duk_hstring *str) { + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); + DUK_ASSERT(str != NULL); + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) str) == DUK_HTYPE_STRING); + + duk_heap_strcache_string_remove(heap, str); + duk_heap_strtable_unlink(heap, str); + duk_free_hstring(heap, str); +} + +/* + * Refzero processing for duk_hbuffer. + */ + +DUK_LOCAL DUK_INLINE void duk__refcount_refzero_hbuffer(duk_heap *heap, duk_hbuffer *buf) { + DUK_ASSERT(heap != NULL); + DUK_ASSERT(heap->heap_thread != NULL); + DUK_ASSERT(buf != NULL); + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) buf) == DUK_HTYPE_BUFFER); + + DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap, (duk_heaphdr *) buf); + duk_free_hbuffer(heap, buf); } /* * Incref and decref functions. * * Decref may trigger immediate refzero handling, which may free and finalize - * an arbitrary number of objects. + * an arbitrary number of objects (a "DECREF cascade"). * * Refzero handling is skipped entirely if (1) mark-and-sweep is running or * (2) execution is paused in the debugger. The objects are left in the heap, @@ -46959,46 +48643,67 @@ DUK_INTERNAL void duk_refzero_free_pending(duk_hthread *thr) { * mark-and-sweep also calls finalizers which would use the ordinary decref * macros anyway. * - * The DUK__RZ_SUPPRESS_CHECK() must be enabled also when mark-and-sweep - * support has been disabled: the flag is also used in heap destruction when - * running finalizers for remaining objects, and the flag prevents objects - * from being moved around in heap linked lists. + * We can't process refzeros (= free objects) when the debugger is running + * as the debugger might make an object unreachable but still continue + * inspecting it (or even cause it to be pushed back). So we must rely on + * mark-and-sweep to collect them. + * + * The DUK__RZ_SUPPRESS_CHECK() condition is also used in heap destruction + * when running finalizers for remaining objects: the flag prevents objects + * from being moved around in heap linked lists while that's being done. + * + * The suppress condition is important to performance. */ -/* The suppress condition is important to performance. The flags being tested - * are in the same duk_heap field so a single TEST instruction (on x86) tests - * for them. - */ +#define DUK__RZ_SUPPRESS_ASSERT1() do { \ + DUK_ASSERT(thr != NULL); \ + DUK_ASSERT(thr->heap != NULL); \ + /* When mark-and-sweep runs, heap_thread must exist. */ \ + DUK_ASSERT(thr->heap->ms_running == 0 || thr->heap->heap_thread != NULL); \ + /* When mark-and-sweep runs, the 'thr' argument always matches heap_thread. \ + * This could be used to e.g. suppress check against 'thr' directly (and \ + * knowing it would be heap_thread); not really used now. \ + */ \ + DUK_ASSERT(thr->heap->ms_running == 0 || thr == thr->heap->heap_thread); \ + /* We may be called when the heap is initializing and we process \ + * refzeros normally, but mark-and-sweep and finalizers are prevented \ + * if that's the case. \ + */ \ + DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->ms_prevent_count > 0); \ + DUK_ASSERT(thr->heap->heap_initializing == 0 || thr->heap->pf_prevent_count > 0); \ + } while (0) + #if defined(DUK_USE_DEBUGGER_SUPPORT) -#define DUK__RZ_SUPPRESS_COND() \ - (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap) || DUK_HEAP_IS_PAUSED(heap)) +#define DUK__RZ_SUPPRESS_ASSERT2() do { \ + /* When debugger is paused, ms_running is set. */ \ + DUK_ASSERT(!DUK_HEAP_HAS_DEBUGGER_PAUSED(thr->heap) || thr->heap->ms_running != 0); \ + } while (0) +#define DUK__RZ_SUPPRESS_COND() (heap->ms_running != 0) #else -#define DUK__RZ_SUPPRESS_COND() \ - (DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)) -#endif +#define DUK__RZ_SUPPRESS_ASSERT2() do { } while (0) +#define DUK__RZ_SUPPRESS_COND() (heap->ms_running != 0) +#endif /* DUK_USE_DEBUGGER_SUPPORT */ + #define DUK__RZ_SUPPRESS_CHECK() do { \ + DUK__RZ_SUPPRESS_ASSERT1(); \ + DUK__RZ_SUPPRESS_ASSERT2(); \ if (DUK_UNLIKELY(DUK__RZ_SUPPRESS_COND())) { \ - DUK_DDD(DUK_DDDPRINT("refzero handling suppressed when mark-and-sweep running, object: %p", (void *) h)); \ + DUK_DDD(DUK_DDDPRINT("refzero handling suppressed (not even queued) when mark-and-sweep running, object: %p", (void *) h)); \ return; \ } \ } while (0) #define DUK__RZ_STRING() do { \ - duk_heap_strcache_string_remove(thr->heap, (duk_hstring *) h); \ - duk_heap_string_remove(heap, (duk_hstring *) h); \ - duk_free_hstring(heap, (duk_hstring *) h); \ + duk__refcount_refzero_hstring(heap, (duk_hstring *) h); \ } while (0) #define DUK__RZ_BUFFER() do { \ - duk_heap_remove_any_from_heap_allocated(heap, (duk_heaphdr *) h); \ - duk_free_hbuffer(heap, (duk_hbuffer *) h); \ + duk__refcount_refzero_hbuffer(heap, (duk_hbuffer *) h); \ } while (0) #define DUK__RZ_OBJECT() do { \ - duk_heap_remove_any_from_heap_allocated(heap, (duk_heaphdr *) h); \ - duk__queue_refzero(heap, (duk_heaphdr *) h); \ - if (!skip_free_pending) { \ - duk_refzero_free_pending(thr); \ - } \ + duk__refcount_refzero_hobject(heap, (duk_hobject *) h, skip_free_pending); \ } while (0) + +/* XXX: test the effect of inlining here vs. NOINLINE in refzero helpers */ #if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) #define DUK__RZ_INLINE DUK_ALWAYS_INLINE #else @@ -47068,42 +48773,39 @@ DUK_LOCAL DUK__RZ_INLINE void duk__heaphdr_refzero_helper(duk_hthread *thr, duk_ DUK__RZ_OBJECT(); break; - case DUK_HTYPE_BUFFER: + default: /* Buffers have no internal references. However, a dynamic * buffer has a separate allocation for the buffer. This is * freed by duk_heap_free_heaphdr_raw(). */ + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(h) == DUK_HTYPE_BUFFER); DUK__RZ_BUFFER(); break; - - default: - DUK_D(DUK_DPRINT("invalid heap type in decref: %ld", (long) DUK_HEAPHDR_GET_TYPE(h))); - DUK_UNREACHABLE(); } } -DUK_INTERNAL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) { +DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) { duk__heaphdr_refzero_helper(thr, h, 0 /*skip_free_pending*/); } -DUK_INTERNAL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h) { +DUK_INTERNAL DUK_NOINLINE void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h) { duk__heaphdr_refzero_helper(thr, h, 1 /*skip_free_pending*/); } -DUK_INTERNAL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h) { +DUK_INTERNAL DUK_NOINLINE void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h) { duk__hstring_refzero_helper(thr, h); } -DUK_INTERNAL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h) { +DUK_INTERNAL DUK_NOINLINE void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h) { duk__hbuffer_refzero_helper(thr, h); } -DUK_INTERNAL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h) { +DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h) { duk__hobject_refzero_helper(thr, h, 0 /*skip_free_pending*/); } -DUK_INTERNAL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h) { +DUK_INTERNAL DUK_NOINLINE void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h) { duk__hobject_refzero_helper(thr, h, 1 /*skip_free_pending*/); } @@ -47117,6 +48819,7 @@ DUK_INTERNAL void duk_tval_incref(duk_tval *tv) { DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); DUK_ASSERT_DISABLE(h->h_refcount >= 0); DUK_HEAPHDR_PREINC_REFCOUNT(h); + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) != 0); /* No wrapping. */ } } @@ -47174,6 +48877,7 @@ DUK_INTERNAL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv) { return; \ } \ DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0); /* No wrapping. */ \ } while (0) #define DUK__DECREF_SHARED() do { \ if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { \ @@ -47186,6 +48890,7 @@ DUK_INTERNAL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv) { #else #define DUK__INCREF_SHARED() do { \ DUK_HEAPHDR_PREINC_REFCOUNT((duk_heaphdr *) h); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) != 0); /* No wrapping. */ \ } while (0) #define DUK__DECREF_SHARED() do { \ if (DUK_HEAPHDR_PREDEC_REFCOUNT((duk_heaphdr *) h) != 0) { \ @@ -47210,6 +48915,11 @@ DUK_INTERNAL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) { DUK__DECREF_ASSERTS(); DUK__DECREF_SHARED(); duk_heaphdr_refzero(thr, h); + + /* Forced mark-and-sweep when GC torture enabled; this could happen + * on any DECREF (but not DECREF_NORZ). + */ + DUK_GC_TORTURE(thr->heap); } DUK_INTERNAL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h) { DUK__DECREF_ASSERTS(); @@ -47265,6 +48975,8 @@ DUK_INTERNAL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h) { #undef DUK__RZ_INLINE #undef DUK__RZ_OBJECT #undef DUK__RZ_STRING +#undef DUK__RZ_SUPPRESS_ASSERT1 +#undef DUK__RZ_SUPPRESS_ASSERT2 #undef DUK__RZ_SUPPRESS_CHECK #undef DUK__RZ_SUPPRESS_COND /* @@ -47353,6 +49065,10 @@ DUK_LOCAL const duk_uint8_t *duk__scan_backwards(const duk_uint8_t *p, const duk * * Typing now assumes 32-bit string byte/char offsets (duk_uint_fast32_t). * Better typing might be to use duk_size_t. + * + * Caller should ensure 'char_offset' is within the string bounds [0,charlen] + * (endpoint is inclusive). If this is not the case, no memory unsafe + * behavior will happen but an error will be thrown. */ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset) { @@ -47362,20 +49078,27 @@ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *t duk_small_int_t i; duk_bool_t use_cache; duk_uint_fast32_t dist_start, dist_end, dist_sce; + duk_uint_fast32_t char_length; const duk_uint8_t *p_start; const duk_uint8_t *p_end; const duk_uint8_t *p_found; - if (char_offset > DUK_HSTRING_GET_CHARLEN(h)) { - goto error; - } - /* * For ASCII strings, the answer is simple. */ - if (DUK_HSTRING_IS_ASCII(h)) { - /* clen == blen -> pure ascii */ + if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) { + return char_offset; + } + + char_length = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h); + DUK_ASSERT(char_offset <= char_length); + + if (DUK_LIKELY(DUK_HSTRING_IS_ASCII(h))) { + /* Must recheck because the 'is ascii' flag may be set + * lazily. Alternatively, we could just compare charlen + * to bytelen. + */ return char_offset; } @@ -47397,7 +49120,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *t heap = thr->heap; sce = NULL; - use_cache = (DUK_HSTRING_GET_CHARLEN(h) > DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT); + use_cache = (char_length > DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT); if (use_cache) { #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) @@ -47428,7 +49151,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *t DUK_ASSERT(DUK_HSTRING_GET_CHARLEN(h) >= char_offset); dist_start = char_offset; - dist_end = DUK_HSTRING_GET_CHARLEN(h) - char_offset; + dist_end = char_length - char_offset; dist_sce = 0; DUK_UNREF(dist_sce); /* initialize for debug prints, needed if sce==NULL */ p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h); @@ -47501,12 +49224,12 @@ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *t scan_done: - if (!p_found) { + if (DUK_UNLIKELY(p_found == NULL)) { /* Scan error: this shouldn't normally happen; it could happen if * string is not valid UTF-8 data, and clen/blen are not consistent * with the scanning algorithm. */ - goto error; + goto scan_error; } DUK_ASSERT(p_found >= p_start); @@ -47561,64 +49284,176 @@ DUK_INTERNAL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *t return byte_offset; - error: + scan_error: DUK_ERROR_INTERNAL(thr); return 0; } /* - * Heap stringtable handling, string interning. + * Heap string table handling, string interning. */ /* #include duk_internal.h -> already included */ -#if defined(DUK_USE_STRTAB_PROBE) -#define DUK__HASH_INITIAL(hash,h_size) DUK_STRTAB_HASH_INITIAL((hash),(h_size)) -#define DUK__HASH_PROBE_STEP(hash) DUK_STRTAB_HASH_PROBE_STEP((hash)) -#define DUK__DELETED_MARKER(heap) DUK_STRTAB_DELETED_MARKER((heap)) +/* Resize checks not needed if minsize == maxsize, typical for low memory + * targets. + */ +#define DUK__STRTAB_RESIZE_CHECK +#if (DUK_USE_STRTAB_MINSIZE == DUK_USE_STRTAB_MAXSIZE) +#undef DUK__STRTAB_RESIZE_CHECK #endif -#define DUK__PREVENT_MS_SIDE_EFFECTS(heap) do { \ - (heap)->mark_and_sweep_base_flags |= \ - DUK_MS_FLAG_NO_STRINGTABLE_RESIZE | /* avoid recursive string table call */ \ - DUK_MS_FLAG_NO_FINALIZERS | /* avoid pressure to add/remove strings, invalidation of call data argument, etc. */ \ - DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid array abandoning which interns strings */ \ - } while (0) +#if defined(DUK_USE_STRTAB_PTRCOMP) +#define DUK__HEAPPTR_ENC16(heap,ptr) DUK_USE_HEAPPTR_ENC16((heap)->heap_udata, (ptr)) +#define DUK__HEAPPTR_DEC16(heap,val) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (val)) +#define DUK__GET_STRTABLE(heap) ((heap)->strtable16) +#else +#define DUK__HEAPPTR_ENC16(heap,ptr) (ptr) +#define DUK__HEAPPTR_DEC16(heap,val) (val) +#define DUK__GET_STRTABLE(heap) ((heap)->strtable) +#endif + +#define DUK__STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */ + +/* + * Debug dump stringtable. + */ + +#if defined(DUK_USE_DEBUG) +DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *strtable; +#else + duk_hstring **strtable; +#endif + duk_uint32_t i; + duk_hstring *h; + duk_size_t count_total = 0; + duk_size_t count_chain; + duk_size_t count_chain_min = DUK_SIZE_MAX; + duk_size_t count_chain_max = 0; + duk_size_t count_len[8]; /* chain lengths from 0 to 7 */ + + if (heap == NULL) { + DUK_D(DUK_DPRINT("string table, heap=NULL")); + return; + } + + strtable = DUK__GET_STRTABLE(heap); + if (strtable == NULL) { + DUK_D(DUK_DPRINT("string table, strtab=NULL")); + return; + } + + DUK_MEMZERO((void *) count_len, sizeof(count_len)); + for (i = 0; i < heap->st_size; i++) { + h = DUK__HEAPPTR_DEC16(heap, strtable[i]); + count_chain = 0; + while (h != NULL) { + count_chain++; + h = h->hdr.h_next; + } + if (count_chain < sizeof(count_len) / sizeof(duk_size_t)) { + count_len[count_chain]++; + } + count_chain_max = (count_chain > count_chain_max ? count_chain : count_chain_max); + count_chain_min = (count_chain < count_chain_min ? count_chain : count_chain_min); + count_total += count_chain; + } + + DUK_D(DUK_DPRINT("string table, strtab=%p, count=%lu, chain min=%lu max=%lu avg=%lf: " + "counts: %lu %lu %lu %lu %lu %lu %lu %lu ...", + (void *) heap->strtable, (unsigned long) count_total, + (unsigned long) count_chain_min, (unsigned long) count_chain_max, + (double) count_total / (double) heap->st_size, + (unsigned long) count_len[0], (unsigned long) count_len[1], + (unsigned long) count_len[2], (unsigned long) count_len[3], + (unsigned long) count_len[4], (unsigned long) count_len[5], + (unsigned long) count_len[6], (unsigned long) count_len[7])); +} +#endif /* DUK_USE_DEBUG */ /* - * Create a hstring and insert into the heap. The created object - * is directly garbage collectable with reference count zero. + * Assertion helper to ensure strtable is populated correctly. + */ + +#if defined(DUK_USE_ASSERTIONS) +DUK_LOCAL void duk__strtable_assert_checks(duk_heap *heap) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *strtable; +#else + duk_hstring **strtable; +#endif + duk_uint32_t i; + duk_hstring *h; + duk_size_t count = 0; + + DUK_ASSERT(heap != NULL); + + strtable = DUK__GET_STRTABLE(heap); + if (strtable != NULL) { + DUK_ASSERT(heap->st_size != 0); + DUK_ASSERT(heap->st_mask == heap->st_size - 1); + + for (i = 0; i < heap->st_size; i++) { + h = DUK__HEAPPTR_DEC16(heap, strtable[i]); + while (h != NULL) { + DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i); + count++; + h = h->hdr.h_next; + } + } + } else { + DUK_ASSERT(heap->st_size == 0); + DUK_ASSERT(heap->st_mask == 0); + } + +#if defined(DUK__STRTAB_RESIZE_CHECK) + DUK_ASSERT(count == (duk_size_t) heap->st_count); +#endif +} +#endif /* DUK_USE_ASSERTIONS */ + +/* + * Allocate and initialize a duk_hstring. + * + * Returns a NULL if allocation or initialization fails for some reason. * - * The caller must place the interned string into the stringtable - * immediately (without chance of a longjmp); otherwise the string - * is lost. + * The string won't be inserted into the string table and isn't tracked in + * any way (link pointers will be NULL). The caller must place the string + * into the string table without any risk of a longjmp, otherwise the string + * is leaked. */ -DUK_LOCAL -duk_hstring *duk__alloc_init_hstring(duk_heap *heap, - const duk_uint8_t *str, - duk_uint32_t blen, - duk_uint32_t strhash, - const duk_uint8_t *extdata) { - duk_hstring *res = NULL; - duk_uint8_t *data; - duk_size_t alloc_size; +DUK_LOCAL duk_hstring *duk__strtable_alloc_hstring(duk_heap *heap, + const duk_uint8_t *str, + duk_uint32_t blen, + duk_uint32_t strhash, + const duk_uint8_t *extdata) { + duk_hstring *res; + const duk_uint8_t *data; #if !defined(DUK_USE_HSTRING_ARRIDX) duk_uarridx_t dummy; #endif - duk_uint32_t clen; + + DUK_ASSERT(heap != NULL); + DUK_UNREF(extdata); #if defined(DUK_USE_STRLEN16) /* If blen <= 0xffffUL, clen is also guaranteed to be <= 0xffffUL. */ if (blen > 0xffffUL) { DUK_D(DUK_DPRINT("16-bit string blen/clen active and blen over 16 bits, reject intern")); - return NULL; + goto alloc_error; } #endif + /* XXX: Memzeroing the allocated structure is not really necessary + * because we could just initialize all fields explicitly (almost + * all fields are initialized explicitly anyway). + */ +#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) if (extdata) { - alloc_size = (duk_size_t) sizeof(duk_hstring_external); - res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); - if (!res) { + res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring_external)); + if (DUK_UNLIKELY(res == NULL)) { goto alloc_error; } DUK_MEMZERO(res, sizeof(duk_hstring_external)); @@ -47627,12 +49462,18 @@ duk_hstring *duk__alloc_init_hstring(duk_heap *heap, #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, DUK_HSTRING_FLAG_EXTDATA); + DUK_ASSERT(extdata[blen] == 0); /* Application responsibility. */ + data = extdata; ((duk_hstring_external *) res)->extdata = extdata; - } else { + } else +#endif /* DUK_USE_HSTRING_EXTDATA && DUK_USE_EXTSTR_INTERN_CHECK */ + { + duk_uint8_t *data_tmp; + /* NUL terminate for convenient C access */ - alloc_size = (duk_size_t) (sizeof(duk_hstring) + blen + 1); - res = (duk_hstring *) DUK_ALLOC(heap, alloc_size); - if (!res) { + DUK_ASSERT(sizeof(duk_hstring) + blen + 1 > blen); /* No wrap, limits ensure. */ + res = (duk_hstring *) DUK_ALLOC(heap, sizeof(duk_hstring) + blen + 1); + if (DUK_UNLIKELY(res == NULL)) { goto alloc_error; } DUK_MEMZERO(res, sizeof(duk_hstring)); @@ -47641,1090 +49482,787 @@ duk_hstring *duk__alloc_init_hstring(duk_heap *heap, #endif DUK_HEAPHDR_SET_TYPE_AND_FLAGS(&res->hdr, DUK_HTYPE_STRING, 0); - data = (duk_uint8_t *) (res + 1); - DUK_MEMCPY(data, str, blen); - data[blen] = (duk_uint8_t) 0; + data_tmp = (duk_uint8_t *) (res + 1); + DUK_MEMCPY(data_tmp, str, blen); + data_tmp[blen] = (duk_uint8_t) 0; + data = (const duk_uint8_t *) data_tmp; } + DUK_HSTRING_SET_BYTELEN(res, blen); + DUK_HSTRING_SET_HASH(res, strhash); + DUK_ASSERT(!DUK_HSTRING_HAS_ARRIDX(res)); #if defined(DUK_USE_HSTRING_ARRIDX) - if (duk_js_to_arrayindex_raw_string(str, blen, &res->arridx)) { + res->arridx = duk_js_to_arrayindex_string(data, blen); + if (res->arridx != DUK_HSTRING_NO_ARRAY_INDEX) { #else - if (duk_js_to_arrayindex_raw_string(str, blen, &dummy)) { + dummy = duk_js_to_arrayindex_string(data, blen); + if (dummy != DUK_HSTRING_NO_ARRAY_INDEX) { #endif + /* Array index strings cannot be symbol strings, + * and they're always pure ASCII so blen == clen. + */ DUK_HSTRING_SET_ARRIDX(res); - } - - /* All strings beginning with specific (invalid UTF-8) byte prefixes - * are treated as symbols. - */ - DUK_ASSERT(!DUK_HSTRING_HAS_SYMBOL(res)); - DUK_ASSERT(!DUK_HSTRING_HAS_HIDDEN(res)); - if (blen > 0) { - if (str[0] == 0xffU) { - DUK_HSTRING_SET_SYMBOL(res); - DUK_HSTRING_SET_HIDDEN(res); - } else if ((str[0] & 0xc0U) == 0x80U) { - DUK_HSTRING_SET_SYMBOL(res); + DUK_HSTRING_SET_ASCII(res); + DUK_ASSERT(duk_unicode_unvalidated_utf8_length(data, (duk_size_t) blen) == blen); + } else { + /* Because 'data' is NUL-terminated, we don't need a + * blen > 0 check here. For NUL (0x00) the symbol + * checks will be false. + */ + if (DUK_UNLIKELY(data[0] >= 0x80U)) { + if (data[0] == 0xffU) { + DUK_HSTRING_SET_SYMBOL(res); + DUK_HSTRING_SET_HIDDEN(res); + } else if (data[0] <= 0xbf) { + /* Check equivalent to: (data[0] & 0xc0U) == 0x80U. */ + DUK_HSTRING_SET_SYMBOL(res); + } } - } - - DUK_HSTRING_SET_HASH(res, strhash); - DUK_HSTRING_SET_BYTELEN(res, blen); - clen = (duk_uint32_t) duk_unicode_unvalidated_utf8_length(str, (duk_size_t) blen); - DUK_ASSERT(clen <= blen); -#if defined(DUK_USE_HSTRING_CLEN) - DUK_HSTRING_SET_CHARLEN(res, clen); -#endif - - /* Using an explicit 'ASCII' flag has larger footprint (one call site - * only) but is quite useful for the case when there's no explicit - * 'clen' in duk_hstring. - */ - DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res)); - if (clen == blen) { - DUK_HSTRING_SET_ASCII(res); + /* Using an explicit 'ASCII' flag has larger footprint (one call site + * only) but is quite useful for the case when there's no explicit + * 'clen' in duk_hstring. + * + * The flag is set lazily for RAM strings. + */ + DUK_ASSERT(!DUK_HSTRING_HAS_ASCII(res)); } - DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, clen=%ld, has_arridx=%ld, has_extdata=%ld", + DUK_DDD(DUK_DDDPRINT("interned string, hash=0x%08lx, blen=%ld, has_arridx=%ld, has_extdata=%ld", (unsigned long) DUK_HSTRING_GET_HASH(res), (long) DUK_HSTRING_GET_BYTELEN(res), - (long) DUK_HSTRING_GET_CHARLEN(res), (long) (DUK_HSTRING_HAS_ARRIDX(res) ? 1 : 0), (long) (DUK_HSTRING_HAS_EXTDATA(res) ? 1 : 0))); + DUK_ASSERT(res != NULL); return res; alloc_error: - DUK_FREE(heap, res); return NULL; } /* - * String table algorithm: fixed size string table with array chaining - * - * The top level string table has a fixed size, with each slot holding - * either NULL, string pointer, or pointer to a separately allocated - * string pointer list. - * - * This is good for low memory environments using a pool allocator: the - * top level allocation has a fixed size and the pointer lists have quite - * small allocation size, which further matches the typical pool sizes - * needed by objects, strings, property tables, etc. + * Grow strtable allocation in-place. */ -#if defined(DUK_USE_STRTAB_CHAIN) +#if defined(DUK__STRTAB_RESIZE_CHECK) +DUK_LOCAL void duk__strtable_grow_inplace(duk_heap *heap) { + duk_uint32_t new_st_size; + duk_uint32_t old_st_size; + duk_uint32_t i; + duk_hstring *h; + duk_hstring *next; + duk_hstring *prev; +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *new_ptr; + duk_uint16_t *new_ptr_high; +#else + duk_hstring **new_ptr; + duk_hstring **new_ptr_high; +#endif -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_uint16_t *lst; - duk_uint16_t *new_lst; - duk_size_t i, n; - duk_uint16_t null16 = heap->heapptr_null16; - duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); + DUK_DD(DUK_DDPRINT("grow in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size * 2)); DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); + DUK_ASSERT(heap->st_resizing == 1); + DUK_ASSERT(heap->st_size >= 2); + DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0); /* 2^N */ + DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); - slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); + new_st_size = heap->st_size << 1U; + DUK_ASSERT(new_st_size > heap->st_size); /* No overflow. */ - e = heap->strtable + slotidx; - if (e->listlen == 0) { - if (e->u.str16 == null16) { - e->u.str16 = h16; - } else { - /* Now two entries in the same slot, alloc list */ - lst = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * 2); - if (lst == NULL) { - return 1; /* fail */ - } - lst[0] = e->u.str16; - lst[1] = h16; - e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) lst); - e->listlen = 2; - } - } else { - DUK_ASSERT(e->u.strlist16 != null16); - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); - DUK_ASSERT(lst != NULL); - for (i = 0, n = e->listlen; i < n; i++) { - if (lst[i] == null16) { - lst[i] = h16; - return 0; - } - } - - if (e->listlen + 1 == 0) { - /* Overflow, relevant mainly when listlen is 16 bits. */ - return 1; /* fail */ - } + /* Reallocate the strtable first and then work in-place to rehash + * strings. We don't need an indirect allocation here: even if GC + * is triggered to satisfy the allocation, recursive strtable resize + * is prevented by flags. This is also why we don't need to use + * DUK_REALLOC_INDIRECT(). + */ - new_lst = (duk_uint16_t *) DUK_REALLOC(heap, lst, sizeof(duk_uint16_t) * (e->listlen + 1)); - if (new_lst == NULL) { - return 1; /* fail */ - } - new_lst[e->listlen++] = h16; - e->u.strlist16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) new_lst); +#if defined(DUK_USE_STRTAB_PTRCOMP) + new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size); +#else + new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size); +#endif + if (DUK_UNLIKELY(new_ptr == NULL)) { + /* If realloc fails we can continue normally: the string table + * won't "fill up" although chains will gradually get longer. + * When string insertions continue, we'll quite soon try again + * with no special handling. + */ + DUK_D(DUK_DPRINT("string table grow failed, ignoring")); + return; } - return 0; -} -#else /* DUK_USE_HEAPPTR16 */ -DUK_LOCAL duk_bool_t duk__insert_hstring_chain(duk_heap *heap, duk_hstring *h) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_hstring **lst; - duk_hstring **new_lst; - duk_size_t i, n; - - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - - slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); - - e = heap->strtable + slotidx; - if (e->listlen == 0) { - if (e->u.str == NULL) { - e->u.str = h; - } else { - /* Now two entries in the same slot, alloc list */ - lst = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * 2); - if (lst == NULL) { - return 1; /* fail */ - } - lst[0] = e->u.str; - lst[1] = h; - e->u.strlist = lst; - e->listlen = 2; - } - } else { - DUK_ASSERT(e->u.strlist != NULL); - lst = e->u.strlist; - for (i = 0, n = e->listlen; i < n; i++) { - if (lst[i] == NULL) { - lst[i] = h; - return 0; - } - } +#if defined(DUK_USE_STRTAB_PTRCOMP) + heap->strtable16 = new_ptr; +#else + heap->strtable = new_ptr; +#endif - if (e->listlen + 1 == 0) { - /* Overflow, relevant mainly when listlen is 16 bits. */ - return 1; /* fail */ - } + /* Rehash a single bucket into two separate ones. When we grow + * by x2 the highest 'new' bit determines whether a string remains + * in its old position (bit is 0) or goes to a new one (bit is 1). + */ - new_lst = (duk_hstring **) DUK_REALLOC(heap, e->u.strlist, sizeof(duk_hstring *) * (e->listlen + 1)); - if (new_lst == NULL) { - return 1; /* fail */ - } - new_lst[e->listlen++] = h; - e->u.strlist = new_lst; - } - return 0; -} -#endif /* DUK_USE_HEAPPTR16 */ + old_st_size = heap->st_size; + new_ptr_high = new_ptr + old_st_size; + for (i = 0; i < old_st_size; i++) { + duk_hstring *new_root; + duk_hstring *new_root_high; -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_uint16_t *lst; - duk_size_t i, n; - duk_uint16_t null16 = heap->heapptr_null16; + h = DUK__HEAPPTR_DEC16(heap, new_ptr[i]); + new_root = h; + new_root_high = NULL; - DUK_ASSERT(heap != NULL); + prev = NULL; + while (h != NULL) { + duk_uint32_t mask; - slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); + DUK_ASSERT((DUK_HSTRING_GET_HASH(h) & heap->st_mask) == i); + next = h->hdr.h_next; - e = heap->strtable + slotidx; - if (e->listlen == 0) { - if (e->u.str16 != null16) { - duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); - DUK_ASSERT(h != NULL); - if (DUK_HSTRING_GET_BYTELEN(h) == blen && - DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { - return h; - } - } - } else { - DUK_ASSERT(e->u.strlist16 != null16); - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); - DUK_ASSERT(lst != NULL); - for (i = 0, n = e->listlen; i < n; i++) { - if (lst[i] != null16) { - duk_hstring *h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[i]); - DUK_ASSERT(h != NULL); - if (DUK_HSTRING_GET_BYTELEN(h) == blen && - DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(h), (size_t) blen) == 0) { - return h; + /* Example: if previous size was 256, previous mask is 0xFF + * and size is 0x100 which corresponds to the new bit that + * comes into play. + */ + DUK_ASSERT(heap->st_mask == old_st_size - 1); + mask = old_st_size; + if (DUK_HSTRING_GET_HASH(h) & mask) { + if (prev != NULL) { + prev->hdr.h_next = h->hdr.h_next; + } else { + DUK_ASSERT(h == new_root); + new_root = h->hdr.h_next; } + + h->hdr.h_next = new_root_high; + new_root_high = h; + } else { + prev = h; } + h = next; } - } - return NULL; -} -#else /* DUK_USE_HEAPPTR16 */ -DUK_LOCAL duk_hstring *duk__find_matching_string_chain(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_hstring **lst; - duk_size_t i, n; + new_ptr[i] = DUK__HEAPPTR_ENC16(heap, new_root); + new_ptr_high[i] = DUK__HEAPPTR_ENC16(heap, new_root_high); + } - DUK_ASSERT(heap != NULL); + heap->st_size = new_st_size; + heap->st_mask = new_st_size - 1; - slotidx = strhash % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); +#if defined(DUK_USE_ASSERTIONS) + duk__strtable_assert_checks(heap); +#endif +} +#endif /* DUK__STRTAB_RESIZE_CHECK */ - e = heap->strtable + slotidx; - if (e->listlen == 0) { - if (e->u.str != NULL && - DUK_HSTRING_GET_BYTELEN(e->u.str) == blen && - DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e->u.str), (size_t) blen) == 0) { - return e->u.str; - } - } else { - DUK_ASSERT(e->u.strlist != NULL); - lst = e->u.strlist; - for (i = 0, n = e->listlen; i < n; i++) { - if (lst[i] != NULL && - DUK_HSTRING_GET_BYTELEN(lst[i]) == blen && - DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(lst[i]), (size_t) blen) == 0) { - return lst[i]; - } - } - } +/* + * Shrink strtable allocation in-place. + */ - return NULL; -} -#endif /* DUK_USE_HEAPPTR16 */ +#if defined(DUK__STRTAB_RESIZE_CHECK) +DUK_LOCAL void duk__strtable_shrink_inplace(duk_heap *heap) { + duk_uint32_t new_st_size; + duk_uint32_t i; + duk_hstring *h; + duk_hstring *other; + duk_hstring *root; +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *old_ptr; + duk_uint16_t *old_ptr_high; + duk_uint16_t *new_ptr; +#else + duk_hstring **old_ptr; + duk_hstring **old_ptr_high; + duk_hstring **new_ptr; +#endif -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_uint16_t *lst; - duk_size_t i, n; - duk_uint16_t h16; - duk_uint16_t null16 = heap->heapptr_null16; + DUK_DD(DUK_DDPRINT("shrink in-place: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size / 2)); DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); + DUK_ASSERT(heap->st_resizing == 1); + DUK_ASSERT(heap->st_size >= 2); + DUK_ASSERT((heap->st_size & (heap->st_size - 1)) == 0); /* 2^N */ + DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); - slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); + new_st_size = heap->st_size >> 1U; - DUK_ASSERT(h != NULL); - h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); + /* Combine two buckets into a single one. When we shrink, one hash + * bit (highest) disappears. + */ + old_ptr = DUK__GET_STRTABLE(heap); + old_ptr_high = old_ptr + new_st_size; + for (i = 0; i < new_st_size; i++) { + h = DUK__HEAPPTR_DEC16(heap, old_ptr[i]); + other = DUK__HEAPPTR_DEC16(heap, old_ptr_high[i]); - e = heap->strtable + slotidx; - if (e->listlen == 0) { - if (e->u.str16 == h16) { - e->u.str16 = null16; - return; - } - } else { - DUK_ASSERT(e->u.strlist16 != null16); - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); - DUK_ASSERT(lst != NULL); - for (i = 0, n = e->listlen; i < n; i++) { - if (lst[i] == h16) { - lst[i] = null16; - return; + if (h == NULL) { + /* First chain is empty, so use second one as is. */ + root = other; + } else { + /* Find end of first chain, and link in the second. */ + root = h; + while (h->hdr.h_next != NULL) { + h = h->hdr.h_next; } + h->hdr.h_next = other; } - } - - DUK_D(DUK_DPRINT("failed to find string that should be in stringtable")); - DUK_UNREACHABLE(); - return; -} -#else /* DUK_USE_HEAPPTR16 */ -DUK_LOCAL void duk__remove_matching_hstring_chain(duk_heap *heap, duk_hstring *h) { - duk_small_uint_t slotidx; - duk_strtab_entry *e; - duk_hstring **lst; - duk_size_t i, n; - DUK_ASSERT(heap != NULL); - DUK_ASSERT(h != NULL); - - slotidx = DUK_HSTRING_GET_HASH(h) % DUK_STRTAB_CHAIN_SIZE; - DUK_ASSERT(slotidx < DUK_STRTAB_CHAIN_SIZE); - - e = heap->strtable + slotidx; - if (e->listlen == 0) { - DUK_ASSERT(h != NULL); - if (e->u.str == h) { - e->u.str = NULL; - return; - } - } else { - DUK_ASSERT(e->u.strlist != NULL); - lst = e->u.strlist; - for (i = 0, n = e->listlen; i < n; i++) { - DUK_ASSERT(h != NULL); - if (lst[i] == h) { - lst[i] = NULL; - return; - } - } + old_ptr[i] = DUK__HEAPPTR_ENC16(heap, root); } - DUK_D(DUK_DPRINT("failed to find string that should be in stringtable")); - DUK_UNREACHABLE(); - return; -} -#endif /* DUK_USE_HEAPPTR16 */ + heap->st_size = new_st_size; + heap->st_mask = new_st_size - 1; -#if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) { - duk_strtab_entry *e; - duk_small_uint_t i; - duk_size_t j, n, used; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *lst; - duk_uint16_t null16 = heap->heapptr_null16; -#else - duk_hstring **lst; -#endif - - DUK_ASSERT(heap != NULL); - - for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { - e = heap->strtable + i; + /* The strtable is now consistent and we can realloc safely. Even + * if side effects cause string interning or removal the strtable + * updates are safe. Recursive resize has been prevented by caller. + * This is also why we don't need to use DUK_REALLOC_INDIRECT(). + * + * We assume a realloc() to a smaller size is guaranteed to succeed. + * It would be relatively straightforward to handle the error by + * essentially performing a "grow" step to recover. + */ - if (e->listlen == 0) { -#if defined(DUK_USE_HEAPPTR16) - DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str16 != null16 ? 1 : 0))); +#if defined(DUK_USE_STRTAB_PTRCOMP) + new_ptr = (duk_uint16_t *) DUK_REALLOC(heap, heap->strtable16, sizeof(duk_uint16_t) * new_st_size); + DUK_ASSERT(new_ptr != NULL); + heap->strtable16 = new_ptr; #else - DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0))); + new_ptr = (duk_hstring **) DUK_REALLOC(heap, heap->strtable, sizeof(duk_hstring *) * new_st_size); + DUK_ASSERT(new_ptr != NULL); + heap->strtable = new_ptr; #endif - } else { - used = 0; -#if defined(DUK_USE_HEAPPTR16) - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); -#else - lst = e->u.strlist; -#endif - DUK_ASSERT(lst != NULL); - for (j = 0, n = e->listlen; j < n; j++) { -#if defined(DUK_USE_HEAPPTR16) - if (lst[j] != null16) { -#else - if (lst[j] != NULL) { + +#if defined(DUK_USE_ASSERTIONS) + duk__strtable_assert_checks(heap); #endif - used++; - } - } - DUK_DD(DUK_DDPRINT("[%03d] -> array %d/%d", (int) i, (int) used, (int) e->listlen)); - } - } } -#endif /* DUK_USE_DEBUG */ - -#endif /* DUK_USE_STRTAB_CHAIN */ +#endif /* DUK__STRTAB_RESIZE_CHECK */ /* - * String table algorithm: closed hashing with a probe sequence - * - * This is the default algorithm and works fine for environments with - * minimal memory constraints. + * Grow/shrink check. */ -#if defined(DUK_USE_STRTAB_PROBE) - -/* Count actually used (non-NULL, non-DELETED) entries. */ -DUK_LOCAL duk_int_t duk__count_used_probe(duk_heap *heap) { - duk_int_t res = 0; - duk_uint_fast32_t i, n; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t null16 = heap->heapptr_null16; - duk_uint16_t deleted16 = heap->heapptr_deleted16; -#endif +#if defined(DUK__STRTAB_RESIZE_CHECK) +DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__strtable_resize_check(duk_heap *heap) { + duk_uint32_t load_factor; /* fixed point */ - n = (duk_uint_fast32_t) heap->st_size; - for (i = 0; i < n; i++) { -#if defined(DUK_USE_HEAPPTR16) - if (heap->strtable16[i] != null16 && heap->strtable16[i] != deleted16) { + DUK_ASSERT(heap != NULL); +#if defined(DUK_USE_STRTAB_PTRCOMP) + DUK_ASSERT(heap->strtable16 != NULL); #else - if (heap->strtable[i] != NULL && heap->strtable[i] != DUK__DELETED_MARKER(heap)) { + DUK_ASSERT(heap->strtable != NULL); #endif - res++; - } + + /* Prevent recursive resizing. */ + if (DUK_UNLIKELY(heap->st_resizing)) { + DUK_D(DUK_DPRINT("prevent recursive strtable resize")); + return; } - return res; -} -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { -#else -DUK_LOCAL void duk__insert_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_uint32_t *p_used, duk_hstring *h) { -#endif - duk_uint32_t i; - duk_uint32_t step; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t null16 = heap->heapptr_null16; - duk_uint16_t deleted16 = heap->heapptr_deleted16; -#endif + heap->st_resizing = 1; - DUK_ASSERT(size > 0); + DUK_ASSERT(heap->st_size >= 16U); + DUK_ASSERT((heap->st_size >> 4U) >= 1); + load_factor = heap->st_count / (heap->st_size >> 4U); - i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size); - step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(h)); - for (;;) { -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t e16 = entries16[i]; -#else - duk_hstring *e = entries[i]; -#endif + DUK_DD(DUK_DDPRINT("resize check string table: size=%lu, count=%lu, load_factor=%lu (fixed point .4; float %lf)", + (unsigned long) heap->st_size, (unsigned long) heap->st_count, + (unsigned long) load_factor, + (double) heap->st_count / (double) heap->st_size)); -#if defined(DUK_USE_HEAPPTR16) - /* XXX: could check for e16 == 0 because NULL is guaranteed to - * encode to zero. - */ - if (e16 == null16) { -#else - if (e == NULL) { -#endif - DUK_DDD(DUK_DDDPRINT("insert hit (null): %ld", (long) i)); -#if defined(DUK_USE_HEAPPTR16) - entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); -#else - entries[i] = h; -#endif - (*p_used)++; - break; -#if defined(DUK_USE_HEAPPTR16) - } else if (e16 == deleted16) { -#else - } else if (e == DUK__DELETED_MARKER(heap)) { + if (load_factor >= DUK_USE_STRTAB_GROW_LIMIT) { + if (heap->st_size >= DUK_USE_STRTAB_MAXSIZE) { + DUK_DD(DUK_DDPRINT("want to grow strtable (based on load factor) but already maximum size")); + } else { + DUK_D(DUK_DPRINT("grow string table: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size * 2)); +#if defined(DUK_USE_DEBUG) + duk_heap_strtable_dump(heap); #endif - /* st_used remains the same, DELETED is counted as used */ - DUK_DDD(DUK_DDDPRINT("insert hit (deleted): %ld", (long) i)); -#if defined(DUK_USE_HEAPPTR16) - entries16[i] = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); -#else - entries[i] = h; + duk__strtable_grow_inplace(heap); + } + } else if (load_factor <= DUK_USE_STRTAB_SHRINK_LIMIT) { + if (heap->st_size <= DUK_USE_STRTAB_MINSIZE) { + DUK_DD(DUK_DDPRINT("want to shrink strtable (based on load factor) but already minimum size")); + } else { + DUK_D(DUK_DPRINT("shrink string table: %lu -> %lu", (unsigned long) heap->st_size, (unsigned long) heap->st_size / 2)); +#if defined(DUK_USE_DEBUG) + duk_heap_strtable_dump(heap); #endif - break; + duk__strtable_shrink_inplace(heap); } - DUK_DDD(DUK_DDDPRINT("insert miss: %ld", (long) i)); - i = (i + step) % size; - - /* looping should never happen */ - DUK_ASSERT(i != DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(h), size)); + } else { + DUK_DD(DUK_DDPRINT("no need for strtable resize")); } + + heap->st_resizing = 0; } +#endif /* DUK__STRTAB_RESIZE_CHECK */ -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { -#else -DUK_LOCAL duk_hstring *duk__find_matching_string_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { -#endif - duk_uint32_t i; - duk_uint32_t step; +/* + * Torture grow/shrink: unconditionally grow and shrink back. + */ - DUK_ASSERT(size > 0); +#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK) +DUK_LOCAL void duk__strtable_resize_torture(duk_heap *heap) { + duk_uint32_t old_st_size; - i = DUK__HASH_INITIAL(strhash, size); - step = DUK__HASH_PROBE_STEP(strhash); - for (;;) { - duk_hstring *e; -#if defined(DUK_USE_HEAPPTR16) - e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, entries16[i]); -#else - e = entries[i]; -#endif + DUK_ASSERT(heap != NULL); - if (!e) { - return NULL; - } - if (e != DUK__DELETED_MARKER(heap) && DUK_HSTRING_GET_BYTELEN(e) == blen) { - if (DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(e), (size_t) blen) == 0) { - DUK_DDD(DUK_DDDPRINT("find matching hit: %ld (step %ld, size %ld)", - (long) i, (long) step, (long) size)); - return e; - } - } - DUK_DDD(DUK_DDDPRINT("find matching miss: %ld (step %ld, size %ld)", - (long) i, (long) step, (long) size)); - i = (i + step) % size; + old_st_size = heap->st_size; + if (old_st_size >= DUK_USE_STRTAB_MAXSIZE) { + return; + } - /* looping should never happen */ - DUK_ASSERT(i != DUK__HASH_INITIAL(strhash, size)); + heap->st_resizing = 1; + duk__strtable_grow_inplace(heap); + if (heap->st_size > old_st_size) { + duk__strtable_shrink_inplace(heap); } - DUK_UNREACHABLE(); + heap->st_resizing = 0; } +#endif /* DUK_USE_STRTAB_TORTURE && DUK__STRTAB_RESIZE_CHECK */ -#if defined(DUK_USE_HEAPPTR16) -DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_uint16_t *entries16, duk_uint32_t size, duk_hstring *h) { +/* + * Raw intern; string already checked not to be present. + */ + +DUK_LOCAL duk_hstring *duk__strtable_do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { + duk_hstring *res; + const duk_uint8_t *extdata; +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *slot; #else -DUK_LOCAL void duk__remove_matching_hstring_probe(duk_heap *heap, duk_hstring **entries, duk_uint32_t size, duk_hstring *h) { -#endif - duk_uint32_t i; - duk_uint32_t step; - duk_uint32_t hash; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t null16 = heap->heapptr_null16; - duk_uint16_t h16 = DUK_USE_HEAPPTR_ENC16(heap->heap_udata, (void *) h); + duk_hstring **slot; #endif - DUK_ASSERT(size > 0); + DUK_DDD(DUK_DDDPRINT("do_intern: heap=%p, str=%p, blen=%lu, strhash=%lx, st_size=%lu, st_count=%lu, load=%lf", + (void *) heap, (const void *) str, (unsigned long) blen, (unsigned long) strhash, + (unsigned long) heap->st_size, (unsigned long) heap->st_count, + (double) heap->st_count / (double) heap->st_size)); - hash = DUK_HSTRING_GET_HASH(h); - i = DUK__HASH_INITIAL(hash, size); - step = DUK__HASH_PROBE_STEP(hash); - for (;;) { -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t e16 = entries16[i]; -#else - duk_hstring *e = entries[i]; -#endif + DUK_ASSERT(heap != NULL); -#if defined(DUK_USE_HEAPPTR16) - if (e16 == null16) { -#else - if (!e) { -#endif - DUK_UNREACHABLE(); - break; - } -#if defined(DUK_USE_HEAPPTR16) - if (e16 == h16) { -#else - if (e == h) { -#endif - /* st_used remains the same, DELETED is counted as used */ - DUK_DDD(DUK_DDDPRINT("free matching hit: %ld", (long) i)); -#if defined(DUK_USE_HEAPPTR16) - entries16[i] = heap->heapptr_deleted16; -#else - entries[i] = DUK__DELETED_MARKER(heap); + /* Prevent any side effects on the string table and the caller provided + * str/blen arguments while interning is in progress. For example, if + * the caller provided str/blen from a dynamic buffer, a finalizer + * might resize or modify that dynamic buffer, invalidating the call + * arguments. + * + * While finalizers must be prevented, mark-and-sweep itself is fine. + * Recursive string table resize is prevented explicitly here. + */ + + heap->pf_prevent_count++; + DUK_ASSERT(heap->pf_prevent_count != 0); /* Wrap. */ + +#if defined(DUK_USE_STRTAB_TORTURE) && defined(DUK__STRTAB_RESIZE_CHECK) + duk__strtable_resize_torture(heap); #endif - break; - } - DUK_DDD(DUK_DDDPRINT("free matching miss: %ld", (long) i)); - i = (i + step) % size; + /* String table grow/shrink check. Because of chaining (and no + * accumulation issues as with hash probe chains and DELETED + * markers) there's never a mandatory need to resize right now. + * Check for the resize only periodically, based on st_count + * bit pattern. Because string table removal doesn't do a shrink + * check, we do that also here. + * + * Do the resize and possible grow/shrink before the new duk_hstring + * has been allocated. Otherwise we may trigger a GC when the result + * duk_hstring is not yet strongly referenced. + */ - /* looping should never happen */ - DUK_ASSERT(i != DUK__HASH_INITIAL(hash, size)); +#if defined(DUK__STRTAB_RESIZE_CHECK) + if (DUK_UNLIKELY((heap->st_count & DUK_USE_STRTAB_RESIZE_CHECK_MASK) == 0)) { + duk__strtable_resize_check(heap); } -} - -DUK_LOCAL duk_bool_t duk__resize_strtab_raw_probe(duk_heap *heap, duk_uint32_t new_size) { -#if defined(DUK_USE_DEBUG) - duk_uint32_t old_used = heap->st_used; -#endif - duk_uint32_t old_size = heap->st_size; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *old_entries = heap->strtable16; - duk_uint16_t *new_entries = NULL; -#else - duk_hstring **old_entries = heap->strtable; - duk_hstring **new_entries = NULL; #endif - duk_uint32_t new_used = 0; - duk_uint32_t i; -#if defined(DUK_USE_DEBUG) - DUK_UNREF(old_used); /* unused with some debug level combinations */ -#endif + /* External string check (low memory optimization). */ -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2) - DUK_DDD(DUK_DDDPRINT("attempt to resize stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load", - (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used, - (long) (((double) old_used) / ((double) old_size) * 100.0), - (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) duk__count_used_probe(heap), - (long) (((double) duk__count_used_probe(heap)) / ((double) new_size) * 100.0))); +#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) + extdata = (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen); +#else + extdata = (const duk_uint8_t *) NULL; #endif - DUK_ASSERT(new_size > (duk_uint32_t) duk__count_used_probe(heap)); /* required for rehash to succeed, equality not that useful */ - DUK_ASSERT(old_entries); - - /* - * The attempt to allocate may cause a GC. Such a GC must not attempt to resize - * the stringtable (though it can be swept); finalizer execution and object - * compaction must also be postponed to avoid the pressure to add strings to the - * string table. Call site must prevent these. + /* Allocate and initialize string, not yet linked. This may cause a + * GC which may cause other strings to be interned and inserted into + * the string table before we insert our string. Finalizer execution + * is disabled intentionally to avoid a finalizer from e.g. resizing + * a buffer used as a data area for 'str'. */ - DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE); - DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_FINALIZERS); - DUK_ASSERT(heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_OBJECT_COMPACTION); + res = duk__strtable_alloc_hstring(heap, str, blen, strhash, extdata); -#if defined(DUK_USE_HEAPPTR16) - new_entries = (duk_uint16_t *) DUK_ALLOC(heap, sizeof(duk_uint16_t) * new_size); -#else - new_entries = (duk_hstring **) DUK_ALLOC(heap, sizeof(duk_hstring *) * new_size); -#endif + /* Allow side effects again: GC must be avoided until duk_hstring + * result (if successful) has been INCREF'd. + */ + DUK_ASSERT(heap->pf_prevent_count > 0); + heap->pf_prevent_count--; - if (!new_entries) { - goto resize_error; - } + /* Alloc error handling. */ -#if defined(DUK_USE_EXPLICIT_NULL_INIT) - for (i = 0; i < new_size; i++) { -#if defined(DUK_USE_HEAPPTR16) - new_entries[i] = heap->heapptr_null16; -#else - new_entries[i] = NULL; + if (DUK_UNLIKELY(res == NULL)) { +#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) + if (extdata != NULL) { + DUK_USE_EXTSTR_FREE(heap->heap_udata, (const void *) extdata); + } #endif + return NULL; } -#else -#if defined(DUK_USE_HEAPPTR16) - /* Relies on NULL encoding to zero. */ - DUK_MEMZERO(new_entries, sizeof(duk_uint16_t) * new_size); -#else - DUK_MEMZERO(new_entries, sizeof(duk_hstring *) * new_size); -#endif -#endif - /* Because new_size > duk__count_used_probe(heap), guaranteed to work */ - for (i = 0; i < old_size; i++) { - duk_hstring *e; + /* Insert into string table. */ -#if defined(DUK_USE_HEAPPTR16) - e = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, old_entries[i]); +#if defined(DUK_USE_STRTAB_PTRCOMP) + slot = heap->strtable16 + (strhash & heap->st_mask); #else - e = old_entries[i]; + slot = heap->strtable + (strhash & heap->st_mask); #endif - if (e == NULL || e == DUK__DELETED_MARKER(heap)) { - continue; - } - /* checking for DUK__DELETED_MARKER is not necessary here, but helper does it now */ - duk__insert_hstring_probe(heap, new_entries, new_size, &new_used, e); - } + DUK_ASSERT(res->hdr.h_next == NULL); /* This is the case now, but unnecessary zeroing/NULLing. */ + res->hdr.h_next = DUK__HEAPPTR_DEC16(heap, *slot); + *slot = DUK__HEAPPTR_ENC16(heap, res); -#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 1) - DUK_DD(DUK_DDPRINT("resized stringtable: %ld entries, %ld bytes, %ld used, %ld%% load -> %ld entries, %ld bytes, %ld used, %ld%% load", - (long) old_size, (long) (sizeof(duk_hstring *) * old_size), (long) old_used, - (long) (((double) old_used) / ((double) old_size) * 100.0), - (long) new_size, (long) (sizeof(duk_hstring *) * new_size), (long) new_used, - (long) (((double) new_used) / ((double) new_size) * 100.0))); -#endif + /* Update string count only for successful inserts. */ -#if defined(DUK_USE_HEAPPTR16) - DUK_FREE(heap, heap->strtable16); - heap->strtable16 = new_entries; -#else - DUK_FREE(heap, heap->strtable); - heap->strtable = new_entries; +#if defined(DUK__STRTAB_RESIZE_CHECK) + heap->st_count++; #endif - heap->st_size = new_size; - heap->st_used = new_used; /* may be less, since DELETED entries are NULLed by rehash */ - - return 0; /* OK */ - resize_error: - DUK_FREE(heap, new_entries); - return 1; /* FAIL */ -} - -DUK_LOCAL duk_bool_t duk__resize_strtab_probe(duk_heap *heap) { - duk_uint32_t new_size; - duk_bool_t ret; - - new_size = (duk_uint32_t) duk__count_used_probe(heap); - if (new_size >= 0x80000000UL) { - new_size = DUK_STRTAB_HIGHEST_32BIT_PRIME; - } else { - new_size = duk_util_get_hash_prime(DUK_STRTAB_GROW_ST_SIZE(new_size)); - new_size = duk_util_get_hash_prime(new_size); - } - DUK_ASSERT(new_size > 0); - - /* rehash even if old and new sizes are the same to get rid of - * DELETED entries. - */ - - ret = duk__resize_strtab_raw_probe(heap, new_size); + /* The duk_hstring is in the string table but is not yet strongly + * reachable. Calling code MUST NOT make any allocations or other + * side effects before the duk_hstring has been INCREF'd and made + * reachable. + */ - return ret; + return res; } -DUK_LOCAL duk_bool_t duk__recheck_strtab_size_probe(duk_heap *heap, duk_uint32_t new_used) { - duk_uint32_t new_free; - duk_uint32_t tmp1; - duk_uint32_t tmp2; - - DUK_ASSERT(new_used <= heap->st_size); /* grow by at most one */ - new_free = heap->st_size - new_used; /* unsigned intentionally */ - - /* new_free / size <= 1 / DIV <=> new_free <= size / DIV */ - /* new_used / size <= 1 / DIV <=> new_used <= size / DIV */ - - tmp1 = heap->st_size / DUK_STRTAB_MIN_FREE_DIVISOR; - tmp2 = heap->st_size / DUK_STRTAB_MIN_USED_DIVISOR; - - if (new_free <= tmp1 || new_used <= tmp2) { - /* load factor too low or high, count actually used entries and resize */ - return duk__resize_strtab_probe(heap); - } else { - return 0; /* OK */ - } -} +/* + * Intern a string from str/blen, returning either an existing duk_hstring + * or adding a new one into the string table. The input string does -not- + * need to be NUL terminated. + * + * The input 'str' argument may point to a Duktape managed data area such as + * the data area of a dynamic buffer. It's crucial to avoid any side effects + * that might affect the data area (e.g. resize the dynamic buffer, or write + * to the buffer) before the string is fully interned. + */ -#if defined(DUK_USE_DEBUG) -DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap) { - duk_uint32_t i; - duk_hstring *h; +#if defined(DUK_USE_ROM_STRINGS) +DUK_LOCAL duk_hstring *duk__strtab_romstring_lookup(duk_heap *heap, const duk_uint8_t *str, duk_size_t blen, duk_uint32_t strhash) { + duk_size_t lookup_hash; + duk_hstring *curr; DUK_ASSERT(heap != NULL); -#if defined(DUK_USE_HEAPPTR16) - DUK_ASSERT(heap->strtable16 != NULL); -#else - DUK_ASSERT(heap->strtable != NULL); -#endif - DUK_UNREF(h); + DUK_UNREF(heap); - for (i = 0; i < heap->st_size; i++) { -#if defined(DUK_USE_HEAPPTR16) - h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->strtable16[i]); -#else - h = heap->strtable[i]; -#endif + lookup_hash = (blen << 4); + if (blen > 0) { + lookup_hash += str[0]; + } + lookup_hash &= 0xff; - DUK_DD(DUK_DDPRINT("[%03d] -> %p", (int) i, (void *) h)); + curr = DUK_LOSE_CONST(duk_rom_strings_lookup[lookup_hash]); + while (curr != NULL) { + if (strhash == DUK_HSTRING_GET_HASH(curr) && + blen == DUK_HSTRING_GET_BYTELEN(curr) && + DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(curr), blen) == 0) { + DUK_DDD(DUK_DDDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", + curr, (unsigned long) strhash, (unsigned long) DUK_HSTRING_GET_HASH(curr))); + return curr; + } + curr = curr->hdr.h_next; } + + return NULL; } -#endif /* DUK_USE_DEBUG */ +#endif /* DUK_USE_ROM_STRINGS */ -#endif /* DUK_USE_STRTAB_PROBE */ +DUK_INTERNAL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { + duk_uint32_t strhash; + duk_hstring *h; -/* - * Raw intern and lookup - */ + DUK_DDD(DUK_DDDPRINT("intern check: heap=%p, str=%p, blen=%lu", (void *) heap, (const void *) str, (unsigned long) blen)); -DUK_LOCAL duk_hstring *duk__do_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t strhash) { - duk_hstring *res; - const duk_uint8_t *extdata; - duk_small_uint_t prev_mark_and_sweep_base_flags; + /* Preliminaries. */ - /* Prevent any side effects on the string table and the caller provided - * str/blen arguments while interning is in progress. For example, if - * the caller provided str/blen from a dynamic buffer, a finalizer might - * resize that dynamic buffer, invalidating the call arguments. - */ - DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); - prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; - DUK__PREVENT_MS_SIDE_EFFECTS(heap); + DUK_ASSERT(heap != NULL); + DUK_ASSERT(blen == 0 || str != NULL); + DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN); /* Caller is responsible for ensuring this. */ + strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen); -#if defined(DUK_USE_STRTAB_PROBE) - if (duk__recheck_strtab_size_probe(heap, heap->st_used + 1)) { - goto failed; - } -#endif + /* String table lookup. */ -#if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_INTERN_CHECK) - extdata = (const duk_uint8_t *) DUK_USE_EXTSTR_INTERN_CHECK(heap->heap_udata, (void *) DUK_LOSE_CONST(str), (duk_size_t) blen); + DUK_ASSERT(DUK__GET_STRTABLE(heap) != NULL); + DUK_ASSERT(heap->st_size > 0); + DUK_ASSERT(heap->st_size == heap->st_mask + 1); +#if defined(DUK_USE_STRTAB_PTRCOMP) + h = DUK__HEAPPTR_DEC16(heap, heap->strtable16[strhash & heap->st_mask]); #else - extdata = (const duk_uint8_t *) NULL; + h = heap->strtable[strhash & heap->st_mask]; #endif - res = duk__alloc_init_hstring(heap, str, blen, strhash, extdata); - if (!res) { - goto failed; + while (h != NULL) { + if (DUK_HSTRING_GET_HASH(h) == strhash && + 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. */ + return h; + } + h = h->hdr.h_next; } -#if defined(DUK_USE_STRTAB_CHAIN) - if (duk__insert_hstring_chain(heap, res)) { - /* failed */ - DUK_FREE(heap, res); - goto failed; + /* ROM table lookup. Because this lookup is slower, do it only after + * RAM lookup. This works because no ROM string is ever interned into + * the RAM string table. + */ + +#if defined(DUK_USE_ROM_STRINGS) + h = duk__strtab_romstring_lookup(heap, str, blen, strhash); + if (h != NULL) { + return h; } -#elif defined(DUK_USE_STRTAB_PROBE) - /* guaranteed to succeed */ - duk__insert_hstring_probe(heap, -#if defined(DUK_USE_HEAPPTR16) - heap->strtable16, -#else - heap->strtable, -#endif - heap->st_size, - &heap->st_used, - res); -#else -#error internal error, invalid strtab options #endif - /* Note: hstring is in heap but has refcount zero and is not strongly reachable. - * Caller should increase refcount and make the hstring reachable before any - * operations which require allocation (and possible gc). - */ - - done: - heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; - return res; + /* Not found in string table; insert. */ - failed: - res = NULL; - goto done; + h = duk__strtable_do_intern(heap, str, blen, strhash); + return h; /* may be NULL */ } -DUK_LOCAL duk_hstring *duk__do_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen, duk_uint32_t *out_strhash) { - duk_hstring *res; +/* + * Intern a string from u32. + */ - DUK_ASSERT(out_strhash); +/* XXX: Could arrange some special handling because we know that the result + * will have an arridx flag and an ASCII flag, won't need a clen check, etc. + */ - *out_strhash = duk_heap_hashstring(heap, str, (duk_size_t) blen); +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; -#if defined(DUK_USE_ROM_STRINGS) - { - duk_small_uint_t i; - /* XXX: This is VERY inefficient now, and should be e.g. a - * binary search or perfect hash, to be fixed. - */ - for (i = 0; i < (duk_small_uint_t) (sizeof(duk_rom_strings) / sizeof(duk_hstring *)); i++) { - duk_hstring *romstr; - romstr = (duk_hstring *) DUK_LOSE_CONST(duk_rom_strings[i]); - if (blen == DUK_HSTRING_GET_BYTELEN(romstr) && - DUK_MEMCMP((const void *) str, (const void *) DUK_HSTRING_GET_DATA(romstr), blen) == 0) { - DUK_DD(DUK_DDPRINT("intern check: rom string: %!O, computed hash 0x%08lx, rom hash 0x%08lx", - romstr, (unsigned long) *out_strhash, (unsigned long) DUK_HSTRING_GET_HASH(romstr))); - DUK_ASSERT(*out_strhash == DUK_HSTRING_GET_HASH(romstr)); - *out_strhash = DUK_HSTRING_GET_HASH(romstr); - return romstr; - } - } - } -#endif /* DUK_USE_ROM_STRINGS */ + DUK_ASSERT(heap != NULL); -#if defined(DUK_USE_STRTAB_CHAIN) - res = duk__find_matching_string_chain(heap, str, blen, *out_strhash); -#elif defined(DUK_USE_STRTAB_PROBE) - res = duk__find_matching_string_probe(heap, -#if defined(DUK_USE_HEAPPTR16) - heap->strtable16, -#else - heap->strtable, -#endif - heap->st_size, - str, - blen, - *out_strhash); -#else -#error internal error, invalid strtab options -#endif + /* This is smaller and faster than a %lu sprintf. */ + p = buf + sizeof(buf); + do { + p--; + *p = duk_lc_digits[val % 10]; + val = val / 10; + } while (val != 0); /* For val == 0, emit exactly one '0'. */ + DUK_ASSERT(p >= buf); - return res; + return duk_heap_strtable_intern(heap, (const duk_uint8_t *) p, (duk_uint32_t) ((buf + sizeof(buf)) - p)); } /* - * Exposed calls + * Checked convenience variants. + * + * XXX: Because the main use case is for the checked variants, make them the + * main functionality and provide a safe variant separately (it is only needed + * during heap init). The problem with that is that longjmp state and error + * creation must already be possible to throw. */ -#if 0 /*unused*/ -DUK_INTERNAL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { - duk_uint32_t strhash; /* dummy */ - return duk__do_lookup(heap, str, blen, &strhash); -} -#endif - -DUK_INTERNAL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen) { +DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) { duk_hstring *res; - duk_uint32_t strhash; - /* caller is responsible for ensuring this */ - DUK_ASSERT(blen <= DUK_HSTRING_MAX_BYTELEN); - - res = duk__do_lookup(heap, str, blen, &strhash); - if (res) { - return res; - } - - res = duk__do_intern(heap, str, blen, strhash); - return res; /* may be NULL */ -} + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(blen == 0 || str != NULL); -DUK_INTERNAL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen) { - duk_hstring *res = duk_heap_string_intern(thr->heap, str, blen); - if (!res) { + res = duk_heap_strtable_intern(thr->heap, str, blen); + if (DUK_UNLIKELY(res == NULL)) { DUK_ERROR_ALLOC_FAILED(thr); } return res; } -#if 0 /*unused*/ -DUK_INTERNAL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val) { - char buf[DUK_STRTAB_U32_MAX_STRLEN+1]; - DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val); - buf[sizeof(buf) - 1] = (char) 0; - DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */ - return duk_heap_string_lookup(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf)); -} -#endif +DUK_INTERNAL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) { + duk_hstring *res; -DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val) { - char buf[DUK_STRTAB_U32_MAX_STRLEN+1]; - DUK_SNPRINTF(buf, sizeof(buf), "%lu", (unsigned long) val); - buf[sizeof(buf) - 1] = (char) 0; - DUK_ASSERT(DUK_STRLEN(buf) <= DUK_UINT32_MAX); /* formatted result limited */ - return duk_heap_string_intern(heap, (const duk_uint8_t *) buf, (duk_uint32_t) DUK_STRLEN(buf)); -} + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); -DUK_INTERNAL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val) { - duk_hstring *res = duk_heap_string_intern_u32(thr->heap, val); - if (!res) { + res = duk_heap_strtable_intern_u32(thr->heap, val); + if (DUK_UNLIKELY(res == NULL)) { DUK_ERROR_ALLOC_FAILED(thr); } return res; } -/* find and remove string from stringtable; caller must free the string itself */ -#if defined(DUK_USE_REFERENCE_COUNTING) -DUK_INTERNAL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h) { - DUK_DDD(DUK_DDDPRINT("remove string from stringtable: %!O", (duk_heaphdr *) h)); +/* + * Remove (unlink) a string from the string table. + * + * Just unlinks the duk_hstring, leaving link pointers as garbage. + * Caller must free the string itself. + */ -#if defined(DUK_USE_STRTAB_CHAIN) - duk__remove_matching_hstring_chain(heap, h); -#elif defined(DUK_USE_STRTAB_PROBE) - duk__remove_matching_hstring_probe(heap, -#if defined(DUK_USE_HEAPPTR16) - heap->strtable16, -#else - heap->strtable, -#endif - heap->st_size, - h); +#if defined(DUK_USE_REFERENCE_COUNTING) +/* Unlink without a 'prev' pointer. */ +DUK_INTERNAL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *slot; #else -#error internal error, invalid strtab options -#endif -} + duk_hstring **slot; #endif + duk_hstring *other; + duk_hstring *prev; -#if defined(DUK_USE_MS_STRINGTABLE_RESIZE) -DUK_INTERNAL void duk_heap_force_strtab_resize(duk_heap *heap) { - duk_small_uint_t prev_mark_and_sweep_base_flags; - /* Force a resize so that DELETED entries are eliminated. - * Another option would be duk__recheck_strtab_size_probe(); - * but since that happens on every intern anyway, this whole - * check can now be disabled. - */ + DUK_DDD(DUK_DDDPRINT("remove: heap=%p, h=%p, blen=%lu, strhash=%lx", + (void *) heap, (void *) h, + (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0), + (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0))); - DUK_ASSERT((heap->mark_and_sweep_base_flags & DUK_MS_FLAG_NO_STRINGTABLE_RESIZE) == 0); - prev_mark_and_sweep_base_flags = heap->mark_and_sweep_base_flags; - DUK__PREVENT_MS_SIDE_EFFECTS(heap); + DUK_ASSERT(heap != NULL); + DUK_ASSERT(h != NULL); -#if defined(DUK_USE_STRTAB_CHAIN) - DUK_UNREF(heap); -#elif defined(DUK_USE_STRTAB_PROBE) - (void) duk__resize_strtab_probe(heap); +#if defined(DUK__STRTAB_RESIZE_CHECK) + DUK_ASSERT(heap->st_count > 0); + heap->st_count--; #endif - heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; -} +#if defined(DUK_USE_STRTAB_PTRCOMP) + slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); +#else + slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); #endif + other = DUK__HEAPPTR_DEC16(heap, *slot); + DUK_ASSERT(other != NULL); /* At least argument string is in the chain. */ -#if defined(DUK_USE_STRTAB_CHAIN) -DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) { - /* Free strings in the stringtable and any allocations needed - * by the stringtable itself. + prev = NULL; + while (other != h) { + prev = other; + other = other->hdr.h_next; + DUK_ASSERT(other != NULL); /* We'll eventually find 'h'. */ + } + if (prev != NULL) { + /* Middle of list. */ + prev->hdr.h_next = h->hdr.h_next; + } else { + /* Head of list. */ + *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next); + } + + /* There's no resize check on a string free. The next string + * intern will do one. */ - duk_uint_fast32_t i, j; - duk_strtab_entry *e; -#if defined(DUK_USE_HEAPPTR16) - duk_uint16_t *lst; - duk_uint16_t null16 = heap->heapptr_null16; -#else - duk_hstring **lst; -#endif - duk_hstring *h; +} +#endif /* DUK_USE_REFERENCE_COUNTING */ - for (i = 0; i < DUK_STRTAB_CHAIN_SIZE; i++) { - e = heap->strtable + i; - if (e->listlen > 0) { -#if defined(DUK_USE_HEAPPTR16) - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); +/* Unlink with a 'prev' pointer. */ +DUK_INTERNAL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *slot; #else - lst = e->u.strlist; + duk_hstring **slot; #endif - DUK_ASSERT(lst != NULL); - for (j = 0; j < e->listlen; j++) { -#if defined(DUK_USE_HEAPPTR16) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, lst[j]); - lst[j] = null16; -#else - h = lst[j]; - lst[j] = NULL; + DUK_DDD(DUK_DDDPRINT("remove: heap=%p, prev=%p, h=%p, blen=%lu, strhash=%lx", + (void *) heap, (void *) prev, (void *) h, + (unsigned long) (h != NULL ? DUK_HSTRING_GET_BYTELEN(h) : 0), + (unsigned long) (h != NULL ? DUK_HSTRING_GET_HASH(h) : 0))); + + DUK_ASSERT(heap != NULL); + DUK_ASSERT(h != NULL); + DUK_ASSERT(prev == NULL || prev->hdr.h_next == h); + +#if defined(DUK__STRTAB_RESIZE_CHECK) + DUK_ASSERT(heap->st_count > 0); + heap->st_count--; #endif - /* strings may have inner refs (extdata) in some cases */ - if (h != NULL) { - duk_free_hstring(heap, h); - } - } -#if defined(DUK_USE_HEAPPTR16) - e->u.strlist16 = null16; + + if (prev != NULL) { + /* Middle of list. */ + prev->hdr.h_next = h->hdr.h_next; + } else { + /* Head of list. */ +#if defined(DUK_USE_STRTAB_PTRCOMP) + slot = heap->strtable16 + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); #else - e->u.strlist = NULL; + slot = heap->strtable + (DUK_HSTRING_GET_HASH(h) & heap->st_mask); #endif - DUK_FREE(heap, lst); - } else { -#if defined(DUK_USE_HEAPPTR16) - h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.str16); - e->u.str16 = null16; + DUK_ASSERT(DUK__HEAPPTR_DEC16(heap, *slot) == h); + *slot = DUK__HEAPPTR_ENC16(heap, h->hdr.h_next); + } +} + +/* + * Force string table resize check in mark-and-sweep. + */ + +DUK_INTERNAL void duk_heap_strtable_force_resize(duk_heap *heap) { + /* Does only one grow/shrink step if needed. The heap->st_resizing + * flag protects against recursive resizing. + */ + + DUK_ASSERT(heap != NULL); + DUK_UNREF(heap); + +#if defined(DUK__STRTAB_RESIZE_CHECK) +#if defined(DUK_USE_STRTAB_PTRCOMP) + if (heap->strtable16 != NULL) { #else - h = e->u.str; - e->u.str = NULL; + if (heap->strtable != NULL) { #endif - if (h != NULL) { - duk_free_hstring(heap, h); - } - } - e->listlen = 0; + duk__strtable_resize_check(heap); } +#endif } -#endif /* DUK_USE_STRTAB_CHAIN */ -#if defined(DUK_USE_STRTAB_PROBE) -DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) { - duk_uint_fast32_t i; - duk_hstring *h; +/* + * Free strings in the string table and the string table itself. + */ -#if defined(DUK_USE_HEAPPTR16) - if (heap->strtable16) { +DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap) { +#if defined(DUK_USE_STRTAB_PTRCOMP) + duk_uint16_t *strtable; + duk_uint16_t *st; #else - if (heap->strtable) { + duk_hstring **strtable; + duk_hstring **st; #endif - for (i = 0; i < (duk_uint_fast32_t) heap->st_size; i++) { -#if defined(DUK_USE_HEAPPTR16) - h = (duk_hstring *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); -#else - h = heap->strtable[i]; + duk_hstring *h; + + DUK_ASSERT(heap != NULL); + +#if defined(DUK_USE_ASSERTIONS) + duk__strtable_assert_checks(heap); #endif - if (h == NULL || h == DUK_STRTAB_DELETED_MARKER(heap)) { - continue; - } - DUK_ASSERT(h != NULL); - /* strings may have inner refs (extdata) in some cases */ + /* Strtable can be NULL if heap init fails. However, in that case + * heap->st_size is 0, so strtable == strtable_end and we skip the + * loop without a special check. + */ + strtable = DUK__GET_STRTABLE(heap); + st = strtable + heap->st_size; + DUK_ASSERT(strtable != NULL || heap->st_size == 0); + + while (strtable != st) { + --st; + h = DUK__HEAPPTR_DEC16(heap, *st); + while (h) { + duk_hstring *h_next; + h_next = h->hdr.h_next; + + /* Strings may have inner refs (extdata) in some cases. */ duk_free_hstring(heap, h); -#if 0 /* not strictly necessary */ - heap->strtable[i] = NULL; -#endif + + h = h_next; } -#if defined(DUK_USE_HEAPPTR16) - DUK_FREE(heap, heap->strtable16); -#else - DUK_FREE(heap, heap->strtable); -#endif -#if 0 /* not strictly necessary */ - heap->strtable = NULL; -#endif } + + DUK_FREE(heap, strtable); } -#endif /* DUK_USE_STRTAB_PROBE */ /* automatic undefs */ -#undef DUK__DELETED_MARKER -#undef DUK__HASH_INITIAL -#undef DUK__HASH_PROBE_STEP -#undef DUK__PREVENT_MS_SIDE_EFFECTS +#undef DUK__GET_STRTABLE +#undef DUK__HEAPPTR_DEC16 +#undef DUK__HEAPPTR_ENC16 +#undef DUK__STRTAB_U32_MAX_STRLEN /* * Hobject allocation. * @@ -48733,19 +50271,29 @@ DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap) { * in "heap allocated" list and has a refcount of zero, so caller must careful. */ +/* XXX: In most cases there's no need for plain allocation without pushing + * to the value stack. Maybe rework contract? + */ + /* #include duk_internal.h -> already included */ -DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_hobject *obj, duk_uint_t hobject_flags) { +/* + * Helpers. + */ + +DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_uint_t hobject_flags, duk_hobject *obj) { + DUK_ASSERT(obj != NULL); + /* Zeroed by caller. */ + + obj->hdr.h_flags = hobject_flags | DUK_HTYPE_OBJECT; + DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(&obj->hdr) == DUK_HTYPE_OBJECT); /* Assume zero shift. */ + #if defined(DUK_USE_EXPLICIT_NULL_INIT) + DUK_HOBJECT_SET_PROTOTYPE(heap, obj, NULL); DUK_HOBJECT_SET_PROPS(heap, obj, NULL); #endif - - /* XXX: macro? sets both heaphdr and object flags */ - obj->hdr.h_flags = hobject_flags; - DUK_HEAPHDR_SET_TYPE(&obj->hdr, DUK_HTYPE_OBJECT); /* also goes into flags */ - #if defined(DUK_USE_HEAPPTR16) - /* Zero encoded pointer is required to match NULL */ + /* Zero encoded pointer is required to match NULL. */ DUK_HEAPHDR_SET_NEXT(heap, &obj->hdr, NULL); #if defined(DUK_USE_DOUBLE_LINKED_HEAP) DUK_HEAPHDR_SET_PREV(heap, &obj->hdr, NULL); @@ -48754,14 +50302,22 @@ DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_hobject *obj, duk_uint DUK_ASSERT_HEAPHDR_LINKS(heap, &obj->hdr); DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &obj->hdr); - /* - * obj->props is intentionally left as NULL, and duk_hobject_props.c must deal - * with this properly. This is intentional: empty objects consume a minimum - * amount of memory. Further, an initial allocation might fail and cause - * 'obj' to "leak" (require a mark-and-sweep) since it is not reachable yet. + /* obj->props is intentionally left as NULL, and duk_hobject_props.c must deal + * with this properly. This is intentional: empty objects consume a minimum + * amount of memory. Further, an initial allocation might fail and cause + * 'obj' to "leak" (require a mark-and-sweep) since it is not reachable yet. */ } +DUK_LOCAL void *duk__hobject_alloc_init(duk_hthread *thr, duk_uint_t hobject_flags, duk_size_t size) { + void *res; + + res = (void *) DUK_ALLOC_CHECKED_ZEROED(thr, size); + DUK_ASSERT(res != NULL); + duk__init_object_parts(thr->heap, hobject_flags, (duk_hobject *) res); + return res; +} + /* * Allocate an duk_hobject. * @@ -48773,7 +50329,7 @@ DUK_LOCAL void duk__init_object_parts(duk_heap *heap, duk_hobject *obj, duk_uint * count before invoking any operation that might require memory allocation. */ -DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobject_flags) { +DUK_INTERNAL duk_hobject *duk_hobject_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) { duk_hobject *res; DUK_ASSERT(heap != NULL); @@ -48781,30 +50337,30 @@ DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_heap *heap, duk_uint_t hobject_f /* 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_THREAD) == 0); - res = (duk_hobject *) DUK_ALLOC(heap, sizeof(duk_hobject)); - if (!res) { + res = (duk_hobject *) DUK_ALLOC_ZEROED(heap, sizeof(duk_hobject)); + if (DUK_UNLIKELY(res == NULL)) { return NULL; } - DUK_MEMZERO(res, sizeof(duk_hobject)); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res)); - duk__init_object_parts(heap, res, hobject_flags); + duk__init_object_parts(heap, hobject_flags, res); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(res)); return res; } -DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) { - duk_hcompfunc *res; +DUK_INTERNAL duk_hobject *duk_hobject_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_hobject *res; - res = (duk_hcompfunc *) DUK_ALLOC(heap, sizeof(duk_hcompfunc)); - if (!res) { - return NULL; - } - DUK_MEMZERO(res, sizeof(duk_hcompfunc)); + res = (duk_hobject *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobject)); + return res; +} - duk__init_object_parts(heap, &res->obj, hobject_flags); +DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_hcompfunc *res; + res = (duk_hcompfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hcompfunc)); #if defined(DUK_USE_EXPLICIT_NULL_INIT) #if defined(DUK_USE_HEAPPTR16) /* NULL pointer is required to encode to zero, so memset is enough. */ @@ -48820,17 +50376,10 @@ DUK_INTERNAL duk_hcompfunc *duk_hcompfunc_alloc(duk_heap *heap, duk_uint_t hobje return res; } -DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_heap *heap, duk_uint_t hobject_flags) { +DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { duk_hnatfunc *res; - res = (duk_hnatfunc *) DUK_ALLOC(heap, sizeof(duk_hnatfunc)); - if (!res) { - return NULL; - } - DUK_MEMZERO(res, sizeof(duk_hnatfunc)); - - duk__init_object_parts(heap, &res->obj, hobject_flags); - + res = (duk_hnatfunc *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hnatfunc)); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->func = NULL; #endif @@ -48839,17 +50388,10 @@ DUK_INTERNAL duk_hnatfunc *duk_hnatfunc_alloc(duk_heap *heap, duk_uint_t hobject } #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) -DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_flags) { +DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { duk_hbufobj *res; - res = (duk_hbufobj *) DUK_ALLOC(heap, sizeof(duk_hbufobj)); - if (!res) { - return NULL; - } - DUK_MEMZERO(res, sizeof(duk_hbufobj)); - - duk__init_object_parts(heap, &res->obj, hobject_flags); - + res = (duk_hbufobj *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hbufobj)); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->buf = NULL; res->buf_prop = NULL; @@ -48860,24 +50402,22 @@ DUK_INTERNAL duk_hbufobj *duk_hbufobj_alloc(duk_heap *heap, duk_uint_t hobject_f } #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */ -/* - * Allocate a new thread. +/* Allocate a new thread. * - * Leaves the built-ins array uninitialized. The caller must either - * initialize a new global context or share existing built-ins from - * another thread. + * Leaves the built-ins array uninitialized. The caller must either + * initialize a new global context or share existing built-ins from + * another thread. */ - -DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_flags) { +DUK_INTERNAL duk_hthread *duk_hthread_alloc_unchecked(duk_heap *heap, duk_uint_t hobject_flags) { duk_hthread *res; res = (duk_hthread *) DUK_ALLOC(heap, sizeof(duk_hthread)); - if (!res) { + if (DUK_UNLIKELY(res == NULL)) { return NULL; } DUK_MEMZERO(res, sizeof(duk_hthread)); - duk__init_object_parts(heap, &res->obj, hobject_flags); + duk__init_object_parts(heap, hobject_flags, &res->obj); #if defined(DUK_USE_EXPLICIT_NULL_INIT) res->ptr_curr_pc = NULL; @@ -48887,6 +50427,7 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_f 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, @@ -48896,7 +50437,7 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_f res->strs = NULL; #endif { - int i; + duk_small_uint_t i; for (i = 0; i < DUK_NUM_BUILTINS; i++) { res->builtins[i] = NULL; } @@ -48913,32 +50454,51 @@ DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_heap *heap, duk_uint_t hobject_f return res; } -#if 0 /* unused now */ -DUK_INTERNAL duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, duk_uint_t hobject_flags) { - duk_hobject *res = duk_hobject_alloc(thr->heap, hobject_flags); - if (!res) { +DUK_INTERNAL duk_hthread *duk_hthread_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_hthread *res; + + res = duk_hthread_alloc_unchecked(thr->heap, hobject_flags); + if (res == NULL) { DUK_ERROR_ALLOC_FAILED(thr); } return res; } + +DUK_INTERNAL duk_harray *duk_harray_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_harray *res; + + res = (duk_harray *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_harray)); + + DUK_ASSERT(res->length == 0); + + return res; +} + +DUK_INTERNAL duk_hdecenv *duk_hdecenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_hdecenv *res; + + res = (duk_hdecenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hdecenv)); +#if defined(DUK_USE_EXPLICIT_NULL_INIT) + res->thread = NULL; + res->varmap = NULL; #endif -/* - * Allocate a new array. - */ + DUK_ASSERT(res->thread == NULL); + DUK_ASSERT(res->varmap == NULL); + DUK_ASSERT(res->regbase == 0); -DUK_INTERNAL duk_harray *duk_harray_alloc(duk_heap *heap, duk_uint_t hobject_flags) { - duk_harray *res; + return res; +} - res = (duk_harray *) DUK_ALLOC(heap, sizeof(duk_harray)); - if (!res) { - return NULL; - } - DUK_MEMZERO(res, sizeof(duk_harray)); +DUK_INTERNAL duk_hobjenv *duk_hobjenv_alloc(duk_hthread *thr, duk_uint_t hobject_flags) { + duk_hobjenv *res; - duk__init_object_parts(heap, &res->obj, hobject_flags); + res = (duk_hobjenv *) duk__hobject_alloc_init(thr, hobject_flags, sizeof(duk_hobjenv)); +#if defined(DUK_USE_EXPLICIT_NULL_INIT) + res->target = NULL; +#endif - DUK_ASSERT(res->length == 0); + DUK_ASSERT(res->target == NULL); return res; } @@ -49186,6 +50746,9 @@ DUK_LOCAL void duk__sort_enum_keys_es6(duk_hthread *thr, duk_hobject *h_obj, duk */ DUK_LOCAL void duk__add_enum_key(duk_context *ctx, 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); @@ -49386,7 +50949,7 @@ 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_string_intern_u32_checked(thr, i); + k = duk_heap_strtable_intern_u32_checked(thr, i); DUK_ASSERT(k); duk__add_enum_key(ctx, k); @@ -49420,7 +50983,7 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint if (DUK_TVAL_IS_UNUSED(tv)) { continue; } - k = duk_heap_string_intern_u32_checked(thr, i); /* Fragile reachability. */ + k = duk_heap_strtable_intern_u32_checked(thr, i); /* Fragile reachability. */ DUK_ASSERT(k); duk__add_enum_key(ctx, k); @@ -49450,7 +51013,7 @@ DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint !DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(thr->heap, curr, i)) { continue; } - if (DUK_HSTRING_HAS_SYMBOL(k)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(k))) { if (!(enum_flags & DUK_ENUM_INCLUDE_HIDDEN) && DUK_HSTRING_HAS_HIDDEN(k)) { continue; @@ -49715,119 +51278,6 @@ DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_sma /* automatic undefs */ #undef DUK__ENUM_START_INDEX -/* - * Run an duk_hobject finalizer. Used for both reference counting - * and mark-and-sweep algorithms. Must never throw an error. - * - * There is no return value. Any return value or error thrown by - * the finalizer is ignored (although errors are debug logged). - * - * Notes: - * - * - The thread used for calling the finalizer is the same as the - * 'thr' argument. This may need to change later. - * - * - The finalizer thread 'top' assertions are there because it is - * critical that strict stack policy is observed (i.e. no cruft - * left on the finalizer stack). - */ - -/* #include duk_internal.h -> already included */ - -#if defined(DUK_USE_FINALIZER_SUPPORT) -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_UNREF(udata); - - DUK_DDD(DUK_DDDPRINT("protected finalization helper running")); - - /* [... obj] */ - - /* XXX: Finalizer lookup should traverse the prototype chain (to allow - * inherited finalizers) but should not invoke accessors or proxy object - * behavior. At the moment this lookup will invoke proxy behavior, so - * 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] */ - if (!duk_is_callable(ctx, -1)) { - DUK_DDD(DUK_DDDPRINT("-> no finalizer or finalizer not callable")); - return 0; - } - duk_dup_m2(ctx); - duk_push_boolean(ctx, DUK_HEAP_HAS_FINALIZER_NORESCUE(thr->heap)); - DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling finalizer")); - duk_call(ctx, 2); /* [ ... obj finalizer obj heapDestruct ] -> [ ... obj retval ] */ - DUK_DDD(DUK_DDDPRINT("finalizer finished successfully")); - return 0; - - /* Note: we rely on duk_safe_call() to fix up the stack for the caller, - * so we don't need to pop stuff here. There is no return value; - * caller determines rescued status based on object refcount. - */ -} - -DUK_INTERNAL void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { - duk_context *ctx = (duk_context *) thr; - duk_ret_t rc; -#if defined(DUK_USE_ASSERTIONS) - duk_idx_t entry_top; -#endif - - DUK_DDD(DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj)); - - DUK_ASSERT(thr != NULL); - DUK_ASSERT(ctx != NULL); - DUK_ASSERT(obj != NULL); - DUK_ASSERT_VALSTACK_SPACE(thr, 1); - -#if defined(DUK_USE_ASSERTIONS) - entry_top = duk_get_top(ctx); -#endif - /* - * Get and call the finalizer. All of this must be wrapped - * in a protected call, because even getting the finalizer - * may trigger an error (getter may throw one, for instance). - */ - - DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)); - if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) obj)) { - DUK_D(DUK_DPRINT("object already finalized, avoid running finalizer twice: %!O", obj)); - return; - } - DUK_HEAPHDR_SET_FINALIZED((duk_heaphdr *) obj); /* ensure never re-entered until rescue cycle complete */ - if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(obj)) { - /* This shouldn't happen; call sites should avoid looking up - * _Finalizer "through" a Proxy, but ignore if we come here - * with a Proxy to avoid finalizer re-entry. - */ - DUK_D(DUK_DPRINT("object is a proxy, skip finalizer call")); - return; - } - - /* XXX: use a NULL error handler for the finalizer call? */ - - DUK_DDD(DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper")); - 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 */ - - if (rc != DUK_EXEC_SUCCESS) { - /* Note: we ask for one return value from duk_safe_call to get this - * error debugging here. - */ - DUK_D(DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", - (void *) obj, (duk_tval *) duk_get_tval(ctx, -1))); - } - duk_pop_2(ctx); /* -> [...] */ - - DUK_ASSERT_TOP(ctx, entry_top); -} -#endif /* DUK_USE_FINALIZER_SUPPORT */ /* * Misc support functions */ @@ -50032,7 +51482,7 @@ DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk if (DUK_HBUFFER_FIXED_GET_SIZE(buf) <= sizeof(duk_uint32_t)) { DUK_DD(DUK_DDPRINT("pc2line lookup failed: buffer is smaller than minimal header")); - goto error; + goto pc2line_error; } hdr = (duk_uint32_t *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, buf); @@ -50041,7 +51491,7 @@ DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk /* Note: pc is unsigned and cannot be negative */ DUK_DD(DUK_DDPRINT("pc2line lookup failed: pc out of bounds (pc=%ld, limit=%ld)", (long) pc, (long) pc_limit)); - goto error; + goto pc2line_error; } curr_line = hdr[1 + hdr_index * 2]; @@ -50049,7 +51499,7 @@ DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk if ((duk_size_t) start_offset > DUK_HBUFFER_FIXED_GET_SIZE(buf)) { DUK_DD(DUK_DDPRINT("pc2line lookup failed: start_offset out of bounds (start_offset=%ld, buffer_size=%ld)", (long) start_offset, (long) DUK_HBUFFER_GET_SIZE((duk_hbuffer *) buf))); - goto error; + goto pc2line_error; } /* @@ -50100,7 +51550,7 @@ DUK_LOCAL duk_uint_fast32_t duk__hobject_pc2line_query_raw(duk_hthread *thr, duk DUK_DDD(DUK_DDDPRINT("pc2line lookup result: pc %ld -> line %ld", (long) pc, (long) curr_line)); return curr_line; - error: + pc2line_error: DUK_D(DUK_DPRINT("pc2line conversion failed for pc=%ld", (long) pc)); return 0; } @@ -50130,7 +51580,7 @@ DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_i #endif /* DUK_USE_PC2LINE */ /* - * Hobject property set/get functionality. + * duk_hobject property access functionality. * * This is very central functionality for size, performance, and compliance. * It is also rather intricate; see hobject-algorithms.rst for discussion on @@ -50171,10 +51621,6 @@ DUK_INTERNAL duk_uint_fast32_t duk_hobject_pc2line_query(duk_context *ctx, duk_i * might be more appropriate. */ -/* - * XXX: duk_uint_fast32_t should probably be used in many places here. - */ - /* #include duk_internal.h -> already included */ /* @@ -50183,10 +51629,6 @@ 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 -/* hash probe sequence */ -#define DUK__HASH_INITIAL(hash,h_size) DUK_HOBJECT_HASH_INITIAL((hash),(h_size)) -#define DUK__HASH_PROBE_STEP(hash) DUK_HOBJECT_HASH_PROBE_STEP((hash)) - /* marker values for hash part */ #define DUK__HASH_UNUSED DUK_HOBJECT_HASHIDX_UNUSED #define DUK__HASH_DELETED DUK_HOBJECT_HASHIDX_DELETED @@ -50349,14 +51791,26 @@ DUK_LOCAL duk_bool_t duk__key_is_plain_buf_ownprop(duk_hthread *thr, duk_hbuffer DUK_LOCAL duk_uint32_t duk__get_default_h_size(duk_uint32_t e_size) { DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES); - if (e_size >= DUK_HOBJECT_E_USE_HASH_LIMIT) { + if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) { duk_uint32_t res; + duk_uint32_t tmp; - /* result: hash_prime(floor(1.2 * e_size)) */ - res = duk_util_get_hash_prime(e_size + e_size / DUK_HOBJECT_H_SIZE_DIVISOR); - - /* if fails, e_size will be zero = not an issue, except performance-wise */ - DUK_ASSERT(res == 0 || res > e_size); + /* Hash size should be 2^N where N is chosen so that 2^N is + * larger than e_size. Extra shifting is used to ensure hash + * is relatively sparse. + */ + tmp = e_size; + res = 2; /* Result will be 2 ** (N + 1). */ + while (tmp >= 0x40) { + tmp >>= 6; + res <<= 6; + } + while (tmp != 0) { + tmp >>= 1; + res <<= 1; + } + DUK_ASSERT((DUK_HOBJECT_MAX_PROPERTIES << 2U) > DUK_HOBJECT_MAX_PROPERTIES); /* Won't wrap, even shifted by 2. */ + DUK_ASSERT(res > e_size); return res; } else { return 0; @@ -50370,7 +51824,7 @@ DUK_LOCAL duk_uint32_t duk__get_min_grow_e(duk_uint32_t e_size) { DUK_ASSERT(e_size <= DUK_HOBJECT_MAX_PROPERTIES); - res = (e_size + DUK_HOBJECT_E_MIN_GROW_ADD) / DUK_HOBJECT_E_MIN_GROW_DIVISOR; + res = (e_size + DUK_USE_HOBJECT_ENTRY_MINGROW_ADD) / DUK_USE_HOBJECT_ENTRY_MINGROW_DIVISOR; DUK_ASSERT(res >= 1); /* important for callers */ return res; } @@ -50381,7 +51835,7 @@ DUK_LOCAL duk_uint32_t duk__get_min_grow_a(duk_uint32_t a_size) { DUK_ASSERT((duk_size_t) a_size <= DUK_HOBJECT_MAX_PROPERTIES); - res = (a_size + DUK_HOBJECT_A_MIN_GROW_ADD) / DUK_HOBJECT_A_MIN_GROW_DIVISOR; + res = (a_size + DUK_USE_HOBJECT_ARRAY_MINGROW_ADD) / DUK_USE_HOBJECT_ARRAY_MINGROW_DIVISOR; DUK_ASSERT(res >= 1); /* important for callers */ return res; } @@ -50456,7 +51910,7 @@ DUK_LOCAL duk_bool_t duk__abandon_array_density_check(duk_uint32_t a_used, duk_u * of the check, but may confuse debugging. */ - return (a_used < DUK_HOBJECT_A_ABANDON_LIMIT * (a_size >> 3)); + return (a_used < DUK_USE_HOBJECT_ARRAY_ABANDON_LIMIT * (a_size >> 3)); } /* Fast check for extending array: check whether or not a slow density check is required. */ @@ -50482,7 +51936,7 @@ DUK_LOCAL duk_bool_t duk__abandon_array_slow_check_required(duk_uint32_t arr_idx * arr_idx > limit'' * ((old_size + 7) / 8) */ - return (arr_idx > DUK_HOBJECT_A_FAST_RESIZE_LIMIT * ((old_size + 7) >> 3)); + return (arr_idx > DUK_USE_HOBJECT_ARRAY_FAST_RESIZE_LIMIT * ((old_size + 7) >> 3)); } /* @@ -50634,29 +52088,26 @@ DUK_LOCAL duk_bool_t duk__proxy_check_prop(duk_hthread *thr, duk_hobject *obj, d /* * Reallocate property allocation, moving properties to the new allocation. * - * Includes key compaction, rehashing, and can also optionally abandoning + * Includes key compaction, rehashing, and can also optionally abandon * the array part, 'migrating' array entries into the beginning of the - * new entry part. Arguments are not validated here, so e.g. new_h_size - * MUST be a valid prime. + * new entry part. * * There is no support for in-place reallocation or just compacting keys * without resizing the property allocation. This is intentional to keep - * code size minimal. + * code size minimal, but would be useful future work. * * The implementation is relatively straightforward, except for the array * abandonment process. Array abandonment requires that new string keys * are interned, which may trigger GC. All keys interned so far must be - * reachable for GC at all times; valstack is used for that now. + * reachable for GC at all times and correctly refcounted for; valstack is + * used for that now. * * Also, a GC triggered during this reallocation process must not interfere - * with the object being resized. This is currently controlled by using - * heap->mark_and_sweep_base_flags to indicate that no finalizers will be - * executed (as they can affect ANY object) and no objects are compacted - * (it would suffice to protect this particular object only, though). - * - * Note: a non-checked variant would be nice but is a bit tricky to - * implement for the array abandonment process. It's easy for - * everything else. + * with the object being resized. This is currently controlled by preventing + * finalizers (as they may affect ANY object) and object compaction in + * mark-and-sweep. It would suffice to protect only this particular object + * from compaction, however. DECREF refzero cascades are side effect free + * and OK. * * Note: because we need to potentially resize the valstack (as part * of abandoning the array part), any tval pointers to the valstack @@ -50670,7 +52121,7 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, duk_uint32_t new_h_size, duk_bool_t abandon_array) { duk_context *ctx = (duk_context *) thr; - duk_small_uint_t prev_mark_and_sweep_base_flags; + duk_small_uint_t prev_ms_base_flags; duk_uint32_t new_alloc_size; duk_uint32_t new_e_size_adjusted; duk_uint8_t *new_p; @@ -50681,6 +52132,10 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, duk_uint32_t *new_h; duk_uint32_t new_e_next; duk_uint_fast32_t i; + duk_size_t array_copy_size; +#if defined(DUK_USE_ASSERTIONS) + duk_bool_t prev_error_not_allowed; +#endif DUK_ASSERT(thr != NULL); DUK_ASSERT(ctx != NULL); @@ -50750,9 +52205,8 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, /* * Property count check. This is the only point where we ensure that * we don't get more (allocated) property space that we can handle. - * There aren't hard limits as such, but some algorithms fail (e.g. - * finding next higher prime, selecting hash part size) if we get too - * close to the 4G property limit. + * There aren't hard limits as such, but some algorithms may fail + * if we get too close to the 4G property limit. * * Since this works based on allocation size (not actually used size), * the limit is a bit approximate but good enough in practice. @@ -50765,43 +52219,46 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, /* * Compute new alloc size and alloc new area. * - * The new area is allocated as a dynamic buffer and placed into the - * valstack for reachability. The actual buffer is then detached at - * the end. - * - * Note: heap_mark_and_sweep_base_flags are altered here to ensure - * no-one touches this object while we're resizing and rehashing it. - * The flags must be reset on every exit path after it. Finalizers - * and compaction is prevented currently for all objects while it - * would be enough to restrict it only for the current object. + * The new area is not tracked in the heap at all, so it's critical + * we get to free/keep it in a controlled manner. */ - prev_mark_and_sweep_base_flags = thr->heap->mark_and_sweep_base_flags; - thr->heap->mark_and_sweep_base_flags |= - DUK_MS_FLAG_NO_FINALIZERS | /* avoid attempts to add/remove object keys */ - DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid attempt to compact the current object */ +#if defined(DUK_USE_ASSERTIONS) + /* Whole path must be error throw free, but we may be called from + * within error handling so can't assert for error_not_allowed == 0. + */ + prev_error_not_allowed = thr->heap->error_not_allowed; + thr->heap->error_not_allowed = 1; +#endif + prev_ms_base_flags = thr->heap->ms_base_flags; + thr->heap->ms_base_flags |= + DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* Avoid attempt to compact the current object (all objects really). */ + thr->heap->pf_prevent_count++; /* Avoid finalizers. */ + DUK_ASSERT(thr->heap->pf_prevent_count != 0); /* Wrap. */ new_alloc_size = DUK_HOBJECT_P_COMPUTE_SIZE(new_e_size_adjusted, new_a_size, new_h_size); DUK_DDD(DUK_DDDPRINT("new hobject allocation size is %ld", (long) new_alloc_size)); if (new_alloc_size == 0) { - /* for zero size, don't push anything on valstack */ DUK_ASSERT(new_e_size_adjusted == 0); DUK_ASSERT(new_a_size == 0); DUK_ASSERT(new_h_size == 0); new_p = NULL; } else { - /* This may trigger mark-and-sweep with arbitrary side effects, - * including an attempted resize of the object we're resizing, - * executing a finalizer which may add or remove properties of - * the object we're resizing etc. - */ - - /* Note: buffer is dynamic so that we can 'steal' the actual - * allocation later. + /* Alloc may trigger mark-and-sweep but no compaction, and + * cannot throw. */ - - new_p = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, new_alloc_size); /* errors out if out of memory */ - DUK_ASSERT(new_p != NULL); /* since new_alloc_size > 0 */ +#if 0 /* XXX: inject test */ + if (1) { + goto alloc_failed; + } +#endif + new_p = (duk_uint8_t *) DUK_ALLOC(thr->heap, new_alloc_size); + if (new_p == NULL) { + /* NULL always indicates alloc failure because + * new_alloc_size > 0. + */ + goto alloc_failed; + } } /* Set up pointers to the new property area: this is hidden behind a macro @@ -50822,27 +52279,27 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, (void *) new_a, (void *) new_h)); /* - * Migrate array to start of entries if requested. + * Migrate array part to start of entries if requested. * * Note: from an enumeration perspective the order of entry keys matters. * Array keys should appear wherever they appeared before the array abandon - * operation. + * operation. (This no longer matters much because keys are ES2015 sorted.) */ if (abandon_array) { - /* - * Note: assuming new_a_size == 0, and that entry part contains - * no conflicting keys, refcounts do not need to be adjusted for - * the values, as they remain exactly the same. + /* Assuming new_a_size == 0, and that entry part contains + * no conflicting keys, refcounts do not need to be adjusted for + * the values, as they remain exactly the same. * - * The keys, however, need to be interned, incref'd, and be - * reachable for GC. Any intern attempt may trigger a GC and - * claim any non-reachable strings, so every key must be reachable - * at all times. + * The keys, however, need to be interned, incref'd, and be + * reachable for GC. Any intern attempt may trigger a GC and + * claim any non-reachable strings, so every key must be reachable + * at all times. Refcounts must be correct to satisfy refcount + * assertions. * - * A longjmp must not occur here, as the new_p allocation would - * be freed without these keys being decref'd, hence the messy - * decref handling if intern fails. + * A longjmp must not occur here, as the new_p allocation would + * leak. Refcounts would come out correctly as the interned + * strings are valstack tracked. */ DUK_ASSERT(new_a_size == 0); @@ -50871,20 +52328,29 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, * must be careful. */ - /* never shrinks; auto-adds DUK_VALSTACK_INTERNAL_EXTRA, which is generous */ +#if 0 /* XXX: inject test */ + if (1) { + goto abandon_error; + } +#endif + /* Never shrinks; auto-adds DUK_VALSTACK_INTERNAL_EXTRA, which + * is generous. + */ if (!duk_check_stack(ctx, 1)) { goto abandon_error; } DUK_ASSERT_VALSTACK_SPACE(thr, 1); - key = duk_heap_string_intern_u32(thr->heap, i); - if (!key) { + key = duk_heap_strtable_intern_u32(thr->heap, i); + if (key == NULL) { goto abandon_error; } duk_push_hstring(ctx, key); /* keep key reachable for GC etc; guaranteed not to fail */ - /* key is now reachable in the valstack */ + /* Key is now reachable in the valstack, don't INCREF + * the new allocation yet (we'll steal the refcounts + * from the value stack once all keys are done). + */ - DUK_HSTRING_INCREF(thr, key); /* second incref for the entry reference */ new_e_k[new_e_next] = key; tv2 = &new_e_pv[new_e_next].v; /* array entries are all plain values */ DUK_TVAL_SET_TVAL(tv2, tv1); @@ -50898,8 +52364,9 @@ 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(ctx, new_e_next); + duk_pop_n_nodecref_unsafe(ctx, new_e_next); } /* @@ -50912,7 +52379,7 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); key = DUK_HOBJECT_E_GET_KEY(thr->heap, obj, i); - if (!key) { + if (key == NULL) { continue; } @@ -50927,53 +52394,46 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, /* the entries [new_e_next, new_e_size_adjusted[ are left uninitialized on purpose (ok, not gc reachable) */ /* - * Copy array elements to new array part. + * Copy array elements to new array part. If the new array part is + * larger, initialize the unused entries as UNUSED because they are + * GC reachable. */ - if (new_a_size > DUK_HOBJECT_GET_ASIZE(obj)) { - /* copy existing entries as is */ - DUK_ASSERT(new_p != NULL && new_a != NULL); - if (DUK_HOBJECT_GET_ASIZE(obj) > 0) { - /* Avoid zero copy with an invalid pointer. If obj->p is NULL, - * the 'new_a' pointer will be invalid which is not allowed even - * when copy size is zero. - */ - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); - DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0); - DUK_MEMCPY((void *) new_a, (void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), sizeof(duk_tval) * DUK_HOBJECT_GET_ASIZE(obj)); - } - - /* fill new entries with -unused- (required, gc reachable) */ - for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) { - duk_tval *tv = &new_a[i]; - DUK_TVAL_SET_UNUSED(tv); - } - } else { #if defined(DUK_USE_ASSERTIONS) - /* caller must have decref'd values above new_a_size (if that is necessary) */ - if (!abandon_array) { - for (i = new_a_size; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { - duk_tval *tv; - tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); - - /* current assertion is quite strong: decref's and set to unused */ - DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv)); - } + /* Caller must have decref'd values above new_a_size (if that is necessary). */ + if (!abandon_array) { + for (i = new_a_size; i < DUK_HOBJECT_GET_ASIZE(obj); i++) { + duk_tval *tv; + tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, obj, i); + DUK_ASSERT(DUK_TVAL_IS_UNUSED(tv)); } + } #endif - if (new_a_size > 0) { - /* Avoid zero copy with an invalid pointer. If obj->p is NULL, - * the 'new_a' pointer will be invalid which is not allowed even - * when copy size is zero. - */ - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); - DUK_ASSERT(new_a_size > 0); - DUK_MEMCPY((void *) new_a, (void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), sizeof(duk_tval) * new_a_size); - } + if (new_a_size > DUK_HOBJECT_GET_ASIZE(obj)) { + array_copy_size = sizeof(duk_tval) * DUK_HOBJECT_GET_ASIZE(obj); + } else { + array_copy_size = sizeof(duk_tval) * new_a_size; + } + if (array_copy_size > 0) { + /* Avoid zero copy with an invalid pointer. If obj->p is NULL, + * the 'new_a' pointer will be invalid which is not allowed even + * when copy size is zero. + */ + DUK_ASSERT(new_a != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(obj) > 0); + DUK_MEMCPY((void *) new_a, + (const void *) DUK_HOBJECT_A_GET_BASE(thr->heap, obj), + array_copy_size); + } + for (i = DUK_HOBJECT_GET_ASIZE(obj); i < new_a_size; i++) { + duk_tval *tv = &new_a[i]; + DUK_TVAL_SET_UNUSED(tv); } /* - * Rebuild the hash part always from scratch (guaranteed to finish). + * Rebuild the hash part always from scratch (guaranteed to finish + * as long as caller gave consistent parameters). * * Any resize of hash part requires rehashing. In addition, by rehashing * get rid of any elements marked deleted (DUK__HASH_DELETED) which is critical @@ -50981,7 +52441,11 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, */ #if defined(DUK_USE_HOBJECT_HASH_PART) - if (DUK_UNLIKELY(new_h_size > 0)) { + if (new_h_size == 0) { + DUK_DDD(DUK_DDDPRINT("no hash part, no rehash")); + } else { + duk_uint32_t mask; + DUK_ASSERT(new_h != NULL); /* fill new_h with u32 0xff = UNUSED */ @@ -50990,13 +52454,15 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, DUK_MEMSET(new_h, 0xff, sizeof(duk_uint32_t) * new_h_size); DUK_ASSERT(new_e_next <= new_h_size); /* equality not actually possible */ + + mask = new_h_size - 1; for (i = 0; i < new_e_next; i++) { duk_hstring *key = new_e_k[i]; duk_uint32_t j, step; DUK_ASSERT(key != NULL); - j = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), new_h_size); - step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key)); + j = DUK_HSTRING_GET_HASH(key) & mask; + step = 1; /* Cache friendly but clustering prone. */ for (;;) { DUK_ASSERT(new_h[j] != DUK__HASH_DELETED); /* should never happen */ @@ -51006,14 +52472,11 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, break; } DUK_DDD(DUK_DDDPRINT("rebuild miss %ld, step %ld", (long) j, (long) step)); - j = (j + step) % new_h_size; + j = (j + step) & mask; - /* guaranteed to finish */ - DUK_ASSERT(j != (duk_uint32_t) DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), new_h_size)); + /* Guaranteed to finish (hash is larger than #props). */ } } - } else { - DUK_DDD(DUK_DDDPRINT("no hash part, no rehash")); } #endif /* DUK_USE_HOBJECT_HASH_PART */ @@ -51052,30 +52515,20 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, DUK_HOBJECT_SET_ASIZE(obj, new_a_size); DUK_HOBJECT_SET_HSIZE(obj, new_h_size); - if (new_p) { - /* - * Detach actual buffer from dynamic buffer in valstack, and - * pop it from the stack. - * - * XXX: the buffer object is certainly not reachable at this point, - * so it would be nice to free it forcibly even with only - * mark-and-sweep enabled. Not a big issue though. - */ - (void) duk_steal_buffer(ctx, -1, NULL); - duk_pop(ctx); - } else { - DUK_ASSERT(new_alloc_size == 0); - /* no need to pop, nothing was pushed */ - } - - /* clear array part flag only after switching */ + /* Clear array part flag only after switching. */ if (abandon_array) { DUK_HOBJECT_CLEAR_ARRAY_PART(obj); } DUK_DDD(DUK_DDDPRINT("resize result: %!O", (duk_heaphdr *) obj)); - thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; + DUK_ASSERT(thr->heap->pf_prevent_count > 0); + thr->heap->pf_prevent_count--; + thr->heap->ms_base_flags = prev_ms_base_flags; +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(thr->heap->error_not_allowed == 1); + thr->heap->error_not_allowed = prev_error_not_allowed; +#endif /* * Post resize assertions. @@ -51087,21 +52540,25 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr, return; /* - * Abandon array failed, need to decref keys already inserted - * into the beginning of new_e_k before unwinding valstack. + * Abandon array failed. We don't need to DECREF anything + * because the references in the new allocation are not + * INCREF'd until abandon is complete. The string interned + * keys are on the value stack and are handled normally by + * unwind. */ abandon_error: - DUK_D(DUK_DPRINT("hobject resize failed during abandon array, decref keys")); - i = new_e_next; - while (i > 0) { - i--; - DUK_ASSERT(new_e_k != NULL); - DUK_ASSERT(new_e_k[i] != NULL); - DUK_HSTRING_DECREF(thr, new_e_k[i]); /* side effects */ - } + alloc_failed: + DUK_D(DUK_DPRINT("object property table resize failed")); - thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; + DUK_FREE(thr->heap, new_p); /* OK for NULL. */ + + thr->heap->pf_prevent_count--; + thr->heap->ms_base_flags = prev_ms_base_flags; +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(thr->heap->error_not_allowed == 1); + thr->heap->error_not_allowed = prev_error_not_allowed; +#endif DUK_ERROR_ALLOC_FAILED(thr); } @@ -51253,7 +52710,7 @@ DUK_INTERNAL void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj) } #if defined(DUK_USE_HOBJECT_HASH_PART) - if (e_size >= DUK_HOBJECT_E_USE_HASH_LIMIT) { + if (e_size >= DUK_USE_HOBJECT_HASH_PROP_LIMIT) { h_size = duk__get_default_h_size(e_size); } else { h_size = 0; @@ -51314,13 +52771,15 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o duk_uint32_t n; duk_uint32_t i, step; duk_uint32_t *h_base; + duk_uint32_t mask; DUK_DDD(DUK_DDDPRINT("duk_hobject_find_existing_entry() using hash part for lookup")); h_base = DUK_HOBJECT_H_GET_BASE(heap, obj); n = DUK_HOBJECT_GET_HSIZE(obj); - i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n); - step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key)); + mask = n - 1; + i = DUK_HSTRING_GET_HASH(key) & mask; + step = 1; /* Cache friendly but clustering prone. */ for (;;) { duk_uint32_t t; @@ -51348,10 +52807,9 @@ DUK_INTERNAL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *o DUK_DDD(DUK_DDDPRINT("lookup miss i=%ld, t=%ld", (long) i, (long) t)); } - i = (i + step) % n; + i = (i + step) & mask; - /* guaranteed to finish, as hash is never full */ - DUK_ASSERT(i != (duk_uint32_t) DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n)); + /* Guaranteed to finish (hash is larger than #props). */ } } #endif /* DUK_USE_HOBJECT_HASH_PART */ @@ -51456,13 +52914,14 @@ DUK_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject *obj #if defined(DUK_USE_HOBJECT_HASH_PART) if (DUK_UNLIKELY(DUK_HOBJECT_GET_HSIZE(obj) > 0)) { - duk_uint32_t n; + duk_uint32_t n, mask; duk_uint32_t i, step; duk_uint32_t *h_base = DUK_HOBJECT_H_GET_BASE(thr->heap, obj); n = DUK_HOBJECT_GET_HSIZE(obj); - i = DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), n); - step = DUK__HASH_PROBE_STEP(DUK_HSTRING_GET_HASH(key)); + mask = n - 1; + i = DUK_HSTRING_GET_HASH(key) & mask; + step = 1; /* Cache friendly but clustering prone. */ for (;;) { duk_uint32_t t = h_base[i]; @@ -51477,10 +52936,9 @@ DUK_LOCAL duk_bool_t duk__alloc_entry_checked(duk_hthread *thr, duk_hobject *obj break; } DUK_DDD(DUK_DDDPRINT("duk__alloc_entry_checked() miss %ld", (long) i)); - i = (i + step) % n; + i = (i + step) & mask; - /* guaranteed to find an empty slot */ - DUK_ASSERT(i != (duk_uint32_t) DUK__HASH_INITIAL(DUK_HSTRING_GET_HASH(key), DUK_HOBJECT_GET_HSIZE(obj))); + /* Guaranteed to finish (hash is larger than #props). */ } } #endif /* DUK_USE_HOBJECT_HASH_PART */ @@ -51880,6 +53338,8 @@ DUK_LOCAL duk_bool_t duk__get_own_propdesc_raw(duk_hthread *thr, duk_hobject *ob DUK_DDD(DUK_DDDPRINT("string object exotic property get for key: %!O, arr_idx: %ld", (duk_heaphdr *) key, (long) arr_idx)); + /* XXX: charlen; avoid multiple lookups? */ + if (arr_idx != DUK__NO_ARRAY_INDEX) { duk_hstring *h_val; @@ -52120,7 +53580,7 @@ DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_h } /* not found in 'curr', next in prototype chain; impose max depth */ - if (sanity-- == 0) { + if (DUK_UNLIKELY(sanity-- == 0)) { if (flags & DUK_GETDESC_FLAG_IGNORE_PROTOLOOP) { /* treat like property not found */ break; @@ -52129,7 +53589,7 @@ DUK_LOCAL duk_bool_t duk__get_propdesc(duk_hthread *thr, duk_hobject *obj, duk_h } } curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr); + } while (curr != NULL); /* out_desc is left untouched (possibly garbage), caller must use return * value to determine whether out_desc can be looked up @@ -52479,7 +53939,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_hstring *h = DUK_TVAL_GET_STRING(tv_obj); duk_int_t pop_count; - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { /* Symbols (ES2015 or hidden) don't have virtual properties. */ DUK_DDD(DUK_DDDPRINT("base object is a symbol, start lookup from symbol prototype")); curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE]; @@ -52820,11 +54280,11 @@ DUK_INTERNAL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, /* XXX: option to pretend property doesn't exist if sanity limit is * hit might be useful. */ - if (sanity-- == 0) { + if (DUK_UNLIKELY(sanity-- == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); } curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr); + } while (curr != NULL); /* * Not found @@ -53504,7 +54964,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, arr_idx = duk__push_tval_to_property_key(ctx, tv_key, &key); DUK_ASSERT(key != NULL); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { /* Symbols (ES2015 or hidden) don't have virtual properties. */ curr = thr->builtins[DUK_BIDX_SYMBOL_PROTOTYPE]; goto lookup; @@ -53920,11 +55380,11 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, /* XXX: option to pretend property doesn't exist if sanity limit is * hit might be useful. */ - if (sanity-- == 0) { + if (DUK_UNLIKELY(sanity-- == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); } curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr); - } while (curr); + } while (curr != NULL); /* * Property not found in prototype chain. @@ -54551,10 +56011,10 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj, /* Note: proxy handling must happen before key is string coerced. */ if (duk__proxy_check_prop(thr, obj, DUK_STRIDX_DELETE_PROPERTY, tv_key, &h_target)) { - /* -> [ ... trap handler ] */ + /* -> [ ... 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_push_tval(ctx, tv_key); /* P */ + duk_dup_m4(ctx); /* P */ duk_call_method(ctx, 2 /*nargs*/); tmp_bool = duk_to_boolean(ctx, -1); duk_pop(ctx); @@ -54565,6 +56025,7 @@ 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); DUK_ASSERT(key != NULL); @@ -54912,6 +56373,41 @@ DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *ob return 0; } +/* + * Fast finalizer check for an object. Walks the prototype chain, checking + * for finalizer presence using DUK_HOBJECT_FLAG_HAVE_FINALIZER which is kept + * in sync with the actual property when setting/removing the finalizer. + */ + +#if defined(DUK_USE_HEAPPTR16) +DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_heap *heap, duk_hobject *obj) { +#else +DUK_INTERNAL duk_bool_t duk_hobject_has_finalizer_fast_raw(duk_hobject *obj) { +#endif + duk_uint_t sanity; + + DUK_ASSERT(obj != NULL); + + sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; + do { + if (DUK_UNLIKELY(DUK_HOBJECT_HAS_HAVE_FINALIZER(obj))) { + return 1; + } + if (DUK_UNLIKELY(sanity-- == 0)) { + DUK_D(DUK_DPRINT("prototype loop when checking for finalizer existence; returning false")); + return 0; + } +#if defined(DUK_USE_HEAPPTR16) + DUK_ASSERT(heap != NULL); + obj = DUK_HOBJECT_GET_PROTOTYPE(heap, obj); +#else + obj = DUK_HOBJECT_GET_PROTOTYPE(NULL, obj); /* 'heap' arg ignored */ +#endif + } while (obj != NULL); + + return 0; +} + /* * Object.getOwnPropertyDescriptor() (E5 Sections 15.2.3.3, 8.10.4) * @@ -56170,8 +57666,6 @@ DUK_INTERNAL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread * /* automatic undefs */ #undef DUK__HASH_DELETED -#undef DUK__HASH_INITIAL -#undef DUK__HASH_PROBE_STEP #undef DUK__HASH_UNUSED #undef DUK__NO_ARRAY_INDEX #undef DUK__VALSTACK_PROXY_LOOKUP @@ -56182,6 +57676,10 @@ DUK_INTERNAL duk_bool_t duk_hobject_object_is_sealed_frozen_helper(duk_hthread * /* #include duk_internal.h -> already included */ +/* + * duk_hstring charCodeAt, with and without surrogate awareness + */ + DUK_INTERNAL 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_uint32_t boff; const duk_uint8_t *p, *p_start, *p_end; @@ -56229,16 +57727,87 @@ DUK_INTERNAL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr, duk return cp1; } -#if !defined(DUK_USE_HSTRING_CLEN) -DUK_INTERNAL duk_size_t duk_hstring_get_charlen(duk_hstring *h) { - if (DUK_HSTRING_HAS_ASCII(h)) { +/* + * duk_hstring charlen access + */ + +#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; + + DUK_ASSERT(h->clen == 0); /* Checked by caller. */ + +#if defined(DUK_USE_ROM_STRINGS) + /* ROM strings have precomputed clen, but if the computed clen is zero + * we can still come here and can't write anything. + */ + if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { + return 0; + } +#endif + + res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); +#if defined(DUK_USE_STRLEN16) + DUK_ASSERT(res <= 0xffffUL); /* Bytelength checked during interning. */ + h->clen16 = (duk_uint16_t) res; +#else + h->clen = (duk_uint32_t) res; +#endif + if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) { + DUK_HSTRING_SET_ASCII(h); + } + return res; +} +#else /* DUK_USE_HSTRING_CLEN */ +DUK_LOCAL duk_size_t duk__hstring_get_charlen_slowpath(duk_hstring *h) { + if (DUK_LIKELY(DUK_HSTRING_HAS_ASCII(h))) { /* Most practical strings will go here. */ return DUK_HSTRING_GET_BYTELEN(h); } else { - return duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); + /* ASCII flag is lazy, so set it here. */ + duk_size_t res; + + /* XXX: here we could use the strcache to speed up the + * computation (matters for 'i < str.length' loops). + */ + + res = duk_unicode_unvalidated_utf8_length(DUK_HSTRING_GET_DATA(h), DUK_HSTRING_GET_BYTELEN(h)); + +#if defined(DUK_USE_ROM_STRINGS) + if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) h)) { + /* For ROM strings, can't write anything; ASCII flag + * is preset so we don't need to update it. + */ + return res; + } +#endif + if (DUK_LIKELY(res == DUK_HSTRING_GET_BYTELEN(h))) { + DUK_HSTRING_SET_ASCII(h); + } + return res; + } +} +#endif /* DUK_USE_HSTRING_CLEN */ + +#if defined(DUK_USE_HSTRING_CLEN) +DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { +#if defined(DUK_USE_STRLEN16) + if (DUK_LIKELY(h->clen16 != 0)) { + return h->clen16; } +#else + if (DUK_LIKELY(h->clen != 0)) { + return h->clen; + } +#endif + return duk__hstring_get_charlen_slowpath(h); +} +#else /* DUK_USE_HSTRING_CLEN */ +DUK_INTERNAL DUK_HOT duk_size_t duk_hstring_get_charlen(duk_hstring *h) { + /* Always use slow path. */ + return duk__hstring_get_charlen_slowpath(h); } -#endif /* !DUK_USE_HSTRING_CLEN */ +#endif /* DUK_USE_HSTRING_CLEN */ /* * duk_hthread allocation and freeing. */ @@ -56262,6 +57831,7 @@ DUK_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr 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 */ @@ -56291,6 +57861,7 @@ DUK_INTERNAL duk_bool_t duk_hthread_init_stacks(duk_heap *heap, duk_hthread *thr 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; @@ -56371,12 +57942,13 @@ DUK_INTERNAL void *duk_hthread_get_catchstack_ptr(duk_heap *heap, void *ud) { #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 *h1; + duk_hobject *h_global; #if defined(DUK_USE_ROM_GLOBAL_CLONE) - duk_hobject *h2; + duk_hobject *h_oldglobal; duk_uint8_t *props; duk_size_t alloc_size; #endif + duk_hobject *h_objenv; ctx = (duk_context *) thr; @@ -56384,82 +57956,84 @@ DUK_LOCAL void duk__duplicate_ram_global_object(duk_hthread *thr) { #if defined(DUK_USE_ROM_GLOBAL_INHERIT) /* Inherit from ROM-based global object: less RAM usage, less transparent. */ - h1 = duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), - DUK_BIDX_GLOBAL); - DUK_ASSERT(h1 != NULL); + h_global = duk_push_object_helper(ctx, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), + DUK_BIDX_GLOBAL); + DUK_ASSERT(h_global != NULL); #elif defined(DUK_USE_ROM_GLOBAL_CLONE) /* Clone the properties of the ROM-based global object to create a * fully RAM-based global object. Uses more memory than the inherit * model but more compliant. */ - h1 = duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), - DUK_BIDX_OBJECT_PROTOTYPE); - DUK_ASSERT(h1 != NULL); - h2 = thr->builtins[DUK_BIDX_GLOBAL]; - DUK_ASSERT(h2 != NULL); + h_global = duk_push_object_helper(ctx, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_FLAG_FASTREFS | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_GLOBAL), + DUK_BIDX_OBJECT_PROTOTYPE); + DUK_ASSERT(h_global != NULL); + h_oldglobal = thr->builtins[DUK_BIDX_GLOBAL]; + DUK_ASSERT(h_oldglobal != NULL); /* Copy the property table verbatim; this handles attributes etc. * For ROM objects it's not necessary (or possible) to update * refcounts so leave them as is. */ - alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h2); + alloc_size = DUK_HOBJECT_P_ALLOC_SIZE(h_oldglobal); DUK_ASSERT(alloc_size > 0); - props = DUK_ALLOC(thr->heap, alloc_size); - if (!props) { - DUK_ERROR_ALLOC_FAILED(thr); - return; - } - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h2) != NULL); - DUK_MEMCPY((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h2), alloc_size); + props = DUK_ALLOC_CHECKED(thr, alloc_size); + DUK_ASSERT(props != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal) != NULL); + DUK_MEMCPY((void *) props, (const void *) DUK_HOBJECT_GET_PROPS(thr->heap, h_oldglobal), alloc_size); /* XXX: keep property attributes or tweak them here? * Properties will now be non-configurable even when they're * normally configurable for the global object. */ - DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h1) == NULL); - DUK_HOBJECT_SET_PROPS(thr->heap, h1, props); - DUK_HOBJECT_SET_ESIZE(h1, DUK_HOBJECT_GET_ESIZE(h2)); - DUK_HOBJECT_SET_ENEXT(h1, DUK_HOBJECT_GET_ENEXT(h2)); - DUK_HOBJECT_SET_ASIZE(h1, DUK_HOBJECT_GET_ASIZE(h2)); - DUK_HOBJECT_SET_HSIZE(h1, DUK_HOBJECT_GET_HSIZE(h2)); + DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, h_global) == NULL); + DUK_HOBJECT_SET_PROPS(thr->heap, h_global, props); + DUK_HOBJECT_SET_ESIZE(h_global, DUK_HOBJECT_GET_ESIZE(h_oldglobal)); + DUK_HOBJECT_SET_ENEXT(h_global, DUK_HOBJECT_GET_ENEXT(h_oldglobal)); + DUK_HOBJECT_SET_ASIZE(h_global, DUK_HOBJECT_GET_ASIZE(h_oldglobal)); + DUK_HOBJECT_SET_HSIZE(h_global, DUK_HOBJECT_GET_HSIZE(h_oldglobal)); #else -#error internal error in defines +#error internal error in config defines #endif - duk_hobject_compact_props(thr, h1); + duk_hobject_compact_props(thr, h_global); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); - DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL])); /* no need to decref */ - thr->builtins[DUK_BIDX_GLOBAL] = h1; - DUK_HOBJECT_INCREF(thr, h1); - DUK_D(DUK_DPRINT("duplicated global object: %!O", h1)); - + DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL])); /* no need to decref: ROM object */ + thr->builtins[DUK_BIDX_GLOBAL] = h_global; + DUK_HOBJECT_INCREF(thr, h_global); + DUK_D(DUK_DPRINT("duplicated global object: %!O", h_global)); /* Create a fresh object environment for the global scope. This is * needed so that the global scope points to the newly created RAM-based * global object. */ - h1 = duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), - -1); /* no prototype */ - DUK_ASSERT(h1 != NULL); - duk_dup_m2(ctx); /* -> [ ... new_global new_globalenv new_global ] */ - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); - /* provideThis=false */ + h_objenv = (duk_hobject *) duk_hobjenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); + DUK_ASSERT(h_objenv != NULL); + DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_objenv) == NULL); + duk_push_hobject(ctx, h_objenv); + + DUK_ASSERT(h_global != NULL); + ((duk_hobjenv *) h_objenv)->target = h_global; + DUK_HOBJECT_INCREF(thr, h_global); + DUK_ASSERT(((duk_hobjenv *) h_objenv)->has_this == 0); - duk_hobject_compact_props(thr, h1); DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL_ENV] != NULL); - DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV])); /* no need to decref */ - thr->builtins[DUK_BIDX_GLOBAL_ENV] = h1; - DUK_HOBJECT_INCREF(thr, h1); - DUK_D(DUK_DPRINT("duplicated global env: %!O", h1)); + DUK_ASSERT(!DUK_HEAPHDR_NEEDS_REFCOUNT_UPDATE((duk_heaphdr *) thr->builtins[DUK_BIDX_GLOBAL_ENV])); /* no need to decref: ROM object */ + thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_objenv; + DUK_HOBJECT_INCREF(thr, h_objenv); + DUK_D(DUK_DPRINT("duplicated global env: %!O", h_objenv)); - duk_pop_2(ctx); + DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) h_objenv); + + duk_pop_2(ctx); /* Pop global object and global env. */ } #endif /* DUK_USE_ROM_GLOBAL_CLONE || DUK_USE_ROM_GLOBAL_INHERIT */ @@ -56565,11 +58139,11 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { duk_small_int_t len = -1; /* must be signed */ class_num = (duk_small_uint_t) duk_bd_decode_varuint(bd); - len = (duk_small_int_t) duk_bd_decode_flagged(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); + len = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__LENGTH_PROP_BITS, (duk_int32_t) -1 /*def_value*/); if (class_num == DUK_HOBJECT_CLASS_FUNCTION) { duk_small_uint_t natidx; - duk_int_t c_nargs; /* must hold DUK_VARARGS */ + duk_small_int_t c_nargs; /* must hold DUK_VARARGS */ duk_c_function c_func; duk_int16_t magic; @@ -56581,7 +58155,7 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { c_func = duk_bi_native_functions[natidx]; DUK_ASSERT(c_func != NULL); - c_nargs = (duk_small_uint_t) duk_bd_decode_flagged(bd, DUK__NARGS_BITS, len /*def_value*/); + c_nargs = (duk_small_int_t) duk_bd_decode_flagged_signed(bd, DUK__NARGS_BITS, len /*def_value*/); if (c_nargs == DUK__NARGS_VARARGS_MARKER) { c_nargs = DUK_VARARGS; } @@ -56624,8 +58198,31 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { ((duk_hnatfunc *) h)->magic = magic; } else if (class_num == DUK_HOBJECT_CLASS_ARRAY) { duk_push_array(ctx); + } else if (class_num == DUK_HOBJECT_CLASS_OBJENV) { + duk_hobjenv *env; + duk_hobject *global; + + DUK_ASSERT(i == DUK_BIDX_GLOBAL_ENV); + DUK_ASSERT(DUK_BIDX_GLOBAL_ENV > DUK_BIDX_GLOBAL); + + env = duk_hobjenv_alloc(thr, + DUK_HOBJECT_FLAG_EXTENSIBLE | + DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); + DUK_ASSERT(env->target == NULL); + duk_push_hobject(ctx, (duk_hobject *) env); + + global = duk_known_hobject(ctx, DUK_BIDX_GLOBAL); + DUK_ASSERT(global != NULL); + env->target = global; + DUK_HOBJECT_INCREF(thr, global); + DUK_ASSERT(env->has_this == 0); + + DUK_ASSERT_HOBJENV_VALID(env); } else { + DUK_ASSERT(class_num != DUK_HOBJECT_CLASS_DECENV); + (void) duk_push_object_helper(ctx, + DUK_HOBJECT_FLAG_FASTREFS | DUK_HOBJECT_FLAG_EXTENSIBLE, -1); /* no prototype or class yet */ @@ -56674,14 +58271,13 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_COMPFUNC(h)); /* DUK_HOBJECT_FLAG_NATFUNC varies */ - DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(h)); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(h) || class_num == DUK_HOBJECT_CLASS_ARRAY); /* DUK_HOBJECT_FLAG_STRICT varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(h) || /* all native functions have NEWENV */ DUK_HOBJECT_HAS_NEWENV(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(h)); DUK_ASSERT(!DUK_HOBJECT_HAS_CREATEARGS(h)); - DUK_ASSERT(!DUK_HOBJECT_HAS_ENVRECCLOSED(h)); /* DUK_HOBJECT_FLAG_EXOTIC_ARRAY varies */ /* DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ varies */ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)); @@ -57030,12 +58626,8 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { #endif " " /* Low memory options */ -#if defined(DUK_USE_STRTAB_CHAIN) - "c" /* chain */ -#elif defined(DUK_USE_STRTAB_PROBE) - "p" /* probe */ -#else - "?" +#if defined(DUK_USE_STRTAB_PTRCOMP) + "s" #endif #if !defined(DUK_USE_HEAPPTR16) && !defined(DUK_DATAPTR16) && !defined(DUK_FUNCPTR16) "n" @@ -57169,7 +58761,6 @@ DUK_INTERNAL void duk_hthread_terminate(duk_hthread *thr) { /* Order of unwinding is important */ duk_hthread_catchstack_unwind(thr, 0); - duk_hthread_callstack_unwind(thr, 0); /* side effects, possibly errors */ thr->valstack_bottom = thr->valstack; @@ -57192,16 +58783,6 @@ DUK_INTERNAL void duk_hthread_terminate(duk_hthread *thr) { */ } -DUK_INTERNAL duk_activation *duk_hthread_get_current_activation(duk_hthread *thr) { - DUK_ASSERT(thr != NULL); - - if (thr->callstack_top > 0) { - return thr->callstack + thr->callstack_top - 1; - } else { - return NULL; - } -} - #if defined(DUK_USE_DEBUGGER_SUPPORT) DUK_INTERNAL duk_uint_fast32_t duk_hthread_get_act_curr_pc(duk_hthread *thr, duk_activation *act) { duk_instr_t *bcode; @@ -57247,7 +58828,9 @@ DUK_INTERNAL void duk_hthread_sync_currpc(duk_hthread *thr) { if (thr->ptr_curr_pc != NULL) { /* ptr_curr_pc != NULL only when bytecode executor is active. */ DUK_ASSERT(thr->callstack_top > 0); - act = thr->callstack + thr->callstack_top - 1; + DUK_ASSERT(thr->callstack_curr != NULL); + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); act->curr_pc = *thr->ptr_curr_pc; } } @@ -57260,7 +58843,9 @@ DUK_INTERNAL void duk_hthread_sync_and_null_currpc(duk_hthread *thr) { if (thr->ptr_curr_pc != NULL) { /* ptr_curr_pc != NULL only when bytecode executor is active. */ DUK_ASSERT(thr->callstack_top > 0); - act = thr->callstack + thr->callstack_top - 1; + DUK_ASSERT(thr->callstack_curr != NULL); + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); act->curr_pc = *thr->ptr_curr_pc; thr->ptr_curr_pc = NULL; } @@ -57289,8 +58874,7 @@ DUK_INTERNAL void duk_hthread_sync_and_null_currpc(duk_hthread *thr) { /* #include duk_internal.h -> already included */ -/* check that there is space for at least one new entry */ -DUK_INTERNAL void duk_hthread_callstack_grow(duk_hthread *thr) { +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; @@ -57299,10 +58883,6 @@ DUK_INTERNAL void duk_hthread_callstack_grow(duk_hthread *thr) { DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */ DUK_ASSERT(thr->callstack_size >= thr->callstack_top); - if (thr->callstack_top < thr->callstack_size) { - return; - } - old_size = thr->callstack_size; new_size = old_size + DUK_CALLSTACK_GROW_STEP; @@ -57327,10 +58907,28 @@ DUK_INTERNAL void duk_hthread_callstack_grow(duk_hthread *thr) { thr->callstack = new_ptr; thr->callstack_size = new_size; + if (thr->callstack_top > 0) { + thr->callstack_curr = thr->callstack + thr->callstack_top - 1; + } else { + thr->callstack_curr = NULL; + } + /* note: any entries above the callstack top are garbage and not zeroed */ } -DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { +/* 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; @@ -57338,10 +58936,6 @@ DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */ DUK_ASSERT(thr->callstack_size >= thr->callstack_top); - if (thr->callstack_size - thr->callstack_top < DUK_CALLSTACK_SHRINK_THRESHOLD) { - return; - } - new_size = thr->callstack_top + DUK_CALLSTACK_SHRINK_SPARE; DUK_ASSERT(new_size >= thr->callstack_top); @@ -57357,6 +58951,12 @@ DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { if (p) { thr->callstack = p; thr->callstack_size = new_size; + + if (thr->callstack_top > 0) { + thr->callstack_curr = thr->callstack + thr->callstack_top - 1; + } else { + thr->callstack_curr = NULL; + } } else { /* Because new_size != 0, if condition doesn't need to be * (p != NULL || new_size == 0). @@ -57368,7 +58968,19 @@ DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { /* note: any entries above the callstack top are garbage and not zeroed */ } -DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_top) { +DUK_INTERNAL void duk_hthread_callstack_shrink_check(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */ + DUK_ASSERT(thr->callstack_size >= thr->callstack_top); + + if (DUK_LIKELY(thr->callstack_size - thr->callstack_top < DUK_CALLSTACK_SHRINK_THRESHOLD)) { + return; + } + + duk__hthread_do_callstack_shrink(thr); +} + +DUK_INTERNAL void duk_hthread_callstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) { duk_size_t idx; DUK_DDD(DUK_DDDPRINT("unwind callstack top of thread %p from %ld to %ld", @@ -57397,9 +59009,7 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ while (idx > new_top) { duk_activation *act; duk_hobject *func; -#if defined(DUK_USE_REFERENCE_COUNTING) duk_hobject *tmp; -#endif #if defined(DUK_USE_DEBUGGER_SUPPORT) duk_heap *heap; #endif @@ -57441,12 +59051,12 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ DUK_TVAL_SET_NULL(tv_caller); /* no incref needed */ DUK_ASSERT(act->prev_caller == NULL); } - DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */ + DUK_TVAL_DECREF_NORZ(thr, &tv_tmp); } else { h_tmp = act->prev_caller; if (h_tmp) { act->prev_caller = NULL; - DUK_HOBJECT_DECREF(thr, h_tmp); /* side effects */ + DUK_HOBJECT_DECREF_NORZ(thr, h_tmp); } } act = thr->callstack + idx; /* avoid side effects */ @@ -57466,8 +59076,12 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ /* Pause for all step types: step into, step over, step out. * This is the only place explicitly handling a step out. */ - DUK_HEAP_SET_PAUSED(heap); - DUK_ASSERT(heap->dbg_step_thread == NULL); + if (duk_debug_is_paused(heap)) { + DUK_D(DUK_DPRINT("step pause trigger but already paused, ignoring")); + } else { + duk_debug_set_paused(heap); + DUK_ASSERT(heap->dbg_step_thread == NULL); + } } #endif @@ -57488,42 +59102,19 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ } /* func is NULL for lightfunc */ + /* Catch sites are required to clean up their environments + * in FINALLY part before propagating, so this should + * always hold here. + */ DUK_ASSERT(act->lex_env == act->var_env); + if (act->var_env != NULL) { DUK_DDD(DUK_DDDPRINT("closing var_env record %p -> %!O", (void *) act->var_env, (duk_heaphdr *) act->var_env)); - duk_js_close_environment_record(thr, act->var_env, func, act->idx_bottom); + duk_js_close_environment_record(thr, act->var_env); act = thr->callstack + idx; /* avoid side effect issues */ } -#if 0 - if (act->lex_env != NULL) { - if (act->lex_env == act->var_env) { - /* common case, already closed, so skip */ - DUK_DD(DUK_DDPRINT("lex_env and var_env are the same and lex_env " - "already closed -> skip closing lex_env")); - ; - } else { - DUK_DD(DUK_DDPRINT("closing lex_env record %p -> %!O", - (void *) act->lex_env, (duk_heaphdr *) act->lex_env)); - duk_js_close_environment_record(thr, act->lex_env, DUK_ACT_GET_FUNC(act), act->idx_bottom); - act = thr->callstack + idx; /* avoid side effect issues */ - } - } -#endif - - DUK_ASSERT((act->lex_env == NULL) || - ((duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->lex_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->lex_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->lex_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->lex_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL))); - - DUK_ASSERT((act->var_env == NULL) || - ((duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->var_env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->var_env, DUK_HTHREAD_STRING_INT_VARMAP(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->var_env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL) && - (duk_hobject_find_existing_entry_tval_ptr(thr->heap, act->var_env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL))); - skip_env_close: /* @@ -57536,55 +59127,38 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ } /* - * Reference count updates - * - * Note: careful manipulation of refcounts. The top is - * not updated yet, so all the activations are reachable - * for mark-and-sweep (which may be triggered by decref). - * However, the pointers are NULL so this is not an issue. + * Reference count updates, using NORZ macros so we don't + * need to handle side effects. */ -#if defined(DUK_USE_REFERENCE_COUNTING) - tmp = act->var_env; -#endif + DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->var_env); act->var_env = NULL; -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); - act = thr->callstack + idx; /* avoid side effect issues */ -#endif - -#if defined(DUK_USE_REFERENCE_COUNTING) - tmp = act->lex_env; -#endif + DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, act->lex_env); act->lex_env = NULL; -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); - act = thr->callstack + idx; /* avoid side effect issues */ -#endif /* Note: this may cause a corner case situation where a finalizer * may see a currently reachable activation whose 'func' is NULL. */ -#if defined(DUK_USE_REFERENCE_COUNTING) tmp = DUK_ACT_GET_FUNC(act); -#endif + DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp); + DUK_UNREF(tmp); act->func = NULL; -#if defined(DUK_USE_REFERENCE_COUNTING) - DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); - act = thr->callstack + idx; /* avoid side effect issues */ - DUK_UNREF(act); -#endif } thr->callstack_top = new_top; + if (new_top > 0) { + thr->callstack_curr = thr->callstack + new_top - 1; + } else { + thr->callstack_curr = NULL; + } /* * We could clear the book-keeping variables for the topmost activation, * but don't do so now. */ #if 0 - if (thr->callstack_top > 0) { - duk_activation *act = thr->callstack + thr->callstack_top - 1; + if (thr->callstack_curr != NULL) { + duk_activation *act = thr->callstack_curr; act->idx_retval = 0; } #endif @@ -57595,7 +59169,12 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ */ } -DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) { +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; @@ -57604,10 +59183,6 @@ DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) { DUK_ASSERT_DISABLE(thr->catchstack_top); /* avoid warning (unsigned) */ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); - if (thr->catchstack_top < thr->catchstack_size) { - return; - } - old_size = thr->catchstack_size; new_size = old_size + DUK_CATCHSTACK_GROW_STEP; @@ -57635,7 +59210,19 @@ DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) { /* note: any entries above the catchstack top are garbage and not zeroed */ } -DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) { +DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT_DISABLE(thr->catchstack_top); /* avoid warning (unsigned) */ + DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); + + if (DUK_LIKELY(thr->catchstack_top < thr->catchstack_size)) { + return; + } + + duk__hthread_do_catchstack_grow(thr); +} + +DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_catchstack_shrink(duk_hthread *thr) { duk_size_t new_size; duk_catcher *p; @@ -57643,10 +59230,6 @@ DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) { DUK_ASSERT_DISABLE(thr->catchstack_top >= 0); /* avoid warning (unsigned) */ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); - if (thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD) { - return; - } - new_size = thr->catchstack_top + DUK_CATCHSTACK_SHRINK_SPARE; DUK_ASSERT(new_size >= thr->catchstack_top); @@ -57673,7 +59256,19 @@ DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) { /* 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_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT_DISABLE(thr->catchstack_top >= 0); /* avoid warning (unsigned) */ + DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); + + if (DUK_LIKELY(thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD)) { + return; + } + + duk__hthread_do_catchstack_shrink(thr); +} + +DUK_INTERNAL void duk_hthread_catchstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) { duk_size_t idx; DUK_DDD(DUK_DDDPRINT("unwind catchstack top of thread %p from %ld to %ld", @@ -57729,7 +59324,8 @@ DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new 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_DECREF(thr, env); + 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. @@ -57741,6 +59337,100 @@ DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new /* 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); +} + +#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 end_off; + duk_ptrdiff_t bottom_off; + duk_ptrdiff_t top_off; + + if (thr->valstack == NULL) { + return; + } + + 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; + if (alloc_size == 0) { + return; + } + + new_ptr = (duk_tval *) DUK_ALLOC(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); + thr->valstack = new_ptr; + 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 */ /* * Shared helpers for arithmetic operations */ @@ -57901,6 +59591,8 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) { /* #include duk_internal.h -> already included */ +/* XXX: heap->error_not_allowed for success path too? */ + /* * Forward declarations. */ @@ -58056,16 +59748,19 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr, arg = duk_push_object_helper(ctx, 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, 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, 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; @@ -58375,7 +60070,7 @@ DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr, (long) num_stack_args, (long) idx_func, duk_get_tval(ctx, idx_func))); } while (--sanity > 0); - if (sanity == 0) { + if (DUK_UNLIKELY(sanity == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT); } @@ -58449,7 +60144,9 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func) return; } - act_callee = thr->callstack + thr->callstack_top - 1; + 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); /* XXX: check .caller writability? */ @@ -58930,16 +60627,6 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr, */ duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func); - /* Success path handles */ - DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth); - DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc); - - /* Longjmp state is kept clean in success path */ - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); - DUK_ASSERT(thr->heap->lj.iserror == 0); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); - thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; return DUK_EXEC_SUCCESS; @@ -58966,11 +60653,6 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr, idx_func, old_jmpbuf_ptr); - /* Longjmp state is cleaned up by error handling */ - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); - DUK_ASSERT(thr->heap->lj.iserror == 0); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); return DUK_EXEC_ERROR; } #if defined(DUK_USE_CPP_EXCEPTIONS) @@ -58996,6 +60678,7 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr, entry_ptr_curr_pc, idx_func, old_jmpbuf_ptr); + return DUK_EXEC_ERROR; } } catch (...) { @@ -59016,6 +60699,7 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr, entry_ptr_curr_pc, idx_func, old_jmpbuf_ptr); + return DUK_EXEC_ERROR; } } @@ -59206,7 +60890,8 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, duk_hthread_callstack_grow(thr); - if (thr->callstack_top > 0) { + act = thr->callstack_curr; + if (act != NULL) { /* * Update idx_retval of current activation. * @@ -59217,12 +60902,13 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, * the Ecmascript call's idx_retval must be set for things to work. */ - (thr->callstack + thr->callstack_top - 1)->idx_retval = entry_valstack_bottom_index + idx_func; + act->idx_retval = entry_valstack_bottom_index + idx_func; } DUK_ASSERT(thr->callstack_top < thr->callstack_size); act = thr->callstack + thr->callstack_top; thr->callstack_top++; + thr->callstack_curr = act; DUK_ASSERT(thr->callstack_top <= thr->callstack_size); DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */ DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); @@ -59313,7 +60999,7 @@ 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 + thr->callstack_top - 1; + act = thr->callstack_curr; } #endif @@ -59359,7 +61045,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, /* [ ... func this arg1 ... argN envobj ] */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; act->lex_env = env; act->var_env = env; DUK_HOBJECT_INCREF(thr, env); @@ -59411,7 +61097,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, * new value stack bottom, and call the target. */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) { /* * Ecmascript call @@ -59454,10 +61140,9 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); /* may need unwind */ DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); - DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1); - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top); duk_hthread_catchstack_shrink_check(thr); - duk_hthread_callstack_unwind(thr, entry_callstack_top); + 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; @@ -59519,7 +61204,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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(thr, entry_callstack_top); + duk_hthread_callstack_unwind_norz(thr, entry_callstack_top); duk_hthread_callstack_shrink_check(thr); thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; @@ -59574,9 +61259,12 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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; @@ -59589,7 +61277,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, * on every return should have no ill effect. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + 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; @@ -59602,6 +61290,14 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr, 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: @@ -59633,6 +61329,7 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, * 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); @@ -59654,9 +61351,9 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, * scopes; this is a sandboxing issue, described in: * https://github.com/svaarala/duktape/issues/476 */ - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top); duk_hthread_catchstack_shrink_check(thr); - duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_unwind_norz(thr, entry_callstack_top); duk_hthread_callstack_shrink_check(thr); thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; @@ -59707,9 +61404,12 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, 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; @@ -59722,7 +61422,7 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, * on every return should have no ill effect. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + 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; @@ -59734,6 +61434,21 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr, #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) duk__interrupt_fixup(thr, entry_curr_thread); #endif + + /* Error handling complete, remove side effect protections and + * process pending finalizers. + */ +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(thr->heap->error_not_allowed == 1); + thr->heap->error_not_allowed = 0; +#endif + DUK_ASSERT(thr->heap->pf_prevent_count > 0); + thr->heap->pf_prevent_count--; + DUK_DD(DUK_DDPRINT("call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count)); + + DUK_ASSERT_LJSTATE_UNSET(thr->heap); + + DUK_REFZERO_CHECK_SLOW(thr); } /* @@ -59831,12 +61546,6 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, entry_callstack_top, entry_catchstack_top); - /* Longjmp state is kept clean in success path */ - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); - DUK_ASSERT(thr->heap->lj.iserror == 0); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); - /* Note: either pointer may be NULL (at entry), so don't assert */ thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr; @@ -59856,12 +61565,6 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, entry_catchstack_top, old_jmpbuf_ptr); - /* Longjmp state is cleaned up by error handling */ - DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN); - DUK_ASSERT(thr->heap->lj.iserror == 0); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1)); - DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2)); - retval = DUK_EXEC_ERROR; } #if defined(DUK_USE_CPP_EXCEPTIONS) @@ -59906,6 +61609,8 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr, DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr); /* success/error path both do this */ + DUK_ASSERT_LJSTATE_UNSET(thr->heap); + duk__handle_safe_call_shared(thr, idx_retbase, num_stack_rets, @@ -60020,6 +61725,10 @@ DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr, DUK_ASSERT(thr->callstack_top == entry_callstack_top); duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc); + + DUK_ASSERT_LJSTATE_UNSET(thr->heap); + + DUK_REFZERO_CHECK_FAST(thr); return; thread_state_error: @@ -60054,6 +61763,7 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, * 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); @@ -60062,9 +61772,9 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); DUK_ASSERT(thr->callstack_top >= entry_callstack_top); - duk_hthread_catchstack_unwind(thr, entry_catchstack_top); + duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top); duk_hthread_catchstack_shrink_check(thr); - duk_hthread_callstack_unwind(thr, entry_callstack_top); + duk_hthread_callstack_unwind_norz(thr, entry_callstack_top); duk_hthread_callstack_shrink_check(thr); thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index; @@ -60096,6 +61806,21 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr, thr->heap->lj.iserror = 0; DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */ + + /* Error handling complete, remove side effect protections and + * process pending finalizers. + */ +#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("safe call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count)); + + DUK_ASSERT_LJSTATE_UNSET(thr->heap); + + DUK_REFZERO_CHECK_SLOW(thr); } DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr, @@ -60142,6 +61867,8 @@ 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); } /* @@ -60329,7 +62056,8 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, DUK_ASSERT(thr->callstack_top >= 1); DUK_ASSERT((call_flags & DUK_CALL_FLAG_IS_RESUME) == 0); - act = thr->callstack + thr->callstack_top - 1; + 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")); @@ -60376,16 +62104,17 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, break; } } - duk_hthread_catchstack_unwind(thr, i_stk + 1); + 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(thr, thr->callstack_top - 1); + 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++; - DUK_ASSERT(thr->callstack_top <= thr->callstack_size); - act = thr->callstack + thr->callstack_top - 1; + thr->callstack_curr = act; /* Start filling in the activation */ act->func = func; /* don't want an intermediate exposed state with func == NULL */ @@ -60402,7 +62131,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, 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 + thr->callstack_top - 1; /* side effects (currently none though) */ + act = thr->callstack_curr; /* side effects (currently none though) */ #endif #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) @@ -60414,7 +62143,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, * is in use. */ duk__update_func_caller_prop(thr, func); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; #endif act->flags = (DUK_HOBJECT_HAS_STRICT(func) ? @@ -60471,7 +62200,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, 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 + 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; @@ -60480,6 +62209,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, 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)); @@ -60512,7 +62242,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY) duk__update_func_caller_prop(thr, func); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; #endif } @@ -60571,7 +62301,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, /* [ ... arg1 ... argN envobj ] */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; act->lex_env = env; act->var_env = env; DUK_HOBJECT_INCREF(thr, act->lex_env); @@ -60607,6 +62337,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr, * the topmost activation. */ + DUK_REFZERO_CHECK_FAST(thr); return 1; } /* @@ -62609,6 +64340,8 @@ DUK_LOCAL duk_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_r duk_pop(ctx); return ret; #else + DUK_UNREF(comp_ctx); + DUK_UNREF(rc); DUK_ASSERT((rc & DUK__CONST_MARKER) == 0); /* caller removes const marker */ return 0; #endif @@ -62860,15 +64593,31 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x DUK_DDD(DUK_DDDPRINT("arith inline check: d1=%lf, d2=%lf, op=%ld", (double) d1, (double) d2, (long) x->op)); switch (x->op) { - case DUK_OP_ADD: d3 = d1 + d2; break; - case DUK_OP_SUB: d3 = d1 - d2; break; - case DUK_OP_MUL: d3 = d1 * d2; break; - case DUK_OP_DIV: d3 = d1 / d2; break; + case DUK_OP_ADD: { + d3 = d1 + d2; + break; + } + case DUK_OP_SUB: { + d3 = d1 - d2; + break; + } + case DUK_OP_MUL: { + d3 = d1 * d2; + break; + } + case DUK_OP_DIV: { + d3 = d1 / d2; + break; + } case DUK_OP_EXP: { d3 = (duk_double_t) duk_js_arith_pow((double) d1, (double) d2); break; } - default: accept_fold = 0; break; + default: { + d3 = 0.0; /* Won't be used, but silence MSVC /W4 warning. */ + accept_fold = 0; + break; + } } if (accept_fold) { @@ -66782,6 +68531,7 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ duk_small_uint_t stmt_flags = 0; duk_int_t label_id = -1; duk_small_uint_t tok; + duk_bool_t test_func_decl; DUK__RECURSION_INCREASE(comp_ctx, thr); @@ -66847,17 +68597,15 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_ * for function statements are modelled after V8, see * test-dev-func-decl-outside-top.js. */ - + test_func_decl = allow_source_elem; #if defined(DUK_USE_NONSTD_FUNC_STMT) /* Lenient: allow function declarations outside top level in * non-strict mode but reject them in strict mode. */ - if (allow_source_elem || !comp_ctx->curr_func.is_strict) -#else /* DUK_USE_NONSTD_FUNC_STMT */ - /* Strict: never allow function declarations outside top level. */ - if (allow_source_elem) + test_func_decl = test_func_decl || !comp_ctx->curr_func.is_strict; #endif /* DUK_USE_NONSTD_FUNC_STMT */ - { + /* Strict: never allow function declarations outside top level. */ + if (test_func_decl) { /* FunctionDeclaration: not strictly a statement but handled as such. * * O(depth^2) parse count for inner functions is handled by recording a @@ -67569,6 +69317,9 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct (duk_tval *) duk_get_tval(ctx, -2), (duk_tval *) duk_get_tval(ctx, -1))); +#if defined(DUK_USE_FASTINT) + DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(ctx, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(ctx, -1))); +#endif duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ } @@ -67634,8 +69385,7 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct duk_push_null(ctx); declvar_flags = DUK_PROPDESC_FLAG_WRITABLE | - DUK_PROPDESC_FLAG_ENUMERABLE | - DUK_BC_DECLVAR_FLAG_UNDEF_VALUE; + DUK_PROPDESC_FLAG_ENUMERABLE; if (configurable_bindings) { declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; } @@ -68312,9 +70062,9 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { DUK_ASSERT(lex_pt != NULL); flags = comp_stk->flags; - is_eval = (flags & DUK_JS_COMPILE_FLAG_EVAL ? 1 : 0); - is_strict = (flags & DUK_JS_COMPILE_FLAG_STRICT ? 1 : 0); - is_funcexpr = (flags & DUK_JS_COMPILE_FLAG_FUNCEXPR ? 1 : 0); + is_eval = (flags & DUK_COMPILE_EVAL ? 1 : 0); + 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 */ @@ -68390,7 +70140,7 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { */ DUK_ASSERT(func->is_setget == 0); - func->is_strict = is_strict; + func->is_strict = (duk_uint8_t) is_strict; DUK_ASSERT(func->is_notail == 0); if (is_funcexpr) { @@ -68405,8 +70155,9 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) { (void) duk__parse_func_like_raw(comp_ctx, 0 /*flags*/); } else { DUK_ASSERT(func->is_function == 0); - func->is_eval = is_eval; - func->is_global = !is_eval; + DUK_ASSERT(is_eval == 0 || is_eval == 1); + func->is_eval = (duk_uint8_t) is_eval; + func->is_global = (duk_uint8_t) !is_eval; DUK_ASSERT(func->is_namebinding == 0); DUK_ASSERT(func->is_constructable == 0); @@ -68446,6 +70197,7 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer DUK_LEXER_INITCTX(&comp_stk.comp_ctx_alloc.lex); comp_stk.comp_ctx_alloc.lex.input = src_buffer; comp_stk.comp_ctx_alloc.lex.input_length = src_length; + comp_stk.comp_ctx_alloc.lex.flags = flags; /* Forward flags directly for now. */ /* [ ... filename ] */ @@ -69276,7 +71028,7 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, DUK_ASSERT(DUK_TVAL_IS_STRING(tv_id)); name = DUK_TVAL_GET_STRING(tv_id); DUK_ASSERT(name != NULL); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [ ... val this ] */ /* XXX: Fastint fast path would be useful here. Also fastints @@ -69295,13 +71047,13 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr, if (op & 0x02) { duk_push_number(ctx, y); /* -> [ ... x this y ] */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict); duk_pop_2(ctx); /* -> [ ... x ] */ } else { duk_pop_2(ctx); /* -> [ ... ] */ duk_push_number(ctx, y); /* -> [ ... y ] */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict); } @@ -69431,17 +71183,19 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type); - duk_hthread_catchstack_unwind(thr, cat_idx + 1); - duk_hthread_callstack_unwind(thr, thr->catchstack[cat_idx].callstack_index + 1); + duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1); + duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1); DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + 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); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); act->curr_pc = thr->catchstack[cat_idx].pc_base + 0; /* +0 = catch */ act = NULL; @@ -69456,8 +71210,7 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval */ if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(&thr->catchstack[cat_idx])) { - duk_hobject *new_env; - duk_hobject *act_lex_env; + duk_hdecenv *new_env; DUK_DDD(DUK_DDDPRINT("catcher has an automatic catch binding")); @@ -69465,7 +71218,8 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval * points, so we re-lookup it multiple times. */ DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); if (act->lex_env == NULL) { DUK_ASSERT(act->var_env == NULL); @@ -69473,22 +71227,26 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval /* this may have side effects, so re-lookup act */ duk_js_init_activation_environment_records_delayed(thr, act); - act = thr->callstack + thr->callstack_top - 1; + 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 */ - act = thr->callstack + thr->callstack_top - 1; - act_lex_env = act->lex_env; - act = NULL; /* invalidated */ - - new_env = duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - act_lex_env); + /* XXX: If an out-of-memory happens here, longjmp state asserts + * will be triggered at present and a try-catch fails to catch. + * That's not sandboxing fatal (C API protected calls are what + * matters), and script catch code can immediately throw anyway + * for almost any operation. + */ + 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_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env)); /* Note: currently the catch binding is handled without a register @@ -69497,14 +71255,20 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval * record regbases etc. */ + /* XXX: duk_xdef_prop() may cause an out-of-memory, see above. */ DUK_ASSERT(thr->catchstack[cat_idx].h_varname != NULL); duk_push_hstring(ctx, thr->catchstack[cat_idx].h_varname); duk_push_tval(ctx, thr->valstack + thr->catchstack[cat_idx].idx_base); duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_W); /* writable, not configurable */ - act = thr->callstack + thr->callstack_top - 1; - act->lex_env = new_env; - DUK_HOBJECT_INCREF(thr, new_env); /* reachable through activation */ + 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; + DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env); /* reachable through activation */ + /* Net refcount change to act->lex_env is 0: incref for new_env's + * prototype, decref for act->lex_env overwrite. + */ DUK_CAT_SET_LEXENV_ACTIVE(&thr->catchstack[cat_idx]); @@ -69524,17 +71288,19 @@ DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_size_t cat_idx, duk_tva duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type); - duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* cat_idx catcher is kept, even for finally */ - duk_hthread_callstack_unwind(thr, thr->catchstack[cat_idx].callstack_index + 1); + 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_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + 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); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); act->curr_pc = thr->catchstack[cat_idx].pc_base + 1; /* +1 = finally */ act = NULL; @@ -69547,7 +71313,8 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack + 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))); @@ -69556,13 +71323,14 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small act->curr_pc = thr->catchstack[cat_idx].pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0); act = NULL; /* invalidated */ - duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* keep label catcher */ + duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1); /* keep label catcher */ /* no need to unwind callstack */ /* valstack should not need changes */ #if defined(DUK_USE_ASSERTIONS) DUK_ASSERT(thr->callstack_top >= 1); - act = thr->callstack + thr->callstack_top - 1; + 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); #endif @@ -69584,7 +71352,7 @@ DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_siz tv1 = resumer->valstack + resumer->callstack[act_idx].idx_retval; /* return value from Duktape.Thread.resume() */ DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable); /* side effects */ - duk_hthread_callstack_unwind(resumer, act_idx + 1); /* unwind to 'resume' caller */ + duk_hthread_callstack_unwind_norz(resumer, act_idx + 1); /* unwind to 'resume' caller */ /* no need to unwind catchstack */ duk__reconfig_valstack_ecma_return(resumer, act_idx); @@ -69647,10 +71415,11 @@ 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(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_resume); - DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */ + DUK_ASSERT(thr->callstack_curr != 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)); @@ -69665,11 +71434,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED || resumee->callstack_top >= 2); /* YIELDED: Ecmascript activation + Duktape.Thread.yield() activation */ DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED || - (DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1))->func == duk_bi_thread_yield)); + (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 + resumee->callstack_top - 2)->idx_retval >= 0); /* idx_retval unsigned */ + (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 */ @@ -69710,7 +71479,7 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, tv2 = &thr->heap->lj.value1; DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2); /* side effects */ - duk_hthread_callstack_unwind(resumee, act_idx + 1); /* unwind to 'yield' caller */ + duk_hthread_callstack_unwind_norz(resumee, act_idx + 1); /* unwind to 'yield' caller */ /* no need to unwind catchstack */ @@ -69789,24 +71558,26 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, DUK_ASSERT(thr != entry_thread); /* Duktape.Thread.yield() should prevent */ 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(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_yield); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* an Ecmascript function */ - DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */ + DUK_ASSERT(thr->callstack_curr != 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 */ resumer = thr->resumer; DUK_ASSERT(resumer != NULL); 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(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1))->func == duk_bi_thread_resume); - DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2))); /* an Ecmascript function */ - DUK_ASSERT_DISABLE((resumer->callstack + resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */ + DUK_ASSERT(resumer->callstack_curr != 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 */ if (thr->heap->lj.iserror) { thr->state = DUK_HTHREAD_STATE_YIELDED; @@ -69908,9 +71679,9 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, * final catcher unwind everything */ #if 0 - duk_hthread_catchstack_unwind(thr, (cat - thr->catchstack) + 1); /* leave 'cat' as top catcher (also works if catchstack exhausted) */ - duk_hthread_callstack_unwind(thr, entry_callstack_index + 1); - + 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; @@ -69927,11 +71698,12 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, DUK_ASSERT(thr->resumer != NULL); DUK_ASSERT(thr->resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */ - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */ - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */ + 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 */ resumer = thr->resumer; @@ -69973,6 +71745,8 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr, 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_GC_TORTURE(thr->heap); + just_return: return retval; @@ -70113,6 +71887,7 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, cat = thr->catchstack + thr->catchstack_top - 1; /* may be < thr->catchstack initially */ DUK_ASSERT(thr->callstack_top > 0); /* ensures callstack_top - 1 >= 0 */ + DUK_ASSERT(thr->callstack_curr != NULL); orig_callstack_index = thr->callstack_top - 1; while (cat >= thr->catchstack) { @@ -70160,22 +71935,22 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr, */ DUK_DDD(DUK_DDDPRINT("return to Ecmascript caller, idx_retval=%ld, lj_value1=%!T", - (long) (thr->callstack + thr->callstack_top - 2)->idx_retval, + (long) (thr->callstack_curr - 1)->idx_retval, (duk_tval *) &thr->heap->lj.value1)); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* must be ecmascript */ + DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr - 1))); /* must be ecmascript */ - tv1 = thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval; + tv1 = thr->valstack + (thr->callstack_curr - 1)->idx_retval; 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 + thr->callstack_top - 2)->idx_retval, - (duk_tval *) (thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval))); + (long) (thr->callstack_curr - 1)->idx_retval, + (duk_tval *) (thr->valstack + (thr->callstack_curr - 1)->idx_retval))); - duk_hthread_catchstack_unwind(thr, new_cat_top); /* leave 'cat' as top catcher (also works if catchstack exhausted) */ - duk_hthread_callstack_unwind(thr, thr->callstack_top - 1); + 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_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller")); @@ -70187,12 +71962,13 @@ 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(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL && - DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) && - ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */ - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL && - DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */ - DUK_ASSERT_DISABLE((thr->resumer->callstack + thr->resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */ + 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_DISABLE((thr->resumer->callstack_curr - 1)->idx_retval >= 0); /* unsigned */ DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED); @@ -70265,7 +72041,8 @@ 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 + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); /* It might seem that replacing 'thr->heap' with just 'heap' below * might be a good idea, but it increases code size slightly @@ -70290,8 +72067,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ (line != thr->heap->dbg_step_startline)) { DUK_D(DUK_DPRINT("STEP STATE TRIGGERED PAUSE at line %ld", (long) line)); - - DUK_HEAP_SET_PAUSED(thr->heap); + duk_debug_set_paused(thr->heap); } /* Check for breakpoints only on line transition. @@ -70317,8 +72093,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ if (act->prev_line != bp->line && line == bp->line) { DUK_D(DUK_DPRINT("BREAKPOINT TRIGGERED at %!O:%ld", (duk_heaphdr *) bp->filename, (long) bp->line)); - - DUK_HEAP_SET_PAUSED(thr->heap); + duk_debug_set_paused(thr->heap); } } } else { @@ -70405,8 +72180,9 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ * above, so we must recheck attach status. */ - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { - act = thr->callstack + thr->callstack_top - 1; /* relookup, may have changed */ + if (duk_debug_is_attached(thr->heap)) { + act = thr->callstack_curr; /* relookup, may have changed */ + 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) && @@ -70429,7 +72205,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_ } #endif /* DUK_USE_DEBUGGER_SUPPORT */ -DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { +DUK_LOCAL DUK_NOINLINE DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { duk_int_t ctr; duk_activation *act; duk_hcompfunc *fun; @@ -70479,7 +72255,8 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { } DUK_HEAP_SET_INTERRUPT_RUNNING(thr->heap); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC((duk_hobject *) fun)); @@ -70515,7 +72292,7 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) { * detaching (to finish off the pending detach). */ duk__interrupt_handle_debugger(thr, &immediate, &retval); - act = thr->callstack + thr->callstack_top - 1; /* relookup if changed */ + act = thr->callstack_curr; /* relookup if changed */ DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */ } #endif /* DUK_USE_DEBUGGER_SUPPORT */ @@ -70657,7 +72434,7 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * (thr->heap->dbg_step_thread != thr || thr->heap->dbg_step_csindex != thr->callstack_top - 1)) { DUK_D(DUK_DPRINT("STEP INTO ACTIVE, FORCE PAUSED")); - DUK_HEAP_SET_PAUSED(thr->heap); + duk_debug_set_paused(thr->heap); } /* Force interrupt right away if we're paused or in "checked mode". @@ -70712,7 +72489,7 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * #if defined(DUK_USE_EXEC_FUN_LOCAL) #define DUK__FUN() fun #else -#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack + (thr)->callstack_top - 1)) +#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr)) #endif #define DUK__STRICT() (DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN())) @@ -70789,12 +72566,12 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation * #define DUK__SYNC_CURR_PC() do { \ duk_activation *act; \ - act = thr->callstack + thr->callstack_top - 1; \ + act = thr->callstack_curr; \ act->curr_pc = curr_pc; \ } while (0) #define DUK__SYNC_AND_NULL_CURR_PC() do { \ duk_activation *act; \ - act = thr->callstack + thr->callstack_top - 1; \ + act = thr->callstack_curr; \ act->curr_pc = curr_pc; \ thr->ptr_curr_pc = NULL; \ } while (0) @@ -70846,15 +72623,27 @@ DUK_LOCAL void duk__handle_executor_error(duk_heap *heap, lj_ret = duk__handle_longjmp(heap->curr_thread, entry_thread, entry_callstack_top); + /* Error handling complete, remove side effect protections. + */ +#if defined(DUK_USE_ASSERTIONS) + DUK_ASSERT(heap->error_not_allowed == 1); + heap->error_not_allowed = 0; +#endif + DUK_ASSERT(heap->pf_prevent_count > 0); + heap->pf_prevent_count--; + DUK_DD(DUK_DDPRINT("executor error handled, pf_prevent_count updated to %ld", (long) heap->pf_prevent_count)); + if (lj_ret == DUK__LONGJMP_RESTART) { /* Restart bytecode execution, possibly with a changed thread. */ - ; + DUK_REFZERO_CHECK_SLOW(heap->curr_thread); } else { - /* Rethrow error to calling state. */ - DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); + /* If an error is propagated, don't run refzero checks here. + * The next catcher will deal with that. Pf_prevent_count + * will be re-bumped by the longjmp. + */ - /* Longjmp handling has restored jmpbuf_ptr. */ - DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); + DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); /* Rethrow error to calling state. */ + DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); /* Longjmp handling has restored jmpbuf_ptr. */ /* Thread may have changed, e.g. YIELD converted to THROW. */ duk_err_longjmp(heap->curr_thread); @@ -70877,8 +72666,10 @@ 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(DUK_ACT_GET_FUNC(exec_thr->callstack + exec_thr->callstack_top - 1) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack + exec_thr->callstack_top - 1))); + 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))); + + DUK_GC_TORTURE(exec_thr->heap); entry_thread = exec_thr; heap = entry_thread->heap; @@ -70969,7 +72760,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { } /* Inner executor, performance critical. */ -DUK_LOCAL DUK_NOINLINE 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_size_t entry_callstack_top) { /* 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 @@ -71010,6 +72801,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th #endif #endif + DUK_GC_TORTURE(entry_thread->heap); + /* * Restart execution by reloading thread state. * @@ -71059,8 +72852,11 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th thr = entry_thread->heap->curr_thread; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->callstack_top >= 1); - DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + 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_GC_TORTURE(thr->heap); thr->ptr_curr_pc = &curr_pc; @@ -71074,7 +72870,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th /* Assume interrupt init/counter are properly initialized here. */ /* Assume that thr->valstack_bottom has been set-up before getting here. */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); DUK_ASSERT(fun != NULL); DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs); @@ -71082,9 +72879,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT(consts != NULL); #if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && !thr->heap->dbg_processing) { + if (duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing) { duk__executor_recheck_debugger(thr, act, fun); - act = thr->callstack + thr->callstack_top - 1; /* relookup after side effects (no side effects currently however) */ + act = thr->callstack_curr; /* relookup after side effects (no side effects currently however) */ + DUK_ASSERT(act != NULL); } #endif /* DUK_USE_DEBUGGER_SUPPORT */ @@ -71134,10 +72932,11 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th duk_small_uint_t exec_int_ret; /* Write curr_pc back for the debugger. */ - DUK_ASSERT(thr->callstack_top > 0); { duk_activation *act; - act = thr->callstack + thr->callstack_top - 1; + DUK_ASSERT(thr->callstack_top > 0); + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); act->curr_pc = (duk_instr_t *) curr_pc; } @@ -71171,7 +72970,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th #if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG) { duk_activation *act; - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; DUK_ASSERT(curr_pc >= DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN())); DUK_ASSERT(curr_pc < DUK_HCOMPFUNC_GET_CODE_END(thr->heap, DUK__FUN())); DUK_UNREF(act); /* if debugging disabled */ @@ -71463,7 +73262,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT(DUK_TVAL_IS_STRING(tv)); name = DUK_TVAL_GET_STRING(tv); tv = NULL; /* lookup has side effects */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) { /* -> [... val this] */ tv = DUK_GET_TVAL_NEGIDX(ctx, -2); @@ -72191,14 +73990,12 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th duk_hstring *name; duk_small_uint_t prop_flags; duk_bool_t is_func_decl; - duk_bool_t is_undef_value; tv1 = DUK__REGCONSTP_B(ins); DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); - is_undef_value = ((a & DUK_BC_DECLVAR_FLAG_UNDEF_VALUE) != 0); is_func_decl = ((a & DUK_BC_DECLVAR_FLAG_FUNC_DECL) != 0); /* XXX: declvar takes an duk_tval pointer, which is awkward and @@ -72210,24 +74007,24 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th */ prop_flags = a & DUK_PROPDESC_FLAGS_MASK; - if (is_undef_value) { + if (is_func_decl) { + duk_push_tval(ctx, DUK__REGCONSTP_C(ins)); + } else { DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */ thr->valstack_top++; - } else { - duk_push_tval(ctx, DUK__REGCONSTP_C(ins)); } tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) { - if (is_undef_value) { - /* Already declared but no initializer value - * (e.g. 'var xyz;'), no-op. - */ - } else { + if (is_func_decl) { /* Already declared, update value. */ tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); + } else { + /* Already declared but no initializer value + * (e.g. 'var xyz;'), no-op. + */ } } @@ -72282,7 +74079,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ idx = (duk_uint_fast_t) DUK_DEC_A(ins); @@ -72309,7 +74106,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT_DISABLE(bc >= 0); /* unsigned */ DUK_ASSERT((duk_uint_t) bc < (duk_uint_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN())); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; fun_act = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act); fun_temp = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun_act)[bc]; DUK_ASSERT(fun_temp != NULL); @@ -72321,7 +74118,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th if (act->lex_env == NULL) { DUK_ASSERT(act->var_env == NULL); duk_js_init_activation_environment_records_delayed(thr, act); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; } DUK_ASSERT(act->lex_env != NULL); DUK_ASSERT(act->var_env != NULL); @@ -72348,7 +74145,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; (void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */ duk_pop(ctx); /* 'this' binding is not needed here */ DUK__REPLACE_TOP_A_BREAK(); @@ -72369,7 +74166,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th */ tv1 = DUK__REGP_A(ins); /* val */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT()); break; } @@ -72384,7 +74181,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); name = DUK_TVAL_GET_STRING(tv1); DUK_ASSERT(name != NULL); - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; rc = duk_js_delvar_activation(thr, act, name); DUK__REPLACE_BOOL_A_BREAK(rc); } @@ -72445,6 +74242,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th thr->valstack_top++; DUK__RETURN_SHARED(); } + /* This will be unused without refcounting. */ case DUK_OP_RETCONST: { duk_tval *tv; @@ -72461,7 +74259,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th DUK__SYNC_AND_NULL_CURR_PC(); tv = DUK__CONSTP_BC(ins); DUK_TVAL_SET_TVAL(thr->valstack_top, tv); +#if defined(DUK_USE_REFERENCE_COUNTING) + /* Without refcounting only RETCONSTN is used. */ DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); /* no INCREF for this constant */ +#endif thr->valstack_top++; DUK__RETURN_SHARED(); } @@ -72585,57 +74386,44 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th a = DUK_DEC_A(ins); bc = DUK_DEC_BC(ins); - act = thr->callstack + thr->callstack_top - 1; - DUK_ASSERT(thr->callstack_top >= 1); - - /* 'with' target must be created first, in case we run out of memory */ - /* XXX: refactor out? */ - - if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) { - DUK_DDD(DUK_DDDPRINT("need to initialize a with binding object")); - - if (act->lex_env == NULL) { - DUK_ASSERT(act->var_env == NULL); - DUK_DDD(DUK_DDDPRINT("delayed environment initialization")); - - /* must relookup act in case of side effects */ - duk_js_init_activation_environment_records_delayed(thr, act); - act = thr->callstack + thr->callstack_top - 1; - DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */ - } - DUK_ASSERT(act->lex_env != NULL); - DUK_ASSERT(act->var_env != NULL); - - (void) duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV), - -1); /* no prototype, updated below */ - - duk_push_tval(ctx, DUK__REGP(bc)); - duk_to_object(ctx, -1); - duk_dup_top(ctx); - - /* [ ... env target ] */ - /* [ ... env target target ] */ - - duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); - duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); /* always provideThis=true */ - - /* [ ... env ] */ + /* 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); - DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iT", - (duk_tval *) duk_get_tval(ctx, -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 (should be atomic) */ + /* 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_hthread_catchstack_grow(thr); - cat = thr->catchstack + thr->catchstack_top; 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; @@ -72646,7 +74434,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th 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__REGP(bc); + tv1 = DUK_GET_TVAL_NEGIDX(thr, -1); DUK_ASSERT(DUK_TVAL_IS_STRING(tv1)); /* borrowed reference; although 'tv1' comes from a register, @@ -72655,54 +74443,69 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th */ cat->h_varname = DUK_TVAL_GET_STRING(tv1); } else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) { - /* env created above to stack top */ - duk_hobject *new_env; + duk_hobjenv *env; + duk_hobject *target; - DUK_DDD(DUK_DDDPRINT("lexenv active flag set to catcher")); - cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE; + /* 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_DDD(DUK_DDDPRINT("activating object env: %!iT", - (duk_tval *) duk_get_tval(ctx, -1))); - new_env = DUK_GET_HOBJECT_NEGIDX(ctx, -1); - DUK_ASSERT(new_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); - act = thr->callstack + thr->callstack_top - 1; /* relookup (side effects) */ + /* 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_UPDREF(thr, new_env, act->lex_env); /* side effects */ + 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. + */ - act = thr->callstack + thr->callstack_top - 1; /* relookup (side effects) */ - act->lex_env = new_env; - DUK_HOBJECT_INCREF(thr, new_env); - duk_pop(ctx); + /* 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 { ; } - /* 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_to_undefined(ctx, bc); - duk_to_undefined(ctx, bc + 1); - - cat = thr->catchstack + thr->catchstack_top - 1; /* relookup (side effects) */ - 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; - 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); + curr_pc += 2; /* skip jump slots */ break; } @@ -72756,7 +74559,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th cat = thr->catchstack + thr->catchstack_top - 1; DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); /* cleared before entering catch part */ - act = thr->callstack + thr->callstack_top - 1; + act = thr->callstack_curr; + DUK_ASSERT(act != NULL); if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) { duk_hobject *prev_env; @@ -72771,6 +74575,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th 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 */ } @@ -72895,7 +74700,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th duk_push_tval(ctx, thr->valstack + cat->idx_base); - duk_err_setup_heap_ljstate(thr, (duk_small_int_t) cont_type); + 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); @@ -72932,7 +74738,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th (duk_tval *) duk_get_tval(ctx, -1))); #endif - duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW); + duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1)); +#if defined(DUK_USE_DEBUGGER_SUPPORT) + duk_err_check_debugger_integration(thr); +#endif DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */ duk_err_longjmp(thr); @@ -73469,7 +75278,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th * from precompiled bytecode. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) - if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) { + if (duk_debug_is_attached(thr->heap)) { DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution")); DUK__SYNC_AND_NULL_CURR_PC(); duk_debug_halt_execution(thr, 1 /*use_prev_pc*/); @@ -73562,22 +75371,18 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th case DUK_OP_UNUSED252: case DUK_OP_UNUSED253: case DUK_OP_UNUSED254: - case DUK_OP_UNUSED255: { - /* Force all case clauses to map to an actual handler - * so that the compiler can emit a jump without a bounds - * check: the switch argument is a duk_uint8_t so that - * the compiler may be able to figure it out. This is - * a small detail and obviously compiler dependent. - */ - volatile duk_small_int_t dummy_volatile; - dummy_volatile = 0; - DUK_UNREF(dummy_volatile); - DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins)); - DUK__INTERNAL_ERROR("invalid opcode"); - break; - } + case DUK_OP_UNUSED255: + /* Force all case clauses to map to an actual handler + * so that the compiler can emit a jump without a bounds + * check: the switch argument is a duk_uint8_t so that + * the compiler may be able to figure it out. This is + * a small detail and obviously compiler dependent. + */ + /* default: clause omitted on purpose */ +#else + default: #endif /* DUK_USE_EXEC_PREFER_SIZE */ - default: { + { /* Default case catches invalid/unsupported opcodes. */ DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins)); DUK__INTERNAL_ERROR("invalid opcode"); @@ -73893,7 +75698,7 @@ DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) { case DUK_TAG_STRING: { /* For Symbols ToNumber() is always a TypeError. */ duk_hstring *h = DUK_TVAL_GET_STRING(tv); - if (DUK_HSTRING_HAS_SYMBOL(h)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) { DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL); } duk_push_hstring(ctx, h); @@ -74625,7 +76430,7 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, DUK_ASSERT(h1 != NULL); DUK_ASSERT(h2 != NULL); - if (!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2)) { + if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) { rc = duk_js_string_compare(h1, h2); duk_pop_2(ctx); if (rc < 0) { @@ -74755,7 +76560,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ /* func support for [[HasInstance]] checked in the beginning of the loop */ } while (--sanity > 0); - if (sanity == 0) { + if (DUK_UNLIKELY(sanity == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT); } @@ -74841,7 +76646,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val); } while (--sanity > 0); - if (sanity == 0) { + if (DUK_UNLIKELY(sanity == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); } DUK_UNREACHABLE(); @@ -74942,7 +76747,7 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) { /* All internal keys are identified as Symbols. */ str = DUK_TVAL_GET_STRING(tv_x); DUK_ASSERT(str != NULL); - if (DUK_HSTRING_HAS_SYMBOL(str)) { + if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(str))) { stridx = DUK_STRIDX_LC_SYMBOL; } else { stridx = DUK_STRIDX_LC_STRING; @@ -74992,32 +76797,30 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) { * * Array index: E5 Section 15.4 * Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write) - * - * duk_js_to_arrayindex_string_helper() computes the array index from - * string contents alone. Depending on options it's only called during - * string intern (and value stored to duk_hstring) or it's called also - * at runtime. */ -DUK_INTERNAL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t *str, duk_uint32_t blen, duk_uarridx_t *out_idx) { +/* Compure array index from string context, or return a "not array index" + * indicator. + */ +DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen) { duk_uarridx_t res; - if (blen == 0 || blen > 10) { - goto parse_fail; - } - if (str[0] == DUK_ASC_0 && blen > 1) { - goto parse_fail; - } - - /* Accept 32-bit decimal integers, no leading zeroes, signs, etc. - * Leading zeroes are not accepted (zero index "0" is an exception - * handled above). + /* Only strings with byte length 1-10 can be 32-bit array indices. + * Leading zeroes (except '0' alone), plus/minus signs are not allowed. + * We could do a lot of prechecks here, but since most strings won't + * start with any digits, it's simpler to just parse the number and + * fail quickly. */ res = 0; - while (blen-- > 0) { - duk_uint8_t c = *str++; - if (c >= DUK_ASC_0 && c <= DUK_ASC_9) { + if (blen == 0) { + goto parse_fail; + } + do { + duk_uarridx_t dig; + dig = (duk_uarridx_t) (*str++) - DUK_ASC_0; + + if (dig <= 9U) { /* Careful overflow handling. When multiplying by 10: * - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding * 0...9 is safe. @@ -75031,44 +76834,75 @@ DUK_INTERNAL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t * goto parse_fail; } DUK_ASSERT(res == 0x19999999UL); - c -= DUK_ASC_0; - if (c >= 6) { + if (dig >= 6U) { goto parse_fail; } - res = 0xfffffffaUL + c; - DUK_ASSERT(res >= 0xfffffffaUL && res <= 0xffffffffUL); + res = 0xfffffffaUL + dig; + DUK_ASSERT(res >= 0xfffffffaUL); + DUK_ASSERT_DISABLE(res <= 0xffffffffUL); /* range */ } else { - res = res * 10U + (duk_uint32_t) (c - DUK_ASC_0); + res = res * 10U + dig; + if (DUK_UNLIKELY(res == 0)) { + /* If 'res' is 0, previous 'res' must + * have been 0 and we scanned in a zero. + * This is only allowed if blen == 1, + * i.e. the exact string '0'. + */ + if (blen == (duk_uint32_t) 1) { + return 0; + } + goto parse_fail; + } } } else { + /* Because 'dig' is unsigned, catches both values + * above '9' and below '0'. + */ goto parse_fail; } - } + } while (--blen > 0); - *out_idx = res; - return 1; + return res; parse_fail: - *out_idx = DUK_HSTRING_NO_ARRAY_INDEX; - return 0; + return DUK_HSTRING_NO_ARRAY_INDEX; } -/* Called by duk_hstring.h macros */ -DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) { +#if !defined(DUK_USE_HSTRING_ARRIDX) +/* Get array index for a string which is known to be an array index. This helper + * is needed when duk_hstring doesn't concretely store the array index, but strings + * are flagged as array indices at intern time. + */ +DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h) { + const duk_uint8_t *p; duk_uarridx_t res; - duk_small_int_t rc; + duk_uint8_t t; + + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(h)); + + p = DUK_HSTRING_GET_DATA(h); + res = 0; + for (;;) { + t = *p++; + if (DUK_UNLIKELY(t == 0)) { + /* 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); + } + return res; +} +DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h) { + DUK_ASSERT(h != NULL); if (!DUK_HSTRING_HAS_ARRIDX(h)) { return DUK_HSTRING_NO_ARRAY_INDEX; } - - rc = duk_js_to_arrayindex_raw_string(DUK_HSTRING_GET_DATA(h), - DUK_HSTRING_GET_BYTELEN(h), - &res); - DUK_UNREF(rc); - DUK_ASSERT(rc != 0); - return res; + return duk_js_to_arrayindex_hstring_fast_known(h); } +#endif /* DUK_USE_HSTRING_ARRIDX */ /* * Identifier access and function closure handling. * @@ -75108,11 +76942,11 @@ DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) { */ 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_tval *this_binding; - duk_hobject *env; + duk_bool_t has_this; /* for object-bound identifiers: provide 'this' binding */ } duk__id_lookup_result; /* @@ -75265,7 +77099,7 @@ void duk_js_push_closure(duk_hthread *thr, DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj)); DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj)); DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj)); - DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&fun_clos->obj)); + DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&fun_clos->obj)); /* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */ /* DUK_HOBJECT_FLAG_NEWENV: handled below */ DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&fun_clos->obj)); @@ -75300,7 +77134,7 @@ void duk_js_push_closure(duk_hthread *thr, #if defined(DUK_USE_FUNC_NAME_PROPERTY) if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_clos->obj)) { duk_hobject *proto; - duk_hobject *new_env; + duk_hdecenv *new_env; /* * Named function expression, name needs to be bound @@ -75318,11 +77152,18 @@ void duk_js_push_closure(duk_hthread *thr, } /* -> [ ... closure template env ] */ - new_env = duk_push_object_helper_proto(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - proto); + 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_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL); + DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, proto); + + DUK_ASSERT(new_env->thread == NULL); /* Closed. */ + DUK_ASSERT(new_env->varmap == NULL); /* It's important that duk_xdef_prop() is a 'raw define' so that any * properties in an ancestor are never an issue (they should never be @@ -75341,10 +77182,10 @@ void duk_js_push_closure(duk_hthread *thr, /* [ ... closure template env ] */ - DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, new_env); - DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, new_env); - DUK_HOBJECT_INCREF(thr, new_env); - DUK_HOBJECT_INCREF(thr, new_env); + DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, (duk_hobject *) new_env); + 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); /* [ ... closure template ] */ @@ -75566,10 +77407,11 @@ 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_hobject *env; + duk_hdecenv *env; duk_hobject *parent; duk_hcompfunc *f; + DUK_ASSERT(ctx != NULL); DUK_ASSERT(thr != NULL); DUK_ASSERT(func != NULL); @@ -75579,25 +77421,44 @@ duk_hobject *duk_create_activation_environment_record(duk_hthread *thr, parent = thr->builtins[DUK_BIDX_GLOBAL_ENV]; } - (void) duk_push_object_helper(ctx, - DUK_HOBJECT_FLAG_EXTENSIBLE | - DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV), - -1); /* no prototype, updated below */ - env = duk_known_hobject(ctx, -1); - DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, env, parent); /* parent env is the prototype */ + env = duk_hdecenv_alloc(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_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL); + DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, parent); /* parent env is the prototype */ /* open scope information, for compiled functions only */ + DUK_ASSERT(env->thread == NULL); + DUK_ASSERT(env->varmap == NULL); + DUK_ASSERT(env->regbase == 0); if (DUK_HOBJECT_IS_COMPFUNC(func)) { - duk_push_hthread(ctx, thr); - duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_THREAD); - duk_push_hobject(ctx, func); - duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_CALLEE); - duk_push_size_t(ctx, idx_bottom); - duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_REGBASE); + duk_hobject *varmap; + duk_tval *tv; + + tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARMAP(thr)); + if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) { + DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); + varmap = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT(varmap != NULL); + env->varmap = varmap; + DUK_HOBJECT_INCREF(thr, varmap); + env->thread = thr; + DUK_HTHREAD_INCREF(thr, thr); + env->regbase = idx_bottom; + } 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); + } } - return env; + return (duk_hobject *) env; } DUK_INTERNAL @@ -75649,156 +77510,103 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr, * Closing environment records. * * The environment record MUST be closed with the thread where its activation - * is. In other words (if 'env' is open): - * - * - 'thr' must match _env.thread - * - 'func' must match _env.callee - * - 'regbase' must match _env.regbase - * - * These are not looked up from the env to minimize code size. - * - * XXX: should access the own properties directly instead of using the API + * is; i.e. if 'env' is open, 'thr' must match env->thread, and the regbase + * and varmap must still be valid. On entry, 'env' must be reachable. */ -DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env, duk_hobject *func, duk_size_t regbase) { +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; + duk_tval *tv; + duk_uint_t regnum; DUK_ASSERT(thr != NULL); DUK_ASSERT(env != NULL); - /* func is NULL for lightfuncs */ - if (!DUK_HOBJECT_IS_DECENV(env) || DUK_HOBJECT_HAS_ENVRECCLOSED(env)) { - DUK_DDD(DUK_DDDPRINT("environment record not a declarative record, " - "or already closed: %!iO", - (duk_heaphdr *) env)); + if (DUK_UNLIKELY(!DUK_HOBJECT_IS_DECENV(env))) { + DUK_DDD(DUK_DDDPRINT("env not a declarative record: %!iO", (duk_heaphdr *) env)); return; } - DUK_DDD(DUK_DDDPRINT("closing environment record: %!iO, func: %!iO, regbase: %ld", - (duk_heaphdr *) env, (duk_heaphdr *) func, (long) regbase)); - - duk_push_hobject(ctx, env); - - /* assertions: env must be closed in the same thread as where it runs */ -#if defined(DUK_USE_ASSERTIONS) - { - /* [... env] */ - - if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE)) { - DUK_ASSERT(duk_is_object(ctx, -1)); - DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) func); - } - duk_pop(ctx); - - if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_THREAD)) { - DUK_ASSERT(duk_is_object(ctx, -1)); - DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) thr); - } - duk_pop(ctx); + varmap = ((duk_hdecenv *) env)->varmap; + if (varmap == NULL) { + DUK_DDD(DUK_DDDPRINT("env already closed: %!iO", (duk_heaphdr *) env)); - if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_REGBASE)) { - DUK_ASSERT(duk_is_number(ctx, -1)); - DUK_ASSERT(duk_get_number(ctx, -1) == (double) regbase); - } - duk_pop(ctx); - - /* [... env] */ + return; } -#endif - - if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) { - duk_hobject *varmap; - duk_hstring *key; - duk_tval *tv; - duk_uint_t regnum; - - /* XXX: additional conditions when to close variables? we don't want to do it - * unless the environment may have "escaped" (referenced in a function closure). - * With delayed environments, the existence is probably good enough of a check. - */ - - /* XXX: any way to detect faster whether something needs to be closed? - * We now look up _Callee and then skip the rest. - */ - - /* Note: we rely on the _Varmap having a bunch of nice properties, like: - * - being compacted and unmodified during this process - * - not containing an array part - * - having correct value types - */ - - /* [... env] */ + DUK_ASSERT(((duk_hdecenv *) env)->thread != NULL); + DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env); - if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE)) { - DUK_DDD(DUK_DDDPRINT("env has no callee property, nothing to close; re-delete the control properties just in case")); - duk_pop(ctx); - goto skip_varmap; - } + DUK_DDD(DUK_DDDPRINT("closing env: %!iO", (duk_heaphdr *) env)); + DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap)); - /* [... env callee] */ + /* Env must be closed in the same thread as where it runs. */ + DUK_ASSERT(((duk_hdecenv *) env)->thread == thr); - if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VARMAP)) { - DUK_DDD(DUK_DDDPRINT("callee has no varmap property, nothing to close; delete the control properties")); - duk_pop_2(ctx); - goto skip_varmap; - } - varmap = duk_require_hobject(ctx, -1); - DUK_ASSERT(varmap != NULL); - - DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap)); - - /* [... env callee varmap] */ - - DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap))); + /* XXX: additional conditions when to close variables? we don't want to do it + * unless the environment may have "escaped" (referenced in a function closure). + * With delayed environments, the existence is probably good enough of a check. + */ - for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) { - key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i); - DUK_ASSERT(key != NULL); /* assume keys are compacted */ + /* Note: we rely on the _Varmap having a bunch of nice properties, like: + * - being compacted and unmodified during this process + * - not containing an array part + * - having correct value types + */ - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */ + DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap))); - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); /* assume value is a number */ - regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv); - DUK_ASSERT_DISABLE(regnum >= 0); /* unsigned */ - DUK_ASSERT(regnum < ((duk_hcompfunc *) func)->nregs); /* regnum is sane */ - DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack); - DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top); + /* Copy over current variable values from value stack to the + * environment record. The scope object is empty but may + * inherit from another scope which has conflicting names. + */ - /* XXX: slightly awkward */ - duk_push_hstring(ctx, key); - duk_push_tval(ctx, thr->valstack + regbase + regnum); - DUK_DDD(DUK_DDDPRINT("closing identifier '%s' -> reg %ld, value %!T", - (const char *) duk_require_string(ctx, -2), - (long) regnum, - (duk_tval *) duk_get_tval(ctx, -1))); + /* XXX: Do this using a once allocated entry area, no side effects. + * Hash part would need special treatment however (maybe copy, and + * 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; - /* [... env callee varmap key val] */ + key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i); + DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */ + DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */ - /* if property already exists, overwrites silently */ - duk_xdef_prop(ctx, -5, DUK_PROPDESC_FLAGS_WE); /* writable but not deletable */ - } + tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i); + DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); + DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */ +#if defined(DUK_USE_FASTINT) + DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); + regnum = (duk_uint_t) DUK_TVAL_GET_FASTINT_U32(tv); +#else + regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv); +#endif - duk_pop_2(ctx); + regbase = ((duk_hdecenv *) env)->regbase; + DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack); + DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top); - /* [... env] */ + /* 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_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T", + (duk_heaphdr *) key, + (long) regnum, + (duk_tval *) duk_get_tval(ctx, -1))); + duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE); } - skip_varmap: - - /* [... env] */ - - duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE); - duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_THREAD); - duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_REGBASE); - - duk_pop(ctx); - - DUK_HOBJECT_SET_ENVRECCLOSED(env); + /* NULL atomically to avoid inconsistent state + side effects. */ + DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->thread); + DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->varmap); + ((duk_hdecenv *) env)->thread = NULL; + ((duk_hdecenv *) env)->varmap = NULL; - DUK_DDD(DUK_DDDPRINT("environment record after being closed: %!O", - (duk_heaphdr *) env)); + DUK_DDD(DUK_DDDPRINT("env after closing: %!O", (duk_heaphdr *) env)); } /* @@ -75828,12 +77636,8 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject DUK_LOCAL duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr, duk_hstring *name, - duk_hobject *env, + duk_hdecenv *env, duk__id_lookup_result *out) { - duk_hthread *env_thr; - duk_hobject *env_func; - duk_size_t env_regbase; - duk_hobject *varmap; duk_tval *tv; duk_size_t reg_rel; duk_size_t idx; @@ -75843,69 +77647,39 @@ duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr, DUK_ASSERT(env != NULL); DUK_ASSERT(out != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_DECENV(env)); + DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) env)); + DUK_ASSERT_HDECENV_VALID(env); - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr)); - if (!tv) { - /* env is closed, should be missing _Callee, _Thread, _Regbase */ - DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL); - DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL); - DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL); + if (env->thread == NULL) { + /* already closed */ return 0; } + DUK_ASSERT(env->varmap != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_TVAL_GET_OBJECT(tv))); - env_func = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(env_func != NULL); - - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env_func, DUK_HTHREAD_STRING_INT_VARMAP(thr)); - if (!tv) { + tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env->varmap, name); + if (DUK_UNLIKELY(tv == NULL)) { return 0; } - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - varmap = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(varmap != NULL); - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, varmap, name); - if (!tv) { - return 0; - } DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); + DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */ +#if defined(DUK_USE_FASTINT) + DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv)); + reg_rel = (duk_size_t) DUK_TVAL_GET_FASTINT_U32(tv); +#else reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv); +#endif DUK_ASSERT_DISABLE(reg_rel >= 0); /* unsigned */ - DUK_ASSERT(reg_rel < ((duk_hcompfunc *) env_func)->nregs); - - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr)); - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv))); - env_thr = (duk_hthread *) DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(env_thr != NULL); - - /* Note: env_thr != thr is quite possible and normal, so careful - * with what thread is used for valstack lookup. - */ - - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr)); - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - env_regbase = (duk_size_t) DUK_TVAL_GET_NUMBER(tv); - idx = env_regbase + reg_rel; - tv = env_thr->valstack + idx; - DUK_ASSERT(tv >= env_thr->valstack && tv < env_thr->valstack_end); /* XXX: more accurate? */ + idx = env->regbase + reg_rel; + tv = env->thread->valstack + idx; + DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end); /* XXX: more accurate? */ out->value = tv; out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */ - out->this_binding = NULL; /* implicit this value always undefined for - * declarative environment records. - */ - out->env = env; + out->env = (duk_hobject *) env; out->holder = NULL; - + out->has_this = 0; return 1; } @@ -75934,6 +77708,7 @@ duk_bool_t duk__getid_activation_regs(duk_hthread *thr, return 0; } + /* XXX: move varmap to duk_hcompfunc struct field. */ tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARMAP(thr)); if (!tv) { return 0; @@ -75957,12 +77732,9 @@ duk_bool_t duk__getid_activation_regs(duk_hthread *thr, out->value = tv; out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */ - out->this_binding = NULL; /* implicit this value always undefined for - * declarative environment records. - */ out->env = NULL; out->holder = NULL; - + out->has_this = 0; return 1; } @@ -75974,7 +77746,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_target; duk_tval tv_name; duk_uint_t sanity; @@ -76015,10 +77786,10 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, if (duk__getid_activation_regs(thr, name, act, out)) { DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O " + "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " "(found from register bindings when env=NULL)", (duk_heaphdr *) name, (duk_tval *) out->value, - (long) out->attrs, (duk_tval *) out->this_binding, + (long) out->attrs, (long) out->has_this, (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder)); return 1; } @@ -76095,37 +77866,30 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, * register-bound variables. */ - if (DUK_HOBJECT_HAS_ENVRECCLOSED(env)) { - /* already closed */ - goto skip_regs; - } - - if (duk__getid_open_decl_env_regs(thr, name, env, out)) { + DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env); + if (duk__getid_open_decl_env_regs(thr, name, (duk_hdecenv *) env, out)) { DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O " + "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " "(declarative environment record, scope open, found in regs)", (duk_heaphdr *) name, (duk_tval *) out->value, - (long) out->attrs, (duk_tval *) out->this_binding, + (long) out->attrs, (long) out->has_this, (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder)); return 1; } - skip_regs: tv = duk_hobject_find_existing_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs); if (tv) { out->value = tv; out->attrs = attrs; - out->this_binding = NULL; /* implicit this value always undefined for - * declarative environment records. - */ out->env = env; out->holder = env; + out->has_this = 0; DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O " + "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " "(declarative environment record, found in properties)", (duk_heaphdr *) name, (duk_tval *) out->value, - (long) out->attrs, (duk_tval *) out->this_binding, + (long) out->attrs, (long) out->has_this, (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder)); return 1; } @@ -76148,11 +77912,9 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, duk_bool_t found; DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV); + DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) env); - tv_target = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr)); - DUK_ASSERT(tv_target != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target)); - target = DUK_TVAL_GET_OBJECT(tv_target); + target = ((duk_hobjenv *) env)->target; DUK_ASSERT(target != NULL); /* Target may be a Proxy or property may be an accessor, so we must @@ -76163,10 +77925,13 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, */ if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)) { + duk_tval tv_target_tmp; + DUK_ASSERT(name != NULL); DUK_TVAL_SET_STRING(&tv_name, name); + DUK_TVAL_SET_OBJECT(&tv_target_tmp, target); - found = duk_hobject_hasprop(thr, tv_target, &tv_name); + found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name); } else { /* XXX: duk_hobject_hasprop() would be correct for * non-Proxy objects too, but it is about ~20-25% @@ -76179,16 +77944,15 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, if (found) { out->value = NULL; /* can't get value, may be accessor */ out->attrs = 0; /* irrelevant when out->value == NULL */ - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THIS(thr)); - out->this_binding = tv; /* may be NULL */ out->env = env; out->holder = target; + out->has_this = ((duk_hobjenv *) env)->has_this; DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: " - "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O " + "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O " "(object environment record)", (duk_heaphdr *) name, (duk_tval *) out->value, - (long) out->attrs, (duk_tval *) out->this_binding, + (long) out->attrs, (long) out->has_this, (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder)); return 1; } @@ -76200,11 +77964,11 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr, goto fail_not_found; } - if (sanity-- == 0) { + if (DUK_UNLIKELY(sanity-- == 0)) { DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT); } env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env); - }; + } /* * Not found (even in global object) @@ -76311,29 +78075,27 @@ duk_bool_t duk__getvar_helper(duk_hthread *thr, parents = 1; /* follow parent chain */ if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) { if (ref.value) { - DUK_ASSERT(ref.this_binding == NULL); /* always for register bindings */ duk_push_tval(ctx, ref.value); duk_push_undefined(ctx); } else { DUK_ASSERT(ref.holder != NULL); - /* Note: getprop may invoke any getter and invalidate any - * duk_tval pointers, so this must be done first. + /* ref.holder is safe across the getprop call (even + * with side effects) because 'env' is reachable and + * ref.holder is a direct heap pointer. */ - if (ref.this_binding) { - duk_push_tval(ctx, ref.this_binding); - } else { - duk_push_undefined(ctx); - } - DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder); DUK_TVAL_SET_STRING(&tv_tmp_key, name); - (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [this value] */ + (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [value] */ - /* ref.value, ref.this.binding invalidated here by getprop call */ + if (ref.has_this) { + duk_push_hobject(ctx, ref.holder); + } else { + duk_push_undefined(ctx); + } - duk_insert(ctx, -2); /* [this value] -> [value this] */ + /* [value this] */ } return 1; @@ -76436,13 +78198,11 @@ void duk__putvar_helper(duk_hthread *thr, */ duk_tval *tv_val; - DUK_ASSERT(ref.this_binding == NULL); /* always for register bindings */ - tv_val = ref.value; DUK_ASSERT(tv_val != NULL); DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, val); /* side effects */ - /* ref.value and ref.this_binding invalidated here */ + /* ref.value invalidated here */ } else { DUK_ASSERT(ref.holder != NULL); @@ -76450,7 +78210,7 @@ void duk__putvar_helper(duk_hthread *thr, DUK_TVAL_SET_STRING(&tv_tmp_key, name); (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, val, strict); - /* ref.value and ref.this_binding invalidated here */ + /* ref.value invalidated here */ } return; @@ -76823,14 +78583,11 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr, */ if (DUK_HOBJECT_IS_DECENV(env)) { + DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env); holder = env; } else { - DUK_ASSERT(DUK_HOBJECT_IS_OBJENV(env)); - - tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr)); - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - holder = DUK_TVAL_GET_OBJECT(tv); + DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) env); + holder = ((duk_hobjenv *) env)->target; DUK_ASSERT(holder != NULL); } @@ -77649,6 +79406,68 @@ DUK_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bo DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE); } +/* Parse legacy octal escape of the form \N{1,3}, e.g. \0, \5, \0377. Maximum + * allowed value is \0377 (U+00FF), longest match is used. Used for both string + * 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_codepoint_t cp; + duk_small_uint_t lookup_idx; + duk_small_int_t adv; + duk_codepoint_t tmp; + + DUK_ASSERT(out_adv != NULL); + DUK_ASSERT(DUK__LOOKUP(lex_ctx, 0) == DUK_ASC_BACKSLASH); + DUK_ASSERT(DUK__LOOKUP(lex_ctx, 1) >= DUK_ASC_0 && DUK__LOOKUP(lex_ctx, 1) <= DUK_ASC_9); + + cp = 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); + if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) { + /* No more valid digits. */ + break; + } + tmp = (cp << 3) + (tmp - DUK_ASC_0); + if (tmp > 0xff) { + /* Three digit octal escapes above \377 (= 0xff) + * are not allowed. + */ + break; + } + cp = tmp; + } + DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp)); + + adv = lookup_idx; + if (lookup_idx == 1) { + DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too")); + DUK_ASSERT(tmp == DUK_ASC_8 || tmp == DUK_ASC_9); + cp = tmp; + adv++; /* correction to above, eat offending character */ + } else if (lookup_idx == 2 && cp == 0) { + /* Note: 'foo\0bar' is OK in strict mode, but 'foo\00bar' is not. + * It won't be interpreted as 'foo\u{0}0bar' but as a SyntaxError. + */ + DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too")); + } else { + /* This clause also handles non-shortest zero, e.g. \00. */ + if (reject_annex_b) { + DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> reject in strict-mode", (long) cp)); + cp = -1; + } else { + DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> accepted", (long) cp)); + DUK_ASSERT(cp >= 0 && cp <= 0xff); + } + } + + *out_adv = adv; + + DUK_ASSERT((cp >= 0 && cp <= 0xff) || (cp == -1 && reject_annex_b)); + return cp; +} + /* 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; @@ -77734,46 +79553,9 @@ DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token * Parse octal (up to 3 digits) from the lookup window. */ - duk_codepoint_t tmp; - duk_small_uint_t lookup_idx; - - emitcp = 0; - for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) { - DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, emitcp=%ld", (long) lookup_idx, (long) emitcp)); - tmp = DUK__LOOKUP(lex_ctx, lookup_idx); - if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) { - /* No more valid digits. */ - break; - } - tmp = (emitcp << 3) + (tmp - DUK_ASC_0); - if (tmp > 0xff) { - /* Three digit octal escapes above \377 (= 0xff) - * are not allowed. - */ - break; - } - emitcp = tmp; - } - DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, emitcp=%ld", (long) lookup_idx, (long) emitcp)); - - adv = lookup_idx; - if (lookup_idx == 1) { - /* \8 or \9 -> treat as literal, accept also - * in strict mode. - */ - DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too")); - emitcp = x; - adv++; /* correction to above, eat offending character */ - } else if (lookup_idx == 2 && emitcp == 0) { - /* Zero escape, also allowed in non-strict mode. */ - DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too")); - } else { - /* Valid octal, only accept in non-strict mode. */ - DUK_DDD(DUK_DDDPRINT("octal literal %ld -> accept only in non-strict-mode", (long) emitcp)); - DUK_ASSERT(emitcp >= 0 && emitcp <= 0xff); - if (strict_mode) { - goto fail_escape; - } + emitcp = duk__lexer_parse_legacy_octal(lex_ctx, &adv, strict_mode /*reject_annex_b*/); + if (emitcp < 0) { + goto fail_escape; } } else if (x < 0) { goto fail_unterminated; @@ -77824,6 +79606,19 @@ DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token return; } +/* Skip to end-of-line (or end-of-file), used for single line comments. */ +DUK_LOCAL void duk__lexer_skip_to_endofline(duk_lexer_ctx *lex_ctx) { + for (;;) { + duk_codepoint_t x; + + x = DUK__L0(); + if (x < 0 || duk_unicode_is_line_terminator(x)) { + break; + } + DUK__ADVANCECHARS(lex_ctx, 1); + } +} + /* * Parse Ecmascript source InputElementDiv or InputElementRegExp * (E5 Section 7), skipping whitespace, comments, and line terminators. @@ -77962,6 +79757,17 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, DUK__ADVANCECHARS(lex_ctx, 1); got_lineterm = 1; goto restart_lineupdate; +#if defined(DUK_USE_SHEBANG_COMMENTS) + case DUK_ASC_HASH: /* '#' */ + if (DUK__L1() == DUK_ASC_EXCLAMATION && lex_ctx->window[0].offset == 0 && + (lex_ctx->flags & DUK_COMPILE_SHEBANG)) { + /* "Shebang" comment ('#! ...') on first line. */ + /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */ + duk__lexer_skip_to_endofline(lex_ctx); + goto restart; /* line terminator will be handled on next round */ + } + goto fail_token; +#endif /* DUK_USE_SHEBANG_COMMENTS */ case DUK_ASC_SLASH: /* '/' */ if (DUK__L1() == DUK_ASC_SLASH) { /* @@ -77969,14 +79775,8 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, * code point). */ - /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but it unnecessary */ - for (;;) { - x = DUK__L0(); - if (x < 0 || duk_unicode_is_line_terminator(x)) { - break; - } - DUK__ADVANCECHARS(lex_ctx, 1); - } + /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */ + duk__lexer_skip_to_endofline(lex_ctx); goto restart; /* line terminator will be handled on next round */ } else if (DUK__L1() == DUK_ASC_STAR) { /* @@ -78161,6 +79961,18 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx, advtok = DUK__ADVTOK(1, DUK_TOK_COMMA); break; case DUK_ASC_LANGLE: /* '<' */ +#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 + * Only allowed: + * - on new line + * - preceded only by whitespace + * - preceded by end of multiline comment and optional whitespace + * + * Since whitespace generates no tokens, and multiline comments + * are treated as a line ending, consulting `got_lineterm` is + * sufficient to test for these three options. + */ + + /* DUK__ADVANCECHARS(lex_ctx, 3) would be correct here, but not necessary */ + duk__lexer_skip_to_endofline(lex_ctx); + goto restart; /* line terminator will be handled on next round */ + } else +#endif /* DUK_USE_HTML_COMMENTS */ if (DUK__L1() == DUK_ASC_MINUS) { advtok = DUK__ADVTOK(2, DUK_TOK_DECREMENT); } else if (DUK__L1() == DUK_ASC_EQUALS) { @@ -78925,6 +80756,8 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token } else if (DUK__L2() == DUK_ASC_COLON) { /* (?: */ advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_START_NONCAPTURE_GROUP); + } else { + goto fail_group; } } else { /* ( */ @@ -78992,6 +80825,10 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE); return; + fail_group: + DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_GROUP); + return; + #if !defined(DUK_USE_ES6_REGEXP_SYNTAX) fail_invalid_char: DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_CHARACTER); @@ -79165,12 +81002,24 @@ DUK_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range sizeof(duk_unicode_re_ranges_not_wordchar) / sizeof(duk_uint16_t)); ch = -1; } else if (DUK__ISDIGIT(x)) { - /* DecimalEscape, only \0 is allowed, no leading zeroes are allowed */ + /* DecimalEscape, only \0 is allowed, no leading + * zeroes are allowed. + * + * ES2015 Annex B also allows (maximal match) legacy + * octal escapes up to \377 and \8 and \9 are + * accepted as literal '8' and '9', also in strict mode. + */ + +#if defined(DUK_USE_ES6_REGEXP_SYNTAX) + ch = duk__lexer_parse_legacy_octal(lex_ctx, &adv, 0 /*reject_annex_b*/); + DUK_ASSERT(ch >= 0); /* no rejections */ +#else if (x == DUK_ASC_0 && !DUK__ISDIGIT(DUK__L2())) { ch = 0x0000; } else { goto fail_escape; } +#endif #if defined(DUK_USE_ES6_REGEXP_SYNTAX) } else if (x >= 0) { /* IdentityEscape: ES2015 Annex B allows almost all @@ -82413,34 +84262,34 @@ DUK_LOCAL duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h) switch (c) { case (duk_uint8_t) 'g': { if (flags & DUK_RE_FLAG_GLOBAL) { - goto error; + goto flags_error; } flags |= DUK_RE_FLAG_GLOBAL; break; } case (duk_uint8_t) 'i': { if (flags & DUK_RE_FLAG_IGNORE_CASE) { - goto error; + goto flags_error; } flags |= DUK_RE_FLAG_IGNORE_CASE; break; } case (duk_uint8_t) 'm': { if (flags & DUK_RE_FLAG_MULTILINE) { - goto error; + goto flags_error; } flags |= DUK_RE_FLAG_MULTILINE; break; } default: { - goto error; + goto flags_error; } } } return flags; - error: + flags_error: DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_REGEXP_FLAGS); return 0; /* never here */ } @@ -83510,6 +85359,7 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_ char_offset = (duk_uint32_t) 0; } + DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input)); sp = re_ctx.input + duk_heap_strcache_offset_char2byte(thr, h_input, char_offset); /* @@ -90685,8 +92535,7 @@ DUK_INTERNAL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx) { } /* Decode a one-bit flag, and if set, decode a value of 'bits', otherwise return - * default value. Return value is signed so that negative marker value can be - * used by caller as a "not present" value. + * default value. */ DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value) { if (duk_bd_decode_flag(ctx)) { @@ -90696,6 +92545,11 @@ DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_sma } } +/* Signed variant, allows negative marker value. */ +DUK_INTERNAL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value) { + return (duk_int32_t) duk_bd_decode_flagged(ctx, bits, (duk_uint32_t) def_value); +} + /* Shared varint encoding. Match dukutil.py BitEncode.varuint(). */ DUK_INTERNAL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx) { duk_small_uint_t t; @@ -90895,7 +92749,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; new_sz = curr_off + sz + add_sz; - if (new_sz < curr_off) { + if (DUK_UNLIKELY(new_sz < curr_off)) { /* overflow */ DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG); return NULL; /* not reachable */ @@ -91382,4 +93236,3 @@ DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) { #undef DUK__RANDOM_XOROSHIRO128PLUS #undef DUK__RND_BIT #undef DUK__UPDATE_RND -#endif diff --git a/content/handlers/javascript/duktape/duktape.h b/content/handlers/javascript/duktape/duktape.h index efd8a32e9..21257d30c 100644 --- a/content/handlers/javascript/duktape/duktape.h +++ b/content/handlers/javascript/duktape/duktape.h @@ -1,10 +1,10 @@ /* - * Duktape public API for Duktape 2.0.2. + * Duktape public API for Duktape 2.1.0. * - * See the API reference for documentation on call semantics. - * The exposed API is inside the DUK_API_PUBLIC_H_INCLUDED - * include guard. Other parts of the header are Duktape - * internal and related to platform/compiler/feature detection. + * 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 external (external). * Git branch external. @@ -87,6 +87,8 @@ * * Brett Vickers (https://github.com/beevik) * * Dominik Okwieka (https://github.com/okitec) * * Remko Tron\u00e7on (https://el-tramo.be) + * * Romero Malaquias (rbsm@ic.ufal.br) + * * Michael Drake * * Other contributions * =================== @@ -135,18 +137,38 @@ #define DUK_SINGLE_FILE -/* External duk_config.h provides platform/compiler/OS dependent - * typedefs and macros, and DUK_USE_xxx config options so that - * the rest of Duktape doesn't need to do any feature detection. +/* + * BEGIN PUBLIC API */ -#include "duk_config.h" /* - * BEGIN PUBLIC API + * Version and Git commit identification + */ + +/* Duktape version, (major * 10000) + (minor * 100) + patch. Allows C code + * to #if (DUK_VERSION >= NNN) against Duktape API version. The same value + * is also available to Ecmascript code in Duktape.version. Unofficial + * 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 + +/* 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 "external" +#define DUK_GIT_DESCRIBE "external" +#define DUK_GIT_BRANCH "external" -#if !defined(DUK_API_PUBLIC_H_INCLUDED) -#define DUK_API_PUBLIC_H_INCLUDED +/* External duk_config.h provides platform/compiler/OS dependent + * typedefs and macros, and DUK_USE_xxx config options so that + * the rest of Duktape doesn't need to do any feature detection. + * DUK_VERSION is defined before including so that configuration + * snippets can react to it. + */ +#include "duk_config.h" /* * Avoid C++ name mangling @@ -247,23 +269,6 @@ struct duk_time_components { * Constants */ -/* Duktape version, (major * 10000) + (minor * 100) + patch. Allows C code - * to #if (DUK_VERSION >= NNN) against Duktape API version. The same value - * is also available to Ecmascript code in Duktape.version. Unofficial - * 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 20002L - -/* 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 "external" -#define DUK_GIT_DESCRIBE "external" -#define DUK_GIT_BRANCH "external" - /* Duktape debug protocol version used by this build. */ #define DUK_DEBUG_PROTOCOL_VERSION 2 @@ -334,11 +339,13 @@ struct duk_time_components { #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_SAFE (1 << 6) /* (internal) catch compilation errors */ -#define DUK_COMPILE_NORESULT (1 << 7) /* (internal) omit eval result */ -#define DUK_COMPILE_NOSOURCE (1 << 8) /* (internal) no source string on stack */ -#define DUK_COMPILE_STRLEN (1 << 9) /* (internal) take strlen() of src_buffer (avoids double evaluation in macro) */ -#define DUK_COMPILE_NOFILENAME (1 << 10) /* (internal) no filename on stack */ +#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) */ @@ -445,9 +452,9 @@ DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_throw_raw(duk_context *ctx)); DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_fatal_raw(duk_context *ctx, const char *err_msg)); #define duk_fatal(ctx,err_msg) \ (duk_fatal_raw((ctx), (err_msg)), (duk_ret_t) 0) +DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...)); #if defined(DUK_API_VARIADIC_MACROS) -DUK_API_NORETURN(DUK_EXTERNAL_DECL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...)); #define duk_error(ctx,err_code,...) \ (duk_error_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), __VA_ARGS__), (duk_ret_t) 0) #define duk_generic_error(ctx,...) \ @@ -516,6 +523,7 @@ DUK_API_NORETURN(DUK_EXTERNAL_DECL duk_ret_t duk_uri_error_stash(duk_context *ct #endif /* DUK_API_VARIADIC_MACROS */ DUK_API_NORETURN(DUK_EXTERNAL_DECL 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)); + #define duk_error_va(ctx,err_code,fmt,ap) \ (duk_error_va_raw((ctx), (duk_errcode_t) (err_code), (const char *) (DUK_FILE_MACRO), (duk_int_t) (DUK_LINE_MACRO), (fmt), (ap)), (duk_ret_t) 0) #define duk_generic_error_va(ctx,fmt,ap) \ @@ -663,19 +671,18 @@ DUK_EXTERNAL_DECL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, d #define duk_push_external_buffer(ctx) \ ((void) duk_push_buffer_raw((ctx), 0, DUK_BUF_FLAG_DYNAMIC | DUK_BUF_FLAG_EXTERNAL)) -#define DUK_BUFOBJ_CREATE_ARRBUF (1 << 4) /* internal flag: create backing ArrayBuffer; keep in one byte */ #define DUK_BUFOBJ_ARRAYBUFFER 0 -#define DUK_BUFOBJ_NODEJS_BUFFER (1 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_DATAVIEW (2 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_INT8ARRAY (3 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_UINT8ARRAY (4 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_UINT8CLAMPEDARRAY (5 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_INT16ARRAY (6 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_UINT16ARRAY (7 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_INT32ARRAY (8 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_UINT32ARRAY (9 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_FLOAT32ARRAY (10 | DUK_BUFOBJ_CREATE_ARRBUF) -#define DUK_BUFOBJ_FLOAT64ARRAY (11 | DUK_BUFOBJ_CREATE_ARRBUF) +#define DUK_BUFOBJ_NODEJS_BUFFER 1 +#define DUK_BUFOBJ_DATAVIEW 2 +#define DUK_BUFOBJ_INT8ARRAY 3 +#define DUK_BUFOBJ_UINT8ARRAY 4 +#define DUK_BUFOBJ_UINT8CLAMPEDARRAY 5 +#define DUK_BUFOBJ_INT16ARRAY 6 +#define DUK_BUFOBJ_UINT16ARRAY 7 +#define DUK_BUFOBJ_INT32ARRAY 8 +#define DUK_BUFOBJ_UINT32ARRAY 9 +#define DUK_BUFOBJ_FLOAT32ARRAY 10 +#define DUK_BUFOBJ_FLOAT64ARRAY 11 DUK_EXTERNAL_DECL 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); @@ -789,8 +796,43 @@ DUK_EXTERNAL_DECL void *duk_get_pointer(duk_context *ctx, duk_idx_t idx); DUK_EXTERNAL_DECL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t idx); DUK_EXTERNAL_DECL duk_context *duk_get_context(duk_context *ctx, duk_idx_t idx); DUK_EXTERNAL_DECL void *duk_get_heapptr(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx); -DUK_EXTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len); + +/* + * Get-with-explicit default operations: like get operations but with an + * explicit default value. + */ + +DUK_EXTERNAL_DECL duk_bool_t duk_get_boolean_default(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value); +DUK_EXTERNAL_DECL duk_double_t duk_get_number_default(duk_context *ctx, duk_idx_t idx, duk_double_t def_value); +DUK_EXTERNAL_DECL duk_int_t duk_get_int_default(duk_context *ctx, duk_idx_t idx, duk_int_t def_value); +DUK_EXTERNAL_DECL duk_uint_t duk_get_uint_default(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value); +DUK_EXTERNAL_DECL const char *duk_get_string_default(duk_context *ctx, duk_idx_t idx, const char *def_value); +DUK_EXTERNAL_DECL 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_DECL 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_EXTERNAL_DECL 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_len); +DUK_EXTERNAL_DECL void *duk_get_pointer_default(duk_context *ctx, duk_idx_t idx, void *def_value); +DUK_EXTERNAL_DECL duk_c_function duk_get_c_function_default(duk_context *ctx, duk_idx_t idx, duk_c_function def_value); +DUK_EXTERNAL_DECL duk_context *duk_get_context_default(duk_context *ctx, duk_idx_t idx, duk_context *def_value); +DUK_EXTERNAL_DECL void *duk_get_heapptr_default(duk_context *ctx, duk_idx_t idx, void *def_value); + +/* + * Opt operations: like require operations but with an explicit default value + * when value is undefined or index is invalid, null and non-matching types + * cause a TypeError. + */ + +DUK_EXTERNAL_DECL duk_bool_t duk_opt_boolean(duk_context *ctx, duk_idx_t idx, duk_bool_t def_value); +DUK_EXTERNAL_DECL duk_double_t duk_opt_number(duk_context *ctx, duk_idx_t idx, duk_double_t def_value); +DUK_EXTERNAL_DECL duk_int_t duk_opt_int(duk_context *ctx, duk_idx_t idx, duk_int_t def_value); +DUK_EXTERNAL_DECL duk_uint_t duk_opt_uint(duk_context *ctx, duk_idx_t idx, duk_uint_t def_value); +DUK_EXTERNAL_DECL const char *duk_opt_string(duk_context *ctx, duk_idx_t idx, const char *def_ptr); +DUK_EXTERNAL_DECL 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_EXTERNAL_DECL 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_EXTERNAL_DECL 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_EXTERNAL_DECL void *duk_opt_pointer(duk_context *ctx, duk_idx_t idx, void *def_value); +DUK_EXTERNAL_DECL duk_c_function duk_opt_c_function(duk_context *ctx, duk_idx_t idx, duk_c_function def_value); +DUK_EXTERNAL_DECL duk_context *duk_opt_context(duk_context *ctx, duk_idx_t idx, duk_context *def_value); +DUK_EXTERNAL_DECL void *duk_opt_heapptr(duk_context *ctx, duk_idx_t idx, void *def_value); /* * Require operations: no coercion, throw error if index or type @@ -868,6 +910,17 @@ DUK_EXTERNAL_DECL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t id #define duk_safe_to_string(ctx,idx) \ duk_safe_to_lstring((ctx), (idx), NULL) +/* + * Value length + */ + +DUK_EXTERNAL_DECL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t idx); +DUK_EXTERNAL_DECL void duk_set_length(duk_context *ctx, duk_idx_t idx, duk_size_t len); +#if 0 +/* duk_require_length()? */ +/* duk_opt_length()? */ +#endif + /* * Misc conversion */ @@ -1228,434 +1281,8 @@ DUK_EXTERNAL_DECL const void * const duk_rom_compressed_pointers[]; } #endif -#endif /* DUK_API_PUBLIC_H_INCLUDED */ - /* * END PUBLIC API */ -/* - * Union to access IEEE double memory representation, indexes for double - * memory representation, and some macros for double manipulation. - * - * Also used by packed duk_tval. Use a union for bit manipulation to - * minimize aliasing issues in practice. The C99 standard does not - * guarantee that this should work, but it's a very widely supported - * practice for low level manipulation. - * - * IEEE double format summary: - * - * seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff - * A B C D E F G H - * - * s sign bit - * eee... exponent field - * fff... fraction - * - * See http://en.wikipedia.org/wiki/Double_precision_floating-point_format. - * - * NaNs are represented as exponent 0x7ff and mantissa != 0. The NaN is a - * signaling NaN when the highest bit of the mantissa is zero, and a quiet - * NaN when the highest bit is set. - * - * At least three memory layouts are relevant here: - * - * A B C D E F G H Big endian (e.g. 68k) DUK_USE_DOUBLE_BE - * H G F E D C B A Little endian (e.g. x86) DUK_USE_DOUBLE_LE - * D C B A H G F E Mixed/cross endian (e.g. ARM) DUK_USE_DOUBLE_ME - * - * ARM is a special case: ARM double values are in mixed/cross endian - * format while ARM duk_uint64_t values are in standard little endian - * format (H G F E D C B A). When a double is read as a duk_uint64_t - * from memory, the register will contain the (logical) value - * E F G H A B C D. This requires some special handling below. - * - * Indexes of various types (8-bit, 16-bit, 32-bit) in memory relative to - * the logical (big endian) order: - * - * byte order duk_uint8_t duk_uint16_t duk_uint32_t - * BE 01234567 0123 01 - * LE 76543210 3210 10 - * ME (ARM) 32107654 1032 01 - * - * Some processors may alter NaN values in a floating point load+store. - * For instance, on X86 a FLD + FSTP may convert a signaling NaN to a - * quiet one. This is catastrophic when NaN space is used in packed - * duk_tval values. See: misc/clang_aliasing.c. - */ - -#if !defined(DUK_DBLUNION_H_INCLUDED) -#define DUK_DBLUNION_H_INCLUDED - -/* - * Union for accessing double parts, also serves as packed duk_tval - */ - -union duk_double_union { - double d; - float f[2]; -#if defined(DUK_USE_64BIT_OPS) - duk_uint64_t ull[1]; -#endif - duk_uint32_t ui[2]; - duk_uint16_t us[4]; - duk_uint8_t uc[8]; -#if defined(DUK_USE_PACKED_TVAL) - void *vp[2]; /* used by packed duk_tval, assumes sizeof(void *) == 4 */ -#endif -}; - -typedef union duk_double_union duk_double_union; - -/* - * Indexes of various types with respect to big endian (logical) layout - */ - -#if defined(DUK_USE_DOUBLE_LE) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 -#endif -#define DUK_DBL_IDX_UI0 1 -#define DUK_DBL_IDX_UI1 0 -#define DUK_DBL_IDX_US0 3 -#define DUK_DBL_IDX_US1 2 -#define DUK_DBL_IDX_US2 1 -#define DUK_DBL_IDX_US3 0 -#define DUK_DBL_IDX_UC0 7 -#define DUK_DBL_IDX_UC1 6 -#define DUK_DBL_IDX_UC2 5 -#define DUK_DBL_IDX_UC3 4 -#define DUK_DBL_IDX_UC4 3 -#define DUK_DBL_IDX_UC5 2 -#define DUK_DBL_IDX_UC6 1 -#define DUK_DBL_IDX_UC7 0 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#elif defined(DUK_USE_DOUBLE_BE) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 -#endif -#define DUK_DBL_IDX_UI0 0 -#define DUK_DBL_IDX_UI1 1 -#define DUK_DBL_IDX_US0 0 -#define DUK_DBL_IDX_US1 1 -#define DUK_DBL_IDX_US2 2 -#define DUK_DBL_IDX_US3 3 -#define DUK_DBL_IDX_UC0 0 -#define DUK_DBL_IDX_UC1 1 -#define DUK_DBL_IDX_UC2 2 -#define DUK_DBL_IDX_UC3 3 -#define DUK_DBL_IDX_UC4 4 -#define DUK_DBL_IDX_UC5 5 -#define DUK_DBL_IDX_UC6 6 -#define DUK_DBL_IDX_UC7 7 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#elif defined(DUK_USE_DOUBLE_ME) -#if defined(DUK_USE_64BIT_OPS) -#define DUK_DBL_IDX_ULL0 0 /* not directly applicable, byte order differs from a double */ -#endif -#define DUK_DBL_IDX_UI0 0 -#define DUK_DBL_IDX_UI1 1 -#define DUK_DBL_IDX_US0 1 -#define DUK_DBL_IDX_US1 0 -#define DUK_DBL_IDX_US2 3 -#define DUK_DBL_IDX_US3 2 -#define DUK_DBL_IDX_UC0 3 -#define DUK_DBL_IDX_UC1 2 -#define DUK_DBL_IDX_UC2 1 -#define DUK_DBL_IDX_UC3 0 -#define DUK_DBL_IDX_UC4 7 -#define DUK_DBL_IDX_UC5 6 -#define DUK_DBL_IDX_UC6 5 -#define DUK_DBL_IDX_UC7 4 -#define DUK_DBL_IDX_VP0 DUK_DBL_IDX_UI0 /* packed tval */ -#define DUK_DBL_IDX_VP1 DUK_DBL_IDX_UI1 /* packed tval */ -#else -#error internal error -#endif - -/* - * Helper macros for reading/writing memory representation parts, used - * by duk_numconv.c and duk_tval.h. - */ - -#define DUK_DBLUNION_SET_DOUBLE(u,v) do { \ - (u)->d = (v); \ - } while (0) - -#define DUK_DBLUNION_SET_HIGH32(u,v) do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ - } while (0) - -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ - } while (0) -#else -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = ((duk_uint64_t) (v)) << 32; \ - } while (0) -#endif -#else /* DUK_USE_64BIT_OPS */ -#define DUK_DBLUNION_SET_HIGH32_ZERO_LOW32(u,v) do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) (v); \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0; \ - } while (0) -#endif /* DUK_USE_64BIT_OPS */ - -#define DUK_DBLUNION_SET_LOW32(u,v) do { \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ - } while (0) - -#define DUK_DBLUNION_GET_DOUBLE(u) ((u)->d) -#define DUK_DBLUNION_GET_HIGH32(u) ((u)->ui[DUK_DBL_IDX_UI0]) -#define DUK_DBLUNION_GET_LOW32(u) ((u)->ui[DUK_DBL_IDX_UI1]) - -#if defined(DUK_USE_64BIT_OPS) -#if defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_SET_UINT64(u,v) do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) ((v) >> 32); \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) (v); \ - } while (0) -#define DUK_DBLUNION_GET_UINT64(u) \ - ((((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI0]) << 32) | \ - ((duk_uint64_t) (u)->ui[DUK_DBL_IDX_UI1])) -#else -#define DUK_DBLUNION_SET_UINT64(u,v) do { \ - (u)->ull[DUK_DBL_IDX_ULL0] = (duk_uint64_t) (v); \ - } while (0) -#define DUK_DBLUNION_GET_UINT64(u) ((u)->ull[DUK_DBL_IDX_ULL0]) -#endif -#define DUK_DBLUNION_SET_INT64(u,v) DUK_DBLUNION_SET_UINT64((u), (duk_uint64_t) (v)) -#define DUK_DBLUNION_GET_INT64(u) ((duk_int64_t) DUK_DBLUNION_GET_UINT64((u))) -#endif /* DUK_USE_64BIT_OPS */ - -/* - * Double NaN manipulation macros related to NaN normalization needed when - * using the packed duk_tval representation. NaN normalization is necessary - * to keep double values compatible with the duk_tval format. - * - * When packed duk_tval is used, the NaN space is used to store pointers - * and other tagged values in addition to NaNs. Actual NaNs are normalized - * to a specific quiet NaN. The macros below are used by the implementation - * to check and normalize NaN values when they might be created. The macros - * are essentially NOPs when the non-packed duk_tval representation is used. - * - * A FULL check is exact and checks all bits. A NOTFULL check is used by - * the packed duk_tval and works correctly for all NaNs except those that - * begin with 0x7ff0. Since the 'normalized NaN' values used with packed - * duk_tval begin with 0x7ff8, the partial check is reliable when packed - * duk_tval is used. The 0x7ff8 prefix means the normalized NaN will be a - * quiet NaN regardless of its remaining lower bits. - * - * The ME variant below is specifically for ARM byte order, which has the - * feature that while doubles have a mixed byte order (32107654), unsigned - * long long values has a little endian byte order (76543210). When writing - * a logical double value through a ULL pointer, the 32-bit words need to be - * swapped; hence the #if defined()s below for ULL writes with DUK_USE_DOUBLE_ME. - * This is not full ARM support but suffices for some environments. - */ - -#if defined(DUK_USE_64BIT_OPS) -#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; \ - } 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)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff80000ULL) -#define DUK__DBLUNION_IS_ANYINF(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x000000007ff00000ULL) -#define DUK__DBLUNION_IS_POSINF(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x000000007ff00000ULL) -#define DUK__DBLUNION_IS_NEGINF(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x00000000fff00000ULL) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & 0xffffffff7fffffffULL) == 0x0000000000000000ULL) -#define DUK__DBLUNION_IS_POSZERO(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL) -#define DUK__DBLUNION_IS_NEGZERO(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000080000000ULL) -#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; \ - } 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)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff8000000000000ULL) -#define DUK__DBLUNION_IS_ANYINF(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x7ff0000000000000ULL) -#define DUK__DBLUNION_IS_POSINF(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x7ff0000000000000ULL) -#define DUK__DBLUNION_IS_NEGINF(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0xfff0000000000000ULL) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - (((u)->ull[DUK_DBL_IDX_ULL0] & 0x7fffffffffffffffULL) == 0x0000000000000000ULL) -#define DUK__DBLUNION_IS_POSZERO(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x0000000000000000ULL) -#define DUK__DBLUNION_IS_NEGZERO(u) \ - ((u)->ull[DUK_DBL_IDX_ULL0] == 0x8000000000000000ULL) -#endif -#else /* DUK_USE_64BIT_OPS */ -/* Macros for no 64-bit ops, any endianness. */ -#define DUK__DBLUNION_SET_NAN_FULL(u) do { \ - (u)->ui[DUK_DBL_IDX_UI0] = (duk_uint32_t) 0x7ff80000UL; \ - (u)->ui[DUK_DBL_IDX_UI1] = (duk_uint32_t) 0x00000000UL; \ - } while (0) -#define DUK__DBLUNION_IS_NAN_FULL(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7ff00000UL) == 0x7ff00000UL) && \ - (((u)->ui[DUK_DBL_IDX_UI0] & 0x000fffffUL) != 0 || \ - (u)->ui[DUK_DBL_IDX_UI1] != 0)) -#define DUK__DBLUNION_IS_NORMALIZED_NAN_FULL(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff80000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_ANYINF(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x7ff00000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_POSINF(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0x7ff00000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_NEGINF(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0xfff00000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_ANYZERO(u) \ - ((((u)->ui[DUK_DBL_IDX_UI0] & 0x7fffffffUL) == 0x00000000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_POSZERO(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0x00000000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#define DUK__DBLUNION_IS_NEGZERO(u) \ - (((u)->ui[DUK_DBL_IDX_UI0] == 0x80000000UL) && \ - ((u)->ui[DUK_DBL_IDX_UI1] == 0x00000000UL)) -#endif /* DUK_USE_64BIT_OPS */ - -#define DUK__DBLUNION_SET_NAN_NOTFULL(u) do { \ - (u)->us[DUK_DBL_IDX_US0] = 0x7ff8UL; \ - } while (0) - -#define DUK__DBLUNION_IS_NAN_NOTFULL(u) \ - /* E == 0x7ff, topmost four bits of F != 0 => assume NaN */ \ - ((((u)->us[DUK_DBL_IDX_US0] & 0x7ff0UL) == 0x7ff0UL) && \ - (((u)->us[DUK_DBL_IDX_US0] & 0x000fUL) != 0x0000UL)) - -#define DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL(u) \ - /* E == 0x7ff, F == 8 => normalized NaN */ \ - ((u)->us[DUK_DBL_IDX_US0] == 0x7ff8UL) - -#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL(u) do { \ - if (DUK__DBLUNION_IS_NAN_FULL((u))) { \ - DUK__DBLUNION_SET_NAN_FULL((u)); \ - } \ - } while (0) - -#define DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL(u) do { \ - if (DUK__DBLUNION_IS_NAN_NOTFULL((u))) { \ - DUK__DBLUNION_SET_NAN_NOTFULL((u)); \ - } \ - } while (0) - -/* Concrete macros for NaN handling used by the implementation internals. - * Chosen so that they match the duk_tval representation: with a packed - * duk_tval, ensure NaNs are properly normalized; with a non-packed duk_tval - * these are essentially NOPs. - */ - -#if defined(DUK_USE_PACKED_TVAL) -#if defined(DUK_USE_FULL_TVAL) -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_FULL((u)) -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_FULL((u)) -#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_FULL((d)) -#else -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) DUK__DBLUNION_NORMALIZE_NAN_CHECK_NOTFULL((u)) -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_NOTFULL((u)) -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NORMALIZED_NAN_NOTFULL((u)) -#define DUK_DBLUNION_SET_NAN(d) DUK__DBLUNION_SET_NAN_NOTFULL((d)) -#endif -#define DUK_DBLUNION_IS_NORMALIZED(u) \ - (!DUK_DBLUNION_IS_NAN((u)) || /* either not a NaN */ \ - DUK_DBLUNION_IS_NORMALIZED_NAN((u))) /* or is a normalized NaN */ -#else /* DUK_USE_PACKED_TVAL */ -#define DUK_DBLUNION_NORMALIZE_NAN_CHECK(u) /* nop: no need to normalize */ -#define DUK_DBLUNION_IS_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ -#define DUK_DBLUNION_IS_NORMALIZED_NAN(u) DUK__DBLUNION_IS_NAN_FULL((u)) /* (DUK_ISNAN((u)->d)) */ -#define DUK_DBLUNION_IS_NORMALIZED(u) 1 /* all doubles are considered normalized */ -#define DUK_DBLUNION_SET_NAN(u) do { \ - /* in non-packed representation we don't care about which NaN is used */ \ - (u)->d = DUK_DOUBLE_NAN; \ - } while (0) -#endif /* DUK_USE_PACKED_TVAL */ - -#define DUK_DBLUNION_IS_ANYINF(u) DUK__DBLUNION_IS_ANYINF((u)) -#define DUK_DBLUNION_IS_POSINF(u) DUK__DBLUNION_IS_POSINF((u)) -#define DUK_DBLUNION_IS_NEGINF(u) DUK__DBLUNION_IS_NEGINF((u)) - -#define DUK_DBLUNION_IS_ANYZERO(u) DUK__DBLUNION_IS_ANYZERO((u)) -#define DUK_DBLUNION_IS_POSZERO(u) DUK__DBLUNION_IS_POSZERO((u)) -#define DUK_DBLUNION_IS_NEGZERO(u) DUK__DBLUNION_IS_NEGZERO((u)) - -/* XXX: native 64-bit byteswaps when available */ - -/* 64-bit byteswap, same operation independent of target endianness. */ -#define DUK_DBLUNION_BSWAP64(u) do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp2; \ - (u)->ui[1] = duk__bswaptmp1; \ - } while (0) - -/* Byteswap an IEEE double in the duk_double_union from host to network - * order. For a big endian target this is a no-op. - */ -#if defined(DUK_USE_DOUBLE_LE) -#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp2; \ - (u)->ui[1] = duk__bswaptmp1; \ - } while (0) -#elif defined(DUK_USE_DOUBLE_ME) -#define DUK_DBLUNION_DOUBLE_HTON(u) do { \ - duk_uint32_t duk__bswaptmp1, duk__bswaptmp2; \ - duk__bswaptmp1 = (u)->ui[0]; \ - duk__bswaptmp2 = (u)->ui[1]; \ - duk__bswaptmp1 = DUK_BSWAP32(duk__bswaptmp1); \ - duk__bswaptmp2 = DUK_BSWAP32(duk__bswaptmp2); \ - (u)->ui[0] = duk__bswaptmp1; \ - (u)->ui[1] = duk__bswaptmp2; \ - } while (0) -#elif defined(DUK_USE_DOUBLE_BE) -#define DUK_DBLUNION_DOUBLE_HTON(u) do { } while (0) -#else -#error internal error, double endianness insane -#endif - -/* Reverse operation is the same. */ -#define DUK_DBLUNION_DOUBLE_NTOH(u) DUK_DBLUNION_DOUBLE_HTON((u)) - -/* 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_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) -#define DUK_DBLUNION_GET_SIGNBIT(u) (((u)->ui[DUK_DBL_IDX_UI0] >> 31U)) -#endif - -#endif /* DUK_DBLUNION_H_INCLUDED */ - #endif /* DUKTAPE_H_INCLUDED */ -- cgit v1.2.3