Skip to content

Commit 595e64e

Browse files
committed
patch 7.4.1279
Problem: jsonencode() is not producing strict JSON. Solution: Add jsencode() and jsdecode(). Make jsonencode() and jsondecode() strict.
1 parent 55fab43 commit 595e64e

12 files changed

Lines changed: 379 additions & 138 deletions

File tree

runtime/doc/channel.txt

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 06
1+
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 07
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -16,7 +16,7 @@ The Netbeans interface also uses a channel. |netbeans|
1616

1717
1. Demo |channel-demo|
1818
2. Opening a channel |channel-open|
19-
3. Using a JSON channel |channel-use|
19+
3. Using a JSON or JS channel |channel-use|
2020
4. Vim commands |channel-commands|
2121
5. Using a raw channel |channel-use|
2222
6. Job control |job-control|
@@ -77,6 +77,7 @@ To open a channel: >
7777

7878
"mode" can be: *channel-mode*
7979
"json" - Use JSON, see below; most convenient way. Default.
80+
"js" - Use JavaScript encoding, more efficient than JSON.
8081
"raw" - Use raw messages
8182

8283
*channel-callback*
@@ -86,7 +87,7 @@ message. Example: >
8687
func Handle(handle, msg)
8788
echo 'Received: ' . a:msg
8889
endfunc
89-
let handle = ch_open("localhost:8765", 'json', "Handle")
90+
let handle = ch_open("localhost:8765", {"callback": "Handle"})
9091
9192
"waittime" is the time to wait for the connection to be made in milliseconds.
9293
The default is zero, don't wait, which is useful if the server is supposed to
@@ -95,12 +96,12 @@ be running already. A negative number waits forever.
9596
"timeout" is the time to wait for a request when blocking, using
9697
ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
9798

98-
When "mode" is "json" the "msg" argument is the body of the received message,
99-
converted to Vim types.
99+
When "mode" is "json" or "js" the "msg" argument is the body of the received
100+
message, converted to Vim types.
100101
When "mode" is "raw" the "msg" argument is the whole message as a string.
101102

102-
When "mode" is "json" the "callback" is optional. When omitted it is only
103-
possible to receive a message after sending one.
103+
When "mode" is "json" or "js" the "callback" is optional. When omitted it is
104+
only possible to receive a message after sending one.
104105

105106
The handler can be added or changed later: >
106107
call ch_setcallback(handle, {callback})
@@ -123,12 +124,15 @@ If there is an error reading or writing a channel it will be closed.
123124
*E896* *E630* *E631*
124125

125126
==============================================================================
126-
3. Using a JSON channel *channel-use*
127+
3. Using a JSON or JS channel *channel-use*
127128

128129
If {mode} is "json" then a message can be sent synchronously like this: >
129130
let response = ch_sendexpr(handle, {expr})
130131
This awaits a response from the other side.
131132

133+
When {mode} is "js" this works the same, except that the messages use
134+
JavaScript encoding. See |jsencode()| for the difference.
135+
132136
To send a message, without handling a response: >
133137
call ch_sendexpr(handle, {expr}, 0)
134138
@@ -231,7 +235,8 @@ Here {number} is the same as what was in the request. Use a negative number
231235
to avoid confusion with message that Vim sends.
232236

233237
{result} is the result of the evaluation and is JSON encoded. If the
234-
evaluation fails it is the string "ERROR".
238+
evaluation fails or the result can't be encoded in JSON it is the string
239+
"ERROR".
235240

236241

237242
Command "expr" ~

runtime/doc/eval.txt

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,8 @@ job_start({command} [, {options}]) Job start a job
19561956
job_status({job}) String get the status of a job
19571957
job_stop({job} [, {how}]) Number stop a job
19581958
join( {list} [, {sep}]) String join {list} items into one String
1959+
jsdecode( {string}) any decode JS style JSON
1960+
jsencode( {expr}) String encode JS style JSON
19591961
jsondecode( {string}) any decode JSON
19601962
jsonencode( {expr}) String encode JSON
19611963
keys( {dict}) List keys in {dict}
@@ -2439,7 +2441,6 @@ bufwinnr({expr}) *bufwinnr()*
24392441
|:wincmd|.
24402442
Only deals with the current tab page.
24412443

