mirror of
https://github.com/neovim/neovim.git
synced 2025-09-08 12:28:18 +00:00
eval/decode: Parse strings with NUL to special dictionaries
This commit is contained in:
@@ -342,6 +342,7 @@ int json_decode_string(const char *const buf, const size_t len,
|
|||||||
char *str = xmalloc(len + 1);
|
char *str = xmalloc(len + 1);
|
||||||
int fst_in_pair = 0;
|
int fst_in_pair = 0;
|
||||||
char *str_end = str;
|
char *str_end = str;
|
||||||
|
bool hasnul = false;
|
||||||
for (const char *t = s; t < p; t++) {
|
for (const char *t = s; t < p; t++) {
|
||||||
if (t[0] != '\\' || t[1] != 'u') {
|
if (t[0] != '\\' || t[1] != 'u') {
|
||||||
if (fst_in_pair != 0) {
|
if (fst_in_pair != 0) {
|
||||||
@@ -357,6 +358,9 @@ int json_decode_string(const char *const buf, const size_t len,
|
|||||||
t += 4;
|
t += 4;
|
||||||
unsigned long ch;
|
unsigned long ch;
|
||||||
vim_str2nr((char_u *) ubuf, NULL, NULL, 0, 0, 2, NULL, &ch);
|
vim_str2nr((char_u *) ubuf, NULL, NULL, 0, 0, 2, NULL, &ch);
|
||||||
|
if (ch == 0) {
|
||||||
|
hasnul = true;
|
||||||
|
}
|
||||||
if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) {
|
if (SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END) {
|
||||||
fst_in_pair = (int) ch;
|
fst_in_pair = (int) ch;
|
||||||
} else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END
|
} else if (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END
|
||||||
@@ -405,9 +409,9 @@ int json_decode_string(const char *const buf, const size_t len,
|
|||||||
str_end += utf_char2bytes((int) fst_in_pair, (char_u *) str_end);
|
str_end += utf_char2bytes((int) fst_in_pair, (char_u *) str_end);
|
||||||
}
|
}
|
||||||
if (conv.vc_type != CONV_NONE) {
|
if (conv.vc_type != CONV_NONE) {
|
||||||
size_t len = (size_t) (str_end - str);
|
size_t str_len = (size_t) (str_end - str);
|
||||||
char *const new_str = (char *) string_convert(&conv, (char_u *) str,
|
char *const new_str = (char *) string_convert(&conv, (char_u *) str,
|
||||||
&len);
|
&str_len);
|
||||||
if (new_str == NULL) {
|
if (new_str == NULL) {
|
||||||
EMSG2(_("E474: Failed to convert string \"%s\" from UTF-8"), str);
|
EMSG2(_("E474: Failed to convert string \"%s\" from UTF-8"), str);
|
||||||
xfree(str);
|
xfree(str);
|
||||||
@@ -415,14 +419,31 @@ int json_decode_string(const char *const buf, const size_t len,
|
|||||||
}
|
}
|
||||||
xfree(str);
|
xfree(str);
|
||||||
str = new_str;
|
str = new_str;
|
||||||
str_end = new_str + len;
|
str_end = new_str + str_len;
|
||||||
|
}
|
||||||
|
if (hasnul) {
|
||||||
|
typval_T obj;
|
||||||
|
list_T *const list = list_alloc();
|
||||||
|
list->lv_refcount++;
|
||||||
|
create_special_dict(&obj, kMPString, ((typval_T) {
|
||||||
|
.v_type = VAR_LIST,
|
||||||
|
.v_lock = 0,
|
||||||
|
.vval = { .v_list = list },
|
||||||
|
}));
|
||||||
|
if (encode_list_write((void *) list, str, (size_t) (str_end - str))
|
||||||
|
== -1) {
|
||||||
|
clear_tv(&obj);
|
||||||
|
goto json_decode_string_fail;
|
||||||
|
}
|
||||||
|
POP(obj);
|
||||||
|
} else {
|
||||||
|
*str_end = NUL;
|
||||||
|
// TODO: return special string in case of NUL bytes
|
||||||
|
POP(((typval_T) {
|
||||||
|
.v_type = VAR_STRING,
|
||||||
|
.vval = { .v_string = (char_u *) str, },
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
*str_end = NUL;
|
|
||||||
// TODO: return special string in case of NUL bytes
|
|
||||||
POP(((typval_T) {
|
|
||||||
.v_type = VAR_STRING,
|
|
||||||
.vval = { .v_string = (char_u *) str, },
|
|
||||||
}));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case '-':
|
case '-':
|
||||||
|
@@ -1,13 +1,63 @@
|
|||||||
local helpers = require('test.functional.helpers')
|
local helpers = require('test.functional.helpers')
|
||||||
local clear = helpers.clear
|
local clear = helpers.clear
|
||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
|
local meths = helpers.meths
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
local eval = helpers.eval
|
local eval = helpers.eval
|
||||||
local execute = helpers.execute
|
local execute = helpers.execute
|
||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
|
|
||||||
describe('jsondecode() function', function()
|
describe('jsondecode() function', function()
|
||||||
before_each(clear)
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
execute([[
|
||||||
|
function Eq(exp, act)
|
||||||
|
let act = a:act
|
||||||
|
let exp = a:exp
|
||||||
|
if type(exp) != type(act)
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
if type(exp) == type({})
|
||||||
|
if sort(keys(exp)) !=# sort(keys(act))
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
if sort(keys(exp)) ==# ['_TYPE', '_VAL']
|
||||||
|
let exp_typ = v:msgpack_types[exp._TYPE]
|
||||||
|
let act_typ = act._TYPE
|
||||||
|
if exp_typ isnot act_typ
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
return Eq(exp._VAL, act._VAL)
|
||||||
|
else
|
||||||
|
return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
if type(exp) == type([])
|
||||||
|
if len(exp) != len(act)
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
return empty(filter(copy(exp), '!Eq(v:val, act[v:key])'))
|
||||||
|
endif
|
||||||
|
return exp ==# act
|
||||||
|
endif
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
]])
|
||||||
|
execute([[
|
||||||
|
function EvalEq(exp, act_expr)
|
||||||
|
let act = eval(a:act_expr)
|
||||||
|
if Eq(a:exp, act)
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return string(act)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
local speq = function(expected, actual_expr)
|
||||||
|
eq(1, funcs.EvalEq(expected, actual_expr))
|
||||||
|
end
|
||||||
|
|
||||||
it('accepts readfile()-style list', function()
|
it('accepts readfile()-style list', function()
|
||||||
eq({Test=1}, funcs.jsondecode({
|
eq({Test=1}, funcs.jsondecode({
|
||||||
@@ -221,6 +271,14 @@ describe('jsondecode() function', function()
|
|||||||
exc_exec('call jsondecode("\\t\\"abc\\\\u00")'))
|
exc_exec('call jsondecode("\\t\\"abc\\\\u00")'))
|
||||||
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
|
eq('Vim(call):E474: Unfinished unicode escape sequence: \t"abc\\u000',
|
||||||
exc_exec('call jsondecode("\\t\\"abc\\\\u000")'))
|
exc_exec('call jsondecode("\\t\\"abc\\\\u000")'))
|
||||||
|
eq('Vim(call):E474: Expected four hex digits after \\u: \\u" ',
|
||||||
|
exc_exec('call jsondecode("\\t\\"abc\\\\u\\" ")'))
|
||||||
|
eq('Vim(call):E474: Expected four hex digits after \\u: \\u0" ',
|
||||||
|
exc_exec('call jsondecode("\\t\\"abc\\\\u0\\" ")'))
|
||||||
|
eq('Vim(call):E474: Expected four hex digits after \\u: \\u00" ',
|
||||||
|
exc_exec('call jsondecode("\\t\\"abc\\\\u00\\" ")'))
|
||||||
|
eq('Vim(call):E474: Expected four hex digits after \\u: \\u000" ',
|
||||||
|
exc_exec('call jsondecode("\\t\\"abc\\\\u000\\" ")'))
|
||||||
eq('Vim(call):E474: Expected string end: \t"abc\\u0000',
|
eq('Vim(call):E474: Expected string end: \t"abc\\u0000',
|
||||||
exc_exec('call jsondecode("\\t\\"abc\\\\u0000")'))
|
exc_exec('call jsondecode("\\t\\"abc\\\\u0000")'))
|
||||||
end)
|
end)
|
||||||
@@ -315,6 +373,17 @@ describe('jsondecode() function', function()
|
|||||||
eq('a\xED\xB0\x80', funcs.jsondecode('"a\\uDC00"'))
|
eq('a\xED\xB0\x80', funcs.jsondecode('"a\\uDC00"'))
|
||||||
eq('\t\xED\xB0\x80', funcs.jsondecode('"\\t\\uDC00"'))
|
eq('\t\xED\xB0\x80', funcs.jsondecode('"\\t\\uDC00"'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local sp_decode_eq = function(expected, json)
|
||||||
|
meths.set_var('__json', json)
|
||||||
|
speq(expected, 'jsondecode(g:__json)')
|
||||||
|
execute('unlet! g:__json')
|
||||||
|
end
|
||||||
|
|
||||||
|
it('parses strings with NUL properly', function()
|
||||||
|
sp_decode_eq({_TYPE='string', _VAL={'\n'}}, '"\\u0000"')
|
||||||
|
sp_decode_eq({_TYPE='string', _VAL={'\n', '\n'}}, '"\\u0000\\n\\u0000"')
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('jsonencode() function', function()
|
describe('jsonencode() function', function()
|
||||||
|
Reference in New Issue
Block a user