1
0
forked from aniani/vim

patch 7.4.1279

Problem:    jsonencode() is not producing strict JSON.
Solution:   Add jsencode() and jsdecode().  Make jsonencode() and jsondecode()
            strict.
This commit is contained in:
Bram Moolenaar
2016-02-07 19:19:53 +01:00
parent 55fab439a6
commit 595e64e259
12 changed files with 379 additions and 138 deletions

View File

@@ -1,4 +1,4 @@
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 06
*channel.txt* For Vim version 7.4. Last change: 2016 Feb 07
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -16,7 +16,7 @@ The Netbeans interface also uses a channel. |netbeans|
1. Demo |channel-demo|
2. Opening a channel |channel-open|
3. Using a JSON channel |channel-use|
3. Using a JSON or JS channel |channel-use|
4. Vim commands |channel-commands|
5. Using a raw channel |channel-use|
6. Job control |job-control|
@@ -77,6 +77,7 @@ To open a channel: >
"mode" can be: *channel-mode*
"json" - Use JSON, see below; most convenient way. Default.
"js" - Use JavaScript encoding, more efficient than JSON.
"raw" - Use raw messages
*channel-callback*
@@ -86,7 +87,7 @@ message. Example: >
func Handle(handle, msg)
echo 'Received: ' . a:msg
endfunc
let handle = ch_open("localhost:8765", 'json', "Handle")
let handle = ch_open("localhost:8765", {"callback": "Handle"})
"waittime" is the time to wait for the connection to be made in milliseconds.
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.
"timeout" is the time to wait for a request when blocking, using
ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
When "mode" is "json" the "msg" argument is the body of the received message,
converted to Vim types.
When "mode" is "json" or "js" the "msg" argument is the body of the received
message, converted to Vim types.
When "mode" is "raw" the "msg" argument is the whole message as a string.
When "mode" is "json" the "callback" is optional. When omitted it is only
possible to receive a message after sending one.
When "mode" is "json" or "js" the "callback" is optional. When omitted it is
only possible to receive a message after sending one.
The handler can be added or changed later: >
call ch_setcallback(handle, {callback})
@@ -123,12 +124,15 @@ If there is an error reading or writing a channel it will be closed.
*E896* *E630* *E631*
==============================================================================
3. Using a JSON channel *channel-use*
3. Using a JSON or JS channel *channel-use*
If {mode} is "json" then a message can be sent synchronously like this: >
let response = ch_sendexpr(handle, {expr})
This awaits a response from the other side.
When {mode} is "js" this works the same, except that the messages use
JavaScript encoding. See |jsencode()| for the difference.
To send a message, without handling a response: >
call ch_sendexpr(handle, {expr}, 0)
@@ -231,7 +235,8 @@ Here {number} is the same as what was in the request. Use a negative number
to avoid confusion with message that Vim sends.
{result} is the result of the evaluation and is JSON encoded. If the
evaluation fails it is the string "ERROR".
evaluation fails or the result can't be encoded in JSON it is the string
"ERROR".
Command "expr" ~

View File

