Browse Source

Merge pull request #153 from AtomicGameEngine/JME-ATOMIC-151

Update Duktape, enable voluntary GC
JoshEngebretson 10 years ago
parent
commit
f1f7baff38

+ 3 - 3
Source/ThirdParty/Duktape/duk_config.h

@@ -1994,11 +1994,11 @@ typedef FILE duk_file;
  *  Apply to function definition only (not declaration).
  *  Apply to function definition only (not declaration).
  */
  */
 
 
-#if defined(DUK_F_CLANG)
+#if defined(DUK_F_CLANG) && (defined(DUK_F_C99) || defined(DUK_F_CPP11))
 #define DUK_NOINLINE        __attribute__((noinline))
 #define DUK_NOINLINE        __attribute__((noinline))
 #define DUK_INLINE          inline
 #define DUK_INLINE          inline
 #define DUK_ALWAYS_INLINE   inline __attribute__((always_inline))
 #define DUK_ALWAYS_INLINE   inline __attribute__((always_inline))
-#elif defined(DUK_F_GCC) && defined(DUK_F_GCC_VERSION)
+#elif defined(DUK_F_GCC) && defined(DUK_F_GCC_VERSION) && (defined(DUK_F_C99) || defined(DUK_F_CPP11))
 #if (DUK_F_GCC_VERSION >= 30101)
 #if (DUK_F_GCC_VERSION >= 30101)
 #define DUK_NOINLINE        __attribute__((noinline))
 #define DUK_NOINLINE        __attribute__((noinline))
 #define DUK_INLINE          inline
 #define DUK_INLINE          inline
@@ -3060,7 +3060,7 @@ DUK_INTERNAL_DECL duk_bool_t duk_bi_date_format_parts_strftime(duk_context *ctx,
 #define DUK_USE_JSON_DECSTRING_FASTPATH
 #define DUK_USE_JSON_DECSTRING_FASTPATH
 #define DUK_USE_JSON_EATWHITE_FASTPATH
 #define DUK_USE_JSON_EATWHITE_FASTPATH
 #define DUK_USE_JSON_QUOTESTRING_FASTPATH
 #define DUK_USE_JSON_QUOTESTRING_FASTPATH
-#undef DUK_USE_JSON_STRINGIFY_FASTPATH
+// #undef DUK_USE_JSON_STRINGIFY_FASTPATH
 
 
 /*
 /*
  *  Alternative customization header
  *  Alternative customization header

+ 672 - 73
Source/ThirdParty/Duktape/duktape.c

@@ -1,6 +1,6 @@
 /*
 /*
  *  Single file autogenerated distributable for Duktape 1.2.99.
  *  Single file autogenerated distributable for Duktape 1.2.99.
- *  Git commit b098f0423266f5dbfd7a67fc3a6146455df9b61c (v1.2.0-257-gb098f04-dirty).
+ *  Git commit 281739096bf0c73d5c494069b3146e80dcbbde58 (v1.2.0-289-g2817390).
  *
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  licensing information.
  *  licensing information.
@@ -61,6 +61,7 @@
 *  * Andreas \u00d6man <[email protected]>
 *  * Andreas \u00d6man <[email protected]>
 *  * L\u00e1szl\u00f3 Lang\u00f3 <[email protected]>
 *  * L\u00e1szl\u00f3 Lang\u00f3 <[email protected]>
 *  * Legimet <[email protected]>
 *  * Legimet <[email protected]>
+*  * Karl Skomski <[email protected]>
 *
 *
 *  Other contributions
 *  Other contributions
 *  ===================
 *  ===================
@@ -4538,7 +4539,7 @@ struct duk_bufwriter_ctx {
 #define DUK_BW_GET_SIZE(thr,bw_ctx) \
 #define DUK_BW_GET_SIZE(thr,bw_ctx) \
 	((duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base))
 	((duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base))
 #define DUK_BW_SET_SIZE(thr,bw_ctx,sz) do { \
 #define DUK_BW_SET_SIZE(thr,bw_ctx,sz) do { \
-		DUK_ASSERT((duk_size_t) (sz) <= (duk_size_t) ((bw_ctx)->p - (bw_ctx->p_base))); \
+		DUK_ASSERT((duk_size_t) (sz) <= (duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base)); \
 		(bw_ctx)->p = (bw_ctx)->p_base + (sz); \
 		(bw_ctx)->p = (bw_ctx)->p_base + (sz); \
 	} while (0)
 	} while (0)
 #define DUK_BW_RESET_SIZE(thr,bw_ctx) do { \
 #define DUK_BW_RESET_SIZE(thr,bw_ctx) do { \
@@ -7453,6 +7454,9 @@ DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr
 #define DUK_HOBJECT_SET_CLASS_NUMBER(h,v)      \
 #define DUK_HOBJECT_SET_CLASS_NUMBER(h,v)      \
 	DUK_HEAPHDR_SET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS, (v))
 	DUK_HEAPHDR_SET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS, (v))
 
 
+#define DUK_HOBJECT_GET_CLASS_MASK(h)          \
+	(1UL << DUK_HEAPHDR_GET_FLAG_RANGE(&(h)->hdr, DUK_HOBJECT_FLAG_CLASS_BASE, DUK_HOBJECT_FLAG_CLASS_BITS))
+
 /* Macro for creating flag initializer from a class number.
 /* Macro for creating flag initializer from a class number.
  * Unsigned type cast is needed to avoid warnings about coercing
  * Unsigned type cast is needed to avoid warnings about coercing
  * a signed integer to an unsigned one; the largest class values
  * a signed integer to an unsigned one; the largest class values
@@ -7491,6 +7495,54 @@ DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr
 #define DUK_HOBJECT_CLASS_UINT32ARRAY          27
 #define DUK_HOBJECT_CLASS_UINT32ARRAY          27
 #define DUK_HOBJECT_CLASS_FLOAT32ARRAY         28
 #define DUK_HOBJECT_CLASS_FLOAT32ARRAY         28
 #define DUK_HOBJECT_CLASS_FLOAT64ARRAY         29
 #define DUK_HOBJECT_CLASS_FLOAT64ARRAY         29
+#define DUK_HOBJECT_CLASS_MAX                  29
+
+/* class masks */
+#define DUK_HOBJECT_CMASK_ALL                  ((1UL << (DUK_HOBJECT_CLASS_MAX + 1)) - 1UL)
+#define DUK_HOBJECT_CMASK_UNUSED               (1UL << DUK_HOBJECT_CLASS_UNUSED)
+#define DUK_HOBJECT_CMASK_ARGUMENTS            (1UL << DUK_HOBJECT_CLASS_ARGUMENTS)
+#define DUK_HOBJECT_CMASK_ARRAY                (1UL << DUK_HOBJECT_CLASS_ARRAY)
+#define DUK_HOBJECT_CMASK_BOOLEAN              (1UL << DUK_HOBJECT_CLASS_BOOLEAN)
+#define DUK_HOBJECT_CMASK_DATE                 (1UL << DUK_HOBJECT_CLASS_DATE)
+#define DUK_HOBJECT_CMASK_ERROR                (1UL << DUK_HOBJECT_CLASS_ERROR)
+#define DUK_HOBJECT_CMASK_FUNCTION             (1UL << DUK_HOBJECT_CLASS_FUNCTION)
+#define DUK_HOBJECT_CMASK_JSON                 (1UL << DUK_HOBJECT_CLASS_JSON)
+#define DUK_HOBJECT_CMASK_MATH                 (1UL << DUK_HOBJECT_CLASS_MATH)
+#define DUK_HOBJECT_CMASK_NUMBER               (1UL << DUK_HOBJECT_CLASS_NUMBER)
+#define DUK_HOBJECT_CMASK_OBJECT               (1UL << DUK_HOBJECT_CLASS_OBJECT)
+#define DUK_HOBJECT_CMASK_REGEXP               (1UL << DUK_HOBJECT_CLASS_REGEXP)
+#define DUK_HOBJECT_CMASK_STRING               (1UL << DUK_HOBJECT_CLASS_STRING)
+#define DUK_HOBJECT_CMASK_GLOBAL               (1UL << DUK_HOBJECT_CLASS_GLOBAL)
+#define DUK_HOBJECT_CMASK_OBJENV               (1UL << DUK_HOBJECT_CLASS_OBJENV)
+#define DUK_HOBJECT_CMASK_DECENV               (1UL << DUK_HOBJECT_CLASS_DECENV)
+#define DUK_HOBJECT_CMASK_BUFFER               (1UL << DUK_HOBJECT_CLASS_BUFFER)
+#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)
+#define DUK_HOBJECT_CMASK_UINT8ARRAY           (1UL << DUK_HOBJECT_CLASS_UINT8ARRAY)
+#define DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY    (1UL << DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY)
+#define DUK_HOBJECT_CMASK_INT16ARRAY           (1UL << DUK_HOBJECT_CLASS_INT16ARRAY)
+#define DUK_HOBJECT_CMASK_UINT16ARRAY          (1UL << DUK_HOBJECT_CLASS_UINT16ARRAY)
+#define DUK_HOBJECT_CMASK_INT32ARRAY           (1UL << DUK_HOBJECT_CLASS_INT32ARRAY)
+#define DUK_HOBJECT_CMASK_UINT32ARRAY          (1UL << DUK_HOBJECT_CLASS_UINT32ARRAY)
+#define DUK_HOBJECT_CMASK_FLOAT32ARRAY         (1UL << DUK_HOBJECT_CLASS_FLOAT32ARRAY)
+#define DUK_HOBJECT_CMASK_FLOAT64ARRAY         (1UL << DUK_HOBJECT_CLASS_FLOAT64ARRAY)
+
+#define DUK_HOBJECT_CMASK_ALL_BUFFEROBJECTS \
+	(DUK_HOBJECT_CMASK_BUFFER | \
+	 DUK_HOBJECT_CMASK_ARRAYBUFFER | \
+	 DUK_HOBJECT_CMASK_DATAVIEW | \
+	 DUK_HOBJECT_CMASK_INT8ARRAY | \
+	 DUK_HOBJECT_CMASK_UINT8ARRAY | \
+	 DUK_HOBJECT_CMASK_UINT8CLAMPEDARRAY | \
+	 DUK_HOBJECT_CMASK_INT16ARRAY | \
+	 DUK_HOBJECT_CMASK_UINT16ARRAY | \
+	 DUK_HOBJECT_CMASK_INT32ARRAY | \
+	 DUK_HOBJECT_CMASK_UINT32ARRAY | \
+	 DUK_HOBJECT_CMASK_FLOAT32ARRAY | \
+	 DUK_HOBJECT_CMASK_FLOAT64ARRAY)
 
 
 #define DUK_HOBJECT_IS_OBJENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV)
 #define DUK_HOBJECT_IS_OBJENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_OBJENV)
 #define DUK_HOBJECT_IS_DECENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV)
 #define DUK_HOBJECT_IS_DECENV(h)               (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DECENV)
@@ -7615,6 +7667,31 @@ DUK_INTERNAL_DECL duk_ucodepoint_t duk_hstring_char_code_at_raw(duk_hthread *thr
                                                  DUK_PROPDESC_FLAG_ENUMERABLE | \
                                                  DUK_PROPDESC_FLAG_ENUMERABLE | \
                                                  DUK_PROPDESC_FLAG_CONFIGURABLE)
                                                  DUK_PROPDESC_FLAG_CONFIGURABLE)
 
 
