eval/typval_encode: Make partial conversions not recursive

Is known to crash in the current state.

Ref #5825.
This commit is contained in:
ZyX
2016-12-25 19:37:13 +03:00
parent efe1476d42
commit c5c75513b8
4 changed files with 207 additions and 98 deletions

View File

@@ -357,11 +357,12 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \ #define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \
TYPVAL_ENCODE_CONV_NIL() TYPVAL_ENCODE_CONV_NIL()
#define TYPVAL_ENCODE_CONV_FUNC(fun) \ #define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
TYPVAL_ENCODE_CONV_NIL() TYPVAL_ENCODE_CONV_NIL()
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
TYPVAL_ENCODE_CONV_NIL() #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
#define TYPVAL_ENCODE_CONV_FUNC_END()
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ #define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 }))) kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
@@ -484,8 +485,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
#undef TYPVAL_ENCODE_CONV_EXT_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FLOAT
#undef TYPVAL_ENCODE_CONV_FUNC #undef TYPVAL_ENCODE_CONV_FUNC_START
#undef TYPVAL_ENCODE_CONV_PARTIAL #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START #undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_EMPTY_DICT

View File

@@ -19070,22 +19070,24 @@ void free_tv(typval_T *varp)
#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3) #define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3)
#define TYPVAL_ENCODE_CONV_FUNC(fun) \ #define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
do { \ do { \
func_unref(fun); \ if (is_partial) { \
if (fun != empty_string) { \ partial_unref(pt); \
xfree(fun); \ tv->vval.v_partial = NULL; \
} else { \
func_unref(fun); \
if (fun != empty_string) { \
xfree(fun); \
} \
tv->vval.v_string = NULL; \
} \ } \
tv->vval.v_string = NULL; \
tv->v_lock = VAR_UNLOCKED; \ tv->v_lock = VAR_UNLOCKED; \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
do { \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
partial_unref(pt); \ #define TYPVAL_ENCODE_CONV_FUNC_END()
pt = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ #define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
do { \ do { \
@@ -19162,8 +19164,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
#undef TYPVAL_ENCODE_CONV_STRING #undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING #undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_FUNC #undef TYPVAL_ENCODE_CONV_FUNC_START
#undef TYPVAL_ENCODE_CONV_PARTIAL #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
#undef TYPVAL_ENCODE_CONV_LIST_START #undef TYPVAL_ENCODE_CONV_LIST_START

View File

@@ -108,9 +108,12 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
{ {
garray_T msg_ga; garray_T msg_ga;
ga_init(&msg_ga, (int)sizeof(char), 80); ga_init(&msg_ga, (int)sizeof(char), 80);
char *const key_msg = _("key %s"); const char *const key_msg = _("key %s");
char *const key_pair_msg = _("key %s at index %i from special map"); const char *const key_pair_msg = _("key %s at index %i from special map");
char *const idx_msg = _("index %i"); const char *const idx_msg = _("index %i");
const char *const partial_arg_msg = _("partial");
const char *const partial_arg_i_msg = _("argument %i");
const char *const partial_self_msg = _("partial self dictionary");
for (size_t i = 0; i < kv_size(*mpstack); i++) { for (size_t i = 0; i < kv_size(*mpstack); i++) {
if (i != 0) { if (i != 0) {
ga_concat(&msg_ga, ", "); ga_concat(&msg_ga, ", ");
@@ -154,6 +157,29 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
} }
break; break;
} }
case kMPConvPartial: {
switch (v.data.p.stage) {
case kMPConvPartialArgs: {
assert(false);
break;
}
case kMPConvPartialSelf: {
ga_concat(&msg_ga, partial_arg_msg);
break;
}
case kMPConvPartialEnd: {
ga_concat(&msg_ga, partial_self_msg);
break;
}
}
break;
}
case kMPConvPartialList: {
const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
ga_concat(&msg_ga, IObuff);
break;
}
} }
} }
EMSG3(msg, objname, (kv_size(*mpstack) == 0 EMSG3(msg, objname, (kv_size(*mpstack) == 0
@@ -308,67 +334,29 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \ } \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_FUNC(fun) \ #define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
do { \ do { \
ga_concat(gap, "function("); \ ga_concat(gap, "function("); \
TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \ TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
ga_append(gap, ')'); \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len) \
do { \ do { \
int i; \ if (len != 0) { \
ga_concat(gap, "function("); \ ga_concat(gap, ", "); \
if (pt->pt_name != NULL) { \ } \
size_t len; \
char_u *p; \
len = 3; \
len += STRLEN(pt->pt_name); \
for (p = pt->pt_name; *p != NUL; mb_ptr_adv(p)) { \
if (*p == '\'') { \
len++; \
} \
} \
char_u *r, *s; \
s = r = xmalloc(len); \
if (r != NULL) { \
*r++ = '\''; \
for (p = pt->pt_name; *p != NUL; ) { \
if (*p == '\'') { \
*r++ = '\''; \
} \
MB_COPY_CHAR(p, r); \
} \
*r++ = '\''; \
*r++ = NUL; \
} \
ga_concat(gap, s); \
xfree(s); \
} \
if (pt->pt_argc > 0) { \
ga_concat(gap, ", ["); \
for (i = 0; i < pt->pt_argc; i++) { \
if (i > 0) { \
ga_concat(gap, ", "); \
} \
char *tofree = encode_tv2string(&pt->pt_argv[i], NULL); \
ga_concat(gap, tofree); \
xfree(tofree); \
} \
ga_append(gap, ']'); \
} \
if (pt->pt_dict != NULL) { \
typval_T dtv; \
ga_concat(gap, ", "); \
dtv.v_type = VAR_DICT; \
dtv.vval.v_dict = pt->pt_dict; \
char *tofree = encode_tv2string(&dtv, NULL); \
ga_concat(gap, tofree); \
xfree(tofree); \
} \
ga_append(gap, ')'); \
} while (0) } while (0)
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len) \
do { \
if ((ptrdiff_t)len != -1) { \
ga_concat(gap, ", "); \
} \
} while (0)
#define TYPVAL_ENCODE_CONV_FUNC_END() \
ga_append(gap, ')')
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ #define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
ga_concat(gap, "[]") ga_concat(gap, "[]")
@@ -709,18 +697,12 @@ static inline int convert_to_json_string(garray_T *const gap,
return FAIL; \ return FAIL; \
} while (0) } while (0)
#undef TYPVAL_ENCODE_CONV_FUNC #undef TYPVAL_ENCODE_CONV_FUNC_START
#define TYPVAL_ENCODE_CONV_FUNC(fun) \ #define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
return conv_error(_("E474: Error while dumping %s, %s: " \ return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump function reference"), \ "attempt to dump function reference"), \
mpstack, objname) mpstack, objname)
#undef TYPVAL_ENCODE_CONV_PARTIAL
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump partial"), \
mpstack, objname)
/// Check whether given key can be used in json_encode() /// Check whether given key can be used in json_encode()
/// ///
/// @param[in] tv Key to check. /// @param[in] tv Key to check.
@@ -777,8 +759,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
#undef TYPVAL_ENCODE_CONV_EXT_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FLOAT
#undef TYPVAL_ENCODE_CONV_FUNC #undef TYPVAL_ENCODE_CONV_FUNC_START
#undef TYPVAL_ENCODE_CONV_PARTIAL #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START #undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
@@ -902,15 +886,14 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \ #define TYPVAL_ENCODE_CONV_FLOAT(flt) \
msgpack_pack_double(packer, (double) (flt)) msgpack_pack_double(packer, (double) (flt))
#define TYPVAL_ENCODE_CONV_FUNC(fun) \ #define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
return conv_error(_("E5004: Error while dumping %s, %s: " \ return conv_error(_("E5004: Error while dumping %s, %s: " \
"attempt to dump function reference"), \ "attempt to dump function reference"), \
mpstack, objname) mpstack, objname)
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
return conv_error(_("E5004: Error while dumping %s, %s: " \ #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
"attempt to dump partial"), \ #define TYPVAL_ENCODE_CONV_FUNC_END()
mpstack, objname)
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \ #define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
msgpack_pack_array(packer, 0) msgpack_pack_array(packer, 0)
@@ -967,8 +950,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
#undef TYPVAL_ENCODE_CONV_EXT_STRING #undef TYPVAL_ENCODE_CONV_EXT_STRING
#undef TYPVAL_ENCODE_CONV_NUMBER #undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT #undef TYPVAL_ENCODE_CONV_FLOAT
#undef TYPVAL_ENCODE_CONV_FUNC #undef TYPVAL_ENCODE_CONV_FUNC_START
#undef TYPVAL_ENCODE_CONV_PARTIAL #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
#undef TYPVAL_ENCODE_CONV_FUNC_END
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START #undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_EMPTY_DICT

View File

@@ -69,10 +69,30 @@
/// ///
/// @param fun Function name. /// @param fun Function name.
/// @def TYPVAL_ENCODE_CONV_PARTIAL /// @def TYPVAL_ENCODE_CONV_FUNC_START
/// @brief Macros used to convert a partial /// @brief Macros used when starting to convert a funcref or a partial
/// ///
/// @param pt Partial name. /// @param fun Function name.
/// @param is_partial True if converted function is a partial.
/// @param pt Pointer to partial or NULL.
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
/// @brief Macros used before starting to convert partial arguments
///
/// @param len Number of arguments. Zero for absent arguments or when
/// converting a funcref.
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
/// @brief Macros used before starting to convert self dictionary
///
/// @param len Number of arguments. May be zero for empty dictionary or -1 for
/// missing self dictionary, also when converting function
/// reference.
/// @def TYPVAL_ENCODE_CONV_FUNC_END
/// @brief Macros used after converting a funcref or a partial
///
/// Accepts no arguments, but still must be a function-like macros.
/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST /// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
/// @brief Macros used to convert an empty list /// @brief Macros used to convert an empty list
@@ -151,11 +171,20 @@
/// Type of the stack entry /// Type of the stack entry
typedef enum { typedef enum {
kMPConvDict, ///< Convert dict_T *dictionary. kMPConvDict, ///< Convert dict_T *dictionary.
kMPConvList, ///< Convert list_T *list. kMPConvList, ///< Convert list_T *list.
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
kMPConvPartial, ///< Convert partial_T* partial.
kMPConvPartialList, ///< Convert argc/argv pair coming from a partial.
} MPConvStackValType; } MPConvStackValType;
/// Stage at which partial is being converted
typedef enum {
kMPConvPartialArgs, ///< About to convert arguments.
kMPConvPartialSelf, ///< About to convert self dictionary.
kMPConvPartialEnd, ///< Already converted everything.
} MPConvPartialStage;
/// Structure representing current VimL to messagepack conversion state /// Structure representing current VimL to messagepack conversion state
typedef struct { typedef struct {
MPConvStackValType type; ///< Type of the stack entry. MPConvStackValType type; ///< Type of the stack entry.
@@ -170,6 +199,15 @@ typedef struct {
list_T *list; ///< Currently converted list. list_T *list; ///< Currently converted list.
listitem_T *li; ///< Currently converted list item. listitem_T *li; ///< Currently converted list item.
} l; ///< State of list or generic mapping conversion. } l; ///< State of list or generic mapping conversion.
struct {
MPConvPartialStage stage; ///< Stage at which partial is being converted.
partial_T *pt; ///< Currently converted partial.
} p; ///< State of partial conversion.
struct {
typval_T *arg; ///< Currently converted argument.
typval_T *argv; ///< Start of the argument list.
size_t todo; ///< Number of items left to process.
} a; ///< State of list or generic mapping conversion.
} data; ///< Data to convert. } data; ///< Data to convert.
} MPConvStackVal; } MPConvStackVal;
@@ -250,11 +288,26 @@ static int name##_convert_one_value(firstargtype firstargname, \
break; \ break; \
} \ } \
case VAR_FUNC: { \ case VAR_FUNC: { \
TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \ TYPVAL_ENCODE_CONV_FUNC_START(tv->vval.v_string, false, NULL); \
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(0); \
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1); \
TYPVAL_ENCODE_CONV_FUNC_END(); \
break; \ break; \
} \ } \
case VAR_PARTIAL: { \ case VAR_PARTIAL: { \
TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \ partial_T *const pt = tv->vval.v_partial; \
(void)pt; \
TYPVAL_ENCODE_CONV_FUNC_START(pt->pt_name, true, pt); \
_mp_push(*mpstack, ((MPConvStackVal) { \
.type = kMPConvPartial, \
.tv = tv, \
.data = { \
.p = { \
.stage = kMPConvPartialArgs, \
.pt = tv->vval.v_partial, \
}, \
}, \
})); \
break; \ break; \
} \ } \
case VAR_LIST: { \ case VAR_LIST: { \
@@ -562,6 +615,70 @@ scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
break; \ break; \
} \ } \
case kMPConvPartial: { \
partial_T *const pt = cur_mpsv->data.p.pt; \
switch (cur_mpsv->data.p.stage) { \
case kMPConvPartialArgs: { \
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(pt->pt_argc); \
cur_mpsv->data.p.stage = kMPConvPartialSelf; \
if (pt->pt_argc > 0) { \
TYPVAL_ENCODE_CONV_LIST_START(pt->pt_argc); \
_mp_push(mpstack, ((MPConvStackVal) { \
.type = kMPConvPartialList, \
.tv = tv, \
.data = { \
.a = { \
.arg = pt->pt_argv, \
.argv = pt->pt_argv, \
.todo = (size_t)pt->pt_argc, \
}, \
}, \
})); \
} \
break; \
} \
case kMPConvPartialSelf: { \
cur_mpsv->data.p.stage = kMPConvPartialEnd; \
dict_T *const dict = pt->pt_dict; \
if (dict != NULL) { \
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(dict->dv_hashtab.ht_used); \
TYPVAL_ENCODE_CONV_DICT_START(dict->dv_hashtab.ht_used); \
_mp_push(mpstack, ((MPConvStackVal) { \
.type = kMPConvDict, \
.tv = tv, \
.data = { \
.d = { \
.dict = dict, \
.hi = dict->dv_hashtab.ht_array, \
.todo = dict->dv_hashtab.ht_used, \
}, \
}, \
})); \
} else { \
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1); \
} \
break; \
} \
case kMPConvPartialEnd: { \
TYPVAL_ENCODE_CONV_FUNC_END(); \
(void) _mp_pop(mpstack); \
break; \
} \
} \
continue; \
} \
case kMPConvPartialList: { \
if (!cur_mpsv->data.a.todo) { \
(void) _mp_pop(mpstack); \
TYPVAL_ENCODE_CONV_LIST_END(); \
continue; \
} else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) { \
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \
} \
cur_tv = cur_mpsv->data.a.arg++; \
cur_mpsv->data.a.todo--; \
break; \
} \
} \ } \
assert(cur_tv != NULL); \ assert(cur_tv != NULL); \
if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \ if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \