Skip to content

Commit f1b6ac7

Browse files
committed
patch 7.4.1407
Problem: json_encode() does not handle NaN and inf properly. (David Barnett) Solution: For JSON turn them into "null". For JS use "NaN" and "Infinity". Add isnan().
1 parent e16e5a9 commit f1b6ac7

4 files changed

Lines changed: 84 additions & 5 deletions

File tree

src/eval.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ static void f_insert(typval_T *argvars, typval_T *rettv);
628628
static void f_invert(typval_T *argvars, typval_T *rettv);
629629
static void f_isdirectory(typval_T *argvars, typval_T *rettv);
630630
static void f_islocked(typval_T *argvars, typval_T *rettv);
631+
#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
632+
static void f_isnan(typval_T *argvars, typval_T *rettv);
633+
#endif
631634
static void f_items(typval_T *argvars, typval_T *rettv);
632635
#ifdef FEAT_JOB
633636
# ifdef FEAT_CHANNEL
@@ -8320,6 +8323,9 @@ static struct fst
83208323
{"invert", 1, 1, f_invert},
83218324
{"isdirectory", 1, 1, f_isdirectory},
83228325
{"islocked", 1, 1, f_islocked},
8326+
#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
8327+
{"isnan", 1, 1, f_isnan},
8328+
#endif
83238329
{"items", 1, 1, f_items},
83248330
#ifdef FEAT_JOB
83258331
# ifdef FEAT_CHANNEL
@@ -14740,6 +14746,18 @@ f_islocked(typval_T *argvars, typval_T *rettv)
1474014746
clear_lval(&lv);
1474114747
}
1474214748

14749+
#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
14750+
/*
14751+
* "isnan()" function
14752+
*/
14753+
static void
14754+
f_isnan(typval_T *argvars, typval_T *rettv)
14755+
{
14756+
rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT
14757+
&& isnan(argvars[0].vval.v_float);
14758+
}
14759+
#endif
14760+
1474314761
static void dict_list(typval_T *argvars, typval_T *rettv, int what);
1474414762

1474514763
/*

src/json.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#include "vim.h"
1717

1818
#if defined(FEAT_EVAL) || defined(PROTO)
19+
20+
#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H)
21+
/* for isnan() and isinf() */
22+
# include <math.h>
23+
#endif
24+
1925
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
2026
static int json_decode_item(js_read_T *reader, typval_T *res, int options);
2127

@@ -267,8 +273,20 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
267273

268274
case VAR_FLOAT:
269275
#ifdef FEAT_FLOAT
270-
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float);
271-
ga_concat(gap, numbuf);
276+
# if defined(HAVE_MATH_H)
277+
if ((options & JSON_JS) && isnan(val->vval.v_float))
278+
ga_concat(gap, (char_u *)"NaN");
279+
else if ((options & JSON_JS) && isinf(val->vval.v_float))
280+
ga_concat(gap, (char_u *)"Infinity");
281+
else if (isnan(val->vval.v_float) || isinf(val->vval.v_float))
282+
ga_concat(gap, (char_u *)"null");
283+
else
284+
# endif
285+
{
286+
vim_snprintf((char *)numbuf, NUMBUFLEN, "%g",
287+
val->vval.v_float);
288+
ga_concat(gap, numbuf);
289+
}
272290
break;
273291
#endif
274292
case VAR_UNKNOWN:
@@ -720,9 +738,36 @@ json_decode_item(js_read_T *reader, typval_T *res, int options)
720738
}
721739
return OK;
722740
}
741+
#ifdef FEAT_FLOAT
742+
if (STRNICMP((char *)p, "NaN", 3) == 0)
743+
{
744+
reader->js_used += 3;
745+
if (res != NULL)
746+
{
747+
res->v_type = VAR_FLOAT;
748+
res->vval.v_float = 0.0 / 0.0;
749+
}
750+
return OK;
751+
}
752+
if (STRNICMP((char *)p, "Infinity", 8) == 0)
753+
{
754+
reader->js_used += 8;
755+
if (res != NULL)
756+
{
757+
res->v_type = VAR_FLOAT;
758+
res->vval.v_float = 1.0 / 0.0;
759+
}
760+
return OK;
761+
}
762+
#endif
723763
/* check for truncated name */
724764
len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
725-
if ((len < 5 && STRNICMP((char *)p, "false", len) == 0)
765+
if (
766+
(len < 5 && STRNICMP((char *)p, "false", len) == 0)
767+
#ifdef FEAT_FLOAT
768+
|| (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0)
769+
|| (len < 3 && STRNICMP((char *)p, "NaN", len) == 0)
770+
#endif
726771
|| (len < 4 && (STRNICMP((char *)p, "true", len) == 0
727772
|| STRNICMP((char *)p, "null", len) == 0)))
728773
return MAYBE;

src/testdir/test_json.vim

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ let s:jsonmb = '"s¢cĴgё"'
1616
let s:varmb = "s¢cĴgё"
1717
let s:jsonnr = '1234'
1818
let s:varnr = 1234
19-
let s:jsonfl = '12.34'
20-
let s:varfl = 12.34
19+
if has('float')
20+
let s:jsonfl = '12.34'
21+
let s:varfl = 12.34
22+
let s:jsoninf = 'null'
23+
let s:jsinf = 'Infinity'
24+
let s:varinf = 1.0 / 0.0
25+
let s:jsonnan = 'null'
26+
let s:jsnan = 'NaN'
27+
let s:varnan = 0.0 / 0.0
28+
endif
2129

2230
let s:jsonl1 = '[1,"a",3]'
2331
let s:varl1 = [1, "a", 3]
@@ -68,6 +76,8 @@ func Test_json_encode()
6876
call assert_equal(s:jsonnr, json_encode(s:varnr))
6977
if has('float')
7078
call assert_equal(s:jsonfl, json_encode(s:varfl))
79+
call assert_equal(s:jsoninf, json_encode(s:varinf))
80+
call assert_equal(s:jsonnan, json_encode(s:varnan))
7181
endif
7282

7383
call assert_equal(s:jsonl1, json_encode(s:varl1))
@@ -165,6 +175,8 @@ func Test_js_encode()
165175
call assert_equal(s:jsonnr, js_encode(s:varnr))
166176
if has('float')
167177
call assert_equal(s:jsonfl, js_encode(s:varfl))
178+
call assert_equal(s:jsinf, js_encode(s:varinf))
179+
call assert_equal(s:jsnan, js_encode(s:varnan))
168180
endif
169181

170182
call assert_equal(s:jsonl1, js_encode(s:varl1))
@@ -201,6 +213,8 @@ func Test_js_decode()
201213
call assert_equal(s:varnr, js_decode(s:jsonnr))
202214
if has('float')
203215
call assert_equal(s:varfl, js_decode(s:jsonfl))
216+
call assert_equal(s:varinf, js_decode(s:jsinf))
217+
call assert_true(isnan(js_decode(s:jsnan)))
204218
endif
205219

206220
call assert_equal(s:varl1, js_decode(s:jsonl1))

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,8 @@ static char *(features[]) =
748748

749749
static int included_patches[] =
750750
{ /* Add new patch number below this line */
751+
/**/
752+
1407,
751753
/**/
752754
1406,
753755
/**/

0 commit comments

Comments
 (0)