+/*
+ *  Macro for object validity check
+ *
+ *  Assert for currently guaranteed relations between flags, for instance.
+ */
+
+#define DUK_ASSERT_HOBJECT_VALID(h) do { \
+		DUK_ASSERT((h) != NULL); \
+		DUK_ASSERT(!DUK_HOBJECT_IS_CALLABLE((h)) || \
+		           DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FUNCTION); \
+		DUK_ASSERT(!DUK_HOBJECT_IS_BUFFEROBJECT((h)) || \
+		           (DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_BUFFER || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_ARRAYBUFFER || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_DATAVIEW || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT8ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT8ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT16ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT16ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_INT32ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_UINT32ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT32ARRAY || \
+		            DUK_HOBJECT_GET_CLASS_NUMBER((h)) == DUK_HOBJECT_CLASS_FLOAT64ARRAY)); \
+	} while (0)
+
 /*
 /*
  *  Macros to access the 'props' allocation.
  *  Macros to access the 'props' allocation.
  */
  */
@@ -8152,6 +8229,12 @@ DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr(duk_heap *h
 DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *out_attrs);
 DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_entry_tval_ptr_and_attrs(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *out_attrs);
 DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i);
 DUK_INTERNAL_DECL duk_tval *duk_hobject_find_existing_array_entry_tval_ptr(duk_heap *heap, duk_hobject *obj, duk_uarridx_t i);
 
 
+/* XXX: when optimizing for guaranteed property slots, use a guaranteed
+ * slot for internal value; this call can then access it directly.
+ */
+#define duk_hobject_get_internal_value_tval_ptr(heap,obj) \
+	duk_hobject_find_existing_entry_tval_ptr((heap), (obj), DUK_HEAP_STRING_INT_VALUE((heap)))
+
 /* core property functions */
 /* core property functions */
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key);
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_getprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key);
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag);
 DUK_INTERNAL_DECL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj, duk_tval *tv_key, duk_tval *tv_val, duk_bool_t throw_flag);
@@ -10638,10 +10721,10 @@ DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp)
 #endif
 #endif
 
 
 /* Encoding/decoding flags */
 /* Encoding/decoding flags */
-#define DUK_JSON_FLAG_ASCII_ONLY          (1 << 0)  /* escape any non-ASCII characters */
-#define DUK_JSON_FLAG_AVOID_KEY_QUOTES    (1 << 1)  /* avoid key quotes when key is an ASCII Identifier */
-#define DUK_JSON_FLAG_EXT_CUSTOM          (1 << 2)  /* extended types: custom encoding */
-#define DUK_JSON_FLAG_EXT_COMPATIBLE      (1 << 3)  /* extended types: compatible encoding */
+#define DUK_JSON_FLAG_ASCII_ONLY              (1 << 0)  /* escape any non-ASCII characters */
+#define DUK_JSON_FLAG_AVOID_KEY_QUOTES        (1 << 1)  /* avoid key quotes when key is an ASCII Identifier */
+#define DUK_JSON_FLAG_EXT_CUSTOM              (1 << 2)  /* extended types: custom encoding */
+#define DUK_JSON_FLAG_EXT_COMPATIBLE          (1 << 3)  /* extended types: compatible encoding */
 
 
 /* How much stack to require on entry to object/array encode */
 /* How much stack to require on entry to object/array encode */
 #define DUK_JSON_ENC_REQSTACK                 32
 #define DUK_JSON_ENC_REQSTACK                 32
@@ -10649,6 +10732,11 @@ DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp)
 /* How much stack to require on entry to object/array decode */
 /* How much stack to require on entry to object/array decode */
 #define DUK_JSON_DEC_REQSTACK                 32
 #define DUK_JSON_DEC_REQSTACK                 32
 
 
