# HG changeset patch # User Christian Brabandt # Date 1456259408 -3600 # Node ID a0e552c51c347cd3ec4c9ae829ad02dd9b0cbf9e # Parent f26bb0f9a8b91bd49313900d0809d3c38e4485d9 commit https://github.com/vim/vim/commit/f1b6ac72293e658bb6e68c5cfd926c405b1b6f34 Author: Bram Moolenaar Date: Tue Feb 23 21:26:43 2016 +0100 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(). diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -628,6 +628,9 @@ static void f_insert(typval_T *argvars, static void f_invert(typval_T *argvars, typval_T *rettv); static void f_isdirectory(typval_T *argvars, typval_T *rettv); static void f_islocked(typval_T *argvars, typval_T *rettv); +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +static void f_isnan(typval_T *argvars, typval_T *rettv); +#endif static void f_items(typval_T *argvars, typval_T *rettv); #ifdef FEAT_JOB # ifdef FEAT_CHANNEL @@ -8320,6 +8323,9 @@ static struct fst {"invert", 1, 1, f_invert}, {"isdirectory", 1, 1, f_isdirectory}, {"islocked", 1, 1, f_islocked}, +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) + {"isnan", 1, 1, f_isnan}, +#endif {"items", 1, 1, f_items}, #ifdef FEAT_JOB # ifdef FEAT_CHANNEL @@ -14740,6 +14746,18 @@ f_islocked(typval_T *argvars, typval_T * clear_lval(&lv); } +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) +/* + * "isnan()" function + */ + static void +f_isnan(typval_T *argvars, typval_T *rettv) +{ + rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT + && isnan(argvars[0].vval.v_float); +} +#endif + static void dict_list(typval_T *argvars, typval_T *rettv, int what); /* diff --git a/src/json.c b/src/json.c --- a/src/json.c +++ b/src/json.c @@ -16,6 +16,12 @@ #include "vim.h" #if defined(FEAT_EVAL) || defined(PROTO) + +#if defined(FEAT_FLOAT) && defined(HAVE_MATH_H) + /* for isnan() and isinf() */ +# include +#endif + static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options); static int json_decode_item(js_read_T *reader, typval_T *res, int options); @@ -267,8 +273,20 @@ json_encode_item(garray_T *gap, typval_T case VAR_FLOAT: #ifdef FEAT_FLOAT - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float); - ga_concat(gap, numbuf); +# if defined(HAVE_MATH_H) + if ((options & JSON_JS) && isnan(val->vval.v_float)) + ga_concat(gap, (char_u *)"NaN"); + else if ((options & JSON_JS) && isinf(val->vval.v_float)) + ga_concat(gap, (char_u *)"Infinity"); + else if (isnan(val->vval.v_float) || isinf(val->vval.v_float)) + ga_concat(gap, (char_u *)"null"); + else +# endif + { + vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", + val->vval.v_float); + ga_concat(gap, numbuf); + } break; #endif case VAR_UNKNOWN: @@ -720,9 +738,36 @@ json_decode_item(js_read_T *reader, typv } return OK; } +#ifdef FEAT_FLOAT + if (STRNICMP((char *)p, "NaN", 3) == 0) + { + reader->js_used += 3; + if (res != NULL) + { + res->v_type = VAR_FLOAT; + res->vval.v_float = 0.0 / 0.0; + } + return OK; + } + if (STRNICMP((char *)p, "Infinity", 8) == 0) + { + reader->js_used += 8; + if (res != NULL) + { + res->v_type = VAR_FLOAT; + res->vval.v_float = 1.0 / 0.0; + } + return OK; + } +#endif /* check for truncated name */ len = (int)(reader->js_end - (reader->js_buf + reader->js_used)); - if ((len < 5 && STRNICMP((char *)p, "false", len) == 0) + if ( + (len < 5 && STRNICMP((char *)p, "false", len) == 0) +#ifdef FEAT_FLOAT + || (len < 8 && STRNICMP((char *)p, "Infinity", len) == 0) + || (len < 3 && STRNICMP((char *)p, "NaN", len) == 0) +#endif || (len < 4 && (STRNICMP((char *)p, "true", len) == 0 || STRNICMP((char *)p, "null", len) == 0))) return MAYBE; diff --git a/src/testdir/test_json.vim b/src/testdir/test_json.vim --- a/src/testdir/test_json.vim +++ b/src/testdir/test_json.vim @@ -16,8 +16,16 @@ let s:jsonmb = '"s¢cĴgё"' let s:varmb = "s¢cĴgё" let s:jsonnr = '1234' let s:varnr = 1234 -let s:jsonfl = '12.34' -let s:varfl = 12.34 +if has('float') + let s:jsonfl = '12.34' + let s:varfl = 12.34 + let s:jsoninf = 'null' + let s:jsinf = 'Infinity' + let s:varinf = 1.0 / 0.0 + let s:jsonnan = 'null' + let s:jsnan = 'NaN' + let s:varnan = 0.0 / 0.0 +endif let s:jsonl1 = '[1,"a",3]' let s:varl1 = [1, "a", 3] @@ -68,6 +76,8 @@ func Test_json_encode() call assert_equal(s:jsonnr, json_encode(s:varnr)) if has('float') call assert_equal(s:jsonfl, json_encode(s:varfl)) + call assert_equal(s:jsoninf, json_encode(s:varinf)) + call assert_equal(s:jsonnan, json_encode(s:varnan)) endif call assert_equal(s:jsonl1, json_encode(s:varl1)) @@ -165,6 +175,8 @@ func Test_js_encode() call assert_equal(s:jsonnr, js_encode(s:varnr)) if has('float') call assert_equal(s:jsonfl, js_encode(s:varfl)) + call assert_equal(s:jsinf, js_encode(s:varinf)) + call assert_equal(s:jsnan, js_encode(s:varnan)) endif call assert_equal(s:jsonl1, js_encode(s:varl1)) @@ -201,6 +213,8 @@ func Test_js_decode() call assert_equal(s:varnr, js_decode(s:jsonnr)) if has('float') call assert_equal(s:varfl, js_decode(s:jsonfl)) + call assert_equal(s:varinf, js_decode(s:jsinf)) + call assert_true(isnan(js_decode(s:jsnan))) endif call assert_equal(s:varl1, js_decode(s:jsonl1)) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1407, +/**/ 1406, /**/ 1405,