[njs] Fixed JSON.stringify() with Number() and String() objects.
Dmitry Volyntsev
xeioex at nginx.com
Wed Feb 26 14:49:01 UTC 2020
details: https://hg.nginx.org/njs/rev/061afad25256
branches:
changeset: 1338:061afad25256
user: Alexander Borisov <alexander.borisov at nginx.com>
date: Tue Feb 25 16:03:20 2020 +0300
description:
Fixed JSON.stringify() with Number() and String() objects.
According to the specification.
diffstat:
src/njs_json.c | 136 ++++++++++++++++++++++++++++++----------------
src/test/njs_unit_test.c | 58 +++++++++++++++----
2 files changed, 133 insertions(+), 61 deletions(-)
diffs (318 lines):
diff -r 112c7f54f402 -r 061afad25256 src/njs_json.c
--- a/src/njs_json.c Wed Feb 26 16:22:12 2020 +0300
+++ b/src/njs_json.c Tue Feb 25 16:03:20 2020 +0300
@@ -94,7 +94,8 @@ static njs_int_t njs_json_stringify_repl
static njs_int_t njs_json_stringify_array(njs_vm_t *vm,
njs_json_stringify_t *stringify);
-static void njs_json_append_value(njs_chb_t *chain, const njs_value_t *value);
+static njs_int_t njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain,
+ njs_value_t *value);
static void njs_json_append_string(njs_chb_t *chain, const njs_value_t *value,
char quote);
static void njs_json_append_number(njs_chb_t *chain, const njs_value_t *value);
@@ -188,7 +189,7 @@ njs_json_stringify(njs_vm_t *vm, njs_val
njs_index_t unused)
{
size_t length;
- double num;
+ int64_t i64;
njs_int_t i;
njs_int_t ret;
njs_value_t *replacer, *space;
@@ -217,45 +218,67 @@ njs_json_stringify(njs_vm_t *vm, njs_val
njs_set_undefined(&stringify->replacer);
}
- stringify->space.length = 0;
-
space = njs_arg(args, nargs, 3);
- if (njs_is_string(space) || njs_is_number(space)) {
- if (njs_is_string(space)) {
- length = njs_string_prop(&prop, space);
-
- if (njs_is_byte_string(&prop)) {
- njs_internal_error(vm, "space argument cannot be"
- " a byte string");
- return NJS_ERROR;
- }
-
- if (length > 10) {
- p = njs_string_offset(prop.start, prop.start + prop.size, 10);
-
- } else {
- p = prop.start + prop.size;
- }
-
- stringify->space.start = prop.start;
- stringify->space.length = p - prop.start;
+ switch (space->type) {
+ case NJS_OBJECT_STRING:
+ ret = njs_value_to_string(vm, space, space);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ /* Fall through. */
+
+ case NJS_STRING:
+ length = njs_string_prop(&prop, space);
+
+ if (njs_is_byte_string(&prop)) {
+ njs_internal_error(vm, "space argument cannot be"
+ " a byte string");
+ return NJS_ERROR;
+ }
+
+ if (length > 10) {
+ p = njs_string_offset(prop.start, prop.start + prop.size, 10);
} else {
- num = njs_number(space);
-
- if (!isnan(num) && !isinf(num) && num > 0) {
- num = njs_min(num, 10);
-
- stringify->space.length = (size_t) num;
- stringify->space.start = stringify->space_buf;
-
- for (i = 0; i < (int) num; i++) {
- stringify->space.start[i] = ' ';
- }
+ p = prop.start + prop.size;
+ }
+
+ stringify->space.start = prop.start;
+ stringify->space.length = p - prop.start;
+
+ break;
+
+ case NJS_OBJECT_NUMBER:
+ ret = njs_value_to_numeric(vm, space, space);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
+ /* Fall through. */
+
+ case NJS_NUMBER:
+ i64 = njs_min(njs_number_to_integer(njs_number(space)), 10);
+
+ if (i64 > 0) {
+ stringify->space.length = i64;
+ stringify->space.start = stringify->space_buf;
+
+ for (i = 0; i < i64; i++) {
+ stringify->space.start[i] = ' ';
}
+
+ break;
}
- }
+
+ /* Fall through. */
+
+ default:
+ stringify->space.length = 0;
+
+ break;
+ }
return njs_json_stringify_iterator(vm, stringify, njs_arg(args, nargs, 1));
@@ -1238,7 +1261,10 @@ njs_json_stringify_iterator(njs_vm_t *vm
break;
}
- njs_json_append_value(&chain, value);
+ ret = njs_json_append_value(vm, &chain, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
break;
@@ -1288,7 +1314,10 @@ njs_json_stringify_iterator(njs_vm_t *vm
}
state->written = 1;
- njs_json_append_value(&chain, value);
+ ret = njs_json_append_value(vm, &chain, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
break;
}
@@ -1471,10 +1500,6 @@ njs_json_stringify_array(njs_vm_t *vm, n
}
switch (value->type) {
- case NJS_OBJECT_NUMBER:
- value = njs_object_value(value);
- /* Fall through. */
-
case NJS_NUMBER:
ret = njs_number_to_string(vm, &num_value, value);
if (njs_slow_path(ret != NJS_OK)) {
@@ -1484,9 +1509,14 @@ njs_json_stringify_array(njs_vm_t *vm, n
value = &num_value;
break;
+ case NJS_OBJECT_NUMBER:
case NJS_OBJECT_STRING:
- value = njs_object_value(value);
- break;
+ ret = njs_value_to_string(vm, value, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ /* Fall through. */
case NJS_STRING:
break;
@@ -1513,12 +1543,18 @@ njs_json_stringify_array(njs_vm_t *vm, n
}
-static void
-njs_json_append_value(njs_chb_t *chain, const njs_value_t *value)
+static njs_int_t
+njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value)
{
+ njs_int_t ret;
+
switch (value->type) {
case NJS_OBJECT_STRING:
- value = njs_object_value(value);
+ ret = njs_value_to_string(vm, value, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
/* Fall through. */
case NJS_STRING:
@@ -1526,7 +1562,11 @@ njs_json_append_value(njs_chb_t *chain,
break;
case NJS_OBJECT_NUMBER:
- value = njs_object_value(value);
+ ret = njs_value_to_numeric(vm, value, value);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return ret;
+ }
+
/* Fall through. */
case NJS_NUMBER:
@@ -1555,6 +1595,8 @@ njs_json_append_value(njs_chb_t *chain,
default:
njs_chb_append_literal(chain, "null");
}
+
+ return NJS_OK;
}
diff -r 112c7f54f402 -r 061afad25256 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Wed Feb 26 16:22:12 2020 +0300
+++ b/src/test/njs_unit_test.c Tue Feb 25 16:03:20 2020 +0300
@@ -15454,6 +15454,10 @@ static njs_unit_test_t njs_test[] =
{ njs_str("JSON.stringify(new String('abc'))"),
njs_str("\"abc\"") },
+ { njs_str("var s = new String('abc'); s.toString = () => 'xxx'; "
+ "JSON.stringify(s)"),
+ njs_str("\"xxx\"") },
+
{ njs_str("JSON.stringify(123)"),
njs_str("123") },
@@ -15466,6 +15470,10 @@ static njs_unit_test_t njs_test[] =
{ njs_str("JSON.stringify(new Number(123))"),
njs_str("123") },
+ { njs_str("var n = new Number(8.5); n.valueOf = () => 42;"
+ "JSON.stringify(n)"),
+ njs_str("42") },
+
{ njs_str("JSON.stringify(true)"),
njs_str("true") },
@@ -15662,30 +15670,44 @@ static njs_unit_test_t njs_test[] =
{ njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, '#')"),
njs_str("[\n#{\n##\"a\": 1,\n##\"b\": {\n###\"c\": 2\n##}\n#},\n#1\n]") },
- { njs_str("JSON.stringify([1], undefined, 'AAAAABBBBBC')"),
+ { njs_str("JSON.stringify([1], null, 'AAAAABBBBBC')"),
+ njs_str("[\nAAAAABBBBB1\n]") },
+
+ { njs_str("var s = new String('A'); s.toString = () => 'AAAAABBBBBC';"
+ "JSON.stringify([1], null, s)"),
njs_str("[\nAAAAABBBBB1\n]") },
- { njs_str("JSON.stringify([1], undefined, 11)"),
+ { njs_str("JSON.stringify([1], null, '!βββββ').length"),
+ njs_str("11") },
+
+ { njs_str("JSON.stringify([1], null, 'ABC') === JSON.stringify([1], null, new String('ABC'))"),
+ njs_str("true") },
+
+ { njs_str("JSON.stringify([1], null, '!!βββββββββββββββββ').length"),
+ njs_str("15") },
+
+ { njs_str("JSON.stringify([1], null, String.bytesFrom([0x9d])).length"),
+ njs_str("InternalError: space argument cannot be a byte string") },
+
+ { njs_str("JSON.stringify([1], null, 11)"),
njs_str("[\n 1\n]") },
+ { njs_str("JSON.stringify([1], null, 5) === JSON.stringify([1], null, 5.9)"),
+ njs_str("true") },
+
+ { njs_str("JSON.stringify([1], null, 5) === JSON.stringify([1], null, new Number(5))"),
+ njs_str("true") },
+
+ { njs_str("var s = new Number(23); s.valueOf = () => 5;"
+ "JSON.stringify([1], null, s)"),
+ njs_str("[\n 1\n]") },
+
{ njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, -1)"),
njs_str("[{\"a\":1,\"b\":{\"c\":2}},1]") },
{ njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, new Date())"),
njs_str("[{\"a\":1,\"b\":{\"c\":2}},1]") },
- { njs_str("JSON.stringify([], null, '!βββββ').length"),
- njs_str("10") },
-
- { njs_str("JSON.stringify([], null, '!!βββββββββββββββββ').length"),
- njs_str("14") },
-
- { njs_str("JSON.stringify([], null, '!βββββββββββββββββ').length"),
- njs_str("14") },
-
- { njs_str("JSON.stringify([], null, String.bytesFrom([0x9d])).length"),
- njs_str("InternalError: space argument cannot be a byte string") },
-
{ njs_str("var o = Object.defineProperty({}, 'a', { get() { return ()=> 1}, enumerable: true });"
"JSON.stringify(o)"),
njs_str("{}") },
@@ -15806,6 +15828,14 @@ static njs_unit_test_t njs_test[] =
{ njs_str("JSON.stringify({'1':1,'2':2,'3':3}, [1, new Number(2)])"),
njs_str("{\"1\":1,\"2\":2}") },
+ { njs_str("var s = new String('str'); s.toString = () => 'xxx';"
+ "JSON.stringify({str:1,xxx:2}, [s])"),
+ njs_str("{\"xxx\":2}") },
+
+ { njs_str("var n = new String(123); n.toString = () => '42';"
+ "JSON.stringify({123:1,42:2}, [n])"),
+ njs_str("{\"42\":2}") },
+
{ njs_str("var objs = []; var o = JSON.stringify({a:1},"
" function(k, v) {objs.push(this); return v});"
"JSON.stringify(objs)"),
More information about the nginx-devel
mailing list