summaryrefslogtreecommitdiff
path: root/content/handlers/javascript/duktape/duktape.c
diff options
context:
space:
mode:
Diffstat (limited to 'content/handlers/javascript/duktape/duktape.c')
-rw-r--r--content/handlers/javascript/duktape/duktape.c11937
1 files changed, 6895 insertions, 5042 deletions
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
* may see a currently reachable activation whose 'func' is NULL.
*/
-#if defined(DUK_USE_REFERENCE_COUNTING)
tmp = DUK_ACT_GET_FUNC(act);
-#endif
+ DUK_HOBJECT_DECREF_NORZ_ALLOWNULL(thr, tmp);
+ DUK_UNREF(tmp);
act->func = NULL;
-#if defined(DUK_USE_REFERENCE_COUNTING)
- DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
- act = thr->callstack + idx; /* avoid side effect issues */
- DUK_UNREF(act);
-#endif
}
thr->callstack_top = new_top;
+ if (new_top > 0) {
+ thr->callstack_curr = thr->callstack + new_top - 1;
+ } else {
+ thr->callstack_curr = NULL;
+ }
/*
* We could clear the book-keeping variables for the topmost activation,
* but don't do so now.
*/
#if 0
- if (thr->callstack_top > 0) {
- duk_activation *act = thr->callstack + thr->callstack_top - 1;
+ if (thr->callstack_curr != NULL) {
+ duk_activation *act = thr->callstack_curr;
act->idx_retval = 0;
}
#endif
@@ -57595,7 +59169,12 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_
*/
}
-DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
+DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_top) {
+ duk_hthread_callstack_unwind_norz(thr, new_top);
+ DUK_REFZERO_CHECK_FAST(thr);
+}
+
+DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_catchstack_grow(duk_hthread *thr) {
duk_catcher *new_ptr;
duk_size_t old_size;
duk_size_t new_size;
@@ -57604,10 +59183,6 @@ DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
DUK_ASSERT_DISABLE(thr->catchstack_top); /* avoid warning (unsigned) */
DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
- if (thr->catchstack_top < thr->catchstack_size) {
- return;
- }
-
old_size = thr->catchstack_size;
new_size = old_size + DUK_CATCHSTACK_GROW_STEP;
@@ -57635,7 +59210,19 @@ DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
/* note: any entries above the catchstack top are garbage and not zeroed */
}
-DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) {
+DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) {
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT_DISABLE(thr->catchstack_top); /* avoid warning (unsigned) */
+ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
+
+ if (DUK_LIKELY(thr->catchstack_top < thr->catchstack_size)) {
+ return;
+ }
+
+ duk__hthread_do_catchstack_grow(thr);
+}
+
+DUK_LOCAL DUK_COLD DUK_NOINLINE void duk__hthread_do_catchstack_shrink(duk_hthread *thr) {
duk_size_t new_size;
duk_catcher *p;
@@ -57643,10 +59230,6 @@ DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) {
DUK_ASSERT_DISABLE(thr->catchstack_top >= 0); /* avoid warning (unsigned) */
DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
- if (thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD) {
- return;
- }
-
new_size = thr->catchstack_top + DUK_CATCHSTACK_SHRINK_SPARE;
DUK_ASSERT(new_size >= thr->catchstack_top);
@@ -57673,7 +59256,19 @@ DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) {
/* note: any entries above the catchstack top are garbage and not zeroed */
}
-DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new_top) {
+DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) {
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT_DISABLE(thr->catchstack_top >= 0); /* avoid warning (unsigned) */
+ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top);
+
+ if (DUK_LIKELY(thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD)) {
+ return;
+ }
+
+ duk__hthread_do_catchstack_shrink(thr);
+}
+
+DUK_INTERNAL void duk_hthread_catchstack_unwind_norz(duk_hthread *thr, duk_size_t new_top) {
duk_size_t idx;
DUK_DDD(DUK_DDDPRINT("unwind catchstack top of thread %p from %ld to %ld",
@@ -57729,7 +59324,8 @@ DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new
env = act->lex_env; /* current lex_env of the activation (created for catcher) */
DUK_ASSERT(env != NULL); /* must be, since env was created when catcher was created */
act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env); /* prototype is lex_env before catcher created */
- DUK_HOBJECT_DECREF(thr, env);
+ DUK_HOBJECT_INCREF(thr, act->lex_env);
+ DUK_HOBJECT_DECREF_NORZ(thr, env);
/* There is no need to decref anything else than 'env': if 'env'
* becomes unreachable, refzero will handle decref'ing its prototype.
@@ -57741,6 +59337,100 @@ DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new
/* note: any entries above the catchstack top are garbage and not zeroed */
}
+
+DUK_INTERNAL void duk_hthread_catchstack_unwind(duk_hthread *thr, duk_size_t new_top) {
+ duk_hthread_catchstack_unwind_norz(thr, new_top);
+ DUK_REFZERO_CHECK_FAST(thr);
+}
+
+#if defined(DUK_USE_FINALIZER_TORTURE)
+DUK_INTERNAL void duk_hthread_valstack_torture_realloc(duk_hthread *thr) {
+ duk_size_t alloc_size;
+ duk_tval *new_ptr;
+ duk_ptrdiff_t end_off;
+ duk_ptrdiff_t bottom_off;
+ duk_ptrdiff_t top_off;
+
+ if (thr->valstack == NULL) {
+ return;
+ }
+
+ end_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_end - (duk_uint8_t *) thr->valstack);
+ bottom_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_bottom - (duk_uint8_t *) thr->valstack);
+ top_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->valstack_top - (duk_uint8_t *) thr->valstack);
+ alloc_size = (duk_size_t) end_off;
+ if (alloc_size == 0) {
+ return;
+ }
+
+ new_ptr = (duk_tval *) DUK_ALLOC(thr->heap, alloc_size);
+ if (new_ptr != NULL) {
+ DUK_MEMCPY((void *) new_ptr, (const void *) thr->valstack, alloc_size);
+ DUK_MEMSET((void *) thr->valstack, 0x55, alloc_size);
+ DUK_FREE(thr->heap, (void *) thr->valstack);
+ thr->valstack = new_ptr;
+ thr->valstack_end = (duk_tval *) ((duk_uint8_t *) new_ptr + end_off);
+ thr->valstack_bottom = (duk_tval *) ((duk_uint8_t *) new_ptr + bottom_off);
+ thr->valstack_top = (duk_tval *) ((duk_uint8_t *) new_ptr + top_off);
+ /* No change in size. */
+ } else {
+ DUK_D(DUK_DPRINT("failed to realloc valstack for torture, ignore"));
+ }
+}
+
+DUK_INTERNAL void duk_hthread_callstack_torture_realloc(duk_hthread *thr) {
+ duk_size_t alloc_size;
+ duk_activation *new_ptr;
+ duk_ptrdiff_t curr_off;
+
+ if (thr->callstack == NULL) {
+ return;
+ }
+
+ curr_off = (duk_ptrdiff_t) ((duk_uint8_t *) thr->callstack_curr - (duk_uint8_t *) thr->callstack);
+ alloc_size = sizeof(duk_activation) * thr->callstack_size;
+ if (alloc_size == 0) {
+ return;
+ }
+
+ new_ptr = (duk_activation *) DUK_ALLOC(thr->heap, alloc_size);
+ if (new_ptr != NULL) {
+ DUK_MEMCPY((void *) new_ptr, (const void *) thr->callstack, alloc_size);
+ DUK_MEMSET((void *) thr->callstack, 0x55, alloc_size);
+ DUK_FREE(thr->heap, (void *) thr->callstack);
+ thr->callstack = new_ptr;
+ thr->callstack_curr = (duk_activation *) ((duk_uint8_t *) new_ptr + curr_off);
+ /* No change in size. */
+ } else {
+ DUK_D(DUK_DPRINT("failed to realloc callstack for torture, ignore"));
+ }
+}
+
+DUK_INTERNAL void duk_hthread_catchstack_torture_realloc(duk_hthread *thr) {
+ duk_size_t alloc_size;
+ duk_catcher *new_ptr;
+
+ if (thr->catchstack == NULL) {
+ return;
+ }
+
+ alloc_size = sizeof(duk_catcher) * thr->catchstack_size;
+ if (alloc_size == 0) {
+ return;
+ }
+
+ new_ptr = (duk_catcher *) DUK_ALLOC(thr->heap, alloc_size);
+ if (new_ptr != NULL) {
+ DUK_MEMCPY((void *) new_ptr, (const void *) thr->catchstack, alloc_size);
+ DUK_MEMSET((void *) thr->catchstack, 0x55, alloc_size);
+ DUK_FREE(thr->heap, (void *) thr->catchstack);
+ thr->catchstack = new_ptr;
+ /* No change in size. */
+ } else {
+ DUK_D(DUK_DPRINT("failed to realloc catchstack for torture, ignore"));
+ }
+}
+#endif /* DUK_USE_FINALIZER_TORTURE */
/*
* Shared helpers for arithmetic operations
*/
@@ -57901,6 +59591,8 @@ DUK_INTERNAL double duk_js_arith_pow(double x, double y) {
/* #include duk_internal.h -> already included */
+/* XXX: heap->error_not_allowed for success path too? */
+
/*
* Forward declarations.
*/
@@ -58056,16 +59748,19 @@ DUK_LOCAL void duk__create_arguments_object(duk_hthread *thr,
arg = duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_FASTREFS |
DUK_HOBJECT_FLAG_ARRAY_PART |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARGUMENTS),
DUK_BIDX_OBJECT_PROTOTYPE);
DUK_ASSERT(arg != NULL);
(void) duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_FASTREFS |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
-1); /* no prototype */
(void) duk_push_object_helper(ctx,
DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_FASTREFS |
DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
-1); /* no prototype */
i_arg = duk_get_top(ctx) - 3;
@@ -58375,7 +60070,7 @@ DUK_LOCAL void duk__handle_bound_chain_for_call(duk_hthread *thr,
(long) num_stack_args, (long) idx_func, duk_get_tval(ctx, idx_func)));
} while (--sanity > 0);
- if (sanity == 0) {
+ if (DUK_UNLIKELY(sanity == 0)) {
DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
}
@@ -58449,7 +60144,9 @@ DUK_LOCAL void duk__update_func_caller_prop(duk_hthread *thr, duk_hobject *func)
return;
}
- act_callee = thr->callstack + thr->callstack_top - 1;
+ DUK_ASSERT(thr->callstack_top > 0);
+ act_callee = thr->callstack_curr;
+ DUK_ASSERT(act_callee != NULL);
act_caller = (thr->callstack_top >= 2 ? act_callee - 1 : NULL);
/* XXX: check .caller writability? */
@@ -58930,16 +60627,6 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
*/
duk__handle_call_inner(thr, num_stack_args, call_flags, idx_func);
- /* Success path handles */
- DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
- DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
-
- /* Longjmp state is kept clean in success path */
- DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
- DUK_ASSERT(thr->heap->lj.iserror == 0);
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
return DUK_EXEC_SUCCESS;
@@ -58966,11 +60653,6 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
idx_func,
old_jmpbuf_ptr);
- /* Longjmp state is cleaned up by error handling */
- DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
- DUK_ASSERT(thr->heap->lj.iserror == 0);
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
return DUK_EXEC_ERROR;
}
#if defined(DUK_USE_CPP_EXCEPTIONS)
@@ -58996,6 +60678,7 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
entry_ptr_curr_pc,
idx_func,
old_jmpbuf_ptr);
+
return DUK_EXEC_ERROR;
}
} catch (...) {
@@ -59016,6 +60699,7 @@ DUK_INTERNAL duk_int_t duk_handle_call_protected(duk_hthread *thr,
entry_ptr_curr_pc,
idx_func,
old_jmpbuf_ptr);
+
return DUK_EXEC_ERROR;
}
}
@@ -59206,7 +60890,8 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
duk_hthread_callstack_grow(thr);
- if (thr->callstack_top > 0) {
+ act = thr->callstack_curr;
+ if (act != NULL) {
/*
* Update idx_retval of current activation.
*
@@ -59217,12 +60902,13 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
* the Ecmascript call's idx_retval must be set for things to work.
*/
- (thr->callstack + thr->callstack_top - 1)->idx_retval = entry_valstack_bottom_index + idx_func;
+ act->idx_retval = entry_valstack_bottom_index + idx_func;
}
DUK_ASSERT(thr->callstack_top < thr->callstack_size);
act = thr->callstack + thr->callstack_top;
thr->callstack_top++;
+ thr->callstack_curr = act;
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
DUK_ASSERT(thr->valstack_top > thr->valstack_bottom); /* at least effective 'this' */
DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func));
@@ -59313,7 +60999,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
if (func) {
duk__update_func_caller_prop(thr, func);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
}
#endif
@@ -59359,7 +61045,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
/* [ ... func this arg1 ... argN envobj ] */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
act->lex_env = env;
act->var_env = env;
DUK_HOBJECT_INCREF(thr, env);
@@ -59411,7 +61097,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
* new value stack bottom, and call the target.
*/
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) {
/*
* Ecmascript call
@@ -59454,10 +61140,9 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top); /* may need unwind */
DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
- DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
- duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
+ duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
duk_hthread_catchstack_shrink_check(thr);
- duk_hthread_callstack_unwind(thr, entry_callstack_top);
+ duk_hthread_callstack_unwind_norz(thr, entry_callstack_top); /* XXX: may now fail */
duk_hthread_callstack_shrink_check(thr);
thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
@@ -59519,7 +61204,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
DUK_ASSERT(thr->catchstack_top == entry_catchstack_top); /* no need to unwind */
DUK_ASSERT(thr->callstack_top == entry_callstack_top + 1);
- duk_hthread_callstack_unwind(thr, entry_callstack_top);
+ duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
duk_hthread_callstack_shrink_check(thr);
thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
@@ -59574,9 +61259,12 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */
thr->state = (duk_uint8_t) entry_thread_state;
+ /* Disabled assert: triggered with some torture tests. */
+#if 0
DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */
(thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */
(thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */
+#endif
thr->heap->call_recursion_depth = entry_call_recursion_depth;
@@ -59589,7 +61277,7 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
* on every return should have no ill effect.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
- if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
+ if (duk_debug_is_attached(thr->heap)) {
DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt"));
DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
thr->interrupt_init -= thr->interrupt_counter;
@@ -59602,6 +61290,14 @@ DUK_LOCAL void duk__handle_call_inner(duk_hthread *thr,
duk__interrupt_fixup(thr, entry_curr_thread);
#endif
+ /* Restored by success path. */
+ DUK_ASSERT(thr->heap->call_recursion_depth == entry_call_recursion_depth);
+ DUK_ASSERT(thr->ptr_curr_pc == entry_ptr_curr_pc);
+
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+ DUK_REFZERO_CHECK_FAST(thr);
+
return;
thread_state_error:
@@ -59633,6 +61329,7 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
* the error here.
*/
DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
+ DUK_ASSERT_LJSTATE_SET(thr->heap);
DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
@@ -59654,9 +61351,9 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
* scopes; this is a sandboxing issue, described in:
* https://github.com/svaarala/duktape/issues/476
*/
- duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
+ duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
duk_hthread_catchstack_shrink_check(thr);
- duk_hthread_callstack_unwind(thr, entry_callstack_top);
+ duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
duk_hthread_callstack_shrink_check(thr);
thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
@@ -59707,9 +61404,12 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
DUK_HEAP_SWITCH_THREAD(thr->heap, entry_curr_thread); /* may be NULL */
thr->state = (duk_uint8_t) entry_thread_state;
+ /* Disabled assert: triggered with some torture tests. */
+#if 0
DUK_ASSERT((thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread == NULL) || /* first call */
(thr->state == DUK_HTHREAD_STATE_INACTIVE && thr->heap->curr_thread != NULL) || /* other call */
(thr->state == DUK_HTHREAD_STATE_RUNNING && thr->heap->curr_thread == thr)); /* current thread */
+#endif
thr->heap->call_recursion_depth = entry_call_recursion_depth;
@@ -59722,7 +61422,7 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
* on every return should have no ill effect.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
- if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
+ if (duk_debug_is_attached(thr->heap)) {
DUK_DD(DUK_DDPRINT("returning with debugger enabled, force interrupt"));
DUK_ASSERT(thr->interrupt_counter <= thr->interrupt_init);
thr->interrupt_init -= thr->interrupt_counter;
@@ -59734,6 +61434,21 @@ DUK_LOCAL void duk__handle_call_error(duk_hthread *thr,
#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
duk__interrupt_fixup(thr, entry_curr_thread);
#endif
+
+ /* Error handling complete, remove side effect protections and
+ * process pending finalizers.
+ */
+#if defined(DUK_USE_ASSERTIONS)
+ DUK_ASSERT(thr->heap->error_not_allowed == 1);
+ thr->heap->error_not_allowed = 0;
+#endif
+ DUK_ASSERT(thr->heap->pf_prevent_count > 0);
+ thr->heap->pf_prevent_count--;
+ DUK_DD(DUK_DDPRINT("call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count));
+
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+ DUK_REFZERO_CHECK_SLOW(thr);
}
/*
@@ -59831,12 +61546,6 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
entry_callstack_top,
entry_catchstack_top);
- /* Longjmp state is kept clean in success path */
- DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
- DUK_ASSERT(thr->heap->lj.iserror == 0);
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
/* Note: either pointer may be NULL (at entry), so don't assert */
thr->heap->lj.jmpbuf_ptr = old_jmpbuf_ptr;
@@ -59856,12 +61565,6 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
entry_catchstack_top,
old_jmpbuf_ptr);
- /* Longjmp state is cleaned up by error handling */
- DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_UNKNOWN);
- DUK_ASSERT(thr->heap->lj.iserror == 0);
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value1));
- DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&thr->heap->lj.value2));
-
retval = DUK_EXEC_ERROR;
}
#if defined(DUK_USE_CPP_EXCEPTIONS)
@@ -59906,6 +61609,8 @@ DUK_INTERNAL duk_int_t duk_handle_safe_call(duk_hthread *thr,
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr == old_jmpbuf_ptr); /* success/error path both do this */
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
duk__handle_safe_call_shared(thr,
idx_retbase,
num_stack_rets,
@@ -60020,6 +61725,10 @@ DUK_LOCAL void duk__handle_safe_call_inner(duk_hthread *thr,
DUK_ASSERT(thr->callstack_top == entry_callstack_top);
duk__safe_call_adjust_valstack(thr, idx_retbase, num_stack_rets, rc);
+
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+ DUK_REFZERO_CHECK_FAST(thr);
return;
thread_state_error:
@@ -60054,6 +61763,7 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
* the error here.
*/
DUK_ASSERT(thr->heap->lj.type == DUK_LJ_TYPE_THROW);
+ DUK_ASSERT_LJSTATE_SET(thr->heap);
DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
@@ -60062,9 +61772,9 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
DUK_ASSERT(thr->catchstack_top >= entry_catchstack_top);
DUK_ASSERT(thr->callstack_top >= entry_callstack_top);
- duk_hthread_catchstack_unwind(thr, entry_catchstack_top);
+ duk_hthread_catchstack_unwind_norz(thr, entry_catchstack_top);
duk_hthread_catchstack_shrink_check(thr);
- duk_hthread_callstack_unwind(thr, entry_callstack_top);
+ duk_hthread_callstack_unwind_norz(thr, entry_callstack_top);
duk_hthread_callstack_shrink_check(thr);
thr->valstack_bottom = thr->valstack + entry_valstack_bottom_index;
@@ -60096,6 +61806,21 @@ DUK_LOCAL void duk__handle_safe_call_error(duk_hthread *thr,
thr->heap->lj.iserror = 0;
DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */
DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */
+
+ /* Error handling complete, remove side effect protections and
+ * process pending finalizers.
+ */
+#if defined(DUK_USE_ASSERTIONS)
+ DUK_ASSERT(thr->heap->error_not_allowed == 1);
+ thr->heap->error_not_allowed = 0;
+#endif
+ DUK_ASSERT(thr->heap->pf_prevent_count > 0);
+ thr->heap->pf_prevent_count--;
+ DUK_DD(DUK_DDPRINT("safe call error handled, pf_prevent_count updated to %ld", (long) thr->heap->pf_prevent_count));
+
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
+
+ DUK_REFZERO_CHECK_SLOW(thr);
}
DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
@@ -60142,6 +61867,8 @@ DUK_LOCAL void duk__handle_safe_call_shared(duk_hthread *thr,
#if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
duk__interrupt_fixup(thr, entry_curr_thread);
#endif
+
+ DUK_ASSERT_LJSTATE_UNSET(thr->heap);
}
/*
@@ -60329,7 +62056,8 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_ASSERT(thr->callstack_top >= 1);
DUK_ASSERT((call_flags & DUK_CALL_FLAG_IS_RESUME) == 0);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
if (act->flags & DUK_ACT_FLAG_PREVENT_YIELD) {
/* See: test-bug-tailcall-preventyield-assert.c. */
DUK_DDD(DUK_DDDPRINT("tail call prevented by current activation having DUK_ACT_FLAG_PREVENTYIELD"));
@@ -60376,16 +62104,17 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
break;
}
}
- duk_hthread_catchstack_unwind(thr, i_stk + 1);
+ duk_hthread_catchstack_unwind_norz(thr, i_stk + 1);
/* Unwind the topmost callstack entry before reusing it */
DUK_ASSERT(thr->callstack_top > 0);
- duk_hthread_callstack_unwind(thr, thr->callstack_top - 1);
+ duk_hthread_callstack_unwind_norz(thr, thr->callstack_top - 1);
/* Then reuse the unwound activation; callstack was not shrunk so there is always space */
+ DUK_ASSERT(thr->callstack_top < thr->callstack_size);
+ act = thr->callstack + thr->callstack_top;
thr->callstack_top++;
- DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
- act = thr->callstack + thr->callstack_top - 1;
+ thr->callstack_curr = act;
/* Start filling in the activation */
act->func = func; /* don't want an intermediate exposed state with func == NULL */
@@ -60402,7 +62131,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_TVAL_SET_OBJECT(&act->tv_func, func); /* borrowed, no refcount */
#if defined(DUK_USE_REFERENCE_COUNTING)
DUK_HOBJECT_INCREF(thr, func);
- act = thr->callstack + thr->callstack_top - 1; /* side effects (currently none though) */
+ act = thr->callstack_curr; /* side effects (currently none though) */
#endif
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
@@ -60414,7 +62143,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
* is in use.
*/
duk__update_func_caller_prop(thr, func);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
#endif
act->flags = (DUK_HOBJECT_HAS_STRICT(func) ?
@@ -60471,7 +62200,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_DDD(DUK_DDDPRINT("update to current activation idx_retval"));
DUK_ASSERT(thr->callstack_top < thr->callstack_size);
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
act->idx_retval = entry_valstack_bottom_index + idx_func;
@@ -60480,6 +62209,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
DUK_ASSERT(thr->callstack_top < thr->callstack_size);
act = thr->callstack + thr->callstack_top;
thr->callstack_top++;
+ thr->callstack_curr = act;
DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
@@ -60512,7 +62242,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
#if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
duk__update_func_caller_prop(thr, func);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
#endif
}
@@ -60571,7 +62301,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
/* [ ... arg1 ... argN envobj ] */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
act->lex_env = env;
act->var_env = env;
DUK_HOBJECT_INCREF(thr, act->lex_env);
@@ -60607,6 +62337,7 @@ DUK_INTERNAL duk_bool_t duk_handle_ecma_call_setup(duk_hthread *thr,
* the topmost activation.
*/
+ DUK_REFZERO_CHECK_FAST(thr);
return 1;
}
/*
@@ -62609,6 +64340,8 @@ DUK_LOCAL duk_bool_t duk__const_needs_refcount(duk_compiler_ctx *comp_ctx, duk_r
duk_pop(ctx);
return ret;
#else
+ DUK_UNREF(comp_ctx);
+ DUK_UNREF(rc);
DUK_ASSERT((rc & DUK__CONST_MARKER) == 0); /* caller removes const marker */
return 0;
#endif
@@ -62860,15 +64593,31 @@ DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x
DUK_DDD(DUK_DDDPRINT("arith inline check: d1=%lf, d2=%lf, op=%ld",
(double) d1, (double) d2, (long) x->op));
switch (x->op) {
- case DUK_OP_ADD: d3 = d1 + d2; break;
- case DUK_OP_SUB: d3 = d1 - d2; break;
- case DUK_OP_MUL: d3 = d1 * d2; break;
- case DUK_OP_DIV: d3 = d1 / d2; break;
+ case DUK_OP_ADD: {
+ d3 = d1 + d2;
+ break;
+ }
+ case DUK_OP_SUB: {
+ d3 = d1 - d2;
+ break;
+ }
+ case DUK_OP_MUL: {
+ d3 = d1 * d2;
+ break;
+ }
+ case DUK_OP_DIV: {
+ d3 = d1 / d2;
+ break;
+ }
case DUK_OP_EXP: {
d3 = (duk_double_t) duk_js_arith_pow((double) d1, (double) d2);
break;
}
- default: accept_fold = 0; break;
+ default: {
+ d3 = 0.0; /* Won't be used, but silence MSVC /W4 warning. */
+ accept_fold = 0;
+ break;
+ }
}
if (accept_fold) {
@@ -66782,6 +68531,7 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_
duk_small_uint_t stmt_flags = 0;
duk_int_t label_id = -1;
duk_small_uint_t tok;
+ duk_bool_t test_func_decl;
DUK__RECURSION_INCREASE(comp_ctx, thr);
@@ -66847,17 +68597,15 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_
* for function statements are modelled after V8, see
* test-dev-func-decl-outside-top.js.
*/
-
+ test_func_decl = allow_source_elem;
#if defined(DUK_USE_NONSTD_FUNC_STMT)
/* Lenient: allow function declarations outside top level in
* non-strict mode but reject them in strict mode.
*/
- if (allow_source_elem || !comp_ctx->curr_func.is_strict)
-#else /* DUK_USE_NONSTD_FUNC_STMT */
- /* Strict: never allow function declarations outside top level. */
- if (allow_source_elem)
+ test_func_decl = test_func_decl || !comp_ctx->curr_func.is_strict;
#endif /* DUK_USE_NONSTD_FUNC_STMT */
- {
+ /* Strict: never allow function declarations outside top level. */
+ if (test_func_decl) {
/* FunctionDeclaration: not strictly a statement but handled as such.
*
* O(depth^2) parse count for inner functions is handled by recording a
@@ -67569,6 +69317,9 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct
(duk_tval *) duk_get_tval(ctx, -2),
(duk_tval *) duk_get_tval(ctx, -1)));
+#if defined(DUK_USE_FASTINT)
+ DUK_ASSERT(DUK_TVAL_IS_NULL(duk_get_tval(ctx, -1)) || DUK_TVAL_IS_FASTINT(duk_get_tval(ctx, -1)));
+#endif
duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */
}
@@ -67634,8 +69385,7 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct
duk_push_null(ctx);
declvar_flags = DUK_PROPDESC_FLAG_WRITABLE |
- DUK_PROPDESC_FLAG_ENUMERABLE |
- DUK_BC_DECLVAR_FLAG_UNDEF_VALUE;
+ DUK_PROPDESC_FLAG_ENUMERABLE;
if (configurable_bindings) {
declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE;
}
@@ -68312,9 +70062,9 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) {
DUK_ASSERT(lex_pt != NULL);
flags = comp_stk->flags;
- is_eval = (flags & DUK_JS_COMPILE_FLAG_EVAL ? 1 : 0);
- is_strict = (flags & DUK_JS_COMPILE_FLAG_STRICT ? 1 : 0);
- is_funcexpr = (flags & DUK_JS_COMPILE_FLAG_FUNCEXPR ? 1 : 0);
+ is_eval = (flags & DUK_COMPILE_EVAL ? 1 : 0);
+ is_strict = (flags & DUK_COMPILE_STRICT ? 1 : 0);
+ is_funcexpr = (flags & DUK_COMPILE_FUNCEXPR ? 1 : 0);
h_filename = duk_get_hstring(ctx, -1); /* may be undefined */
@@ -68390,7 +70140,7 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) {
*/
DUK_ASSERT(func->is_setget == 0);
- func->is_strict = is_strict;
+ func->is_strict = (duk_uint8_t) is_strict;
DUK_ASSERT(func->is_notail == 0);
if (is_funcexpr) {
@@ -68405,8 +70155,9 @@ DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx, void *udata) {
(void) duk__parse_func_like_raw(comp_ctx, 0 /*flags*/);
} else {
DUK_ASSERT(func->is_function == 0);
- func->is_eval = is_eval;
- func->is_global = !is_eval;
+ DUK_ASSERT(is_eval == 0 || is_eval == 1);
+ func->is_eval = (duk_uint8_t) is_eval;
+ func->is_global = (duk_uint8_t) !is_eval;
DUK_ASSERT(func->is_namebinding == 0);
DUK_ASSERT(func->is_constructable == 0);
@@ -68446,6 +70197,7 @@ DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer
DUK_LEXER_INITCTX(&comp_stk.comp_ctx_alloc.lex);
comp_stk.comp_ctx_alloc.lex.input = src_buffer;
comp_stk.comp_ctx_alloc.lex.input_length = src_length;
+ comp_stk.comp_ctx_alloc.lex.flags = flags; /* Forward flags directly for now. */
/* [ ... filename ] */
@@ -69276,7 +71028,7 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr,
DUK_ASSERT(DUK_TVAL_IS_STRING(tv_id));
name = DUK_TVAL_GET_STRING(tv_id);
DUK_ASSERT(name != NULL);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [ ... val this ] */
/* XXX: Fastint fast path would be useful here. Also fastints
@@ -69295,13 +71047,13 @@ DUK_LOCAL DUK__INLINE_PERF void duk__prepost_incdec_var_helper(duk_hthread *thr,
if (op & 0x02) {
duk_push_number(ctx, y); /* -> [ ... x this y ] */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict);
duk_pop_2(ctx); /* -> [ ... x ] */
} else {
duk_pop_2(ctx); /* -> [ ... ] */
duk_push_number(ctx, y); /* -> [ ... y ] */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
duk_js_putvar_activation(thr, act, name, DUK_GET_TVAL_NEGIDX(ctx, -1), is_strict);
}
@@ -69431,17 +71183,19 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval
duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type);
- duk_hthread_catchstack_unwind(thr, cat_idx + 1);
- duk_hthread_callstack_unwind(thr, thr->catchstack[cat_idx].callstack_index + 1);
+ duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1);
+ duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1);
DUK_ASSERT(thr->callstack_top >= 1);
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
+ DUK_ASSERT(thr->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
+ DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
duk__reconfig_valstack_ecma_catcher(thr, thr->callstack_top - 1, cat_idx);
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
act->curr_pc = thr->catchstack[cat_idx].pc_base + 0; /* +0 = catch */
act = NULL;
@@ -69456,8 +71210,7 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval
*/
if (DUK_CAT_HAS_CATCH_BINDING_ENABLED(&thr->catchstack[cat_idx])) {
- duk_hobject *new_env;
- duk_hobject *act_lex_env;
+ duk_hdecenv *new_env;
DUK_DDD(DUK_DDDPRINT("catcher has an automatic catch binding"));
@@ -69465,7 +71218,8 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval
* points, so we re-lookup it multiple times.
*/
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
if (act->lex_env == NULL) {
DUK_ASSERT(act->var_env == NULL);
@@ -69473,22 +71227,26 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval
/* this may have side effects, so re-lookup act */
duk_js_init_activation_environment_records_delayed(thr, act);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
}
DUK_ASSERT(act->lex_env != NULL);
DUK_ASSERT(act->var_env != NULL);
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_UNREF(act); /* unreferenced without assertions */
- act = thr->callstack + thr->callstack_top - 1;
- act_lex_env = act->lex_env;
- act = NULL; /* invalidated */
-
- new_env = duk_push_object_helper_proto(ctx,
- DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
- act_lex_env);
+ /* XXX: If an out-of-memory happens here, longjmp state asserts
+ * will be triggered at present and a try-catch fails to catch.
+ * That's not sandboxing fatal (C API protected calls are what
+ * matters), and script catch code can immediately throw anyway
+ * for almost any operation.
+ */
+ new_env = duk_hdecenv_alloc(thr,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
DUK_ASSERT(new_env != NULL);
+ duk_push_hobject(ctx, (duk_hobject *) new_env);
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
DUK_DDD(DUK_DDDPRINT("new_env allocated: %!iO", (duk_heaphdr *) new_env));
/* Note: currently the catch binding is handled without a register
@@ -69497,14 +71255,20 @@ DUK_LOCAL void duk__handle_catch(duk_hthread *thr, duk_size_t cat_idx, duk_tval
* record regbases etc.
*/
+ /* XXX: duk_xdef_prop() may cause an out-of-memory, see above. */
DUK_ASSERT(thr->catchstack[cat_idx].h_varname != NULL);
duk_push_hstring(ctx, thr->catchstack[cat_idx].h_varname);
duk_push_tval(ctx, thr->valstack + thr->catchstack[cat_idx].idx_base);
duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_W); /* writable, not configurable */
- act = thr->callstack + thr->callstack_top - 1;
- act->lex_env = new_env;
- DUK_HOBJECT_INCREF(thr, new_env); /* reachable through activation */
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
+ DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, act->lex_env);
+ act->lex_env = (duk_hobject *) new_env;
+ DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env); /* reachable through activation */
+ /* Net refcount change to act->lex_env is 0: incref for new_env's
+ * prototype, decref for act->lex_env overwrite.
+ */
DUK_CAT_SET_LEXENV_ACTIVE(&thr->catchstack[cat_idx]);
@@ -69524,17 +71288,19 @@ DUK_LOCAL void duk__handle_finally(duk_hthread *thr, duk_size_t cat_idx, duk_tva
duk__set_catcher_regs(thr, cat_idx, tv_val_unstable, lj_type);
- duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* cat_idx catcher is kept, even for finally */
- duk_hthread_callstack_unwind(thr, thr->catchstack[cat_idx].callstack_index + 1);
+ duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1); /* cat_idx catcher is kept, even for finally */
+ duk_hthread_callstack_unwind_norz(thr, thr->catchstack[cat_idx].callstack_index + 1);
DUK_ASSERT(thr->callstack_top >= 1);
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
+ DUK_ASSERT(thr->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
+ DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
duk__reconfig_valstack_ecma_catcher(thr, thr->callstack_top - 1, cat_idx);
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
act->curr_pc = thr->catchstack[cat_idx].pc_base + 1; /* +1 = finally */
act = NULL;
@@ -69547,7 +71313,8 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
DUK_ASSERT(DUK_ACT_GET_FUNC(act) != NULL);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(DUK_ACT_GET_FUNC(act)));
@@ -69556,13 +71323,14 @@ DUK_LOCAL void duk__handle_label(duk_hthread *thr, duk_size_t cat_idx, duk_small
act->curr_pc = thr->catchstack[cat_idx].pc_base + (lj_type == DUK_LJ_TYPE_CONTINUE ? 1 : 0);
act = NULL; /* invalidated */
- duk_hthread_catchstack_unwind(thr, cat_idx + 1); /* keep label catcher */
+ duk_hthread_catchstack_unwind_norz(thr, cat_idx + 1); /* keep label catcher */
/* no need to unwind callstack */
/* valstack should not need changes */
#if defined(DUK_USE_ASSERTIONS)
DUK_ASSERT(thr->callstack_top >= 1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack_bottom) ==
(duk_size_t) ((duk_hcompfunc *) DUK_ACT_GET_FUNC(act))->nregs);
#endif
@@ -69584,7 +71352,7 @@ DUK_LOCAL void duk__handle_yield(duk_hthread *thr, duk_hthread *resumer, duk_siz
tv1 = resumer->valstack + resumer->callstack[act_idx].idx_retval; /* return value from Duktape.Thread.resume() */
DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv_val_unstable); /* side effects */
- duk_hthread_callstack_unwind(resumer, act_idx + 1); /* unwind to 'resume' caller */
+ duk_hthread_callstack_unwind_norz(resumer, act_idx + 1); /* unwind to 'resume' caller */
/* no need to unwind catchstack */
duk__reconfig_valstack_ecma_return(resumer, act_idx);
@@ -69647,10 +71415,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged by Duktape.Thread.resume() */
DUK_ASSERT(thr->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_resume);
- DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */
+ DUK_ASSERT(thr->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_resume);
+ DUK_ASSERT_DISABLE((thr->callstack_curr - 1)->idx_retval >= 0); /* unsigned */
tv = &thr->heap->lj.value2; /* resumee */
DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
@@ -69665,11 +71434,11 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
resumee->callstack_top >= 2); /* YIELDED: Ecmascript activation + Duktape.Thread.yield() activation */
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
- (DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack + resumee->callstack_top - 1))->func == duk_bi_thread_yield));
+ (DUK_ACT_GET_FUNC(resumee->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumee->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumee->callstack_curr))->func == duk_bi_thread_yield));
DUK_ASSERT_DISABLE(resumee->state != DUK_HTHREAD_STATE_YIELDED ||
- (resumee->callstack + resumee->callstack_top - 2)->idx_retval >= 0); /* idx_retval unsigned */
+ (resumee->callstack_curr - 1)->idx_retval >= 0); /* idx_retval unsigned */
DUK_ASSERT(resumee->state != DUK_HTHREAD_STATE_INACTIVE ||
resumee->callstack_top == 0); /* INACTIVE: no activation, single function value on valstack */
@@ -69710,7 +71479,7 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
tv2 = &thr->heap->lj.value1;
DUK_TVAL_SET_TVAL_UPDREF(thr, tv, tv2); /* side effects */
- duk_hthread_callstack_unwind(resumee, act_idx + 1); /* unwind to 'yield' caller */
+ duk_hthread_callstack_unwind_norz(resumee, act_idx + 1); /* unwind to 'yield' caller */
/* no need to unwind catchstack */
@@ -69789,24 +71558,26 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr != entry_thread); /* Duktape.Thread.yield() should prevent */
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); /* unchanged from Duktape.Thread.yield() */
DUK_ASSERT(thr->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.yield() activation */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1))->func == duk_bi_thread_yield);
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2) != NULL &&
- DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* an Ecmascript function */
- DUK_ASSERT_DISABLE((thr->callstack + thr->callstack_top - 2)->idx_retval >= 0); /* unsigned */
+ DUK_ASSERT(thr->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->callstack_curr))->func == duk_bi_thread_yield);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr - 1) != NULL &&
+ DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr - 1))); /* an Ecmascript function */
+ DUK_ASSERT_DISABLE((thr->callstack_curr - 1)->idx_retval >= 0); /* unsigned */
resumer = thr->resumer;
DUK_ASSERT(resumer != NULL);
DUK_ASSERT(resumer->state == DUK_HTHREAD_STATE_RESUMED); /* written by a previous RESUME handling */
DUK_ASSERT(resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
- DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 1))->func == duk_bi_thread_resume);
- DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2) != NULL &&
- DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack + resumer->callstack_top - 2))); /* an Ecmascript function */
- DUK_ASSERT_DISABLE((resumer->callstack + resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */
+ DUK_ASSERT(resumer->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(resumer->callstack_curr))->func == duk_bi_thread_resume);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(resumer->callstack_curr - 1) != NULL &&
+ DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(resumer->callstack_curr - 1))); /* an Ecmascript function */
+ DUK_ASSERT_DISABLE((resumer->callstack_curr - 1)->idx_retval >= 0); /* unsigned */
if (thr->heap->lj.iserror) {
thr->state = DUK_HTHREAD_STATE_YIELDED;
@@ -69908,9 +71679,9 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
* final catcher unwind everything
*/
#if 0
- duk_hthread_catchstack_unwind(thr, (cat - thr->catchstack) + 1); /* leave 'cat' as top catcher (also works if catchstack exhausted) */
- duk_hthread_callstack_unwind(thr, entry_callstack_index + 1);
-
+ duk_hthread_catchstack_unwind_norz(thr, (cat - thr->catchstack) + 1); /* leave 'cat' as top catcher (also works if catchstack exhausted) */
+ duk_hthread_callstack_unwind_norz(thr, entry_callstack_index + 1);
+ DUK_REFZERO_CHECK_SLOW(thr);
#endif
DUK_D(DUK_DPRINT("-> throw propagated up to entry level, rethrow and exit bytecode executor"));
retval = DUK__LONGJMP_RETHROW;
@@ -69927,11 +71698,12 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_ASSERT(thr->resumer != NULL);
DUK_ASSERT(thr->resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL &&
- DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */
+ DUK_ASSERT(thr->resumer->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1) != NULL &&
+ DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1))); /* an Ecmascript function */
resumer = thr->resumer;
@@ -69973,6 +71745,8 @@ duk_small_uint_t duk__handle_longjmp(duk_hthread *thr,
DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value1); /* side effects */
DUK_TVAL_SET_UNDEFINED_UPDREF(thr, &thr->heap->lj.value2); /* side effects */
+ DUK_GC_TORTURE(thr->heap);
+
just_return:
return retval;
@@ -70113,6 +71887,7 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr,
cat = thr->catchstack + thr->catchstack_top - 1; /* may be < thr->catchstack initially */
DUK_ASSERT(thr->callstack_top > 0); /* ensures callstack_top - 1 >= 0 */
+ DUK_ASSERT(thr->callstack_curr != NULL);
orig_callstack_index = thr->callstack_top - 1;
while (cat >= thr->catchstack) {
@@ -70160,22 +71935,22 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr,
*/
DUK_DDD(DUK_DDDPRINT("return to Ecmascript caller, idx_retval=%ld, lj_value1=%!T",
- (long) (thr->callstack + thr->callstack_top - 2)->idx_retval,
+ (long) (thr->callstack_curr - 1)->idx_retval,
(duk_tval *) &thr->heap->lj.value1));
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 2))); /* must be ecmascript */
+ DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr - 1))); /* must be ecmascript */
- tv1 = thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval;
+ tv1 = thr->valstack + (thr->callstack_curr - 1)->idx_retval;
DUK_ASSERT(thr->valstack_top - 1 >= thr->valstack_bottom);
tv2 = thr->valstack_top - 1;
DUK_TVAL_SET_TVAL_UPDREF(thr, tv1, tv2); /* side effects */
DUK_DDD(DUK_DDDPRINT("return value at idx_retval=%ld is %!T",
- (long) (thr->callstack + thr->callstack_top - 2)->idx_retval,
- (duk_tval *) (thr->valstack + (thr->callstack + thr->callstack_top - 2)->idx_retval)));
+ (long) (thr->callstack_curr - 1)->idx_retval,
+ (duk_tval *) (thr->valstack + (thr->callstack_curr - 1)->idx_retval)));
- duk_hthread_catchstack_unwind(thr, new_cat_top); /* leave 'cat' as top catcher (also works if catchstack exhausted) */
- duk_hthread_callstack_unwind(thr, thr->callstack_top - 1);
+ duk_hthread_catchstack_unwind_norz(thr, new_cat_top); /* leave 'cat' as top catcher (also works if catchstack exhausted) */
+ duk_hthread_callstack_unwind_norz(thr, thr->callstack_top - 1);
duk__reconfig_valstack_ecma_return(thr, thr->callstack_top - 1);
DUK_DD(DUK_DDPRINT("-> return not intercepted, restart execution in caller"));
@@ -70187,12 +71962,13 @@ DUK_LOCAL duk_small_uint_t duk__handle_return(duk_hthread *thr,
DUK_ASSERT(thr->resumer != NULL);
DUK_ASSERT(thr->resumer->callstack_top >= 2); /* Ecmascript activation + Duktape.Thread.resume() activation */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1) != NULL &&
- DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1)) &&
- ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 1))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2) != NULL &&
- DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack + thr->resumer->callstack_top - 2))); /* an Ecmascript function */
- DUK_ASSERT_DISABLE((thr->resumer->callstack + thr->resumer->callstack_top - 2)->idx_retval >= 0); /* unsigned */
+ DUK_ASSERT(thr->resumer->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr) != NULL &&
+ DUK_HOBJECT_IS_NATFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr)) &&
+ ((duk_hnatfunc *) DUK_ACT_GET_FUNC(thr->resumer->callstack_curr))->func == duk_bi_thread_resume); /* Duktape.Thread.resume() */
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1) != NULL &&
+ DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->resumer->callstack_curr - 1))); /* an Ecmascript function */
+ DUK_ASSERT_DISABLE((thr->resumer->callstack_curr - 1)->idx_retval >= 0); /* unsigned */
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->resumer->state == DUK_HTHREAD_STATE_RESUMED);
@@ -70265,7 +72041,8 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
DUK_ASSERT(thr->heap->dbg_processing == 0); /* don't re-enter e.g. during Eval */
ctx = (duk_context *) thr;
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
/* It might seem that replacing 'thr->heap' with just 'heap' below
* might be a good idea, but it increases code size slightly
@@ -70290,8 +72067,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
(line != thr->heap->dbg_step_startline)) {
DUK_D(DUK_DPRINT("STEP STATE TRIGGERED PAUSE at line %ld",
(long) line));
-
- DUK_HEAP_SET_PAUSED(thr->heap);
+ duk_debug_set_paused(thr->heap);
}
/* Check for breakpoints only on line transition.
@@ -70317,8 +72093,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
if (act->prev_line != bp->line && line == bp->line) {
DUK_D(DUK_DPRINT("BREAKPOINT TRIGGERED at %!O:%ld",
(duk_heaphdr *) bp->filename, (long) bp->line));
-
- DUK_HEAP_SET_PAUSED(thr->heap);
+ duk_debug_set_paused(thr->heap);
}
}
} else {
@@ -70405,8 +72180,9 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
* above, so we must recheck attach status.
*/
- if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
- act = thr->callstack + thr->callstack_top - 1; /* relookup, may have changed */
+ if (duk_debug_is_attached(thr->heap)) {
+ act = thr->callstack_curr; /* relookup, may have changed */
+ DUK_ASSERT(act != NULL);
if (act->flags & DUK_ACT_FLAG_BREAKPOINT_ACTIVE ||
((thr->heap->dbg_step_type == DUK_STEP_TYPE_INTO ||
thr->heap->dbg_step_type == DUK_STEP_TYPE_OVER) &&
@@ -70429,7 +72205,7 @@ DUK_LOCAL void duk__interrupt_handle_debugger(duk_hthread *thr, duk_bool_t *out_
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
-DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
+DUK_LOCAL DUK_NOINLINE DUK_COLD duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
duk_int_t ctr;
duk_activation *act;
duk_hcompfunc *fun;
@@ -70479,7 +72255,8 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
}
DUK_HEAP_SET_INTERRUPT_RUNNING(thr->heap);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC((duk_hobject *) fun));
@@ -70515,7 +72292,7 @@ DUK_LOCAL duk_small_uint_t duk__executor_interrupt(duk_hthread *thr) {
* detaching (to finish off the pending detach).
*/
duk__interrupt_handle_debugger(thr, &immediate, &retval);
- act = thr->callstack + thr->callstack_top - 1; /* relookup if changed */
+ act = thr->callstack_curr; /* relookup if changed */
DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
@@ -70657,7 +72434,7 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation *
(thr->heap->dbg_step_thread != thr ||
thr->heap->dbg_step_csindex != thr->callstack_top - 1)) {
DUK_D(DUK_DPRINT("STEP INTO ACTIVE, FORCE PAUSED"));
- DUK_HEAP_SET_PAUSED(thr->heap);
+ duk_debug_set_paused(thr->heap);
}
/* Force interrupt right away if we're paused or in "checked mode".
@@ -70712,7 +72489,7 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation *
#if defined(DUK_USE_EXEC_FUN_LOCAL)
#define DUK__FUN() fun
#else
-#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack + (thr)->callstack_top - 1))
+#define DUK__FUN() ((duk_hcompfunc *) DUK_ACT_GET_FUNC((thr)->callstack_curr))
#endif
#define DUK__STRICT() (DUK_HOBJECT_HAS_STRICT((duk_hobject *) DUK__FUN()))
@@ -70789,12 +72566,12 @@ DUK_LOCAL void duk__executor_recheck_debugger(duk_hthread *thr, duk_activation *
#define DUK__SYNC_CURR_PC() do { \
duk_activation *act; \
- act = thr->callstack + thr->callstack_top - 1; \
+ act = thr->callstack_curr; \
act->curr_pc = curr_pc; \
} while (0)
#define DUK__SYNC_AND_NULL_CURR_PC() do { \
duk_activation *act; \
- act = thr->callstack + thr->callstack_top - 1; \
+ act = thr->callstack_curr; \
act->curr_pc = curr_pc; \
thr->ptr_curr_pc = NULL; \
} while (0)
@@ -70846,15 +72623,27 @@ DUK_LOCAL void duk__handle_executor_error(duk_heap *heap,
lj_ret = duk__handle_longjmp(heap->curr_thread, entry_thread, entry_callstack_top);
+ /* Error handling complete, remove side effect protections.
+ */
+#if defined(DUK_USE_ASSERTIONS)
+ DUK_ASSERT(heap->error_not_allowed == 1);
+ heap->error_not_allowed = 0;
+#endif
+ DUK_ASSERT(heap->pf_prevent_count > 0);
+ heap->pf_prevent_count--;
+ DUK_DD(DUK_DDPRINT("executor error handled, pf_prevent_count updated to %ld", (long) heap->pf_prevent_count));
+
if (lj_ret == DUK__LONGJMP_RESTART) {
/* Restart bytecode execution, possibly with a changed thread. */
- ;
+ DUK_REFZERO_CHECK_SLOW(heap->curr_thread);
} else {
- /* Rethrow error to calling state. */
- DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW);
+ /* If an error is propagated, don't run refzero checks here.
+ * The next catcher will deal with that. Pf_prevent_count
+ * will be re-bumped by the longjmp.
+ */
- /* Longjmp handling has restored jmpbuf_ptr. */
- DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr);
+ DUK_ASSERT(lj_ret == DUK__LONGJMP_RETHROW); /* Rethrow error to calling state. */
+ DUK_ASSERT(heap->lj.jmpbuf_ptr == entry_jmpbuf_ptr); /* Longjmp handling has restored jmpbuf_ptr. */
/* Thread may have changed, e.g. YIELD converted to THROW. */
duk_err_longjmp(heap->curr_thread);
@@ -70877,8 +72666,10 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
DUK_ASSERT(exec_thr->heap->curr_thread != NULL);
DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR((duk_heaphdr *) exec_thr);
DUK_ASSERT(exec_thr->callstack_top >= 1); /* at least one activation, ours */
- DUK_ASSERT(DUK_ACT_GET_FUNC(exec_thr->callstack + exec_thr->callstack_top - 1) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack + exec_thr->callstack_top - 1)));
+ DUK_ASSERT(DUK_ACT_GET_FUNC(exec_thr->callstack_curr) != NULL);
+ DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(exec_thr->callstack_curr)));
+
+ DUK_GC_TORTURE(exec_thr->heap);
entry_thread = exec_thr;
heap = entry_thread->heap;
@@ -70969,7 +72760,7 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) {
}
/* Inner executor, performance critical. */
-DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_size_t entry_callstack_top) {
+DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *entry_thread, duk_size_t entry_callstack_top) {
/* Current PC, accessed by other functions through thr->ptr_to_curr_pc.
* Critical for performance. It would be safest to make this volatile,
* but that eliminates performance benefits; aliasing guarantees
@@ -71010,6 +72801,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
#endif
#endif
+ DUK_GC_TORTURE(entry_thread->heap);
+
/*
* Restart execution by reloading thread state.
*
@@ -71059,8 +72852,11 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
thr = entry_thread->heap->curr_thread;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(thr->callstack_top >= 1);
- DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack + thr->callstack_top - 1)));
+ DUK_ASSERT(thr->callstack_curr != NULL);
+ DUK_ASSERT(DUK_ACT_GET_FUNC(thr->callstack_curr) != NULL);
+ DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_ACT_GET_FUNC(thr->callstack_curr)));
+
+ DUK_GC_TORTURE(thr->heap);
thr->ptr_curr_pc = &curr_pc;
@@ -71074,7 +72870,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
/* Assume interrupt init/counter are properly initialized here. */
/* Assume that thr->valstack_bottom has been set-up before getting here. */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
fun = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
DUK_ASSERT(fun != NULL);
DUK_ASSERT(thr->valstack_top - thr->valstack_bottom == fun->nregs);
@@ -71082,9 +72879,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(consts != NULL);
#if defined(DUK_USE_DEBUGGER_SUPPORT)
- if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap) && !thr->heap->dbg_processing) {
+ if (duk_debug_is_attached(thr->heap) && !thr->heap->dbg_processing) {
duk__executor_recheck_debugger(thr, act, fun);
- act = thr->callstack + thr->callstack_top - 1; /* relookup after side effects (no side effects currently however) */
+ act = thr->callstack_curr; /* relookup after side effects (no side effects currently however) */
+ DUK_ASSERT(act != NULL);
}
#endif /* DUK_USE_DEBUGGER_SUPPORT */
@@ -71134,10 +72932,11 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
duk_small_uint_t exec_int_ret;
/* Write curr_pc back for the debugger. */
- DUK_ASSERT(thr->callstack_top > 0);
{
duk_activation *act;
- act = thr->callstack + thr->callstack_top - 1;
+ DUK_ASSERT(thr->callstack_top > 0);
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
act->curr_pc = (duk_instr_t *) curr_pc;
}
@@ -71171,7 +72970,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
#if defined(DUK_USE_ASSERTIONS) || defined(DUK_USE_DEBUG)
{
duk_activation *act;
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
DUK_ASSERT(curr_pc >= DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, DUK__FUN()));
DUK_ASSERT(curr_pc < DUK_HCOMPFUNC_GET_CODE_END(thr->heap, DUK__FUN()));
DUK_UNREF(act); /* if debugging disabled */
@@ -71463,7 +73262,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(DUK_TVAL_IS_STRING(tv));
name = DUK_TVAL_GET_STRING(tv);
tv = NULL; /* lookup has side effects */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
if (duk_js_getvar_activation(thr, act, name, 0 /*throw*/)) {
/* -> [... val this] */
tv = DUK_GET_TVAL_NEGIDX(ctx, -2);
@@ -72191,14 +73990,12 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
duk_hstring *name;
duk_small_uint_t prop_flags;
duk_bool_t is_func_decl;
- duk_bool_t is_undef_value;
tv1 = DUK__REGCONSTP_B(ins);
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
- is_undef_value = ((a & DUK_BC_DECLVAR_FLAG_UNDEF_VALUE) != 0);
is_func_decl = ((a & DUK_BC_DECLVAR_FLAG_FUNC_DECL) != 0);
/* XXX: declvar takes an duk_tval pointer, which is awkward and
@@ -72210,24 +74007,24 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
*/
prop_flags = a & DUK_PROPDESC_FLAGS_MASK;
- if (is_undef_value) {
+ if (is_func_decl) {
+ duk_push_tval(ctx, DUK__REGCONSTP_C(ins));
+ } else {
DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* valstack policy */
thr->valstack_top++;
- } else {
- duk_push_tval(ctx, DUK__REGCONSTP_C(ins));
}
tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
if (duk_js_declvar_activation(thr, act, name, tv1, prop_flags, is_func_decl)) {
- if (is_undef_value) {
- /* Already declared but no initializer value
- * (e.g. 'var xyz;'), no-op.
- */
- } else {
+ if (is_func_decl) {
/* Already declared, update value. */
tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1);
duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
+ } else {
+ /* Already declared but no initializer value
+ * (e.g. 'var xyz;'), no-op.
+ */
}
}
@@ -72282,7 +74079,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
idx = (duk_uint_fast_t) DUK_DEC_A(ins);
@@ -72309,7 +74106,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT_DISABLE(bc >= 0); /* unsigned */
DUK_ASSERT((duk_uint_t) bc < (duk_uint_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, DUK__FUN()));
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
fun_act = (duk_hcompfunc *) DUK_ACT_GET_FUNC(act);
fun_temp = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, fun_act)[bc];
DUK_ASSERT(fun_temp != NULL);
@@ -72321,7 +74118,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
if (act->lex_env == NULL) {
DUK_ASSERT(act->var_env == NULL);
duk_js_init_activation_environment_records_delayed(thr, act);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
}
DUK_ASSERT(act->lex_env != NULL);
DUK_ASSERT(act->var_env != NULL);
@@ -72348,7 +74145,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
(void) duk_js_getvar_activation(thr, act, name, 1 /*throw*/); /* -> [... val this] */
duk_pop(ctx); /* 'this' binding is not needed here */
DUK__REPLACE_TOP_A_BREAK();
@@ -72369,7 +74166,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
*/
tv1 = DUK__REGP_A(ins); /* val */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
duk_js_putvar_activation(thr, act, name, tv1, DUK__STRICT());
break;
}
@@ -72384,7 +74181,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
name = DUK_TVAL_GET_STRING(tv1);
DUK_ASSERT(name != NULL);
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
rc = duk_js_delvar_activation(thr, act, name);
DUK__REPLACE_BOOL_A_BREAK(rc);
}
@@ -72445,6 +74242,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
thr->valstack_top++;
DUK__RETURN_SHARED();
}
+ /* This will be unused without refcounting. */
case DUK_OP_RETCONST: {
duk_tval *tv;
@@ -72461,7 +74259,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK__SYNC_AND_NULL_CURR_PC();
tv = DUK__CONSTP_BC(ins);
DUK_TVAL_SET_TVAL(thr->valstack_top, tv);
+#if defined(DUK_USE_REFERENCE_COUNTING)
+ /* Without refcounting only RETCONSTN is used. */
DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); /* no INCREF for this constant */
+#endif
thr->valstack_top++;
DUK__RETURN_SHARED();
}
@@ -72585,57 +74386,44 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
a = DUK_DEC_A(ins);
bc = DUK_DEC_BC(ins);
- act = thr->callstack + thr->callstack_top - 1;
- DUK_ASSERT(thr->callstack_top >= 1);
-
- /* 'with' target must be created first, in case we run out of memory */
- /* XXX: refactor out? */
-
- if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) {
- DUK_DDD(DUK_DDDPRINT("need to initialize a with binding object"));
-
- if (act->lex_env == NULL) {
- DUK_ASSERT(act->var_env == NULL);
- DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
-
- /* must relookup act in case of side effects */
- duk_js_init_activation_environment_records_delayed(thr, act);
- act = thr->callstack + thr->callstack_top - 1;
- DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */
- }
- DUK_ASSERT(act->lex_env != NULL);
- DUK_ASSERT(act->var_env != NULL);
-
- (void) duk_push_object_helper(ctx,
- DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV),
- -1); /* no prototype, updated below */
-
- duk_push_tval(ctx, DUK__REGP(bc));
- duk_to_object(ctx, -1);
- duk_dup_top(ctx);
-
- /* [ ... env target ] */
- /* [ ... env target target ] */
-
- duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
- duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); /* always provideThis=true */
-
- /* [ ... env ] */
+ /* Registers 'bc' and 'bc + 1' are written in longjmp handling
+ * and if their previous values (which are temporaries) become
+ * unreachable -and- have a finalizer, there'll be a function
+ * call during error handling which is not supported now (GH-287).
+ * Ensure that both 'bc' and 'bc + 1' have primitive values to
+ * guarantee no finalizer calls in error handling. Scrubbing also
+ * ensures finalizers for the previous values run here rather than
+ * later. Error handling related values are also written to 'bc'
+ * and 'bc + 1' but those values never become unreachable during
+ * error handling, so there's no side effect problem even if the
+ * error value has a finalizer.
+ */
+ duk_dup(ctx, bc); /* Stabilize value. */
+ duk_to_undefined(ctx, bc);
+ duk_to_undefined(ctx, bc + 1);
- DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iT",
- (duk_tval *) duk_get_tval(ctx, -1)));
- }
+ /* Ensure a catchstack entry is available. One entry
+ * is guaranteed even if side effects cause function
+ * calls and the catchstack is shrunk because some
+ * spare room is left behind by a shrink operation.
+ */
+ duk_hthread_catchstack_grow(thr);
- /* allocate catcher and populate it (should be atomic) */
+ /* Allocate catcher and populate it. Doesn't have to
+ * be fully atomic, but the catcher must be in a
+ * consistent state if side effects (such as finalizer
+ * calls) occur.
+ */
- duk_hthread_catchstack_grow(thr);
- cat = thr->catchstack + thr->catchstack_top;
DUK_ASSERT(thr->catchstack_top + 1 <= thr->catchstack_size);
+ cat = thr->catchstack + thr->catchstack_top;
thr->catchstack_top++;
cat->flags = DUK_CAT_TYPE_TCF;
cat->h_varname = NULL;
+ cat->callstack_index = thr->callstack_top - 1;
+ cat->pc_base = (duk_instr_t *) curr_pc; /* pre-incremented, points to first jump slot */
+ cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;
if (a & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) {
cat->flags |= DUK_CAT_FLAG_CATCH_ENABLED;
@@ -72646,7 +74434,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
if (a & DUK_BC_TRYCATCH_FLAG_CATCH_BINDING) {
DUK_DDD(DUK_DDDPRINT("catch binding flag set to catcher"));
cat->flags |= DUK_CAT_FLAG_CATCH_BINDING_ENABLED;
- tv1 = DUK__REGP(bc);
+ tv1 = DUK_GET_TVAL_NEGIDX(thr, -1);
DUK_ASSERT(DUK_TVAL_IS_STRING(tv1));
/* borrowed reference; although 'tv1' comes from a register,
@@ -72655,54 +74443,69 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
*/
cat->h_varname = DUK_TVAL_GET_STRING(tv1);
} else if (a & DUK_BC_TRYCATCH_FLAG_WITH_BINDING) {
- /* env created above to stack top */
- duk_hobject *new_env;
+ duk_hobjenv *env;
+ duk_hobject *target;
- DUK_DDD(DUK_DDDPRINT("lexenv active flag set to catcher"));
- cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE;
+ /* Delayed env initialization for activation (if needed). */
+ DUK_ASSERT(thr->callstack_top >= 1);
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
+ if (act->lex_env == NULL) {
+ DUK_DDD(DUK_DDDPRINT("delayed environment initialization"));
+ DUK_ASSERT(act->var_env == NULL);
- DUK_DDD(DUK_DDDPRINT("activating object env: %!iT",
- (duk_tval *) duk_get_tval(ctx, -1)));
- new_env = DUK_GET_HOBJECT_NEGIDX(ctx, -1);
- DUK_ASSERT(new_env != NULL);
+ duk_js_init_activation_environment_records_delayed(thr, act);
+ act = thr->callstack_curr; /* relookup, side effects */
+ DUK_UNREF(act); /* 'act' is no longer accessed, scanbuild fix */
+ }
+ DUK_ASSERT(act->lex_env != NULL);
+ DUK_ASSERT(act->var_env != NULL);
+
+ /* Coerce 'with' target. */
+ target = duk_to_hobject(ctx, -1);
+ DUK_ASSERT(target != NULL);
- act = thr->callstack + thr->callstack_top - 1; /* relookup (side effects) */
+ /* Create an object environment; it is not pushed
+ * so avoid side effects very carefully until it is
+ * referenced.
+ */
+ env = duk_hobjenv_alloc(thr,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV));
+ DUK_ASSERT(env != NULL);
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
+ env->target = target; /* always provideThis=true */
+ DUK_HOBJECT_INCREF(thr, target);
+ env->has_this = 1;
+ DUK_ASSERT_HOBJENV_VALID(env);
+ DUK_DDD(DUK_DDDPRINT("environment for with binding: %!iO", env));
+
+ act = thr->callstack_curr; /* relookup (side effects) */
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
DUK_ASSERT(act->lex_env != NULL);
- DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, new_env, act->lex_env); /* side effects */
+ DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, act->lex_env);
+ act->lex_env = (duk_hobject *) env; /* Now reachable. */
+ DUK_HOBJECT_INCREF(thr, (duk_hobject *) env);
+ /* Net refcount change to act->lex_env is 0: incref for env's
+ * prototype, decref for act->lex_env overwrite.
+ */
- act = thr->callstack + thr->callstack_top - 1; /* relookup (side effects) */
- act->lex_env = new_env;
- DUK_HOBJECT_INCREF(thr, new_env);
- duk_pop(ctx);
+ /* Set catcher lex_env active (affects unwind)
+ * only when the whole setup is complete.
+ */
+ cat = thr->catchstack + thr->catchstack_top - 1;
+ cat->flags |= DUK_CAT_FLAG_LEXENV_ACTIVE;
} else {
;
}
- /* Registers 'bc' and 'bc + 1' are written in longjmp handling
- * and if their previous values (which are temporaries) become
- * unreachable -and- have a finalizer, there'll be a function
- * call during error handling which is not supported now (GH-287).
- * Ensure that both 'bc' and 'bc + 1' have primitive values to
- * guarantee no finalizer calls in error handling. Scrubbing also
- * ensures finalizers for the previous values run here rather than
- * later. Error handling related values are also written to 'bc'
- * and 'bc + 1' but those values never become unreachable during
- * error handling, so there's no side effect problem even if the
- * error value has a finalizer.
- */
- duk_to_undefined(ctx, bc);
- duk_to_undefined(ctx, bc + 1);
-
- cat = thr->catchstack + thr->catchstack_top - 1; /* relookup (side effects) */
- cat->callstack_index = thr->callstack_top - 1;
- cat->pc_base = (duk_instr_t *) curr_pc; /* pre-incremented, points to first jump slot */
- cat->idx_base = (duk_size_t) (thr->valstack_bottom - thr->valstack) + bc;
-
DUK_DDD(DUK_DDDPRINT("TRYCATCH catcher: flags=0x%08lx, callstack_index=%ld, pc_base=%ld, "
"idx_base=%ld, h_varname=%!O",
(unsigned long) cat->flags, (long) cat->callstack_index,
(long) cat->pc_base, (long) cat->idx_base, (duk_heaphdr *) cat->h_varname));
+ duk_pop(ctx);
+
curr_pc += 2; /* skip jump slots */
break;
}
@@ -72756,7 +74559,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
cat = thr->catchstack + thr->catchstack_top - 1;
DUK_ASSERT(!DUK_CAT_HAS_CATCH_ENABLED(cat)); /* cleared before entering catch part */
- act = thr->callstack + thr->callstack_top - 1;
+ act = thr->callstack_curr;
+ DUK_ASSERT(act != NULL);
if (DUK_CAT_HAS_LEXENV_ACTIVE(cat)) {
duk_hobject *prev_env;
@@ -72771,6 +74575,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
DUK_ASSERT(prev_env != NULL);
act->lex_env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, prev_env);
DUK_CAT_CLEAR_LEXENV_ACTIVE(cat);
+ DUK_HOBJECT_INCREF(thr, act->lex_env);
DUK_HOBJECT_DECREF(thr, prev_env); /* side effects */
}
@@ -72895,7 +74700,8 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
duk_push_tval(ctx, thr->valstack + cat->idx_base);
- duk_err_setup_heap_ljstate(thr, (duk_small_int_t) cont_type);
+ duk_err_setup_ljstate1(thr, (duk_small_int_t) cont_type, thr->valstack + cat->idx_base);
+ /* No debugger Throw notify check on purpose (rethrow). */
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
@@ -72932,7 +74738,10 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
(duk_tval *) duk_get_tval(ctx, -1)));
#endif
- duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW);
+ duk_err_setup_ljstate1(thr, DUK_LJ_TYPE_THROW, DUK_GET_TVAL_NEGIDX(ctx, -1));
+#if defined(DUK_USE_DEBUGGER_SUPPORT)
+ duk_err_check_debugger_integration(thr);
+#endif
DUK_ASSERT(thr->heap->lj.jmpbuf_ptr != NULL); /* always in executor */
duk_err_longjmp(thr);
@@ -73469,7 +75278,7 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
* from precompiled bytecode.
*/
#if defined(DUK_USE_DEBUGGER_SUPPORT)
- if (DUK_HEAP_IS_DEBUGGER_ATTACHED(thr->heap)) {
+ if (duk_debug_is_attached(thr->heap)) {
DUK_D(DUK_DPRINT("DEBUGGER statement encountered, halt execution"));
DUK__SYNC_AND_NULL_CURR_PC();
duk_debug_halt_execution(thr, 1 /*use_prev_pc*/);
@@ -73562,22 +75371,18 @@ DUK_LOCAL DUK_NOINLINE void duk__js_execute_bytecode_inner(duk_hthread *entry_th
case DUK_OP_UNUSED252:
case DUK_OP_UNUSED253:
case DUK_OP_UNUSED254:
- case DUK_OP_UNUSED255: {
- /* Force all case clauses to map to an actual handler
- * so that the compiler can emit a jump without a bounds
- * check: the switch argument is a duk_uint8_t so that
- * the compiler may be able to figure it out. This is
- * a small detail and obviously compiler dependent.
- */
- volatile duk_small_int_t dummy_volatile;
- dummy_volatile = 0;
- DUK_UNREF(dummy_volatile);
- DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins));
- DUK__INTERNAL_ERROR("invalid opcode");
- break;
- }
+ case DUK_OP_UNUSED255:
+ /* Force all case clauses to map to an actual handler
+ * so that the compiler can emit a jump without a bounds
+ * check: the switch argument is a duk_uint8_t so that
+ * the compiler may be able to figure it out. This is
+ * a small detail and obviously compiler dependent.
+ */
+ /* default: clause omitted on purpose */
+#else
+ default:
#endif /* DUK_USE_EXEC_PREFER_SIZE */
- default: {
+ {
/* Default case catches invalid/unsupported opcodes. */
DUK_D(DUK_DPRINT("invalid opcode: %ld - %!I", (long) op, ins));
DUK__INTERNAL_ERROR("invalid opcode");
@@ -73893,7 +75698,7 @@ DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
case DUK_TAG_STRING: {
/* For Symbols ToNumber() is always a TypeError. */
duk_hstring *h = DUK_TVAL_GET_STRING(tv);
- if (DUK_HSTRING_HAS_SYMBOL(h)) {
+ if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL);
}
duk_push_hstring(ctx, h);
@@ -74625,7 +76430,7 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x,
DUK_ASSERT(h1 != NULL);
DUK_ASSERT(h2 != NULL);
- if (!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2)) {
+ if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) {
rc = duk_js_string_compare(h1, h2);
duk_pop_2(ctx);
if (rc < 0) {
@@ -74755,7 +76560,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
/* func support for [[HasInstance]] checked in the beginning of the loop */
} while (--sanity > 0);
- if (sanity == 0) {
+ if (DUK_UNLIKELY(sanity == 0)) {
DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
}
@@ -74841,7 +76646,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val);
} while (--sanity > 0);
- if (sanity == 0) {
+ if (DUK_UNLIKELY(sanity == 0)) {
DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
}
DUK_UNREACHABLE();
@@ -74942,7 +76747,7 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) {
/* All internal keys are identified as Symbols. */
str = DUK_TVAL_GET_STRING(tv_x);
DUK_ASSERT(str != NULL);
- if (DUK_HSTRING_HAS_SYMBOL(str)) {
+ if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(str))) {
stridx = DUK_STRIDX_LC_SYMBOL;
} else {
stridx = DUK_STRIDX_LC_STRING;
@@ -74992,32 +76797,30 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) {
*
* Array index: E5 Section 15.4
* Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write)
- *
- * duk_js_to_arrayindex_string_helper() computes the array index from
- * string contents alone. Depending on options it's only called during
- * string intern (and value stored to duk_hstring) or it's called also
- * at runtime.
*/
-DUK_INTERNAL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t *str, duk_uint32_t blen, duk_uarridx_t *out_idx) {
+/* Compure array index from string context, or return a "not array index"
+ * indicator.
+ */
+DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen) {
duk_uarridx_t res;
- if (blen == 0 || blen > 10) {
- goto parse_fail;
- }
- if (str[0] == DUK_ASC_0 && blen > 1) {
- goto parse_fail;
- }
-
- /* Accept 32-bit decimal integers, no leading zeroes, signs, etc.
- * Leading zeroes are not accepted (zero index "0" is an exception
- * handled above).
+ /* Only strings with byte length 1-10 can be 32-bit array indices.
+ * Leading zeroes (except '0' alone), plus/minus signs are not allowed.
+ * We could do a lot of prechecks here, but since most strings won't
+ * start with any digits, it's simpler to just parse the number and
+ * fail quickly.
*/
res = 0;
- while (blen-- > 0) {
- duk_uint8_t c = *str++;
- if (c >= DUK_ASC_0 && c <= DUK_ASC_9) {
+ if (blen == 0) {
+ goto parse_fail;
+ }
+ do {
+ duk_uarridx_t dig;
+ dig = (duk_uarridx_t) (*str++) - DUK_ASC_0;
+
+ if (dig <= 9U) {
/* Careful overflow handling. When multiplying by 10:
* - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding
* 0...9 is safe.
@@ -75031,44 +76834,75 @@ DUK_INTERNAL duk_small_int_t duk_js_to_arrayindex_raw_string(const duk_uint8_t *
goto parse_fail;
}
DUK_ASSERT(res == 0x19999999UL);
- c -= DUK_ASC_0;
- if (c >= 6) {
+ if (dig >= 6U) {
goto parse_fail;
}
- res = 0xfffffffaUL + c;
- DUK_ASSERT(res >= 0xfffffffaUL && res <= 0xffffffffUL);
+ res = 0xfffffffaUL + dig;
+ DUK_ASSERT(res >= 0xfffffffaUL);
+ DUK_ASSERT_DISABLE(res <= 0xffffffffUL); /* range */
} else {
- res = res * 10U + (duk_uint32_t) (c - DUK_ASC_0);
+ res = res * 10U + dig;
+ if (DUK_UNLIKELY(res == 0)) {
+ /* If 'res' is 0, previous 'res' must
+ * have been 0 and we scanned in a zero.
+ * This is only allowed if blen == 1,
+ * i.e. the exact string '0'.
+ */
+ if (blen == (duk_uint32_t) 1) {
+ return 0;
+ }
+ goto parse_fail;
+ }
}
} else {
+ /* Because 'dig' is unsigned, catches both values
+ * above '9' and below '0'.
+ */
goto parse_fail;
}
- }
+ } while (--blen > 0);
- *out_idx = res;
- return 1;
+ return res;
parse_fail:
- *out_idx = DUK_HSTRING_NO_ARRAY_INDEX;
- return 0;
+ return DUK_HSTRING_NO_ARRAY_INDEX;
}
-/* Called by duk_hstring.h macros */
-DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) {
+#if !defined(DUK_USE_HSTRING_ARRIDX)
+/* Get array index for a string which is known to be an array index. This helper
+ * is needed when duk_hstring doesn't concretely store the array index, but strings
+ * are flagged as array indices at intern time.
+ */
+DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h) {
+ const duk_uint8_t *p;
duk_uarridx_t res;
- duk_small_int_t rc;
+ duk_uint8_t t;
+
+ DUK_ASSERT(h != NULL);
+ DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(h));
+ p = DUK_HSTRING_GET_DATA(h);
+ res = 0;
+ for (;;) {
+ t = *p++;
+ if (DUK_UNLIKELY(t == 0)) {
+ /* Scanning to NUL is always safe for interned strings. */
+ break;
+ }
+ DUK_ASSERT(t >= DUK_ASC_0 && t <= DUK_ASC_9);
+ res = res * 10U + (t - DUK_ASC_0);
+ }
+ return res;
+}
+
+DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h) {
+ DUK_ASSERT(h != NULL);
if (!DUK_HSTRING_HAS_ARRIDX(h)) {
return DUK_HSTRING_NO_ARRAY_INDEX;
}
-
- rc = duk_js_to_arrayindex_raw_string(DUK_HSTRING_GET_DATA(h),
- DUK_HSTRING_GET_BYTELEN(h),
- &res);
- DUK_UNREF(rc);
- DUK_ASSERT(rc != 0);
- return res;
+ return duk_js_to_arrayindex_hstring_fast_known(h);
}
+#endif /* DUK_USE_HSTRING_ARRIDX */
/*
* Identifier access and function closure handling.
*
@@ -75108,11 +76942,11 @@ DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) {
*/
typedef struct {
+ duk_hobject *env;
duk_hobject *holder; /* for object-bound identifiers */
duk_tval *value; /* for register-bound and declarative env identifiers */
duk_int_t attrs; /* property attributes for identifier (relevant if value != NULL) */
- duk_tval *this_binding;
- duk_hobject *env;
+ duk_bool_t has_this; /* for object-bound identifiers: provide 'this' binding */
} duk__id_lookup_result;
/*
@@ -75265,7 +77099,7 @@ void duk_js_push_closure(duk_hthread *thr,
DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj));
DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj));
DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj));
- DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&fun_clos->obj));
+ DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&fun_clos->obj));
/* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */
/* DUK_HOBJECT_FLAG_NEWENV: handled below */
DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&fun_clos->obj));
@@ -75300,7 +77134,7 @@ void duk_js_push_closure(duk_hthread *thr,
#if defined(DUK_USE_FUNC_NAME_PROPERTY)
if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_clos->obj)) {
duk_hobject *proto;
- duk_hobject *new_env;
+ duk_hdecenv *new_env;
/*
* Named function expression, name needs to be bound
@@ -75318,11 +77152,18 @@ void duk_js_push_closure(duk_hthread *thr,
}
/* -> [ ... closure template env ] */
- new_env = duk_push_object_helper_proto(ctx,
- DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
- proto);
+ new_env = duk_hdecenv_alloc(thr,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
DUK_ASSERT(new_env != NULL);
+ duk_push_hobject(ctx, (duk_hobject *) new_env);
+
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
+ DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto);
+ DUK_HOBJECT_INCREF_ALLOWNULL(thr, proto);
+
+ DUK_ASSERT(new_env->thread == NULL); /* Closed. */
+ DUK_ASSERT(new_env->varmap == NULL);
/* It's important that duk_xdef_prop() is a 'raw define' so that any
* properties in an ancestor are never an issue (they should never be
@@ -75341,10 +77182,10 @@ void duk_js_push_closure(duk_hthread *thr,
/* [ ... closure template env ] */
- DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, new_env);
- DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, new_env);
- DUK_HOBJECT_INCREF(thr, new_env);
- DUK_HOBJECT_INCREF(thr, new_env);
+ DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, (duk_hobject *) new_env);
+ DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, (duk_hobject *) new_env);
+ DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
+ DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
duk_pop(ctx);
/* [ ... closure template ] */
@@ -75566,10 +77407,11 @@ duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
duk_hobject *func,
duk_size_t idx_bottom) {
duk_context *ctx = (duk_context *) thr;
- duk_hobject *env;
+ duk_hdecenv *env;
duk_hobject *parent;
duk_hcompfunc *f;
+ DUK_ASSERT(ctx != NULL);
DUK_ASSERT(thr != NULL);
DUK_ASSERT(func != NULL);
@@ -75579,25 +77421,44 @@ duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
parent = thr->builtins[DUK_BIDX_GLOBAL_ENV];
}
- (void) duk_push_object_helper(ctx,
- DUK_HOBJECT_FLAG_EXTENSIBLE |
- DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
- -1); /* no prototype, updated below */
- env = duk_known_hobject(ctx, -1);
- DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, env, parent); /* parent env is the prototype */
+ env = duk_hdecenv_alloc(thr,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
+ DUK_ASSERT(env != NULL);
+ duk_push_hobject(ctx, (duk_hobject *) env);
+
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
+ DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent);
+ DUK_HOBJECT_INCREF_ALLOWNULL(thr, parent); /* parent env is the prototype */
/* open scope information, for compiled functions only */
+ DUK_ASSERT(env->thread == NULL);
+ DUK_ASSERT(env->varmap == NULL);
+ DUK_ASSERT(env->regbase == 0);
if (DUK_HOBJECT_IS_COMPFUNC(func)) {
- duk_push_hthread(ctx, thr);
- duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_THREAD);
- duk_push_hobject(ctx, func);
- duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_CALLEE);
- duk_push_size_t(ctx, idx_bottom);
- duk_xdef_prop_stridx_short_wec(ctx, -2, DUK_STRIDX_INT_REGBASE);
+ duk_hobject *varmap;
+ duk_tval *tv;
+
+ tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
+ if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) {
+ DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
+ varmap = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(varmap != NULL);
+ env->varmap = varmap;
+ DUK_HOBJECT_INCREF(thr, varmap);
+ env->thread = thr;
+ DUK_HTHREAD_INCREF(thr, thr);
+ env->regbase = idx_bottom;
+ } else {
+ /* If function has no _Varmap, leave the environment closed. */
+ DUK_ASSERT(env->thread == NULL);
+ DUK_ASSERT(env->varmap == NULL);
+ DUK_ASSERT(env->regbase == 0);
+ }
}
- return env;
+ return (duk_hobject *) env;
}
DUK_INTERNAL
@@ -75649,156 +77510,103 @@ void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
* Closing environment records.
*
* The environment record MUST be closed with the thread where its activation
- * is. In other words (if 'env' is open):
- *
- * - 'thr' must match _env.thread
- * - 'func' must match _env.callee
- * - 'regbase' must match _env.regbase
- *
- * These are not looked up from the env to minimize code size.
- *
- * XXX: should access the own properties directly instead of using the API
+ * is; i.e. if 'env' is open, 'thr' must match env->thread, and the regbase
+ * and varmap must still be valid. On entry, 'env' must be reachable.
*/
-DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env, duk_hobject *func, duk_size_t regbase) {
+DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env) {
duk_context *ctx = (duk_context *) thr;
duk_uint_fast32_t i;
+ duk_hobject *varmap;
+ duk_hstring *key;
+ duk_tval *tv;
+ duk_uint_t regnum;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(env != NULL);
- /* func is NULL for lightfuncs */
- if (!DUK_HOBJECT_IS_DECENV(env) || DUK_HOBJECT_HAS_ENVRECCLOSED(env)) {
- DUK_DDD(DUK_DDDPRINT("environment record not a declarative record, "
- "or already closed: %!iO",
- (duk_heaphdr *) env));
+ if (DUK_UNLIKELY(!DUK_HOBJECT_IS_DECENV(env))) {
+ DUK_DDD(DUK_DDDPRINT("env not a declarative record: %!iO", (duk_heaphdr *) env));
return;
}
- DUK_DDD(DUK_DDDPRINT("closing environment record: %!iO, func: %!iO, regbase: %ld",
- (duk_heaphdr *) env, (duk_heaphdr *) func, (long) regbase));
+ varmap = ((duk_hdecenv *) env)->varmap;
+ if (varmap == NULL) {
+ DUK_DDD(DUK_DDDPRINT("env already closed: %!iO", (duk_heaphdr *) env));
- duk_push_hobject(ctx, env);
-
- /* assertions: env must be closed in the same thread as where it runs */
-#if defined(DUK_USE_ASSERTIONS)
- {
- /* [... env] */
-
- if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE)) {
- DUK_ASSERT(duk_is_object(ctx, -1));
- DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) func);
- }
- duk_pop(ctx);
-
- if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_THREAD)) {
- DUK_ASSERT(duk_is_object(ctx, -1));
- DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) thr);
- }
- duk_pop(ctx);
-
- if (duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_REGBASE)) {
- DUK_ASSERT(duk_is_number(ctx, -1));
- DUK_ASSERT(duk_get_number(ctx, -1) == (double) regbase);
- }
- duk_pop(ctx);
-
- /* [... env] */
+ return;
}
-#endif
-
- if (func != NULL && DUK_HOBJECT_IS_COMPFUNC(func)) {
- duk_hobject *varmap;
- duk_hstring *key;
- duk_tval *tv;
- duk_uint_t regnum;
-
- /* XXX: additional conditions when to close variables? we don't want to do it
- * unless the environment may have "escaped" (referenced in a function closure).
- * With delayed environments, the existence is probably good enough of a check.
- */
+ DUK_ASSERT(((duk_hdecenv *) env)->thread != NULL);
+ DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env);
- /* XXX: any way to detect faster whether something needs to be closed?
- * We now look up _Callee and then skip the rest.
- */
-
- /* Note: we rely on the _Varmap having a bunch of nice properties, like:
- * - being compacted and unmodified during this process
- * - not containing an array part
- * - having correct value types
- */
-
- /* [... env] */
+ DUK_DDD(DUK_DDDPRINT("closing env: %!iO", (duk_heaphdr *) env));
+ DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap));
- if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE)) {
- DUK_DDD(DUK_DDDPRINT("env has no callee property, nothing to close; re-delete the control properties just in case"));
- duk_pop(ctx);
- goto skip_varmap;
- }
+ /* Env must be closed in the same thread as where it runs. */
+ DUK_ASSERT(((duk_hdecenv *) env)->thread == thr);
- /* [... env callee] */
-
- if (!duk_get_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_VARMAP)) {
- DUK_DDD(DUK_DDDPRINT("callee has no varmap property, nothing to close; delete the control properties"));
- duk_pop_2(ctx);
- goto skip_varmap;
- }
- varmap = duk_require_hobject(ctx, -1);
- DUK_ASSERT(varmap != NULL);
-
- DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap));
-
- /* [... env callee varmap] */
-
- DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap)));
+ /* XXX: additional conditions when to close variables? we don't want to do it
+ * unless the environment may have "escaped" (referenced in a function closure).
+ * With delayed environments, the existence is probably good enough of a check.
+ */
- for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
- key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
- DUK_ASSERT(key != NULL); /* assume keys are compacted */
+ /* Note: we rely on the _Varmap having a bunch of nice properties, like:
+ * - being compacted and unmodified during this process
+ * - not containing an array part
+ * - having correct value types
+ */
- DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */
+ DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap)));
- tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); /* assume value is a number */
- regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
- DUK_ASSERT_DISABLE(regnum >= 0); /* unsigned */
- DUK_ASSERT(regnum < ((duk_hcompfunc *) func)->nregs); /* regnum is sane */
- DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack);
- DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top);
+ /* Copy over current variable values from value stack to the
+ * environment record. The scope object is empty but may
+ * inherit from another scope which has conflicting names.
+ */
- /* XXX: slightly awkward */
- duk_push_hstring(ctx, key);
- duk_push_tval(ctx, thr->valstack + regbase + regnum);
- DUK_DDD(DUK_DDDPRINT("closing identifier '%s' -> reg %ld, value %!T",
- (const char *) duk_require_string(ctx, -2),
- (long) regnum,
- (duk_tval *) duk_get_tval(ctx, -1)));
+ /* XXX: Do this using a once allocated entry area, no side effects.
+ * Hash part would need special treatment however (maybe copy, and
+ * then realloc with hash part if large enough).
+ */
+ for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
+ duk_size_t regbase;
- /* [... env callee varmap key val] */
+ key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
+ DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */
+ DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */
- /* if property already exists, overwrites silently */
- duk_xdef_prop(ctx, -5, DUK_PROPDESC_FLAGS_WE); /* writable but not deletable */
- }
+ tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i);
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */
+#if defined(DUK_USE_FASTINT)
+ DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
+ regnum = (duk_uint_t) DUK_TVAL_GET_FASTINT_U32(tv);
+#else
+ regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
+#endif
- duk_pop_2(ctx);
+ regbase = ((duk_hdecenv *) env)->regbase;
+ DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack);
+ DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top);
- /* [... env] */
+ /* If property already exists, overwrites silently.
+ * Property is writable, but not deletable (not configurable
+ * in terms of property attributes).
+ */
+ duk_push_tval(ctx, thr->valstack + regbase + regnum);
+ DUK_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T",
+ (duk_heaphdr *) key,
+ (long) regnum,
+ (duk_tval *) duk_get_tval(ctx, -1)));
+ duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
}
- skip_varmap:
-
- /* [... env] */
-
- duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_CALLEE);
- duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_THREAD);
- duk_del_prop_stridx_short(ctx, -1, DUK_STRIDX_INT_REGBASE);
+ /* NULL atomically to avoid inconsistent state + side effects. */
+ DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->thread);
+ DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->varmap);
+ ((duk_hdecenv *) env)->thread = NULL;
+ ((duk_hdecenv *) env)->varmap = NULL;
- duk_pop(ctx);
-
- DUK_HOBJECT_SET_ENVRECCLOSED(env);
-
- DUK_DDD(DUK_DDDPRINT("environment record after being closed: %!O",
- (duk_heaphdr *) env));
+ DUK_DDD(DUK_DDDPRINT("env after closing: %!O", (duk_heaphdr *) env));
}
/*
@@ -75828,12 +77636,8 @@ DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject
DUK_LOCAL
duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr,
duk_hstring *name,
- duk_hobject *env,
+ duk_hdecenv *env,
duk__id_lookup_result *out) {
- duk_hthread *env_thr;
- duk_hobject *env_func;
- duk_size_t env_regbase;
- duk_hobject *varmap;
duk_tval *tv;
duk_size_t reg_rel;
duk_size_t idx;
@@ -75843,69 +77647,39 @@ duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr,
DUK_ASSERT(env != NULL);
DUK_ASSERT(out != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_DECENV(env));
+ DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) env));
+ DUK_ASSERT_HDECENV_VALID(env);
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr));
- if (!tv) {
- /* env is closed, should be missing _Callee, _Thread, _Regbase */
- DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL);
- DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL);
- DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL);
+ if (env->thread == NULL) {
+ /* already closed */
return 0;
}
+ DUK_ASSERT(env->varmap != NULL);
- DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
- DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(DUK_TVAL_GET_OBJECT(tv)));
- env_func = DUK_TVAL_GET_OBJECT(tv);
- DUK_ASSERT(env_func != NULL);
-
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env_func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
- if (!tv) {
+ tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env->varmap, name);
+ if (DUK_UNLIKELY(tv == NULL)) {
return 0;
}
- DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
- varmap = DUK_TVAL_GET_OBJECT(tv);
- DUK_ASSERT(varmap != NULL);
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, varmap, name);
- if (!tv) {
- return 0;
- }
DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */
+#if defined(DUK_USE_FASTINT)
+ DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
+ reg_rel = (duk_size_t) DUK_TVAL_GET_FASTINT_U32(tv);
+#else
reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
+#endif
DUK_ASSERT_DISABLE(reg_rel >= 0); /* unsigned */
- DUK_ASSERT(reg_rel < ((duk_hcompfunc *) env_func)->nregs);
-
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr));
- DUK_ASSERT(tv != NULL);
- DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
- DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL);
- DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv)));
- env_thr = (duk_hthread *) DUK_TVAL_GET_OBJECT(tv);
- DUK_ASSERT(env_thr != NULL);
-
- /* Note: env_thr != thr is quite possible and normal, so careful
- * with what thread is used for valstack lookup.
- */
-
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr));
- DUK_ASSERT(tv != NULL);
- DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
- env_regbase = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
- idx = env_regbase + reg_rel;
- tv = env_thr->valstack + idx;
- DUK_ASSERT(tv >= env_thr->valstack && tv < env_thr->valstack_end); /* XXX: more accurate? */
+ idx = env->regbase + reg_rel;
+ tv = env->thread->valstack + idx;
+ DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end); /* XXX: more accurate? */
out->value = tv;
out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */
- out->this_binding = NULL; /* implicit this value always undefined for
- * declarative environment records.
- */
- out->env = env;
+ out->env = (duk_hobject *) env;
out->holder = NULL;
-
+ out->has_this = 0;
return 1;
}
@@ -75934,6 +77708,7 @@ duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
return 0;
}
+ /* XXX: move varmap to duk_hcompfunc struct field. */
tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
if (!tv) {
return 0;
@@ -75957,12 +77732,9 @@ duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
out->value = tv;
out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */
- out->this_binding = NULL; /* implicit this value always undefined for
- * declarative environment records.
- */
out->env = NULL;
out->holder = NULL;
-
+ out->has_this = 0;
return 1;
}
@@ -75974,7 +77746,6 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
duk_bool_t parents,
duk__id_lookup_result *out) {
duk_tval *tv;
- duk_tval *tv_target;
duk_tval tv_name;
duk_uint_t sanity;
@@ -76015,10 +77786,10 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
if (duk__getid_activation_regs(thr, name, act, out)) {
DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
- "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
+ "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
"(found from register bindings when env=NULL)",
(duk_heaphdr *) name, (duk_tval *) out->value,
- (long) out->attrs, (duk_tval *) out->this_binding,
+ (long) out->attrs, (long) out->has_this,
(duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
return 1;
}
@@ -76095,37 +77866,30 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
* register-bound variables.
*/
- if (DUK_HOBJECT_HAS_ENVRECCLOSED(env)) {
- /* already closed */
- goto skip_regs;
- }
-
- if (duk__getid_open_decl_env_regs(thr, name, env, out)) {
+ DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env);
+ if (duk__getid_open_decl_env_regs(thr, name, (duk_hdecenv *) env, out)) {
DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
- "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
+ "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
"(declarative environment record, scope open, found in regs)",
(duk_heaphdr *) name, (duk_tval *) out->value,
- (long) out->attrs, (duk_tval *) out->this_binding,
+ (long) out->attrs, (long) out->has_this,
(duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
return 1;
}
- skip_regs:
tv = duk_hobject_find_existing_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs);
if (tv) {
out->value = tv;
out->attrs = attrs;
- out->this_binding = NULL; /* implicit this value always undefined for
- * declarative environment records.
- */
out->env = env;
out->holder = env;
+ out->has_this = 0;
DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
- "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
+ "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
"(declarative environment record, found in properties)",
(duk_heaphdr *) name, (duk_tval *) out->value,
- (long) out->attrs, (duk_tval *) out->this_binding,
+ (long) out->attrs, (long) out->has_this,
(duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
return 1;
}
@@ -76148,11 +77912,9 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
duk_bool_t found;
DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV);
+ DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) env);
- tv_target = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr));
- DUK_ASSERT(tv_target != NULL);
- DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target));
- target = DUK_TVAL_GET_OBJECT(tv_target);
+ target = ((duk_hobjenv *) env)->target;
DUK_ASSERT(target != NULL);
/* Target may be a Proxy or property may be an accessor, so we must
@@ -76163,10 +77925,13 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
*/
if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)) {
+ duk_tval tv_target_tmp;
+
DUK_ASSERT(name != NULL);
DUK_TVAL_SET_STRING(&tv_name, name);
+ DUK_TVAL_SET_OBJECT(&tv_target_tmp, target);
- found = duk_hobject_hasprop(thr, tv_target, &tv_name);
+ found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name);
} else {
/* XXX: duk_hobject_hasprop() would be correct for
* non-Proxy objects too, but it is about ~20-25%
@@ -76179,16 +77944,15 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
if (found) {
out->value = NULL; /* can't get value, may be accessor */
out->attrs = 0; /* irrelevant when out->value == NULL */
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THIS(thr));
- out->this_binding = tv; /* may be NULL */
out->env = env;
out->holder = target;
+ out->has_this = ((duk_hobjenv *) env)->has_this;
DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
- "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
+ "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
"(object environment record)",
(duk_heaphdr *) name, (duk_tval *) out->value,
- (long) out->attrs, (duk_tval *) out->this_binding,
+ (long) out->attrs, (long) out->has_this,
(duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
return 1;
}
@@ -76200,11 +77964,11 @@ duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
goto fail_not_found;
}
- if (sanity-- == 0) {
+ if (DUK_UNLIKELY(sanity-- == 0)) {
DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
}
env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);
- };
+ }
/*
* Not found (even in global object)
@@ -76311,29 +78075,27 @@ duk_bool_t duk__getvar_helper(duk_hthread *thr,
parents = 1; /* follow parent chain */
if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
if (ref.value) {
- DUK_ASSERT(ref.this_binding == NULL); /* always for register bindings */
duk_push_tval(ctx, ref.value);
duk_push_undefined(ctx);
} else {
DUK_ASSERT(ref.holder != NULL);
- /* Note: getprop may invoke any getter and invalidate any
- * duk_tval pointers, so this must be done first.
+ /* ref.holder is safe across the getprop call (even
+ * with side effects) because 'env' is reachable and
+ * ref.holder is a direct heap pointer.
*/
- if (ref.this_binding) {
- duk_push_tval(ctx, ref.this_binding);
- } else {
- duk_push_undefined(ctx);
- }
-
DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
DUK_TVAL_SET_STRING(&tv_tmp_key, name);
- (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [this value] */
+ (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [value] */
- /* ref.value, ref.this.binding invalidated here by getprop call */
+ if (ref.has_this) {
+ duk_push_hobject(ctx, ref.holder);
+ } else {
+ duk_push_undefined(ctx);
+ }
- duk_insert(ctx, -2); /* [this value] -> [value this] */
+ /* [value this] */
}
return 1;
@@ -76436,13 +78198,11 @@ void duk__putvar_helper(duk_hthread *thr,
*/
duk_tval *tv_val;
- DUK_ASSERT(ref.this_binding == NULL); /* always for register bindings */
-
tv_val = ref.value;
DUK_ASSERT(tv_val != NULL);
DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, val); /* side effects */
- /* ref.value and ref.this_binding invalidated here */
+ /* ref.value invalidated here */
} else {
DUK_ASSERT(ref.holder != NULL);
@@ -76450,7 +78210,7 @@ void duk__putvar_helper(duk_hthread *thr,
DUK_TVAL_SET_STRING(&tv_tmp_key, name);
(void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, val, strict);
- /* ref.value and ref.this_binding invalidated here */
+ /* ref.value invalidated here */
}
return;
@@ -76823,14 +78583,11 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr,
*/
if (DUK_HOBJECT_IS_DECENV(env)) {
+ DUK_ASSERT_HDECENV_VALID((duk_hdecenv *) env);
holder = env;
} else {
- DUK_ASSERT(DUK_HOBJECT_IS_OBJENV(env));
-
- tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr));
- DUK_ASSERT(tv != NULL);
- DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
- holder = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT_HOBJENV_VALID((duk_hobjenv *) env);
+ holder = ((duk_hobjenv *) env)->target;
DUK_ASSERT(holder != NULL);
}
@@ -77649,6 +79406,68 @@ DUK_LOCAL duk_codepoint_t duk__lexer_parse_escape(duk_lexer_ctx *lex_ctx, duk_bo
DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_ESCAPE);
}
+/* Parse legacy octal escape of the form \N{1,3}, e.g. \0, \5, \0377. Maximum
+ * allowed value is \0377 (U+00FF), longest match is used. Used for both string
+ * RegExp octal escape parsing. Window[0] must be the slash '\' and the first
+ * digit must already be validated to be in [0-9] by the caller.
+ */
+DUK_LOCAL duk_codepoint_t duk__lexer_parse_legacy_octal(duk_lexer_ctx *lex_ctx, duk_small_int_t *out_adv, duk_bool_t reject_annex_b) {
+ duk_codepoint_t cp;
+ duk_small_uint_t lookup_idx;
+ duk_small_int_t adv;
+ duk_codepoint_t tmp;
+
+ DUK_ASSERT(out_adv != NULL);
+ DUK_ASSERT(DUK__LOOKUP(lex_ctx, 0) == DUK_ASC_BACKSLASH);
+ DUK_ASSERT(DUK__LOOKUP(lex_ctx, 1) >= DUK_ASC_0 && DUK__LOOKUP(lex_ctx, 1) <= DUK_ASC_9);
+
+ cp = 0;
+ for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) {
+ DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp));
+ tmp = DUK__LOOKUP(lex_ctx, lookup_idx);
+ if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) {
+ /* No more valid digits. */
+ break;
+ }
+ tmp = (cp << 3) + (tmp - DUK_ASC_0);
+ if (tmp > 0xff) {
+ /* Three digit octal escapes above \377 (= 0xff)
+ * are not allowed.
+ */
+ break;
+ }
+ cp = tmp;
+ }
+ DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, cp=%ld", (long) lookup_idx, (long) cp));
+
+ adv = lookup_idx;
+ if (lookup_idx == 1) {
+ DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too"));
+ DUK_ASSERT(tmp == DUK_ASC_8 || tmp == DUK_ASC_9);
+ cp = tmp;
+ adv++; /* correction to above, eat offending character */
+ } else if (lookup_idx == 2 && cp == 0) {
+ /* Note: 'foo\0bar' is OK in strict mode, but 'foo\00bar' is not.
+ * It won't be interpreted as 'foo\u{0}0bar' but as a SyntaxError.
+ */
+ DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too"));
+ } else {
+ /* This clause also handles non-shortest zero, e.g. \00. */
+ if (reject_annex_b) {
+ DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> reject in strict-mode", (long) cp));
+ cp = -1;
+ } else {
+ DUK_DDD(DUK_DDDPRINT("non-zero octal literal %ld -> accepted", (long) cp));
+ DUK_ASSERT(cp >= 0 && cp <= 0xff);
+ }
+ }
+
+ *out_adv = adv;
+
+ DUK_ASSERT((cp >= 0 && cp <= 0xff) || (cp == -1 && reject_annex_b));
+ return cp;
+}
+
/* XXX: move strict mode to lex_ctx? */
DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token *out_token, duk_small_int_t quote, duk_bool_t strict_mode) {
duk_small_int_t adv;
@@ -77734,46 +79553,9 @@ DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token
* Parse octal (up to 3 digits) from the lookup window.
*/
- duk_codepoint_t tmp;
- duk_small_uint_t lookup_idx;
-
- emitcp = 0;
- for (lookup_idx = 1; lookup_idx <= 3; lookup_idx++) {
- DUK_DDD(DUK_DDDPRINT("lookup_idx=%ld, emitcp=%ld", (long) lookup_idx, (long) emitcp));
- tmp = DUK__LOOKUP(lex_ctx, lookup_idx);
- if (tmp < DUK_ASC_0 || tmp > DUK_ASC_7) {
- /* No more valid digits. */
- break;
- }
- tmp = (emitcp << 3) + (tmp - DUK_ASC_0);
- if (tmp > 0xff) {
- /* Three digit octal escapes above \377 (= 0xff)
- * are not allowed.
- */
- break;
- }
- emitcp = tmp;
- }
- DUK_DDD(DUK_DDDPRINT("final lookup_idx=%ld, emitcp=%ld", (long) lookup_idx, (long) emitcp));
-
- adv = lookup_idx;
- if (lookup_idx == 1) {
- /* \8 or \9 -> treat as literal, accept also
- * in strict mode.
- */
- DUK_DDD(DUK_DDDPRINT("\\8 or \\9 -> treat as literal, accept in strict mode too"));
- emitcp = x;
- adv++; /* correction to above, eat offending character */
- } else if (lookup_idx == 2 && emitcp == 0) {
- /* Zero escape, also allowed in non-strict mode. */
- DUK_DDD(DUK_DDDPRINT("\\0 -> accept in strict mode too"));
- } else {
- /* Valid octal, only accept in non-strict mode. */
- DUK_DDD(DUK_DDDPRINT("octal literal %ld -> accept only in non-strict-mode", (long) emitcp));
- DUK_ASSERT(emitcp >= 0 && emitcp <= 0xff);
- if (strict_mode) {
- goto fail_escape;
- }
+ emitcp = duk__lexer_parse_legacy_octal(lex_ctx, &adv, strict_mode /*reject_annex_b*/);
+ if (emitcp < 0) {
+ goto fail_escape;
}
} else if (x < 0) {
goto fail_unterminated;
@@ -77824,6 +79606,19 @@ DUK_LOCAL void duk__lexer_parse_string_literal(duk_lexer_ctx *lex_ctx, duk_token
return;
}
+/* Skip to end-of-line (or end-of-file), used for single line comments. */
+DUK_LOCAL void duk__lexer_skip_to_endofline(duk_lexer_ctx *lex_ctx) {
+ for (;;) {
+ duk_codepoint_t x;
+
+ x = DUK__L0();
+ if (x < 0 || duk_unicode_is_line_terminator(x)) {
+ break;
+ }
+ DUK__ADVANCECHARS(lex_ctx, 1);
+ }
+}
+
/*
* Parse Ecmascript source InputElementDiv or InputElementRegExp
* (E5 Section 7), skipping whitespace, comments, and line terminators.
@@ -77962,6 +79757,17 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
DUK__ADVANCECHARS(lex_ctx, 1);
got_lineterm = 1;
goto restart_lineupdate;
+#if defined(DUK_USE_SHEBANG_COMMENTS)
+ case DUK_ASC_HASH: /* '#' */
+ if (DUK__L1() == DUK_ASC_EXCLAMATION && lex_ctx->window[0].offset == 0 &&
+ (lex_ctx->flags & DUK_COMPILE_SHEBANG)) {
+ /* "Shebang" comment ('#! ...') on first line. */
+ /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */
+ duk__lexer_skip_to_endofline(lex_ctx);
+ goto restart; /* line terminator will be handled on next round */
+ }
+ goto fail_token;
+#endif /* DUK_USE_SHEBANG_COMMENTS */
case DUK_ASC_SLASH: /* '/' */
if (DUK__L1() == DUK_ASC_SLASH) {
/*
@@ -77969,14 +79775,8 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
* code point).
*/
- /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but it unnecessary */
- for (;;) {
- x = DUK__L0();
- if (x < 0 || duk_unicode_is_line_terminator(x)) {
- break;
- }
- DUK__ADVANCECHARS(lex_ctx, 1);
- }
+ /* DUK__ADVANCECHARS(lex_ctx, 2) would be correct here, but not necessary */
+ duk__lexer_skip_to_endofline(lex_ctx);
goto restart; /* line terminator will be handled on next round */
} else if (DUK__L1() == DUK_ASC_STAR) {
/*
@@ -78161,6 +79961,18 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
advtok = DUK__ADVTOK(1, DUK_TOK_COMMA);
break;
case DUK_ASC_LANGLE: /* '<' */
+#if defined(DUK_USE_HTML_COMMENTS)
+ if (DUK__L1() == DUK_ASC_EXCLAMATION && DUK__L2() == DUK_ASC_MINUS && DUK__L3() == DUK_ASC_MINUS) {
+ /*
+ * ES6: B.1.3, handle "<!--" SingleLineHTMLOpenComment
+ */
+
+ /* DUK__ADVANCECHARS(lex_ctx, 4) would be correct here, but not necessary */
+ duk__lexer_skip_to_endofline(lex_ctx);
+ goto restart; /* line terminator will be handled on next round */
+ }
+ else
+#endif /* DUK_USE_HTML_COMMENTS */
if (DUK__L1() == DUK_ASC_LANGLE && DUK__L2() == DUK_ASC_EQUALS) {
advtok = DUK__ADVTOK(3, DUK_TOK_ALSHIFT_EQ);
} else if (DUK__L1() == DUK_ASC_EQUALS) {
@@ -78214,6 +80026,25 @@ void duk_lexer_parse_js_input_element(duk_lexer_ctx *lex_ctx,
}
break;
case DUK_ASC_MINUS: /* '-' */
+#if defined(DUK_USE_HTML_COMMENTS)
+ if (got_lineterm && DUK__L1() == DUK_ASC_MINUS && DUK__L2() == DUK_ASC_RANGLE) {
+ /*
+ * ES6: B.1.3, handle "-->" SingleLineHTMLCloseComment
+ * Only allowed:
+ * - on new line
+ * - preceded only by whitespace
+ * - preceded by end of multiline comment and optional whitespace
+ *
+ * Since whitespace generates no tokens, and multiline comments
+ * are treated as a line ending, consulting `got_lineterm` is
+ * sufficient to test for these three options.
+ */
+
+ /* DUK__ADVANCECHARS(lex_ctx, 3) would be correct here, but not necessary */
+ duk__lexer_skip_to_endofline(lex_ctx);
+ goto restart; /* line terminator will be handled on next round */
+ } else
+#endif /* DUK_USE_HTML_COMMENTS */
if (DUK__L1() == DUK_ASC_MINUS) {
advtok = DUK__ADVTOK(2, DUK_TOK_DECREMENT);
} else if (DUK__L1() == DUK_ASC_EQUALS) {
@@ -78925,6 +80756,8 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token
} else if (DUK__L2() == DUK_ASC_COLON) {
/* (?: */
advtok = DUK__ADVTOK(3, DUK_RETOK_ATOM_START_NONCAPTURE_GROUP);
+ } else {
+ goto fail_group;
}
} else {
/* ( */
@@ -78992,6 +80825,10 @@ DUK_INTERNAL void duk_lexer_parse_re_token(duk_lexer_ctx *lex_ctx, duk_re_token
DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_ESCAPE);
return;
+ fail_group:
+ DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_GROUP);
+ return;
+
#if !defined(DUK_USE_ES6_REGEXP_SYNTAX)
fail_invalid_char:
DUK_ERROR_SYNTAX(lex_ctx->thr, DUK_STR_INVALID_REGEXP_CHARACTER);
@@ -79165,12 +81002,24 @@ DUK_INTERNAL void duk_lexer_parse_re_ranges(duk_lexer_ctx *lex_ctx, duk_re_range
sizeof(duk_unicode_re_ranges_not_wordchar) / sizeof(duk_uint16_t));
ch = -1;
} else if (DUK__ISDIGIT(x)) {
- /* DecimalEscape, only \0 is allowed, no leading zeroes are allowed */
+ /* DecimalEscape, only \0 is allowed, no leading
+ * zeroes are allowed.
+ *
+ * ES2015 Annex B also allows (maximal match) legacy
+ * octal escapes up to \377 and \8 and \9 are
+ * accepted as literal '8' and '9', also in strict mode.
+ */
+
+#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
+ ch = duk__lexer_parse_legacy_octal(lex_ctx, &adv, 0 /*reject_annex_b*/);
+ DUK_ASSERT(ch >= 0); /* no rejections */
+#else
if (x == DUK_ASC_0 && !DUK__ISDIGIT(DUK__L2())) {
ch = 0x0000;
} else {
goto fail_escape;
}
+#endif
#if defined(DUK_USE_ES6_REGEXP_SYNTAX)
} else if (x >= 0) {
/* IdentityEscape: ES2015 Annex B allows almost all
@@ -82413,34 +84262,34 @@ DUK_LOCAL duk_uint32_t duk__parse_regexp_flags(duk_hthread *thr, duk_hstring *h)
switch (c) {
case (duk_uint8_t) 'g': {
if (flags & DUK_RE_FLAG_GLOBAL) {
- goto error;
+ goto flags_error;
}
flags |= DUK_RE_FLAG_GLOBAL;
break;
}
case (duk_uint8_t) 'i': {
if (flags & DUK_RE_FLAG_IGNORE_CASE) {
- goto error;
+ goto flags_error;
}
flags |= DUK_RE_FLAG_IGNORE_CASE;
break;
}
case (duk_uint8_t) 'm': {
if (flags & DUK_RE_FLAG_MULTILINE) {
- goto error;
+ goto flags_error;
}
flags |= DUK_RE_FLAG_MULTILINE;
break;
}
default: {
- goto error;
+ goto flags_error;
}
}
}
return flags;
- error:
+ flags_error:
DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_REGEXP_FLAGS);
return 0; /* never here */
}
@@ -83510,6 +85359,7 @@ DUK_LOCAL void duk__regexp_match_helper(duk_hthread *thr, duk_small_int_t force_
char_offset = (duk_uint32_t) 0;
}
+ DUK_ASSERT(char_offset <= DUK_HSTRING_GET_CHARLEN(h_input));
sp = re_ctx.input + duk_heap_strcache_offset_char2byte(thr, h_input, char_offset);
/*
@@ -90685,8 +92535,7 @@ DUK_INTERNAL duk_small_uint_t duk_bd_decode_flag(duk_bitdecoder_ctx *ctx) {
}
/* Decode a one-bit flag, and if set, decode a value of 'bits', otherwise return
- * default value. Return value is signed so that negative marker value can be
- * used by caller as a "not present" value.
+ * default value.
*/
DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_uint32_t def_value) {
if (duk_bd_decode_flag(ctx)) {
@@ -90696,6 +92545,11 @@ DUK_INTERNAL duk_uint32_t duk_bd_decode_flagged(duk_bitdecoder_ctx *ctx, duk_sma
}
}
+/* Signed variant, allows negative marker value. */
+DUK_INTERNAL duk_int32_t duk_bd_decode_flagged_signed(duk_bitdecoder_ctx *ctx, duk_small_int_t bits, duk_int32_t def_value) {
+ return (duk_int32_t) duk_bd_decode_flagged(ctx, bits, (duk_uint32_t) def_value);
+}
+
/* Shared varint encoding. Match dukutil.py BitEncode.varuint(). */
DUK_INTERNAL duk_uint32_t duk_bd_decode_varuint(duk_bitdecoder_ctx *ctx) {
duk_small_uint_t t;
@@ -90895,7 +92749,7 @@ DUK_INTERNAL duk_uint8_t *duk_bw_resize(duk_hthread *thr, duk_bufwriter_ctx *bw_
curr_off = (duk_size_t) (bw_ctx->p - bw_ctx->p_base);
add_sz = (curr_off >> DUK_BW_SPARE_SHIFT) + DUK_BW_SPARE_ADD;
new_sz = curr_off + sz + add_sz;
- if (new_sz < curr_off) {
+ if (DUK_UNLIKELY(new_sz < curr_off)) {
/* overflow */
DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG);
return NULL; /* not reachable */
@@ -91382,4 +93236,3 @@ DUK_INTERNAL duk_double_t duk_util_tinyrandom_get_double(duk_hthread *thr) {
#undef DUK__RANDOM_XOROSHIRO128PLUS
#undef DUK__RND_BIT
#undef DUK__UPDATE_RND
-#endif