+/* How large a loop detection stack to use for fast path */
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+#define DUK_JSON_ENC_LOOPARRAY                64
+#endif
+
 /* Encoding state.  Heap object references are all borrowed. */
 /* Encoding state.  Heap object references are all borrowed. */
 typedef struct {
 typedef struct {
 	duk_hthread *thr;
 	duk_hthread *thr;
@@ -10675,6 +10763,9 @@ typedef struct {
 	duk_small_uint_t stridx_custom_posinf;
 	duk_small_uint_t stridx_custom_posinf;
 	duk_small_uint_t stridx_custom_function;
 	duk_small_uint_t stridx_custom_function;
 #endif
 #endif
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+	duk_hobject *visiting[DUK_JSON_ENC_LOOPARRAY];  /* indexed by recursion_depth */
+#endif
 } duk_json_enc_ctx;
 } duk_json_enc_ctx;
 
 
 typedef struct {
 typedef struct {
@@ -19417,7 +19508,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
 		h_bufobj->buf = h_val;
 		h_bufobj->buf = h_val;
 		DUK_HBUFFER_INCREF(thr, h_val);
 		DUK_HBUFFER_INCREF(thr, h_val);
 		DUK_ASSERT(h_bufobj->offset == 0);
 		DUK_ASSERT(h_bufobj->offset == 0);
-		h_bufobj->length = DUK_HBUFFER_GET_SIZE(h_val);
+		h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
 		DUK_ASSERT(h_bufobj->shift == 0);
 		DUK_ASSERT(h_bufobj->shift == 0);
 		DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
 		DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
 
 
@@ -23410,7 +23501,7 @@ DUK_LOCAL void duk__set_bufobj_buffer(duk_context *ctx, duk_hbufferobject *h_buf
 
 
 	h_bufobj->buf = h_val;
 	h_bufobj->buf = h_val;
 	DUK_HBUFFER_INCREF(thr, h_val);
 	DUK_HBUFFER_INCREF(thr, h_val);
-	h_bufobj->length = DUK_HBUFFER_GET_SIZE(h_val);
+	h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
 	DUK_ASSERT(h_bufobj->shift == 0);
 	DUK_ASSERT(h_bufobj->shift == 0);
 	DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
 	DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
 
 
@@ -24624,7 +24715,7 @@ DUK_INTERNAL duk_ret_t duk_bi_nodejs_buffer_write(duk_context *ctx) {
 	/* XXX: encoding is ignored now. */
 	/* XXX: encoding is ignored now. */
 
 
 	if (length > str_len) {
 	if (length > str_len) {
-		length = str_len;
+		length = (duk_uint_t) str_len;
 	}
 	}
 
 
 	if (DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) {
 	if (DUK_HBUFFEROBJECT_VALID_SLICE(h_this)) {
@@ -28492,9 +28583,8 @@ DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int
 
 
 	duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_TAB);
 	duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_TAB);
 	duk_push_this(ctx);
 	duk_push_this(ctx);
-	duk_to_string(ctx, -1);
 
 
-	/* [ ... this tracedata sep ToString(this) ] */
+	/* [ ... this tracedata sep this ] */
 
 
 	/* XXX: skip null filename? */
 	/* XXX: skip null filename? */
 
 
@@ -28622,11 +28712,15 @@ DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int
 		}
 		}
 	}
 	}
 
 
-	/* [ ... this tracedata sep ToString(this) str1 ... strN ] */
+	/* [ ... this tracedata sep this str1 ... strN ] */
 
 
 	if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) {
 	if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) {
 		return 0;
 		return 0;
 	} else {
 	} else {
+		/* The 'this' after 'sep' will get ToString() coerced by
+		 * duk_join() automatically.  We don't want to do that
+		 * coercion when providing .fileName or .lineNumber (GH-254).
+		 */
 		duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/);
 		duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/);
 		return 1;
 		return 1;
 	}
 	}
@@ -30298,6 +30392,9 @@ DUK_LOCAL_DECL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx);
 
 
 DUK_LOCAL_DECL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch);
 DUK_LOCAL_DECL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch);
 DUK_LOCAL_DECL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2);
 DUK_LOCAL_DECL void duk__emit_2(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch1, duk_uint_fast8_t ch2);
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+DUK_LOCAL_DECL void duk__unemit_1(duk_json_enc_ctx *js_ctx);
+#endif
 DUK_LOCAL_DECL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h);
 DUK_LOCAL_DECL void duk__emit_hstring(duk_json_enc_ctx *js_ctx, duk_hstring *h);
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
 #if defined(DUK_USE_JX) || defined(DUK_USE_JC)
 DUK_LOCAL_DECL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *p);
 DUK_LOCAL_DECL void duk__emit_cstring(duk_json_enc_ctx *js_ctx, const char *p);
@@ -30313,6 +30410,10 @@ DUK_LOCAL_DECL void duk__enc_array(duk_json_enc_ctx *js_ctx);
 DUK_LOCAL_DECL duk_bool_t duk__enc_value1(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder);
 DUK_LOCAL_DECL duk_bool_t duk__enc_value1(duk_json_enc_ctx *js_ctx, duk_idx_t idx_holder);
 DUK_LOCAL_DECL void duk__enc_value2(duk_json_enc_ctx *js_ctx);
 DUK_LOCAL_DECL void duk__enc_value2(duk_json_enc_ctx *js_ctx);
 DUK_LOCAL_DECL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv);
 DUK_LOCAL_DECL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv);
+DUK_LOCAL_DECL void duk__enc_double(duk_json_enc_ctx *js_ctx);
+#if defined(DUK_USE_FASTINT)
+DUK_LOCAL_DECL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv);
+#endif
 
 
 /*
 /*
  *  Helper tables
  *  Helper tables
@@ -31269,6 +31370,9 @@ DUK_LOCAL void duk__dec_reviver_walk(duk_json_dec_ctx *js_ctx) {
 #define DUK__EMIT_CSTR(js_ctx,p)        duk__emit_cstring((js_ctx), (p))
 #define DUK__EMIT_CSTR(js_ctx,p)        duk__emit_cstring((js_ctx), (p))
 #endif
 #endif
 #define DUK__EMIT_STRIDX(js_ctx,i)      duk__emit_stridx((js_ctx), (i))
 #define DUK__EMIT_STRIDX(js_ctx,i)      duk__emit_stridx((js_ctx), (i))
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+#define DUK__UNEMIT_1(js_ctx)           duk__unemit_1((js_ctx))
+#endif
 
 
 DUK_LOCAL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch) {
 DUK_LOCAL void duk__emit_1(duk_json_enc_ctx *js_ctx, duk_uint_fast8_t ch) {
 	DUK_BW_WRITE_ENSURE_U8(js_ctx->thr, &js_ctx->bw, ch);
 	DUK_BW_WRITE_ENSURE_U8(js_ctx->thr, &js_ctx->bw, ch);
@@ -31299,6 +31403,13 @@ DUK_LOCAL void duk__emit_stridx(duk_json_enc_ctx *js_ctx, duk_small_uint_t strid
 	DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h);
 	DUK_BW_WRITE_ENSURE_HSTRING(js_ctx->thr, &js_ctx->bw, h);
 }
 }
 
 
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+DUK_LOCAL void duk__unemit_1(duk_json_enc_ctx *js_ctx) {
+	DUK_ASSERT(DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw) >= 1);
+	DUK_BW_ADD_PTR(js_ctx->thr, &js_ctx->bw, -1);
+}
+#endif  /* DUK_USE_JSON_STRINGIFY_FASTPATH */
+
 #define DUK__MKESC(nybbles,esc1,esc2)  \
 #define DUK__MKESC(nybbles,esc1,esc2)  \
 	(((duk_uint_fast32_t) (nybbles)) << 16) | \
 	(((duk_uint_fast32_t) (nybbles)) << 16) | \
 	(((duk_uint_fast32_t) (esc1)) << 8) | \
 	(((duk_uint_fast32_t) (esc1)) << 8) | \