2442-
24432444
byte2line({byte}) *byte2line()*
24442445
Return the line number that contains the character at byte
24452446
count {byte} in the current buffer. This includes the
@@ -2688,7 +2689,7 @@ ch_open({address} [, {argdict}]) *ch_open()*
26882689

26892690
If {argdict} is given it must be a |Dictionary|. The optional
26902691
items are:
2691-
mode "raw" or "json".
2692+
mode "raw", "js" or "json".
26922693
Default "json".
26932694
callback function to call for requests with a zero
26942695
sequence number. See |channel-callback|.
@@ -4381,17 +4382,33 @@ join({list} [, {sep}]) *join()*
43814382
converted into a string like with |string()|.
43824383
The opposite function is |split()|.
43834384

4385+
jsdecode({string}) *jsdecode()*
4386+
This is similar to |jsondecode()| with these differences:
4387+
- Object key names do not have to be in quotes.
4388+
- Empty items in an array (between two commas) are allowed and
4389+
result in v:none items.
4390+
4391+
jsencode({expr}) *jsencode()*
4392+
This is similar to |jsonencode()| with these differences:
4393+
- Object key names are not in quotes.
4394+
- v:none items in an array result in an empty item between
4395+
commas.
4396+
For example, the Vim object:
4397+
[1,v:none,{"one":1}],v:none ~
4398+
Will be encoded as:
4399+
[1,,{one:1},,] ~
4400+
While jsonencode() would produce:
4401+
[1,null,{"one":1},null] ~
4402+
This encoding is valid for JavaScript. It is more efficient
4403+
than JSON, especially when using an array with optional items.
4404+
4405+
43844406
jsondecode({string}) *jsondecode()*
43854407
This parses a JSON formatted string and returns the equivalent
43864408
in Vim values. See |jsonencode()| for the relation between
43874409
JSON and Vim values.
43884410
The decoding is permissive:
43894411
- A trailing comma in an array and object is ignored.
4390-
- An empty item in an array, two commas with nothing or white
4391-
space in between, results in v:none.
4392-
- When an object member name is not a string it is converted
4393-
to a string. E.g. the number 123 is used as the string
4394-
"123".
43954412
- More floating point numbers are recognized, e.g. "1." for
43964413
"1.0".
43974414
The result must be a valid Vim type:
@@ -4413,7 +4430,7 @@ jsonencode({expr}) *jsonencode()*
44134430
used recursively: {}
44144431
v:false "false"
44154432
v:true "true"
4416-
v:none nothing
4433+
v:none "null"
44174434
v:null "null"
44184435
Note that using v:none is permitted, although the JSON
44194436
standard does not allow empty items. This can be useful for

src/channel.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ typedef struct {
119119
char_u *ch_callback; /* function to call when a msg is not handled */
120120
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
121121

122-
int ch_json_mode; /* TRUE for a json channel */
122+
ch_mode_T ch_mode;
123123
jsonq_T ch_json_head; /* dummy node, header for circular queue */
124124

125125
int ch_timeout; /* request timeout in msec */
@@ -526,12 +526,12 @@ channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void))
526526
}
527527

528528
/*
529-
* Set the json mode of channel "idx" to TRUE or FALSE.
529+
* Set the json mode of channel "idx" to "ch_mode".
530530
*/
531531
void
532-
channel_set_json_mode(int idx, int json_mode)
532+
channel_set_json_mode(int idx, ch_mode_T ch_mode)
533533
{
534-
channels[idx].ch_json_mode = json_mode;
534+
channels[idx].ch_mode = ch_mode;
535535
}
536536