@@ -1956,6 +1956,8 @@ job_start({command} [, {options}]) Job start a job
job_status({job}) String get the status of a job
job_stop({job} [, {how}]) Number stop a job
join( {list} [, {sep}]) String join {list} items into one String
jsdecode( {string}) any decode JS style JSON
jsencode( {expr}) String encode JS style JSON
jsondecode( {string}) any decode JSON
jsonencode( {expr}) String encode JSON
keys( {dict}) List keys in {dict}
@@ -2439,7 +2441,6 @@ bufwinnr({expr}) *bufwinnr()*
|:wincmd|.
Only deals with the current tab page.
byte2line({byte}) *byte2line()*
Return the line number that contains the character at byte
count {byte} in the current buffer. This includes the
@@ -2688,7 +2689,7 @@ ch_open({address} [, {argdict}]) *ch_open()*
If {argdict} is given it must be a |Dictionary|. The optional
items are:
mode "raw" or "json".
mode "raw", "js" or "json".
Default "json".
callback function to call for requests with a zero
sequence number. See |channel-callback|.
@@ -4381,17 +4382,33 @@ join({list} [, {sep}]) *join()*
converted into a string like with |string()|.
The opposite function is |split()|.
jsdecode({string}) *jsdecode()*
This is similar to |jsondecode()| with these differences:
- Object key names do not have to be in quotes.
- Empty items in an array (between two commas) are allowed and
result in v:none items.
jsencode({expr}) *jsencode()*
This is similar to |jsonencode()| with these differences:
- Object key names are not in quotes.
- v:none items in an array result in an empty item between
commas.
For example, the Vim object:
[1,v:none,{"one":1}],v:none ~
Will be encoded as:
[1,,{one:1},,] ~
While jsonencode() would produce:
[1,null,{"one":1},null] ~
This encoding is valid for JavaScript. It is more efficient
than JSON, especially when using an array with optional items.
jsondecode({string}) *jsondecode()*
This parses a JSON formatted string and returns the equivalent
in Vim values. See |jsonencode()| for the relation between
JSON and Vim values.
The decoding is permissive:
- A trailing comma in an array and object is ignored.
- An empty item in an array, two commas with nothing or white
space in between, results in v:none.
- When an object member name is not a string it is converted
to a string. E.g. the number 123 is used as the string
"123".
- More floating point numbers are recognized, e.g. "1." for
"1.0".
The result must be a valid Vim type:
@@ -4413,7 +4430,7 @@ jsonencode({expr}) *jsonencode()*
used recursively: {}
v:false "false"
v:true "true"
v:none nothing
v:none "null"
v:null "null"
Note that using v:none is permitted, although the JSON
standard does not allow empty items. This can be useful for

View File