@@ -31308,6 +31419,8 @@ DUK_LOCAL duk_uint8_t *duk__emit_esc_auto_fast(duk_json_enc_ctx *js_ctx, duk_uin
 	duk_uint_fast32_t tmp;
 	duk_uint_fast32_t tmp;
 	duk_small_uint_t dig;
 	duk_small_uint_t dig;
 
 
+	DUK_UNREF(js_ctx);
+
 	/* Caller ensures space for at least DUK__JSON_MAX_ESC_LEN. */
 	/* Caller ensures space for at least DUK__JSON_MAX_ESC_LEN. */
 
 
 	/* Select appropriate escape format automatically, and set 'tmp' to a
 	/* Select appropriate escape format automatically, and set 'tmp' to a
@@ -31537,6 +31650,98 @@ DUK_LOCAL void duk__enc_quote_string(duk_json_enc_ctx *js_ctx, duk_hstring *h_st
 	DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE);
 	DUK__EMIT_1(js_ctx, DUK_ASC_DOUBLEQUOTE);
 }
 }
 
 
+/* Encode a double (checked by caller) from stack top.  Stack top may be
+ * replaced by serialized string but is not popped (caller does that).
+ */
+DUK_LOCAL void duk__enc_double(duk_json_enc_ctx *js_ctx) {
+	duk_context *ctx;
+	duk_tval *tv;
+	duk_double_t d;
+	duk_small_int_t c;
+	duk_small_int_t s;
+	duk_small_uint_t stridx;
+	duk_small_uint_t n2s_flags;
+	duk_hstring *h_str;
+
+	DUK_ASSERT(js_ctx != NULL);
+	ctx = (duk_context *) js_ctx->thr;
+	DUK_ASSERT(ctx != NULL);
+
+	/* Caller must ensure 'tv' is indeed a double and not a fastint! */
+	tv = duk_get_tval(ctx, -1);
+	DUK_ASSERT(tv != NULL);
+	DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
+	d = DUK_TVAL_GET_DOUBLE(tv);
+
+	c = (duk_small_int_t) DUK_FPCLASSIFY(d);
+	s = (duk_small_int_t) DUK_SIGNBIT(d);
+	DUK_UNREF(s);
+
+	if (DUK_LIKELY(!(c == DUK_FP_INFINITE || c == DUK_FP_NAN))) {
+		DUK_ASSERT(DUK_ISFINITE(d));
+
+#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
+		/* Negative zero needs special handling in JX/JC because
+		 * it would otherwise serialize to '0', not '-0'.
+		 */
+		if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 &&
+		                 (js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible))) {
+			duk_push_hstring_stridx(ctx, DUK_STRIDX_MINUS_ZERO);  /* '-0' */
+		} else
+#endif  /* DUK_USE_JX || DUK_USE_JC */
+		{
+			n2s_flags = 0;
+			/* [ ... number ] -> [ ... string ] */
+			duk_numconv_stringify(ctx, 10 /*radix*/, 0 /*digits*/, n2s_flags);
+		}
+		h_str = duk_to_hstring(ctx, -1);
+		DUK_ASSERT(h_str != NULL);
+		DUK__EMIT_HSTR(js_ctx, h_str);
+		return;
+	}
+
+#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
+	if (!(js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM |
+	                       DUK_JSON_FLAG_EXT_COMPATIBLE))) {
+		stridx = DUK_STRIDX_LC_NULL;
+	} else if (c == DUK_FP_NAN) {
+		stridx = js_ctx->stridx_custom_nan;
+	} else if (s == 0) {
+		stridx = js_ctx->stridx_custom_posinf;
+	} else {
+		stridx = js_ctx->stridx_custom_neginf;
+	}
+#else
+	stridx = DUK_STRIDX_LC_NULL;
+#endif
+	DUK__EMIT_STRIDX(js_ctx, stridx);
+}
+
+#if defined(DUK_USE_FASTINT)
+/* Encode a fastint from duk_tval ptr, no value stack effects. */
+DUK_LOCAL void duk__enc_fastint_tval(duk_json_enc_ctx *js_ctx, duk_tval *tv) {
+	duk_int64_t v;
+
+	/* Fastint range is signed 48-bit so longest value is -2^47 = -140737488355328
+	 * (16 chars long), longest signed 64-bit value is -2^63 = -9223372036854775808
+	 * (20 chars long).  Alloc space for 64-bit range to be safe.
+	 */
+	duk_uint8_t buf[20 + 1];
+
+	/* Caller must ensure 'tv' is indeed a fastint! */
+	DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
+	v = DUK_TVAL_GET_FASTINT(tv);
+
+	/* XXX: There are no format strings in duk_config.h yet, could add
+	 * one for formatting duk_int64_t.  For now, assumes "%lld" and that
+	 * "long long" type exists.  Could also rely on C99 directly but that
+	 * won't work for older MSVC.
+	 */
+	DUK_SPRINTF((char *) buf, "%lld", (long long) v);
+	DUK__EMIT_CSTR(js_ctx, (const char *) buf);
+}
+#endif
+
 /* Shared entry handling for object/array serialization: indent/stepback,
 /* Shared entry handling for object/array serialization: indent/stepback,
  * loop detection.
  * loop detection.
  */
  */
@@ -31552,8 +31757,12 @@ DUK_LOCAL void duk__enc_objarr_entry(duk_json_enc_ctx *js_ctx, duk_hstring **h_s
 
 
 	h_target = duk_get_hobject(ctx, -1);  /* object or array */
 	h_target = duk_get_hobject(ctx, -1);  /* object or array */
 	DUK_ASSERT(h_target != NULL);
 	DUK_ASSERT(h_target != NULL);
-	duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
 
 
+	/* XXX: this check is very expensive, perhaps use a small
+	 * array to make it faster for at least reasonably shallow
+	 * objects?
+	 */
+	duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
 	duk_dup_top(ctx);  /* -> [ ... voidp voidp ] */
 	duk_dup_top(ctx);  /* -> [ ... voidp voidp ] */
 	if (duk_has_prop(ctx, js_ctx->idx_loop)) {
 	if (duk_has_prop(ctx, js_ctx->idx_loop)) {
 		DUK_ERROR((duk_hthread *) ctx, DUK_ERR_TYPE_ERROR, DUK_STR_CYCLIC_INPUT);
 		DUK_ERROR((duk_hthread *) ctx, DUK_ERR_TYPE_ERROR, DUK_STR_CYCLIC_INPUT);
@@ -31629,8 +31838,9 @@ DUK_LOCAL void duk__enc_objarr_exit(duk_json_enc_ctx *js_ctx, duk_hstring **h_st
 
 
 	h_target = duk_get_hobject(ctx, *entry_top - 1);  /* original target at entry_top - 1 */
 	h_target = duk_get_hobject(ctx, *entry_top - 1);  /* original target at entry_top - 1 */
 	DUK_ASSERT(h_target != NULL);
 	DUK_ASSERT(h_target != NULL);
-	duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
 
 
+	/* XXX: this check is very expensive */
+	duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) h_target);
 	duk_del_prop(ctx, js_ctx->idx_loop);  /* -> [ ... ] */
 	duk_del_prop(ctx, js_ctx->idx_loop);  /* -> [ ... ] */
 
 
 	/* restore stack top after unbalanced code paths */
 	/* restore stack top after unbalanced code paths */
@@ -32137,60 +32347,18 @@ DUK_LOCAL void duk__enc_value2(duk_json_enc_ctx *js_ctx) {
 	}
 	}
 #if defined(DUK_USE_FASTINT)
 #if defined(DUK_USE_FASTINT)
 	case DUK_TAG_FASTINT:
 	case DUK_TAG_FASTINT:
+		/* Number serialization has a significant impact relative to
+		 * other fast path code, so careful fast path for fastints.
+		 */
+		duk__enc_fastint_tval(js_ctx, tv);
+		break;
 #endif
 #endif
 	default: {
 	default: {
 		/* number */
 		/* number */
-		duk_double_t d;
-		duk_small_int_t c;
-		duk_small_int_t s;
-		duk_small_uint_t stridx;
-		duk_small_uint_t n2s_flags;
-		duk_hstring *h_str;
-
-		DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
-		d = DUK_TVAL_GET_NUMBER(tv);
-		c = (duk_small_int_t) DUK_FPCLASSIFY(d);
-		s = (duk_small_int_t) DUK_SIGNBIT(d);
-		DUK_UNREF(s);
-
-		if (DUK_LIKELY(!(c == DUK_FP_INFINITE || c == DUK_FP_NAN))) {
-			DUK_ASSERT(DUK_ISFINITE(d));
-
-#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
-			/* Negative zero needs special handling in JX/JC because
-			 * it would otherwise serialize to '0', not '-0'.
-			 */
-			if (DUK_UNLIKELY(c == DUK_FP_ZERO && s != 0 &&
-			                 (js_ctx->flag_ext_custom || js_ctx->flag_ext_compatible))) {
-				duk_push_hstring_stridx(ctx, DUK_STRIDX_MINUS_ZERO);  /* '-0' */
-			} else
-#endif  /* DUK_USE_JX || DUK_USE_JC */
-			{
-				n2s_flags = 0;
-				/* [ ... number ] -> [ ... string ] */
-				duk_numconv_stringify(ctx, 10 /*radix*/, 0 /*digits*/, n2s_flags);
-			}
-			h_str = duk_to_hstring(ctx, -1);
-			DUK_ASSERT(h_str != NULL);
-			DUK__EMIT_HSTR(js_ctx, h_str);
-			break;
-		}
-
-#if defined(DUK_USE_JX) || defined(DUK_USE_JC)
-		if (!(js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM |
-		                       DUK_JSON_FLAG_EXT_COMPATIBLE))) {
-			stridx = DUK_STRIDX_LC_NULL;
-		} else if (c == DUK_FP_NAN) {
-			stridx = js_ctx->stridx_custom_nan;
-		} else if (s == 0) {
-			stridx = js_ctx->stridx_custom_posinf;
-		} else {
-			stridx = js_ctx->stridx_custom_neginf;
-		}
-#else
-		stridx = DUK_STRIDX_LC_NULL;
-#endif
-		DUK__EMIT_STRIDX(js_ctx, stridx);
+		/* XXX: A fast path for usual integers would be useful when
+		 * fastint support is not enabled.
+		 */
+		duk__enc_double(js_ctx);
 		break;
 		break;
 	}
 	}
 	}
 	}
