From: Alexander Borisov Date: Tue, 25 Feb 2020 13:03:20 +0000 (+0300) Subject: Fixed JSON.stringify() with Number() and String() objects. X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=3ad1529dd1ead43076a893a1929a27ed9cb9949a;p=njs.git Fixed JSON.stringify() with Number() and String() objects. According to the specification. --- diff --git a/src/njs_json.c b/src/njs_json.c index 2f8e86c4..a7df7cd4 100644 --- a/src/njs_json.c +++ b/src/njs_json.c @@ -94,7 +94,8 @@ static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify, 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_value_t *args, njs_uint_t nargs, 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_value_t *args, njs_uint_t nargs, 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); + switch (space->type) { + case NJS_OBJECT_STRING: + ret = njs_value_to_string(vm, space, space); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - if (njs_is_byte_string(&prop)) { - njs_internal_error(vm, "space argument cannot be" - " a byte string"); - return NJS_ERROR; - } + /* Fall through. */ - if (length > 10) { - p = njs_string_offset(prop.start, prop.start + prop.size, 10); + case NJS_STRING: + length = njs_string_prop(&prop, space); - } else { - p = prop.start + prop.size; - } + if (njs_is_byte_string(&prop)) { + njs_internal_error(vm, "space argument cannot be" + " a byte string"); + return NJS_ERROR; + } - stringify->space.start = prop.start; - stringify->space.length = p - prop.start; + if (length > 10) { + p = njs_string_offset(prop.start, prop.start + prop.size, 10); } else { - num = njs_number(space); + p = prop.start + prop.size; + } - if (!isnan(num) && !isinf(num) && num > 0) { - num = njs_min(num, 10); + stringify->space.start = prop.start; + stringify->space.length = p - prop.start; - stringify->space.length = (size_t) num; - stringify->space.start = stringify->space_buf; + break; - for (i = 0; i < (int) num; i++) { - stringify->space.start[i] = ' '; - } + 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, njs_json_stringify_t *stringify, 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, njs_json_stringify_t *stringify, } 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, njs_json_stringify_t *stringify) } 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, njs_json_stringify_t *stringify) 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, 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) { + 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, const njs_value_t *value) 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, const njs_value_t *value) default: njs_chb_append_literal(chain, "null"); } + + return NJS_OK; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 0b5b9037..9fbfe39c 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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("JSON.stringify([1], undefined, 11)"), + { njs_str("var s = new String('A'); s.toString = () => 'AAAAABBBBBC';" + "JSON.stringify([1], null, s)"), + njs_str("[\nAAAAABBBBB1\n]") }, + + { 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)"),