aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZyX <kp-pav@yandex.ru>2016-12-25 19:37:13 +0300
committerZyX <kp-pav@yandex.ru>2017-01-03 06:39:23 +0300
commitc5c75513b81398f05a4e63b2f7207ae74de25ecc (patch)
tree336a14150e8b59d04fdc9b8bae28a8dd99ccc5c0
parentefe1476d4293170496f0e933a4d3c955f0559b03 (diff)
downloadrneovim-c5c75513b81398f05a4e63b2f7207ae74de25ecc.tar.gz
rneovim-c5c75513b81398f05a4e63b2f7207ae74de25ecc.tar.bz2
rneovim-c5c75513b81398f05a4e63b2f7207ae74de25ecc.zip
eval/typval_encode: Make partial conversions not recursive
Is known to crash in the current state. Ref #5825.
-rw-r--r--src/nvim/api/private/helpers.c13
-rw-r--r--src/nvim/eval.c30
-rw-r--r--src/nvim/eval/encode.c131
-rw-r--r--src/nvim/eval/typval_encode.h131
4 files changed, 207 insertions, 98 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index b004cfc7a1..0d1d88055f 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -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) \
TYPVAL_ENCODE_CONV_NIL()
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
TYPVAL_ENCODE_CONV_NIL()
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
+#define TYPVAL_ENCODE_CONV_FUNC_END()
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
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_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#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_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index ea42a58cdd..dbc279685a 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -19070,22 +19070,24 @@ void free_tv(typval_T *varp)
#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 { \
- func_unref(fun); \
- if (fun != empty_string) { \
- xfree(fun); \
+ if (is_partial) { \
+ partial_unref(pt); \
+ 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; \
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
- do { \
- partial_unref(pt); \
- pt = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
+#define TYPVAL_ENCODE_CONV_FUNC_END()
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
do { \
@@ -19162,8 +19164,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#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_DICT
#undef TYPVAL_ENCODE_CONV_LIST_START
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 5af4893975..7e59998bf0 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -108,9 +108,12 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
{
garray_T msg_ga;
ga_init(&msg_ga, (int)sizeof(char), 80);
- char *const key_msg = _("key %s");
- char *const key_pair_msg = _("key %s at index %i from special map");
- char *const idx_msg = _("index %i");
+ const char *const key_msg = _("key %s");
+ const char *const key_pair_msg = _("key %s at index %i from special map");
+ 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++) {
if (i != 0) {
ga_concat(&msg_ga, ", ");
@@ -154,6 +157,29 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
}
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
@@ -308,67 +334,29 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
do { \
ga_concat(gap, "function("); \
TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len) \
do { \
- int i; \
- ga_concat(gap, "function("); \
- 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, ')'); \
+ if (len != 0) { \
+ ga_concat(gap, ", "); \
+ } \
+ } 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() \
ga_concat(gap, "[]")
@@ -709,18 +697,12 @@ static inline int convert_to_json_string(garray_T *const gap,
return FAIL; \
} while (0)
-#undef TYPVAL_ENCODE_CONV_FUNC
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#define TYPVAL_ENCODE_CONV_FUNC_START(fun, is_partial, pt) \
return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
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()
///
/// @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_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#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_LIST_START
#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) \
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: " \
"attempt to dump function reference"), \
mpstack, objname)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- return conv_error(_("E5004: Error while dumping %s, %s: " \
- "attempt to dump partial"), \
- mpstack, objname)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(len)
+#define TYPVAL_ENCODE_CONV_FUNC_END()
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
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_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
-#undef TYPVAL_ENCODE_CONV_FUNC
-#undef TYPVAL_ENCODE_CONV_PARTIAL
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#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_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index b79158b30c..149dd22ff7 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -69,10 +69,30 @@
///
/// @param fun Function name.
-/// @def TYPVAL_ENCODE_CONV_PARTIAL
-/// @brief Macros used to convert a partial
+/// @def TYPVAL_ENCODE_CONV_FUNC_START
+/// @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
/// @brief Macros used to convert an empty list
@@ -151,11 +171,20 @@
/// Type of the stack entry
typedef enum {
- kMPConvDict, ///< Convert dict_T *dictionary.
- kMPConvList, ///< Convert list_T *list.
+ kMPConvDict, ///< Convert dict_T *dictionary.
+ kMPConvList, ///< Convert list_T *list.
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;
+/// 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
typedef struct {
MPConvStackValType type; ///< Type of the stack entry.
@@ -170,6 +199,15 @@ typedef struct {
list_T *list; ///< Currently converted list.
listitem_T *li; ///< Currently converted list item.
} 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.
} MPConvStackVal;
@@ -250,11 +288,26 @@ static int name##_convert_one_value(firstargtype firstargname, \
break; \
} \
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; \
} \
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; \
} \
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; \
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); \
if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \