|
|
@@ -1,6 +1,6 @@
|
|
|
/*
|
|
|
* 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
|
|
|
* licensing information.
|
|
|
@@ -61,6 +61,7 @@
|
|
|
* * Andreas \u00d6man <[email protected]>
|
|
|
* * L\u00e1szl\u00f3 Lang\u00f3 <[email protected]>
|
|
|
* * Legimet <[email protected]>
|
|
|
+* * Karl Skomski <[email protected]>
|
|
|
*
|
|
|
* Other contributions
|
|
|
* ===================
|
|
|
@@ -4538,7 +4539,7 @@ struct duk_bufwriter_ctx {
|
|
|
#define DUK_BW_GET_SIZE(thr,bw_ctx) \
|
|
|
((duk_size_t) ((bw_ctx)->p - (bw_ctx)->p_base))
|
|
|
#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); \
|
|
|
} while (0)
|
|
|
#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) \
|
|
|
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.
|
|
|
* Unsigned type cast is needed to avoid warnings about coercing
|
|
|
* 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_FLOAT32ARRAY 28
|
|
|
#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_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_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.
|
|
|
*/
|
|
|
@@ -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_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 */
|
|
|
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);
|
|
|
@@ -10638,10 +10721,10 @@ DUK_INTERNAL_DECL duk_small_int_t duk_unicode_re_is_wordchar(duk_codepoint_t cp)
|
|
|
#endif
|
|
|
|
|
|
/* 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 */
|
|
|
#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 */
|
|
|
#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. */
|
|
|
typedef struct {
|
|
|
duk_hthread *thr;
|
|
|
@@ -10675,6 +10763,9 @@ typedef struct {
|
|
|
duk_small_uint_t stridx_custom_posinf;
|
|
|
duk_small_uint_t stridx_custom_function;
|
|
|
#endif
|
|
|
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
|
|
|
+ duk_hobject *visiting[DUK_JSON_ENC_LOOPARRAY]; /* indexed by recursion_depth */
|
|
|
+#endif
|
|
|
} duk_json_enc_ctx;
|
|
|
|
|
|
typedef struct {
|
|
|
@@ -19417,7 +19508,7 @@ DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
|
|
|
h_bufobj->buf = h_val;
|
|
|
DUK_HBUFFER_INCREF(thr, h_val);
|
|
|
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->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;
|
|
|
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->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. */
|
|
|
|
|
|
if (length > str_len) {
|
|
|
- length = str_len;
|
|
|
+ length = (duk_uint_t) str_len;
|
|
|
}
|
|
|
|
|
|
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_this(ctx);
|
|
|
- duk_to_string(ctx, -1);
|
|
|
|
|
|
- /* [ ... this tracedata sep ToString(this) ] */
|
|
|
+ /* [ ... this tracedata sep this ] */
|
|
|
|
|
|
/* 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) {
|
|
|
return 0;
|
|
|
} 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*/);
|
|
|
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_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);
|
|
|
#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);
|
|
|
@@ -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 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 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
|
|
|
@@ -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))
|
|
|
#endif
|
|
|
#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_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);
|
|
|
}
|
|
|
|
|
|
+#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) \
|
|
|
(((duk_uint_fast32_t) (nybbles)) << 16) | \
|
|
|
(((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_small_uint_t dig;
|
|
|
|
|
|
+ DUK_UNREF(js_ctx);
|
|
|
+
|
|
|
/* Caller ensures space for at least DUK__JSON_MAX_ESC_LEN. */
|
|
|
|
|
|
/* 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);
|
|
|
}
|
|
|
|
|
|
+/* 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,
|
|
|
* 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 */
|
|
|
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 ] */
|
|
|
if (duk_has_prop(ctx, js_ctx->idx_loop)) {
|
|
|
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 */
|
|
|
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); /* -> [ ... ] */
|
|
|
|
|
|
/* 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)
|
|
|
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: {
|
|
|
/* 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;
|
|
|
}
|
|
|
}
|
|
|
@@ -32220,6 +32388,365 @@ DUK_LOCAL duk_bool_t duk__enc_allow_into_proplist(duk_tval *tv) {
|
|
|
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
|
|
|
*/
|
|
|
@@ -32253,6 +32780,7 @@ void duk_bi_json_parse_helper(duk_context *ctx,
|
|
|
/* nothing now */
|
|
|
#endif
|
|
|
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
|
|
|
* 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;
|
|
|
#endif
|
|
|
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
|
|
|
* 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) ] */
|
|
|
|
|
|
+ /*
|
|
|
+ * 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
|
|
|
*/
|
|
|
@@ -32558,6 +33155,8 @@ void duk_bi_json_stringify_helper(duk_context *ctx,
|
|
|
|
|
|
/* [ ... 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 ] */
|
|
|
|
|
|
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_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
|
|
|
* desired one explicitly.
|
|
|
*/
|
|
|
|
|
|
+#if defined(DUK_USE_JSON_STRINGIFY_FASTPATH)
|
|
|
+ replace_finished:
|
|
|
+#endif
|
|
|
duk_replace(ctx, entry_top);
|
|
|
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->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) {
|
|
|
/* Slice starting point is beyond current length. */
|
|
|
return 0;
|
|
|
@@ -46643,7 +47242,7 @@ void duk__realloc_props(duk_hthread *thr,
|
|
|
#ifdef DUK_USE_MARK_AND_SWEEP
|
|
|
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_FINALIZERS | /* avoid attempts to add/remove object keys */
|
|
|
DUK_MS_FLAG_NO_OBJECT_COMPACTION; /* avoid attempt to compact the current object */
|
|
|
#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));
|
|
|
|
|
|
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));
|
|
|
} else {
|