diff options
Diffstat (limited to 'content/handlers/javascript')
-rw-r--r-- | content/handlers/javascript/duktape/duk_config.h | 114 | ||||
-rw-r--r-- | content/handlers/javascript/duktape/duktape.c | 11937 | ||||
-rw-r--r-- | content/handlers/javascript/duktape/duktape.h | 577 |
3 files changed, 7089 insertions, 5539 deletions
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 <windows.h> +#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 <michael.drake@codethink.co.uk> * * 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. */ @@ -5880,6 +6364,16 @@ DUK_INTERNAL_DECL duk_size_t duk_hstring_get_charlen(duk_hstring *h); #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 @@ -7645,10 +8165,17 @@ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); #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; - /* marker for detecting internal "double faults", see duk_error_throw.c */ - duk_bool_t handling_error; + /* 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; + + /* 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; + +#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 */ + /* 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) */ -#endif - - /* 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]; + 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 */ - /* 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); -#endif -#if defined(DUK_USE_MS_STRINGTABLE_RESIZE) -DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap); +DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h); #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); @@ -12134,86 +12687,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); - 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 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); + + 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); @@ -36406,6 +37433,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,10 +45412,21 @@ 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; } @@ -44495,6 +45817,455 @@ duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, #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). * * String hashing is performance critical because a string hash is computed @@ -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) @@ -44779,21 +46550,24 @@ DUK_LOCAL void duk__mark_heaphdr(duk_heap *heap, duk_heaphdr *h) { return; } #endif +#if defined(DUK_USE_ASSERTIONS) && defined(DUK_USE_REFERENCE_COUNTING) + h->h_assert_refcount++; /* Comparison refcount: bump even if already reachable. */ +#endif if (DUK_HEAPHDR_HAS_REACHABLE(h)) { DUK_DDD(DUK_DDDPRINT("already marked reachable, skip")); return; } 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)) { @@ -44851,37 +46626,12 @@ DUK_LOCAL void duk__mark_roots_heap(duk_heap *heap) { } /* - * 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); @@ -45495,81 +47130,6 @@ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_ } /* - * 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. * * Compaction is assumed to never throw an error. @@ -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); - if (DUK_HEAPHDR_GET_PREV(heap, hdr)) { - DUK_HEAPHDR_SET_NEXT(heap, DUK_HEAPHDR_GET_PREV(heap, hdr), DUK_HEAPHDR_GET_NEXT(heap, hdr)); + 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); + + /* 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 */ @@ -46504,36 +48181,6 @@ DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { #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. * * When an object is about to be freed, all other objects it refers to must @@ -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 + +#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__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) +#define DUK__STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */ /* - * Create a hstring and insert into the heap. The created object - * is directly garbage collectable with reference count zero. + * 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 */ + +/* + * 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 */ - -#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); + heap->st_size = new_st_size; + heap->st_mask = new_st_size - 1; - 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))); -#else - DUK_DD(DUK_DDPRINT("[%03d] -> plain %d", (int) i, (int) (e->u.str ? 1 : 0))); -#endif - } else { - used = 0; -#if defined(DUK_USE_HEAPPTR16) - lst = (duk_uint16_t *) DUK_USE_HEAPPTR_DEC16(heap->heap_udata, e->u.strlist16); +#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 - lst = e->u.strlist; + 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 - 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) +#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 */ -/* 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 - - 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. */ + + 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); + } -#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. + /* 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; @@ -49716,119 +51279,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")); + + DUK_FREE(thr->heap, new_p); /* OK for NULL. */ - thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags; + 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); @@ -54913,6 +56374,41 @@ DUK_INTERNAL duk_size_t duk_hobject_get_length(duk_hthread *thr, duk_hobject *ob } /* + * 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) * * [ ... key ] -> [ ... desc/undefined ] @@ -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 */ +#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 */ /* * 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 |