@@ -119,7 +119,7 @@ typedef struct {
char_u *ch_callback; /* function to call when a msg is not handled */
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
int ch_json_mode; /* TRUE for a json channel */
ch_mode_T ch_mode;
jsonq_T ch_json_head; /* dummy node, header for circular queue */
int ch_timeout; /* request timeout in msec */
@@ -526,12 +526,12 @@ channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void))
}
/*
* Set the json mode of channel "idx" to TRUE or FALSE.
* Set the json mode of channel "idx" to "ch_mode".
*/
void
channel_set_json_mode(int idx, int json_mode)
channel_set_json_mode(int idx, ch_mode_T ch_mode)
{
channels[idx].ch_json_mode = json_mode;
channels[idx].ch_mode = ch_mode;
}
/*
@@ -672,7 +672,8 @@ channel_parse_json(int ch_idx)
js_read_T reader;
typval_T listtv;
jsonq_T *item;
jsonq_T *head = &channels[ch_idx].ch_json_head;
channel_T *channel = &channels[ch_idx];
jsonq_T *head = &channel->ch_json_head;
int ret;
if (channel_peek(ch_idx) == NULL)
@@ -685,7 +686,8 @@ channel_parse_json(int ch_idx)
reader.js_fill = NULL;
/* reader.js_fill = channel_fill; */
reader.js_cookie = &ch_idx;
ret = json_decode(&reader, &listtv);
ret = json_decode(&reader, &listtv,
channel->ch_mode == MODE_JS ? JSON_JS : 0);
if (ret == OK)
{
/* Only accept the response when it is a list with at least two
@@ -854,6 +856,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
typval_T *tv;
typval_T err_tv;
char_u *json = NULL;
channel_T *channel = &channels[idx];
int options = channel->ch_mode == MODE_JS ? JSON_JS : 0;
/* Don't pollute the display with errors. */
++emsg_skip;
@@ -861,7 +865,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
if (is_eval)
{
if (tv != NULL)
json = json_encode_nr_expr(arg3->vval.v_number, tv);
json = json_encode_nr_expr(arg3->vval.v_number, tv,
options);
if (tv == NULL || (json != NULL && *json == NUL))
{
/* If evaluation failed or the result can't be encoded
@@ -869,7 +874,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3)
err_tv.v_type = VAR_STRING;
err_tv.vval.v_string = (char_u *)"ERROR";
tv = &err_tv;
json = json_encode_nr_expr(arg3->vval.v_number, tv);
json = json_encode_nr_expr(arg3->vval.v_number, tv,
options);
}
if (json != NULL)
{
@@ -900,13 +906,13 @@ may_invoke_callback(int idx)
typval_T argv[3];
int seq_nr = -1;
channel_T *channel = &channels[idx];
int json_mode = channel->ch_json_mode;
ch_mode_T ch_mode = channel->ch_mode;
if (channel->ch_close_cb != NULL)
/* this channel is handled elsewhere (netbeans) */
return FALSE;
if (json_mode)
if (ch_mode != MODE_RAW)
{
/* Get any json message in the queue. */
if (channel_get_json(idx, -1, &listtv) == FAIL)

View File

@@ -628,6 +628,8 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv);
static void f_job_status(typval_T *argvars, typval_T *rettv);
#endif
static void f_join(typval_T *argvars, typval_T *rettv);
static void f_jsdecode(typval_T *argvars, typval_T *rettv);
static void f_jsencode(typval_T *argvars, typval_T *rettv);
static void f_jsondecode(typval_T *argvars, typval_T *rettv);
static void f_jsonencode(typval_T *argvars, typval_T *rettv);
static void f_keys(typval_T *argvars, typval_T *rettv);
@@ -8206,6 +8208,8 @@ static struct fst
{"job_stop", 1, 1, f_job_stop},
#endif
{"join", 1, 2, f_join},
{"jsdecode", 1, 1, f_jsdecode},
{"jsencode", 1, 1, f_jsencode},
{"jsondecode", 1, 1, f_jsondecode},
{"jsonencode", 1, 1, f_jsonencode},
{"keys", 1, 1, f_keys},
@@ -9829,7 +9833,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
int port;
int waittime = 0;
int timeout = 2000;
int json_mode = TRUE;
ch_mode_T ch_mode = MODE_JSON;
int ch_idx;
/* default: fail */
@@ -9868,8 +9872,12 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
{
mode = get_dict_string(dict, (char_u *)"mode", FALSE);
if (STRCMP(mode, "raw") == 0)
json_mode = FALSE;
else if (STRCMP(mode, "json") != 0)
ch_mode = MODE_RAW;
else if (STRCMP(mode, "js") == 0)
ch_mode = MODE_JS;
else if (STRCMP(mode, "json") == 0)
ch_mode = MODE_JSON;
else
{
EMSG2(_(e_invarg2), mode);
return;
@@ -9891,7 +9899,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv)
ch_idx = channel_open((char *)address, port, waittime, NULL);
if (ch_idx >= 0)
{
channel_set_json_mode(ch_idx, json_mode);
channel_set_json_mode(ch_idx, ch_mode);
channel_set_timeout(ch_idx, timeout);
if (callback != NULL && *callback != NUL)
channel_set_callback(ch_idx, callback);
@@ -9946,7 +9954,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;
id = channel_get_id();
text = json_encode_nr_expr(id, &argvars[1]);
text = json_encode_nr_expr(id, &argvars[1], 0);
if (text == NULL)
return;
@@ -14442,6 +14450,31 @@ f_join(typval_T *argvars, typval_T *rettv)
rettv->vval.v_string = NULL;
}
/*
* "jsdecode()" function
*/
static void
f_jsdecode(typval_T *argvars, typval_T *rettv)
{
js_read_T reader;
reader.js_buf = get_tv_string(&argvars[0]);
reader.js_fill = NULL;
reader.js_used = 0;
if (json_decode_all(&reader, rettv, JSON_JS) != OK)
EMSG(_(e_invarg));
}
/*
* "jsencode()" function
*/
static void
f_jsencode(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = json_encode(&argvars[0], JSON_JS);
}
/*
* "jsondecode()" function
*/
@@ -14453,7 +14486,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
reader.js_buf = get_tv_string(&argvars[0]);
reader.js_fill = NULL;
reader.js_used = 0;
if (json_decode_all(&reader, rettv) != OK)
if (json_decode_all(&reader, rettv, 0) != OK)
EMSG(_(e_invarg));
}
@@ -14464,7 +14497,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv)
f_jsonencode(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = json_encode(&argvars[0]);
rettv->vval.v_string = json_encode(&argvars[0], 0);
}
/*

View File

@@ -16,22 +16,23 @@
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none);
static int json_decode_item(js_read_T *reader, typval_T *res);
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);
/*
* Encode "val" into a JSON format string.
* The result is in allocated memory.
* The result is empty when encoding fails.
* "options" can be JSON_JS or zero;
*/
char_u *
json_encode(typval_T *val)
json_encode(typval_T *val, int options)
{
garray_T ga;
/* Store bytes in the growarray. */
ga_init2(&ga, 1, 4000);
if (json_encode_item(&ga, val, get_copyID(), TRUE) == FAIL)
if (json_encode_item(&ga, val, get_copyID(), options) == FAIL)
{
vim_free(ga.ga_data);
return vim_strsave((char_u *)"");
@@ -41,10 +42,11 @@ json_encode(typval_T *val)
/*
* Encode ["nr", "val"] into a JSON format string in allocated memory.
* "options" can be JSON_JS or zero;
* Returns NULL when out of memory.
*/
char_u *
json_encode_nr_expr(int nr, typval_T *val)
json_encode_nr_expr(int nr, typval_T *val, int options)
{
typval_T listtv;
typval_T nrtv;
@@ -61,7 +63,7 @@ json_encode_nr_expr(int nr, typval_T *val)
return NULL;
}
text = json_encode(&listtv);
text = json_encode(&listtv, options);
list_unref(listtv.vval.v_list);
return text;
}
@@ -122,12 +124,30 @@ write_string(garray_T *gap, char_u *str)
}
}
/*
* Return TRUE if "key" can be used without quotes.
* That is when it starts with a letter and only contains letters, digits and
* underscore.
*/
static int
is_simple_key(char_u *key)
{
char_u *p;
if (!ASCII_ISALPHA(*key))
return FALSE;
for (p = key + 1; *p != NUL; ++p)
if (!ASCII_ISALPHA(*p) && *p != '_' && !vim_isdigit(*p))
return FALSE;
return TRUE;
}
/*
* Encode "val" into "gap".
* Return FAIL or OK.
*/
static int
json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none)
json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
{
char_u numbuf[NUMBUFLEN];
char_u *res;
@@ -141,13 +161,11 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none)
{
case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
case VVAL_NONE: if (!allow_none)
{
/* TODO: better error */
EMSG(_(e_invarg));
return FAIL;
}
case VVAL_NONE: if ((options & JSON_JS) != 0
&& (options & JSON_NO_NONE) == 0)
/* empty item */
break;
/* FALLTHROUGH */
case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
}
break;
@@ -185,9 +203,15 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none)
ga_append(gap, '[');
for (li = l->lv_first; li != NULL && !got_int; )
{
if (json_encode_item(gap, &li->li_tv, copyID, TRUE)
== FAIL)
if (json_encode_item(gap, &li->li_tv, copyID,
options & JSON_JS) == FAIL)
return FAIL;
if ((options & JSON_JS)
&& li->li_next == NULL
&& li->li_tv.v_type == VAR_SPECIAL
&& li->li_tv.vval.v_number == VVAL_NONE)
/* add an extra comma if the last item is v:none */
ga_append(gap, ',');
li = li->li_next;
if (li != NULL)
ga_append(gap, ',');
@@ -224,10 +248,14 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none)
first = FALSE;
else
ga_append(gap, ',');
if ((options & JSON_JS)
&& is_simple_key(hi->hi_key))
ga_concat(gap, hi->hi_key);
else
write_string(gap, hi->hi_key);
ga_append(gap, ':');
if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
copyID, FALSE) == FAIL)
copyID, options | JSON_NO_NONE) == FAIL)
return FAIL;
}
ga_append(gap, '}');
@@ -265,7 +293,8 @@ fill_numbuflen(js_read_T *reader)
}
/*
* Skip white space in "reader".
* Skip white space in "reader". All characters <= space are considered white
* space.
* Also tops up readahead when needed.
*/
static void
@@ -282,7 +311,7 @@ json_skip_white(js_read_T *reader)
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
continue;
}
if (c != ' ' && c != TAB && c != NL && c != CAR)
if (c == NUL || c > ' ')
break;
++reader->js_used;
}
@@ -290,7 +319,7 @@ json_skip_white(js_read_T *reader)
}
static int
json_decode_array(js_read_T *reader, typval_T *res)
json_decode_array(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
typval_T item;
@@ -317,7 +346,7 @@ json_decode_array(js_read_T *reader, typval_T *res)
break;
}
ret = json_decode_item(reader, res == NULL ? NULL : &item);
ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
if (ret != OK)
return ret;
if (res != NULL)
@@ -347,7 +376,7 @@ json_decode_array(js_read_T *reader, typval_T *res)
}
static int
json_decode_object(js_read_T *reader, typval_T *res)
json_decode_object(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
typval_T tvkey;
@@ -377,7 +406,21 @@ json_decode_object(js_read_T *reader, typval_T *res)
break;
}
ret = json_decode_item(reader, res == NULL ? NULL : &tvkey);
if ((options & JSON_JS) && reader->js_buf[reader->js_used] != '"')
{
/* accept a key that is not in quotes */
key = p = reader->js_buf + reader->js_used;
while (*p != NUL && *p != ':' && *p > ' ')
++p;
tvkey.v_type = VAR_STRING;
tvkey.vval.v_string = vim_strnsave(key, (int)(p - key));
reader->js_used += (int)(p - key);
key = tvkey.vval.v_string;
}
else
{
ret = json_decode_item(reader, res == NULL ? NULL : &tvkey,
options);
if (ret != OK)
return ret;
if (res != NULL)
@@ -389,6 +432,7 @@ json_decode_object(js_read_T *reader, typval_T *res)
return FAIL;
}
}
}
json_skip_white(reader);
p = reader->js_buf + reader->js_used;
@@ -403,7 +447,7 @@ json_decode_object(js_read_T *reader, typval_T *res)
++reader->js_used;
json_skip_white(reader);
ret = json_decode_item(reader, res == NULL ? NULL : &item);
ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
if (ret != OK)
{
if (res != NULL)
@@ -569,7 +613,7 @@ json_decode_string(js_read_T *reader, typval_T *res)
* Return MAYBE for an incomplete message.
*/
static int
json_decode_item(js_read_T *reader, typval_T *res)
json_decode_item(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
int len;
@@ -579,15 +623,18 @@ json_decode_item(js_read_T *reader, typval_T *res)
switch (*p)
{
case '[': /* array */
return json_decode_array(reader, res);
return json_decode_array(reader, res, options);
case '{': /* object */
return json_decode_object(reader, res);
return json_decode_object(reader, res, options);
case '"': /* string */
return json_decode_string(reader, res);
case ',': /* comma: empty item */
if ((options & JSON_JS) == 0)
return FAIL;
/* FALLTHROUGH */
case NUL: /* empty */
if (res != NULL)
{
@@ -691,17 +738,18 @@ json_decode_item(js_read_T *reader, typval_T *res)
/*
* Decode the JSON from "reader" and store the result in "res".
* "options" can be JSON_JS or zero;
* Return FAIL if not the whole message was consumed.
*/
int
json_decode_all(js_read_T *reader, typval_T *res)
json_decode_all(js_read_T *reader, typval_T *res, int options)
{
int ret;
/* We get the end once, to avoid calling strlen() many times. */
/* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
ret = json_decode_item(reader, res);
ret = json_decode_item(reader, res, options);
if (ret != OK)
return FAIL;
json_skip_white(reader);
@@ -712,18 +760,19 @@ json_decode_all(js_read_T *reader, typval_T *res)
/*
* Decode the JSON from "reader" and store the result in "res".
* "options" can be JSON_JS or zero;
* Return FAIL if the message has a decoding error or the message is
* truncated. Consumes the message anyway.
*/
int
json_decode(js_read_T *reader, typval_T *res)
json_decode(js_read_T *reader, typval_T *res, int options)
{
int ret;
/* We get the end once, to avoid calling strlen() many times. */
/* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
ret = json_decode_item(reader, res);
ret = json_decode_item(reader, res, options);
json_skip_white(reader);
return ret == OK ? OK : FAIL;
@@ -731,6 +780,7 @@ json_decode(js_read_T *reader, typval_T *res)
/*
* Decode the JSON from "reader" to find the end of the message.
* "options" can be JSON_JS or zero;
* Return FAIL if the message has a decoding error.
* Return MAYBE if the message is truncated, need to read more.
* This only works reliable if the message contains an object, array or
@@ -738,15 +788,15 @@ json_decode(js_read_T *reader, typval_T *res)
* Does not advance the reader.
*/
int
json_find_end(js_read_T *reader)
json_find_end(js_read_T *reader, int options)
{
int used_save = reader->js_used;
int ret;
/* We get the end once, to avoid calling strlen() many times. */
/* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
ret = json_decode_item(reader, NULL);
ret = json_decode_item(reader, NULL, options);
reader->js_used = used_save;
return ret;
}

View File

@@ -35,107 +35,107 @@ test_decode_find_end(void)
/* string and incomplete string */
reader.js_buf = (char_u *)"\"hello\"";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" \"hello\" ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"\"hello";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* number and dash (incomplete number) */
reader.js_buf = (char_u *)"123";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"-";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* false, true and null, also incomplete */
reader.js_buf = (char_u *)"false";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"f";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fa";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fal";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fals";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"true";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"t";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"tr";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"tru";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"null";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"n";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"nu";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"nul";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* object without white space */
reader.js_buf = (char_u *)"{\"a\":123}";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"{\"a\":123";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a\":";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a\"";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* object with white space */
reader.js_buf = (char_u *)" { \"a\" : 123 } ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" { \"a\" : 123 ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" : ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* array without white space */
reader.js_buf = (char_u *)"[\"a\",123]";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"[\"a\",123";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a\",";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a\"";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
/* array with white space */
reader.js_buf = (char_u *)" [ \"a\" , 123 ] ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" , ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ ";
assert(json_find_end(&reader) == MAYBE);
assert(json_find_end(&reader, 0) == MAYBE);
}
static int
@@ -157,15 +157,15 @@ test_fill_called_on_find_end(void)
reader.js_used = 0;
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
reader.js_cookie = " [ \"a\" , 123 ] ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" , ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ ";
assert(json_find_end(&reader) == OK);
assert(json_find_end(&reader, 0) == OK);
}
/*

View File

@@ -1,7 +1,7 @@
/* channel.c */
void channel_gui_register_all(void);
int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
void channel_set_json_mode(int idx, int json_mode);
void channel_set_json_mode(int idx, ch_mode_T ch_mode);
void channel_set_timeout(int idx, int timeout);
void channel_set_callback(int idx, char_u *callback);
void channel_set_req_callback(int idx, char_u *callback, int id);

View File

@@ -1,7 +1,7 @@
/* json.c */
char_u *json_encode(typval_T *val);
char_u *json_encode_nr_expr(int nr, typval_T *val);
int json_decode_all(js_read_T *reader, typval_T *res);
int json_decode(js_read_T *reader, typval_T *res);
int json_find_end(js_read_T *reader);
char_u *json_encode(typval_T *val, int options);
char_u *json_encode_nr_expr(int nr, typval_T *val, int options);
int json_decode_all(js_read_T *reader, typval_T *res, int options);
int json_decode(js_read_T *reader, typval_T *res, int options);
int json_find_end(js_read_T *reader, int options);
/* vim: set ft=c : */

View File

@@ -2728,3 +2728,11 @@ struct js_reader
void *js_cookie; /* can be used by js_fill */
};
typedef struct js_reader js_read_T;
/* mode for a channel */
typedef enum
{
MODE_RAW = 0,
MODE_JSON,
MODE_JS
} ch_mode_T;

View File

@@ -32,9 +32,12 @@ let l3 = [1, 2]
let s:varl3 = [l3, l3]
let s:jsond1 = '{"a":1,"b":"bee","c":[1,2]}'
let s:jsd1 = '{a:1,b:"bee",c:[1,2]}'
let s:vard1 = {"a": 1, "b": "bee","c": [1,2]}
let s:jsond2 = '{"1":1,"2":{"a":"aa","b":{},"c":"cc"},"3":3}'
let s:jsd2 = '{"1":1,"2":{a:"aa",b:{},c:"cc"},"3":3}'
let s:jsond2s = " { \"1\" : 1 , \"2\" :\n{ \"a\"\r: \"aa\" , \"b\" : {\<Tab>} , \"c\" : \"cc\" } , \"3\" : 3 }\r\n"
let s:jsd2s = " { \"1\" : 1 , \"2\" :\n{ a\r: \"aa\" , b : {\<Tab>} , c : \"cc\" } , \"3\" : 3 }\r\n"
let s:vard2 = {"1": 1, "2": 2, "3": 3}
let d2 = {"a": "aa", "b": s:vard2, "c": "cc"}
let s:vard2["2"] = d2
@@ -42,11 +45,16 @@ let s:vard2x = {"1": 1, "2": {"a": "aa", "b": {}, "c": "cc"}, "3": 3}
let d3 = {"a": 1, "b": 2}
let s:vard3 = {"x": d3, "y": d3}
let s:jsond3 = '{"x":{"a":1,"b":2},"y":{"a":1,"b":2}}'
let s:jsd3 = '{x:{a:1,b:2},y:{a:1,b:2}}'
let s:vard4 = {"key": v:none}
let s:vard4x = {"key": v:null}
let s:jsond4 = '{"key":null}'
let s:jsd4 = '{key:null}'
let s:jsonvals = '[true,false,,null]'
let s:varvals = [v:true, v:false, v:none, v:null]
let s:jsonvals = '[true,false,null,null]'
let s:varvals = [v:true, v:false, v:null, v:null]
func Test_encode()
func Test_json_encode()
call assert_equal(s:json1, jsonencode(s:var1))
call assert_equal(s:json2, jsonencode(s:var2))
call assert_equal(s:json3, jsonencode(s:var3))
@@ -69,18 +77,18 @@ func Test_encode()
call assert_equal(s:jsond1, jsonencode(s:vard1))
call assert_equal(s:jsond2, jsonencode(s:vard2))
call assert_equal(s:jsond3, jsonencode(s:vard3))
call assert_equal(s:jsond4, jsonencode(s:vard4))
call assert_equal(s:jsonvals, jsonencode(s:varvals))
call assert_fails('echo jsonencode(function("tr"))', 'E474:')
call assert_fails('echo jsonencode([function("tr")])', 'E474:')
call assert_fails('echo jsonencode({"key":v:none})', 'E474:')
silent! let res = jsonencode(function("tr"))
call assert_equal("", res)
endfunc
func Test_decode()
func Test_json_decode()
call assert_equal(s:var1, jsondecode(s:json1))
call assert_equal(s:var2, jsondecode(s:json2))
call assert_equal(s:var3, jsondecode(s:json3))
@@ -103,7 +111,9 @@ func Test_decode()
call assert_equal(s:vard1, jsondecode(s:jsond1))
call assert_equal(s:vard2x, jsondecode(s:jsond2))
call assert_equal(s:vard2x, jsondecode(s:jsond2s))
call assert_equal(s:vard3, jsondecode(s:jsond3))
call assert_equal(s:vard4x, jsondecode(s:jsond4))
call assert_equal(s:varvals, jsondecode(s:jsonvals))
@@ -134,4 +144,110 @@ func Test_decode()
call assert_fails('call jsondecode("[1")', "E474:")
call assert_fails('call jsondecode("[1,")', "E474:")
call assert_fails('call jsondecode("[1 2]")', "E474:")
call assert_fails('call jsondecode("[1,,2]")', "E474:")
endfunc
let s:jsl5 = '[7,,,]'
let s:varl5 = [7, v:none, v:none]
func Test_js_encode()
call assert_equal(s:json1, jsencode(s:var1))
call assert_equal(s:json2, jsencode(s:var2))
call assert_equal(s:json3, jsencode(s:var3))
call assert_equal(s:json4, jsencode(s:var4))
call assert_equal(s:json5, jsencode(s:var5))
if has('multi_byte')
call assert_equal(s:jsonmb, jsencode(s:varmb))
endif
call assert_equal(s:jsonnr, jsencode(s:varnr))
if has('float')
call assert_equal(s:jsonfl, jsencode(s:varfl))
endif
call assert_equal(s:jsonl1, jsencode(s:varl1))
call assert_equal(s:jsonl2, jsencode(s:varl2))
call assert_equal(s:jsonl3, jsencode(s:varl3))
call assert_equal(s:jsd1, jsencode(s:vard1))
call assert_equal(s:jsd2, jsencode(s:vard2))
call assert_equal(s:jsd3, jsencode(s:vard3))
call assert_equal(s:jsd4, jsencode(s:vard4))
call assert_equal(s:jsonvals, jsencode(s:varvals))
call assert_fails('echo jsencode(function("tr"))', 'E474:')
call assert_fails('echo jsencode([function("tr")])', 'E474:')
silent! let res = jsencode(function("tr"))
call assert_equal("", res)
call assert_equal(s:jsl5, jsencode(s:varl5))
endfunc
func Test_js_decode()
call assert_equal(s:var1, jsdecode(s:json1))
call assert_equal(s:var2, jsdecode(s:json2))
call assert_equal(s:var3, jsdecode(s:json3))
call assert_equal(s:var4, jsdecode(s:json4))
call assert_equal(s:var5, jsdecode(s:json5))
if has('multi_byte')
call assert_equal(s:varmb, jsdecode(s:jsonmb))
endif
call assert_equal(s:varnr, jsdecode(s:jsonnr))
if has('float')
call assert_equal(s:varfl, jsdecode(s:jsonfl))
endif
call assert_equal(s:varl1, jsdecode(s:jsonl1))
call assert_equal(s:varl2x, jsdecode(s:jsonl2))
call assert_equal(s:varl2x, jsdecode(s:jsonl2s))
call assert_equal(s:varl3, jsdecode(s:jsonl3))
call assert_equal(s:vard1, jsdecode(s:jsond1))
call assert_equal(s:vard1, jsdecode(s:jsd1))
call assert_equal(s:vard2x, jsdecode(s:jsond2))
call assert_equal(s:vard2x, jsdecode(s:jsd2))
call assert_equal(s:vard2x, jsdecode(s:jsond2s))
call assert_equal(s:vard2x, jsdecode(s:jsd2s))
call assert_equal(s:vard3, jsdecode(s:jsond3))
call assert_equal(s:vard3, jsdecode(s:jsd3))
call assert_equal(s:vard4x, jsdecode(s:jsond4))
call assert_equal(s:vard4x, jsdecode(s:jsd4))
call assert_equal(s:varvals, jsdecode(s:jsonvals))
call assert_equal(v:true, jsdecode('true'))
call assert_equal(type(v:true), type(jsdecode('true')))
call assert_equal(v:none, jsdecode(''))
call assert_equal(type(v:none), type(jsdecode('')))
call assert_equal("", jsdecode('""'))
call assert_equal({'n': 1}, jsdecode('{"n":1,}'))
call assert_fails('call jsdecode("\"")', "E474:")
call assert_fails('call jsdecode("blah")', "E474:")
call assert_fails('call jsdecode("true blah")', "E474:")
call assert_fails('call jsdecode("<foobar>")', "E474:")
call assert_fails('call jsdecode("{")', "E474:")
call assert_fails('call jsdecode("{foobar}")', "E474:")
call assert_fails('call jsdecode("{\"n\",")', "E474:")
call assert_fails('call jsdecode("{\"n\":")', "E474:")
call assert_fails('call jsdecode("{\"n\":1")', "E474:")
call assert_fails('call jsdecode("{\"n\":1,")', "E474:")
call assert_fails('call jsdecode("{\"n\",1}")', "E474:")
call assert_fails('call jsdecode("{-}")', "E474:")
call assert_fails('call jsdecode("[foobar]")', "E474:")
call assert_fails('call jsdecode("[")', "E474:")
call assert_fails('call jsdecode("[1")', "E474:")
call assert_fails('call jsdecode("[1,")', "E474:")
call assert_fails('call jsdecode("[1 2]")', "E474:")
call assert_equal(s:varl5, jsdecode(s:jsl5))
endfunc

View File

@@ -747,6 +747,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1279,
/**/
1278,
/**/

View File

@@ -2317,6 +2317,10 @@ typedef int sock_T;
# define MAX_OPEN_CHANNELS 0
#endif
/* Options for json_encode() and json_decode. */
#define JSON_JS 1 /* use JS instead of JSON */
#define JSON_NO_NONE 2 /* v:none item not allowed */
#ifdef FEAT_MZSCHEME
/* this is in main.c, cproto can't handle it. */
int vim_main2(int argc, char **argv);