537537
/*
@@ -672,7 +672,8 @@ channel_parse_json(int ch_idx)
672672
js_read_T reader;
673673
typval_T listtv;
674674
jsonq_T *item;
675-
jsonq_T *head = &channels[ch_idx].ch_json_head;
675+
channel_T *channel = &channels[ch_idx];
676+
jsonq_T *head = &channel->ch_json_head;
676677
int ret;
677678

678679
if (channel_peek(ch_idx) == NULL)
@@ -685,7 +686,8 @@ channel_parse_json(int ch_idx)
685686
reader.js_fill = NULL;
686687
/* reader.js_fill = channel_fill; */
687688
reader.js_cookie = &ch_idx;
688-
ret = json_decode(&reader, &listtv);
689+
ret = json_decode(&reader, &listtv,
690+
channel->ch_mode == MODE_JS ? JSON_JS : 0);
689691
if (ret == OK)
690692
{
691693
/* Only accept the response when it is a list with at least two
@@ -854,22 +856,26 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
854856
typval_T *tv;
855857
typval_T err_tv;
856858
char_u *json = NULL;
859+
channel_T *channel = &channels[idx];
860+
int options = channel->ch_mode == MODE_JS ? JSON_JS : 0;
857861

858862
/* Don't pollute the display with errors. */
859863
++emsg_skip;
860864
tv = eval_expr(arg, NULL);
861865
if (is_eval)
862866
{
863867
if (tv != NULL)
864-
json = json_encode_nr_expr(arg3->vval.v_number, tv);
868+
json = json_encode_nr_expr(arg3->vval.v_number, tv,
869+
options);
865870
if (tv == NULL || (json != NULL && *json == NUL))
866871
{
867872
/* If evaluation failed or the result can't be encoded
868873
* then return the string "ERROR". */
869874
err_tv.v_type = VAR_STRING;
870875
err_tv.vval.v_string = (char_u *)"ERROR";
871876
tv = &err_tv;
872-
json = json_encode_nr_expr(arg3->vval.v_number, tv);
877+
json = json_encode_nr_expr(arg3->vval.v_number, tv,
878+
options);
873879
}
874880
if (json != NULL)
875881
{
@@ -900,13 +906,13 @@ may_invoke_callback(int idx)
900906
typval_T argv[3];
901907
int seq_nr = -1;
902908
channel_T *channel = &channels[idx];
903-
int json_mode = channel->ch_json_mode;
909+
ch_mode_T ch_mode = channel->ch_mode;
904910

905911
if (channel->ch_close_cb != NULL)
906912
/* this channel is handled elsewhere (netbeans) */
907913
return FALSE;
908914

909-
if (json_mode)
915+
if (ch_mode != MODE_RAW)
910916
{
911917
/* Get any json message in the queue. */
912918
if (channel_get_json(idx, -1, &listtv) == FAIL)

src/eval.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,8 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv);
628628
static void f_job_status(typval_T *argvars, typval_T *rettv);
629629
#endif
630630
static void f_join(typval_T *argvars, typval_T *rettv);
631+
static void f_jsdecode(typval_T *argvars, typval_T *rettv);
632+
static void f_jsencode(typval_T *argvars, typval_T *rettv);
631633
static void f_jsondecode(typval_T *argvars, typval_T *rettv);
632634
static void f_jsonencode(typval_T *argvars, typval_T *rettv);
633635
static void f_keys(typval_T *argvars, typval_T *rettv);
@@ -8206,6 +8208,8 @@ static struct fst
82068208
{"job_stop", 1, 1, f_job_stop},
82078209
#endif
82088210
{"join", 1, 2, f_join},
8211+
{"jsdecode", 1, 1, f_jsdecode},
8212+
{"jsencode", 1, 1, f_jsencode},
82098213
{"jsondecode", 1, 1, f_jsondecode},
82108214
{"jsonencode", 1, 1, f_jsonencode},
82118215
{"keys", 1, 1, f_keys},
@@ -9829,7 +9833,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
98299833
int port;
98309834
int waittime = 0;
98319835
int timeout = 2000;
9832-
int json_mode = TRUE;
9836+
ch_mode_T ch_mode = MODE_JSON;
98339837
int ch_idx;
98349838

98359839
/* default: fail */
@@ -9868,8 +9872,12 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
98689872
{
98699873
mode = get_dict_string(dict, (char_u *)"mode", FALSE);
98709874
if (STRCMP(mode, "raw") == 0)
9871-
json_mode = FALSE;
9872-
else if (STRCMP(mode, "json") != 0)
9875+
ch_mode = MODE_RAW;
9876+
else if (STRCMP(mode, "js") == 0)
9877+
ch_mode = MODE_JS;
9878+
else if (STRCMP(mode, "json") == 0)
9879+
ch_mode = MODE_JSON;
9880+
else
98739881
{
98749882
EMSG2(_(e_invarg2), mode);
98759883
return;
@@ -9891,7 +9899,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
98919899
ch_idx = channel_open((char *)address, port, waittime, NULL);
98929900
if (ch_idx >= 0)
98939901
{
9894-
channel_set_json_mode(ch_idx, json_mode);
9902+
channel_set_json_mode(ch_idx, ch_mode);
98959903
channel_set_timeout(ch_idx, timeout);
98969904
if (callback != NULL && *callback != NUL)
98979905
channel_set_callback(ch_idx, callback);
@@ -9946,7 +9954,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
99469954
rettv->vval.v_string = NULL;
99479955

99489956
id = channel_get_id();
9949-
text = json_encode_nr_expr(id, &argvars[1]);
9957+
text = json_encode_nr_expr(id, &argvars[1], 0);
99509958
if (text == NULL)
99519959
return;
99529960

@@ -14442,6 +14450,31 @@ f_join(typval_T *argvars, typval_T *rettv)
1444214450
rettv->vval.v_string = NULL;
1444314451
}
1444414452

14453+
/*
14454+
* "jsdecode()" function
14455+
*/
14456+
static void
14457+
f_jsdecode(typval_T *argvars, typval_T *rettv)
14458+
{
14459+
js_read_T reader;
14460+
14461+
reader.js_buf = get_tv_string(&argvars[0]);
14462+
reader.js_fill = NULL;
14463+
reader.js_used = 0;
14464+
if (json_decode_all(&reader, rettv, JSON_JS) != OK)
14465+
EMSG(_(e_invarg));
14466+
}
14467+
14468+
/*
14469+
* "jsencode()" function
14470+
*/
14471+
static void
14472+
f_jsencode(typval_T *argvars, typval_T *rettv)
14473+
{
14474+
rettv->v_type = VAR_STRING;
14475+
rettv->vval.v_string = json_encode(&argvars[0], JSON_JS);
14476+
}
14477+
1444514478
/*
1444614479
* "jsondecode()" function
1444714480
*/
@@ -14453,7 +14486,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
1445314486
reader.js_buf = get_tv_string(&argvars[0]);
1445414487
reader.js_fill = NULL;
1445514488
reader.js_used = 0;
14456-
if (json_decode_all(&reader, rettv) != OK)
14489+
if (json_decode_all(&reader, rettv, 0) != OK)
1445714490
EMSG(_(e_invarg));
1445814491
}
1445914492

@@ -14464,7 +14497,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
1446414497
f_jsonencode(typval_T *argvars, typval_T *rettv)
1446514498
{
1446614499
rettv->v_type = VAR_STRING;
14467-
rettv->vval.v_string = json_encode(&argvars[0]);
14500+
rettv->vval.v_string = json_encode(&argvars[0], 0);
1446814501
}
1446914502

1447014503
/*

0 commit comments

Comments
 (0)