@@ -32220,6 +32388,365 @@ DUK_LOCAL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv) {
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ *  JSON.stringify() fast path
+ */
+
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+DUK_LOCAL duk_bool_t duk__json_stringify_fast_value(duk_json_enc_ctx *js_ctx, duk_tval *tv) {
+	duk_uint_fast32_t i, n;
+
+	DUK_DDD(DUK_DDDPRINT("stringify fast: %!T", tv));
+
+	DUK_ASSERT(js_ctx != NULL);
+	DUK_ASSERT(js_ctx->thr != NULL);
+#if defined(DUK_USE_JX)
+	DUK_ASSERT(js_ctx->flag_ext_custom == 0);
+#endif
+#if defined(DUK_USE_JC)
+	DUK_ASSERT(js_ctx->flag_ext_compatible == 0);
+#endif
+
+ restart_match:
+	DUK_ASSERT(tv != NULL);
+
+	switch (DUK_TVAL_GET_TAG(tv)) {
+	case DUK_TAG_UNDEFINED: {
+		goto emit_undefined;
+	}
+	case DUK_TAG_NULL: {
+		DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
+		break;
+	}
+	case DUK_TAG_BOOLEAN: {
+		DUK__EMIT_STRIDX(js_ctx, DUK_TVAL_GET_BOOLEAN(tv) ?
+		                 DUK_STRIDX_TRUE : DUK_STRIDX_FALSE);
+		break;
+	}
+	case DUK_TAG_STRING: {
+		duk_hstring *h;
+
+		h = DUK_TVAL_GET_STRING(tv);
+		DUK_ASSERT(h != NULL);
+		duk__enc_quote_string(js_ctx, h);
+		break;
+	}
+	case DUK_TAG_OBJECT: {
+		duk_hobject *obj;
+		duk_tval *tv_val;
+		duk_bool_t emitted = 0;
+		duk_uint32_t c_bit, c_all, c_array, c_unbox, c_undef, c_object;
+
+		/* For objects JSON.stringify() only looks for own, enumerable
+		 * properties which is nice for the fast path here.
+		 *
+		 * For arrays JSON.stringify() uses [[Get]] so it will actually
+		 * inherit properties during serialization!  This fast path
+		 * supports gappy arrays as long as there's no actual inherited
+		 * property (which might be a getter etc).
+		 *
+		 * Since recursion only happens for objects, we can have both
+		 * recursion and loop checks here.  We use a simple, depth-limited
+		 * loop check in the fast path because the object-based tracking
+		 * is very slow (when tested, it accounted for 50% of fast path
+		 * execution time for input data with a lot of small objects!).
+		 */
+
+		obj = DUK_TVAL_GET_OBJECT(tv);
+		DUK_ASSERT(obj != NULL);
+
+		/* We rely on a few object flag / class number relationships here,
+		 * assert for them.
+		 */
+		DUK_ASSERT_HOBJECT_VALID(obj);
+
+		/* Once recursion depth is increased, exit path must decrease
+		 * it (though it's OK to abort the fast path).
+		 */
+
+		DUK_ASSERT(js_ctx->recursion_depth >= 0);
+		DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
+		if (js_ctx->recursion_depth >= js_ctx->recursion_limit) {
+			DUK_DD(DUK_DDPRINT("fast path recursion limit"));
+			DUK_ERROR(js_ctx->thr, DUK_ERR_RANGE_ERROR, DUK_STR_JSONDEC_RECLIMIT);
+		}
+
+		for (i = 0, n = (duk_uint_fast32_t) js_ctx->recursion_depth; i < n; i++) {
+			if (js_ctx->visiting[i] == obj) {
+				DUK_DD(DUK_DDPRINT("fast path loop detect"));
+				DUK_ERROR(js_ctx->thr, DUK_ERR_TYPE_ERROR, DUK_STR_CYCLIC_INPUT);
+			}
+		}
+
+		/* Guaranteed by recursion_limit setup so we don't have to
+		 * check twice.
+		 */
+		DUK_ASSERT(js_ctx->recursion_depth < DUK_JSON_ENC_LOOPARRAY);
+		js_ctx->visiting[js_ctx->recursion_depth] = obj;
+		js_ctx->recursion_depth++;
+
+		/* If object has a .toJSON() property, we can't be certain
+		 * that it wouldn't mutate any value arbitrarily, so bail
+		 * out of the fast path.
+		 */
+		if (duk_hobject_hasprop_raw(js_ctx->thr, obj, DUK_HTHREAD_STRING_TO_JSON(js_ctx->thr))) {
+			DUK_DD(DUK_DDPRINT("object has a .toJSON property, abort fast path"));
+			goto abort_fastpath;
+		}
+
+		/* We could use a switch-case for the class number but it turns out
+		 * a small if-else ladder on class masks is better.  The if-ladder
+		 * should be in order of relevancy.
+		 */
+
+		DUK_ASSERT(DUK_HOBJECT_CLASS_MAX <= 31);
+		c_all = DUK_HOBJECT_CMASK_ALL;
+		c_array = DUK_HOBJECT_CMASK_ARRAY;
+		c_unbox = DUK_HOBJECT_CMASK_NUMBER |
+		          DUK_HOBJECT_CMASK_STRING |
+		          DUK_HOBJECT_CMASK_BOOLEAN;
+		c_undef = DUK_HOBJECT_CMASK_FUNCTION |
+		          DUK_HOBJECT_CMASK_ALL_BUFFEROBJECTS;
+		c_object = c_all & ~(c_array | c_unbox | c_undef);
+
+		c_bit = DUK_HOBJECT_GET_CLASS_MASK(obj);
+		if (c_bit & c_object) {
+			/* All other object types. */
+			DUK__EMIT_1(js_ctx, DUK_ASC_LCURLY);
+
+			/* A non-Array object should not have an array part in practice.
+			 * But since it is supported internally (and perhaps used at some
+			 * point), check and abandon if that's the case.
+			 */
+			if (DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
+				DUK_DD(DUK_DDPRINT("non-Array object has array part, abort fast path"));
+				goto abort_fastpath;
+			}
+
+			for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(obj); i++) {
+				duk_hstring *k;
+				duk_size_t prev_size;
+
+				k = DUK_HOBJECT_E_GET_KEY(js_ctx->thr->heap, obj, i);
+				if (!k) {
+					continue;
+				}
+				if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(js_ctx->thr->heap, obj, i)) {
+					continue;
+				}
+				if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(js_ctx->thr->heap, obj, i)) {
+					/* Getter might have arbitrary side effects,
+					 * so bail out.
+					 */
+					DUK_DD(DUK_DDPRINT("property is an accessor, abort fast path"));
+					goto abort_fastpath;
+				}
+				if (DUK_HSTRING_HAS_INTERNAL(k)) {
+					continue;
+				}
+
+				tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(js_ctx->thr->heap, obj, i);
+
+				prev_size = DUK_BW_GET_SIZE(js_ctx->thr, &js_ctx->bw);
+				duk__enc_quote_string(js_ctx, k);
+				DUK__EMIT_1(js_ctx, DUK_ASC_COLON);
+				if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
+					DUK_DD(DUK_DDPRINT("prop value not supported, rewind key and colon"));
+					DUK_BW_SET_SIZE(js_ctx->thr, &js_ctx->bw, prev_size);
+				} else {
+					DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
+					emitted = 1;
+				}
+			}
+
+			/* If any non-Array value had enumerable virtual own
+			 * properties, they should be serialized here.  Standard
+			 * types don't.
+			 */
+
+			if (emitted) {
+				DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
+			}
+			DUK__EMIT_1(js_ctx, DUK_ASC_RCURLY);
+		} else if (c_bit & c_array) {
+			duk_uint_fast32_t arr_len;
+			duk_uint_fast32_t asize;
+
+			DUK__EMIT_1(js_ctx, DUK_ASC_LBRACKET);
+
+			/* Assume arrays are dense in the fast path. */
+			if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
+				DUK_DD(DUK_DDPRINT("Array object is sparse, abort fast path"));
+				goto abort_fastpath;
+			}
+
+			arr_len = (duk_uint_fast32_t) duk_hobject_get_length(js_ctx->thr, obj);
+			asize = (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj);
+			if (arr_len > asize) {
+				/* Array length is larger than 'asize'.  This shouldn't
+				 * happen in practice.  Bail out just in case.
+				 */
+				DUK_DD(DUK_DDPRINT("arr_len > asize, abort fast path"));
+				goto abort_fastpath;
+			}
+			/* Array part may be larger than 'length'; if so, iterate
+			 * only up to array 'length'.
+			 */
+			for (i = 0; i < arr_len; i++) {
+				DUK_ASSERT(i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(obj));
+
+				tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(js_ctx->thr->heap, obj, i);
+
+				if (DUK_UNLIKELY(DUK_TVAL_IS_UNDEFINED_UNUSED(tv_val))) {
+					/* Gap in array; check for inherited property,
+					 * bail out if one exists.  This should be enough
+					 * to support gappy arrays for all practical code.
+					 */
+					duk_hstring *h_tmp;
+					duk_bool_t has_inherited;
+
+					/* XXX: refactor into an internal helper, pretty awkward */
+					duk_push_uint((duk_context *) js_ctx->thr, (duk_uint_t) i);
+					h_tmp = duk_to_hstring((duk_context *) js_ctx->thr, -1);
+					DUK_ASSERT(h_tmp != NULL);
+					has_inherited = duk_hobject_hasprop_raw(js_ctx->thr, obj, h_tmp);
+					duk_pop((duk_context *) js_ctx->thr);
+
+					if (has_inherited) {
+						DUK_D(DUK_DPRINT("gap in array, conflicting inherited property, abort fast path"));
+						goto abort_fastpath;
+					}
+
+					/* Ordinary gap, undefined encodes to 'null' in
+					 * standard JSON (and no JX/JC support here now).
+					 */
+					DUK_D(DUK_DPRINT("gap in array, no conflicting inherited property, remain on fast path"));
+					DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
+				} else {
+					if (duk__json_stringify_fast_value(js_ctx, tv_val) == 0) {
+						DUK__EMIT_STRIDX(js_ctx, DUK_STRIDX_LC_NULL);
+					}
+				}
+				DUK__EMIT_1(js_ctx, DUK_ASC_COMMA);
+				emitted = 1;
+			}
+
+			if (emitted) {
+				DUK__UNEMIT_1(js_ctx);  /* eat trailing comma */
+			}
+			DUK__EMIT_1(js_ctx, DUK_ASC_RBRACKET);
+		} else if (c_bit & c_unbox) {
+			/* These three boxed types are required to go through
+			 * automatic unboxing.  Rely on internal value being
+			 * sane (to avoid infinite recursion).
+			 */
+			duk_tval *tv_internal;
+
+			DUK_DD(DUK_DDPRINT("auto unboxing in fast path"));
+
+			tv_internal = duk_hobject_get_internal_value_tval_ptr(js_ctx->thr->heap, obj);
+			DUK_ASSERT(tv_internal != NULL);
+			DUK_ASSERT(DUK_TVAL_IS_STRING(tv_internal) ||
+			           DUK_TVAL_IS_NUMBER(tv_internal) ||
+			           DUK_TVAL_IS_BOOLEAN(tv_internal));
+
+			/* XXX: for JX/JC, special handling for Pointer, and Buffer? */
+			tv = tv_internal;
+			goto restart_match;
+		} else {
+			DUK_ASSERT((c_bit & c_undef) != 0);
+
+			/* Function objects are treated as "undefined" by JSON.
+			 *
+			 * The slow path replaces a buffer object automatically with
+			 * the binary data which then gets treated like "undefined".
+			 * Since we don't support buffers here now, treat as "undefined".
+			 */
+
+			/* Must decrease recursion depth before returning. */
+			DUK_ASSERT(js_ctx->recursion_depth > 0);
+			DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
+			js_ctx->recursion_depth--;
+			goto emit_undefined;
+		}
+
+		DUK_ASSERT(js_ctx->recursion_depth > 0);
+		DUK_ASSERT(js_ctx->recursion_depth <= js_ctx->recursion_limit);
+		js_ctx->recursion_depth--;
+		break;
+	}
+	case DUK_TAG_BUFFER: {
+		goto emit_undefined;
+	}
+	case DUK_TAG_POINTER: {
+		goto emit_undefined;
+	}
+	case DUK_TAG_LIGHTFUNC: {
+		/* A lightfunc might also inherit a .toJSON() so just bail out. */
+		DUK_DD(DUK_DDPRINT("value is a lightfunc, abort fast path"));
+		goto abort_fastpath;
+	}
+#if defined(DUK_USE_FASTINT)
+	case DUK_TAG_FASTINT: {
+		/* Number serialization has a significant impact relative to
+		 * other fast path code, so careful fast path for fastints.
+		 */
+		duk__enc_fastint_tval(js_ctx, tv);
+		break;
+	}
+#endif
+	default: {
+		/* XXX: A fast path for usual integers would be useful when
+		 * fastint support is not enabled.
+		 */
+		/* XXX: Stack discipline is annoying, could be changed in numconv. */
+		duk_push_tval((duk_context *) js_ctx->thr, tv);
+		duk__enc_double(js_ctx);
+		duk_pop((duk_context *) js_ctx->thr);
+
+#if 0
+		/* Could also rely on native sprintf(), but it will handle
+		 * values like NaN, Infinity, -0, exponent notation etc in
+		 * a JSON-incompatible way.
+		 */
+		duk_double_t d;
+		char buf[64];
+
+		DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
+		d = DUK_TVAL_GET_DOUBLE(tv);
+		DUK_SPRINTF(buf, "%lg", d);
+		DUK__EMIT_CSTR(js_ctx, buf);
+#endif
+	}
+	}
+	return 1;  /* not undefined */
+
+ emit_undefined:
+	return 0;  /* value was undefined/unsupported */
+
+ abort_fastpath:
+	/* Error message doesn't matter: the error is ignored anyway. */
+	DUK_DD(DUK_DDPRINT("aborting fast path"));
+	DUK_ERROR(js_ctx->thr, DUK_ERR_ERROR, DUK_STR_INTERNAL_ERROR);
+	return 0;  /* unreachable */
+}
+
+DUK_LOCAL duk_ret_t duk__json_stringify_fast(duk_context *ctx) {
+	duk_json_enc_ctx *js_ctx;
+
+	DUK_ASSERT(ctx != NULL);
+	js_ctx = (duk_json_enc_ctx *) duk_get_pointer(ctx, -2);
+	DUK_ASSERT(js_ctx != NULL);
+
+	if (duk__json_stringify_fast_value(js_ctx, duk_get_tval((duk_context *) (js_ctx->thr), -1)) == 0) {
+		DUK_DD(DUK_DDPRINT("top level value not supported, fail fast path"));
+		return DUK_RET_ERROR;  /* error message doesn't matter, ignored anyway */
+	}
+
+	return 0;
+}
+#endif  /* DUK_USE_JSON_STRINGIFY_FASTPATH */
+
 /*
 /*
  *  Top level wrappers
  *  Top level wrappers
  */
  */
