viml/executor: Directly generate typval_T values

Note: this will *still* crash when using API in cases similar to the one
described in first commit. Just it needs different code to reproduce.
This commit is contained in:
ZyX
2016-07-10 08:03:14 +03:00
parent e7bbd8256b
commit f551df17f3
6 changed files with 715 additions and 186 deletions

View File

@@ -7,6 +7,7 @@
#include "nvim/eval/encode.h"
#include "nvim/ascii.h"
#include "nvim/message.h"
#include "nvim/globals.h"
#include "nvim/charset.h" // vim_str2nr
#include "nvim/lib/kvec.h"
#include "nvim/vim.h" // OK, FAIL
@@ -218,6 +219,69 @@ static inline int json_decoder_pop(ValuesStackItem obj,
} \
} while (0)
/// Create a new special dictionary that ought to represent a MAP
///
/// @param[out] ret_tv Address where new special dictionary is saved.
///
/// @return [allocated] list which should contain key-value pairs. Return value
/// may be safely ignored.
list_T *decode_create_map_special_dict(typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
list_T *const list = list_alloc();
list->lv_refcount++;
create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
return list;
}
/// Convert char* string to typval_T
///
/// Depending on whether string has (no) NUL bytes, it may use a special
/// dictionary or decode string to VAR_STRING.
///
/// @param[in] s String to decode.
/// @param[in] len String length.
/// @param[in] hasnul Whether string has NUL byte, not or it was not yet
/// determined.
/// @param[in] binary If true, save special string type as kMPBinary,
/// otherwise kMPString.
///
/// @return Decoded string.
typval_T decode_string(const char *const s, const size_t len,
const TriState hasnul, const bool binary)
FUNC_ATTR_WARN_UNUSED_RESULT
{
assert(s != NULL || len == 0);
const bool really_hasnul = (hasnul == kNone
? memchr(s, NUL, len) != NULL
: (bool)hasnul);
if (really_hasnul) {
list_T *const list = list_alloc();
list->lv_refcount++;
typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
if (encode_list_write((void *)list, s, len) == -1) {
clear_tv(&tv);
return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
}
return tv;
} else {
return (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
.vval = { .v_string = xmemdupz(s, len) },
};
}
}
/// Parse JSON double-quoted string
///
/// @param[in] conv Defines conversion necessary to convert UTF-8 string to
@@ -428,29 +492,13 @@ static inline int parse_json_string(vimconv_T *const conv,
str = new_str;
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 = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
if (encode_list_write((void *) list, str, (size_t) (str_end - str))
== -1) {
clear_tv(&obj);
goto parse_json_string_fail;
}
xfree(str);
POP(obj, true);
} else {
*str_end = NUL;
POP(((typval_T) {
.v_type = VAR_STRING,
.vval = { .v_string = (char_u *) str },
}), false);
typval_T obj;
obj = decode_string(str, (size_t)(str_end - str), hasnul ? kTrue : kFalse,
false);
if (obj.v_type == VAR_UNKNOWN) {
goto parse_json_string_fail;
}
POP(obj, obj.v_type != VAR_STRING);
goto parse_json_string_ret;
parse_json_string_fail:
ret = FAIL;
@@ -827,13 +875,7 @@ json_decode_string_cycle_start:
list_T *val_list = NULL;
if (next_map_special) {
next_map_special = false;
val_list = list_alloc();
val_list->lv_refcount++;
create_special_dict(&tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = val_list },
}));
val_list = decode_create_map_special_dict(&tv);
} else {
dict_T *dict = dict_alloc();
dict->dv_refcount++;
@@ -980,37 +1022,15 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
break;
}
case MSGPACK_OBJECT_STR: {
list_T *const list = list_alloc();
list->lv_refcount++;
create_special_dict(rettv, kMPString, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
== -1) {
*rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false);
if (rettv->v_type == VAR_UNKNOWN) {
return FAIL;
}
break;
}
case MSGPACK_OBJECT_BIN: {
if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
*rettv = (typval_T) {
.v_type = VAR_STRING,
.v_lock = VAR_UNLOCKED,
.vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
};
break;
}
list_T *const list = list_alloc();
list->lv_refcount++;
create_special_dict(rettv, kMPBinary, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
== -1) {
*rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true);
if (rettv->v_type == VAR_UNKNOWN) {
return FAIL;
}
break;
@@ -1067,13 +1087,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
msgpack_to_vim_generic_map: {}
list_T *const list = list_alloc();
list->lv_refcount++;
create_special_dict(rettv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
.v_lock = VAR_UNLOCKED,
.vval = { .v_list = list },
}));
list_T *const list = decode_create_map_special_dict(rettv);
for (size_t i = 0; i < mobj.via.map.size; i++) {
list_T *const kv_pair = list_alloc();
list_append_list(list, kv_pair);