mirror of
https://github.com/bellard/quickjs.git
synced 2024-11-22 21:58:12 +08:00
Improve JSON.stringify
- changed error messages - clarify `toJSON` method usage - simplify boxed objects handling - for ECMA conformity, BigInt objects need a toJSON method in the prototype chain including boxed objects
This commit is contained in:
parent
ce6b6dcacd
commit
203fe2d539
82
quickjs.c
82
quickjs.c
@ -45088,7 +45088,7 @@ static JSValue json_parse_value(JSParseState *s)
|
|||||||
default:
|
default:
|
||||||
def_token:
|
def_token:
|
||||||
if (s->token.val == TOK_EOF) {
|
if (s->token.val == TOK_EOF) {
|
||||||
js_parse_error(s, "unexpected end of input");
|
js_parse_error(s, "Unexpected end of JSON input");
|
||||||
} else {
|
} else {
|
||||||
js_parse_error(s, "unexpected token: '%.*s'",
|
js_parse_error(s, "unexpected token: '%.*s'",
|
||||||
(int)(s->buf_ptr - s->token.ptr), s->token.ptr);
|
(int)(s->buf_ptr - s->token.ptr), s->token.ptr);
|
||||||
@ -45255,22 +45255,27 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
|
|||||||
JSValue v;
|
JSValue v;
|
||||||
JSValueConst args[2];
|
JSValueConst args[2];
|
||||||
|
|
||||||
if (JS_IsObject(val) ||
|
/* check for object.toJSON method */
|
||||||
JS_IsBigInt(ctx, val) /* XXX: probably useless */
|
/* ECMA specifies this is done only for Object and BigInt */
|
||||||
|
/* we do it for BigFloat and BigDecimal as an extension */
|
||||||
|
if (JS_IsObject(val) || JS_IsBigInt(ctx, val)
|
||||||
|
#ifdef CONFIG_BIGNUM
|
||||||
|
|| JS_IsBigFloat(val) || JS_IsBigDecimal(val)
|
||||||
|
#endif
|
||||||
) {
|
) {
|
||||||
JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
|
JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
|
||||||
if (JS_IsException(f))
|
if (JS_IsException(f))
|
||||||
|
goto exception;
|
||||||
|
if (JS_IsFunction(ctx, f)) {
|
||||||
|
v = JS_CallFree(ctx, f, val, 1, &key);
|
||||||
|
JS_FreeValue(ctx, val);
|
||||||
|
val = v;
|
||||||
|
if (JS_IsException(val))
|
||||||
goto exception;
|
goto exception;
|
||||||
if (JS_IsFunction(ctx, f)) {
|
} else {
|
||||||
v = JS_CallFree(ctx, f, val, 1, &key);
|
JS_FreeValue(ctx, f);
|
||||||
JS_FreeValue(ctx, val);
|
|
||||||
val = v;
|
|
||||||
if (JS_IsException(val))
|
|
||||||
goto exception;
|
|
||||||
} else {
|
|
||||||
JS_FreeValue(ctx, f);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!JS_IsUndefined(jsc->replacer_func)) {
|
if (!JS_IsUndefined(jsc->replacer_func)) {
|
||||||
args[0] = key;
|
args[0] = key;
|
||||||
@ -45289,12 +45294,13 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
|
|||||||
case JS_TAG_STRING:
|
case JS_TAG_STRING:
|
||||||
case JS_TAG_INT:
|
case JS_TAG_INT:
|
||||||
case JS_TAG_FLOAT64:
|
case JS_TAG_FLOAT64:
|
||||||
#ifdef CONFIG_BIGNUM
|
|
||||||
case JS_TAG_BIG_FLOAT:
|
|
||||||
#endif
|
|
||||||
case JS_TAG_BOOL:
|
case JS_TAG_BOOL:
|
||||||
case JS_TAG_NULL:
|
case JS_TAG_NULL:
|
||||||
case JS_TAG_BIG_INT:
|
case JS_TAG_BIG_INT:
|
||||||
|
#ifdef CONFIG_BIGNUM
|
||||||
|
case JS_TAG_BIG_FLOAT:
|
||||||
|
case JS_TAG_BIG_DECIMAL:
|
||||||
|
#endif
|
||||||
case JS_TAG_EXCEPTION:
|
case JS_TAG_EXCEPTION:
|
||||||
return val;
|
return val;
|
||||||
default:
|
default:
|
||||||
@ -45324,36 +45330,29 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
|
|||||||
tab = JS_UNDEFINED;
|
tab = JS_UNDEFINED;
|
||||||
prop = JS_UNDEFINED;
|
prop = JS_UNDEFINED;
|
||||||
|
|
||||||
switch (JS_VALUE_GET_NORM_TAG(val)) {
|
if (JS_IsObject(val)) {
|
||||||
case JS_TAG_OBJECT:
|
|
||||||
p = JS_VALUE_GET_OBJ(val);
|
p = JS_VALUE_GET_OBJ(val);
|
||||||
cl = p->class_id;
|
cl = p->class_id;
|
||||||
if (cl == JS_CLASS_STRING) {
|
if (cl == JS_CLASS_STRING) {
|
||||||
val = JS_ToStringFree(ctx, val);
|
val = JS_ToStringFree(ctx, val);
|
||||||
if (JS_IsException(val))
|
if (JS_IsException(val))
|
||||||
goto exception;
|
goto exception;
|
||||||
val = JS_ToQuotedStringFree(ctx, val);
|
goto concat_primitive;
|
||||||
if (JS_IsException(val))
|
|
||||||
goto exception;
|
|
||||||
return string_buffer_concat_value_free(jsc->b, val);
|
|
||||||
} else if (cl == JS_CLASS_NUMBER) {
|
} else if (cl == JS_CLASS_NUMBER) {
|
||||||
val = JS_ToNumberFree(ctx, val);
|
val = JS_ToNumberFree(ctx, val);
|
||||||
if (JS_IsException(val))
|
if (JS_IsException(val))
|
||||||
goto exception;
|
goto exception;
|
||||||
return string_buffer_concat_value_free(jsc->b, val);
|
goto concat_primitive;
|
||||||
} else if (cl == JS_CLASS_BOOLEAN) {
|
} else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT
|
||||||
ret = string_buffer_concat_value(jsc->b, p->u.object_data);
|
|
||||||
JS_FreeValue(ctx, val);
|
|
||||||
return ret;
|
|
||||||
} else
|
|
||||||
#ifdef CONFIG_BIGNUM
|
#ifdef CONFIG_BIGNUM
|
||||||
if (cl == JS_CLASS_BIG_FLOAT) {
|
|| cl == JS_CLASS_BIG_FLOAT
|
||||||
return string_buffer_concat_value_free(jsc->b, val);
|
|| cl == JS_CLASS_BIG_DECIMAL
|
||||||
} else
|
|
||||||
#endif
|
#endif
|
||||||
if (cl == JS_CLASS_BIG_INT) {
|
)
|
||||||
JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
|
{
|
||||||
goto exception;
|
/* This will thow the same error as for the primitive object */
|
||||||
|
set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
|
||||||
|
goto concat_primitive;
|
||||||
}
|
}
|
||||||
v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
|
v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
|
||||||
if (JS_IsException(v))
|
if (JS_IsException(v))
|
||||||
@ -45466,6 +45465,9 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
|
|||||||
JS_FreeValue(ctx, indent1);
|
JS_FreeValue(ctx, indent1);
|
||||||
JS_FreeValue(ctx, prop);
|
JS_FreeValue(ctx, prop);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
concat_primitive:
|
||||||
|
switch (JS_VALUE_GET_NORM_TAG(val)) {
|
||||||
case JS_TAG_STRING:
|
case JS_TAG_STRING:
|
||||||
val = JS_ToQuotedStringFree(ctx, val);
|
val = JS_ToQuotedStringFree(ctx, val);
|
||||||
if (JS_IsException(val))
|
if (JS_IsException(val))
|
||||||
@ -45477,15 +45479,17 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
|
|||||||
}
|
}
|
||||||
goto concat_value;
|
goto concat_value;
|
||||||
case JS_TAG_INT:
|
case JS_TAG_INT:
|
||||||
#ifdef CONFIG_BIGNUM
|
|
||||||
case JS_TAG_BIG_FLOAT:
|
|
||||||
#endif
|
|
||||||
case JS_TAG_BOOL:
|
case JS_TAG_BOOL:
|
||||||
case JS_TAG_NULL:
|
case JS_TAG_NULL:
|
||||||
concat_value:
|
concat_value:
|
||||||
return string_buffer_concat_value_free(jsc->b, val);
|
return string_buffer_concat_value_free(jsc->b, val);
|
||||||
case JS_TAG_BIG_INT:
|
case JS_TAG_BIG_INT:
|
||||||
JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
|
#ifdef CONFIG_BIGNUM
|
||||||
|
case JS_TAG_BIG_FLOAT:
|
||||||
|
case JS_TAG_BIG_DECIMAL:
|
||||||
|
#endif
|
||||||
|
/* reject big numbers: use toJSON method to override */
|
||||||
|
JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
|
||||||
goto exception;
|
goto exception;
|
||||||
default:
|
default:
|
||||||
JS_FreeValue(ctx, val);
|
JS_FreeValue(ctx, val);
|
||||||
|
Loading…
Reference in New Issue
Block a user