@@ -32253,6 +32780,7 @@ void duk_bi_json_parse_helper(duk_context *ctx,
 	/* nothing now */
 	/* nothing now */
 #endif
 #endif
 	js_ctx->recursion_limit = DUK_JSON_DEC_RECURSION_LIMIT;
 	js_ctx->recursion_limit = DUK_JSON_DEC_RECURSION_LIMIT;
+	DUK_ASSERT(js_ctx->recursion_depth == 0);
 
 
 	/* Flag handling currently assumes that flags are consistent.  This is OK
 	/* Flag handling currently assumes that flags are consistent.  This is OK
 	 * because the call sites are now strictly controlled.
 	 * because the call sites are now strictly controlled.
@@ -32363,7 +32891,6 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
 	js_ctx->h_indent = NULL;
 	js_ctx->h_indent = NULL;
 #endif
 #endif
 	js_ctx->idx_proplist = -1;
 	js_ctx->idx_proplist = -1;
-	js_ctx->recursion_limit = DUK_JSON_ENC_RECURSION_LIMIT;
 
 
 	/* Flag handling currently assumes that flags are consistent.  This is OK
 	/* Flag handling currently assumes that flags are consistent.  This is OK
 	 * because the call sites are now strictly controlled.
 	 * because the call sites are now strictly controlled.
@@ -32534,6 +33061,76 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
 
 
 	/* [ ... buf loop (proplist) (gap) ] */
 	/* [ ... buf loop (proplist) (gap) ] */
 
 
+	/*
+	 *  Fast path: assume no mutation, iterate object property tables
+	 *  directly; bail out if that assumption doesn't hold.
+	 */
+
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+	/* For now fast path is limited to plain JSON (no JX/JC).  This would
+	 * be easy to fix but must go through value type handling in the fast
+	 * path.
+	 */
+	if (flags == 0 &&
+	    js_ctx->h_replacer == NULL &&
+	    js_ctx->idx_proplist == -1 &&
+	    js_ctx->h_gap == NULL &&
+	    js_ctx->h_indent == NULL) {
+		duk_int_t pcall_rc;
+#ifdef DUK_USE_MARK_AND_SWEEP
+		duk_small_uint_t prev_mark_and_sweep_base_flags;
+#endif
+
+		DUK_DD(DUK_DDPRINT("try JSON.stringify() fast path"));
+
+		/* Use recursion_limit to ensure we don't overwrite js_ctx->visiting[]
+		 * array so we don't need two counter checks in the fast path.  The
+		 * slow path has a much larger recursion limit which we'll use if
+		 * necessary.
+		 */
+		DUK_ASSERT(DUK_JSON_ENC_RECURSION_LIMIT >= DUK_JSON_ENC_LOOPARRAY);
+		js_ctx->recursion_limit = DUK_JSON_ENC_LOOPARRAY;
+		DUK_ASSERT(js_ctx->recursion_depth == 0);
+
+		/* Execute the fast path in a protected call.  If any error is thrown,
+		 * fall back to the slow path.  This includes e.g. recursion limit
+		 * because the fast path has a smaller recursion limit (and simpler,
+		 * limited loop detection).
+		 */
+
+		duk_push_pointer(ctx, (void *) js_ctx);
+		duk_dup(ctx, idx_value);
+
+#if defined(DUK_USE_MARK_AND_SWEEP)
+		/* 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 */
+#endif
+
+		pcall_rc = duk_safe_call(ctx, duk__json_stringify_fast, 2 /*nargs*/, 0 /*nret*/);
+
+#if defined(DUK_USE_MARK_AND_SWEEP)
+		thr->heap->mark_and_sweep_base_flags = prev_mark_and_sweep_base_flags;
+#endif
+		if (pcall_rc == DUK_EXEC_SUCCESS) {
+			DUK_DD(DUK_DDPRINT("fast path successful"));
+			DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw);
+			goto replace_finished;
+		}
+
+		/* We come here for actual aborts (like encountering .toJSON())
+		 * but also for recursion/loop errors.  Bufwriter size can be
+		 * kept because we'll probably need at least as much as we've
+		 * allocated so far.
+		 */
+		DUK_DD(DUK_DDPRINT("fast path failed, serialize using slow path instead"));
+		DUK_BW_RESET_SIZE(thr, &js_ctx->bw);
+		js_ctx->recursion_depth = 0;
+	}
+#endif
+
 	/*
 	/*
 	 *  Create wrapper object and serialize
 	 *  Create wrapper object and serialize
 	 */
 	 */
@@ -32558,6 +33155,8 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
 
 
 	/* [ ... buf loop (proplist) (gap) holder "" ] */
 	/* [ ... buf loop (proplist) (gap) holder "" ] */
 
 
+	js_ctx->recursion_limit = DUK_JSON_ENC_RECURSION_LIMIT;
+	DUK_ASSERT(js_ctx->recursion_depth == 0);
 	undef = duk__enc_value1(js_ctx, idx_holder);  /* [ ... holder key ] -> [ ... holder key val ] */
 	undef = duk__enc_value1(js_ctx, idx_holder);  /* [ ... holder key ] -> [ ... holder key val ] */
 
 
 	DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, "
 	DUK_DDD(DUK_DDDPRINT("after: flags=0x%08lx, loop=%!T, replacer=%!O, "
@@ -32583,16 +33182,16 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
 
 
 		duk__enc_value2(js_ctx);  /* [ ... key val ] -> [ ... ] */
 		duk__enc_value2(js_ctx);  /* [ ... key val ] -> [ ... ] */
 
 
-		DUK_BW_COMPACT(thr, &js_ctx->bw);
-		DUK_ASSERT(js_ctx->bw.buf != NULL);
-		duk_push_hbuffer(ctx, (duk_hbuffer *) js_ctx->bw.buf);
-		duk_to_string(ctx, -1);
+		DUK_BW_PUSH_AS_STRING(thr, &js_ctx->bw);
 	}
 	}
 
 
 	/* The stack has a variable shape here, so force it to the
 	/* The stack has a variable shape here, so force it to the
 	 * desired one explicitly.
 	 * desired one explicitly.
 	 */
 	 */
 
 
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
+ replace_finished:
+#endif
 	duk_replace(ctx, entry_top);
 	duk_replace(ctx, entry_top);
 	duk_set_top(ctx, entry_top + 1);
 	duk_set_top(ctx, entry_top + 1);
 
 
@@ -40144,7 +40743,7 @@ DUK_INTERNAL duk_uint_t duk_hbufferobject_clamp_bytelength(duk_hbufferobject *h_
 	DUK_ASSERT(h_bufobj != NULL);
 	DUK_ASSERT(h_bufobj != NULL);
 	DUK_ASSERT(h_bufobj->buf != NULL);
 	DUK_ASSERT(h_bufobj->buf != NULL);
 
 
-	buf_size = DUK_HBUFFER_GET_SIZE(h_bufobj->buf);
+	buf_size = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_bufobj->buf);
 	if (h_bufobj->offset > buf_size) {
 	if (h_bufobj->offset > buf_size) {
 		/* Slice starting point is beyond current length. */
 		/* Slice starting point is beyond current length. */
 		return 0;
 		return 0;
@@ -46643,7 +47242,7 @@ void duk__realloc_props(duk_hthread *thr,
 #ifdef DUK_USE_MARK_AND_SWEEP
 #ifdef DUK_USE_MARK_AND_SWEEP
 	prev_mark_and_sweep_base_flags = thr->heap->mark_and_sweep_base_flags;
 	prev_mark_and_sweep_base_flags = thr->heap->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_FINALIZERS |         /* avoid attempts to add/remove object keys */
 	        DUK_MS_FLAG_NO_OBJECT_COMPACTION;   /* avoid attempt to compact the current object */
 	        DUK_MS_FLAG_NO_OBJECT_COMPACTION;   /* avoid attempt to compact the current object */
 #endif
 #endif
 
 
@@ -74037,7 +74636,7 @@ DUK_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) {
 			tc1 = (duk__bi_compare(&nc_ctx->r, &nc_ctx->mm) <= (nc_ctx->low_ok ? 0 : -1));
 			tc1 = (duk__bi_compare(&nc_ctx->r, &nc_ctx->mm) <= (nc_ctx->low_ok ? 0 : -1));
 
 
 			duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 <- (+ r m+) */
 			duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp);  /* t1 <- (+ r m+) */
-			tc2 = (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (&nc_ctx->high_ok ? 0 : 1));
+			tc2 = (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1));
 
 
 			DUK_DDD(DUK_DDDPRINT("tc1=%ld, tc2=%ld", (long) tc1, (long) tc2));
 			DUK_DDD(DUK_DDDPRINT("tc1=%ld, tc2=%ld", (long) tc1, (long) tc2));
 		} else {
 		} else {

+ 28 - 19
Source/ThirdParty/Duktape/duktape.h

@@ -5,7 +5,7 @@
  *  include guard.  Other parts of the header are Duktape
  *  include guard.  Other parts of the header are Duktape
  *  internal and related to platform/compiler/feature detection.
  *  internal and related to platform/compiler/feature detection.
  *
  *
- *  Git commit b098f0423266f5dbfd7a67fc3a6146455df9b61c (v1.2.0-257-gb098f04-dirty).
+ *  Git commit 281739096bf0c73d5c494069b3146e80dcbbde58 (v1.2.0-289-g2817390).
  *
  *
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  See Duktape AUTHORS.rst and LICENSE.txt for copyright and
  *  licensing information.
  *  licensing information.
@@ -16,21 +16,21 @@
  *  ===============
  *  ===============
  *  Duktape license
  *  Duktape license
  *  ===============
  *  ===============
- *  
+ *
  *  (http://opensource.org/licenses/MIT)
  *  (http://opensource.org/licenses/MIT)
- *  
+ *
  *  Copyright (c) 2013-2015 by Duktape authors (see AUTHORS.rst)
  *  Copyright (c) 2013-2015 by Duktape authors (see AUTHORS.rst)
- *  
+ *
  *  Permission is hereby granted, free of charge, to any person obtaining a copy
  *  Permission is hereby granted, free of charge, to any person obtaining a copy
  *  of this software and associated documentation files (the "Software"), to deal
  *  of this software and associated documentation files (the "Software"), to deal
  *  in the Software without restriction, including without limitation the rights
  *  in the Software without restriction, including without limitation the rights
  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  *  copies of the Software, and to permit persons to whom the Software is
  *  copies of the Software, and to permit persons to whom the Software is
  *  furnished to do so, subject to the following conditions:
  *  furnished to do so, subject to the following conditions:
- *  
+ *
  *  The above copyright notice and this permission notice shall be included in
  *  The above copyright notice and this permission notice shall be included in
  *  all copies or substantial portions of the Software.
  *  all copies or substantial portions of the Software.
- *  
+ *
  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -45,35 +45,36 @@
  *  ===============
  *  ===============
  *  Duktape authors
  *  Duktape authors
  *  ===============
  *  ===============
- *  
+ *
  *  Copyright
  *  Copyright
  *  =========
  *  =========
- *  
+ *
  *  Duktape copyrights are held by its authors.  Each author has a copyright
  *  Duktape copyrights are held by its authors.  Each author has a copyright
  *  to their contribution, and agrees to irrevocably license the contribution
  *  to their contribution, and agrees to irrevocably license the contribution
  *  under the Duktape ``LICENSE.txt``.
  *  under the Duktape ``LICENSE.txt``.
- *  
+ *
  *  Authors
  *  Authors
  *  =======
  *  =======
- *  
+ *
  *  Please include an e-mail address, a link to your GitHub profile, or something
  *  Please include an e-mail address, a link to your GitHub profile, or something
  *  similar to allow your contribution to be identified accurately.
  *  similar to allow your contribution to be identified accurately.
- *  
+ *
  *  The following people have contributed code and agreed to irrevocably license
  *  The following people have contributed code and agreed to irrevocably license
  *  their contributions under the Duktape ``LICENSE.txt`` (in order of appearance):
  *  their contributions under the Duktape ``LICENSE.txt`` (in order of appearance):
- *  
+ *
  *  * Sami Vaarala <[email protected]>
  *  * Sami Vaarala <[email protected]>
  *  * Niki Dobrev
  *  * Niki Dobrev
  *  * Andreas \u00d6man <[email protected]>
  *  * Andreas \u00d6man <[email protected]>
  *  * L\u00e1szl\u00f3 Lang\u00f3 <[email protected]>
  *  * L\u00e1szl\u00f3 Lang\u00f3 <[email protected]>
  *  * Legimet <[email protected]>
  *  * Legimet <[email protected]>
- *  
+ *  * Karl Skomski <[email protected]>
+ *
  *  Other contributions
  *  Other contributions
  *  ===================
  *  ===================
- *  
+ *
  *  The following people have contributed something other than code (e.g. reported
  *  The following people have contributed something other than code (e.g. reported
  *  bugs, provided ideas, etc; roughly in order of appearance):
  *  bugs, provided ideas, etc; roughly in order of appearance):
- *  
+ *
  *  * Greg Burns
  *  * Greg Burns
  *  * Anthony Rabine
  *  * Anthony Rabine
  *  * Carlos Costa
  *  * Carlos Costa
@@ -102,7 +103,7 @@
  *  * https://github.com/Kelledin
  *  * https://github.com/Kelledin
  *  * https://github.com/sstruchtrup
  *  * https://github.com/sstruchtrup
  *  * Michael Drake (https://github.com/tlsa)
  *  * Michael Drake (https://github.com/tlsa)
- *  
+ *
  *  If you are accidentally missing from this list, send me an e-mail
  *  If you are accidentally missing from this list, send me an e-mail
  *  (``[email protected]``) and I'll fix the omission.
  *  (``[email protected]``) and I'll fix the omission.
  */
  */
@@ -114,8 +115,16 @@
 
 
 // ATOMIC BEGIN
 // ATOMIC BEGIN
 
 
-// ensure option is also enabled in Duktape's Makefile for building dist!
-#define DUK_OPT_NO_VOLUNTARY_GC
+// Disabling this 8/8/2015, if having problems with GC (objects being rescued
+// during finalization), reenable and make sure option is also enabled when
+// building Duktape's dist
+// #define DUK_OPT_NO_VOLUNTARY_GC
+
+// enable JSON string fast path which requires fast int
+// https://github.com/svaarala/duktape/issues/204
+#define DUK_USE_JSON_STRINGIFY_FASTPATH
+#define DUK_USE_FASTINT
+#define DUK_OPT_FASTINT
 
 
 // ATOMIC END
 // ATOMIC END
 
 
@@ -219,7 +228,7 @@ struct duk_number_list_entry {
  * so that application code can easily log which Duktape snapshot was used.
  * so that application code can easily log which Duktape snapshot was used.
  * Not available in the Ecmascript environment.
  * Not available in the Ecmascript environment.
  */
  */
-#define DUK_GIT_DESCRIBE                  "v1.2.0-257-gb098f04-dirty"
+#define DUK_GIT_DESCRIBE                  "v1.2.0-289-g2817390"
 
 
 /* Duktape debug protocol version used by this build. */
 /* Duktape debug protocol version used by this build. */
 #define DUK_DEBUG_PROTOCOL_VERSION        1
 #define DUK_DEBUG_PROTOCOL_VERSION        1