diff options
author | ZyX <kp-pav@yandex.ru> | 2016-12-25 19:37:13 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-01-03 06:39:23 +0300 |
commit | c5c75513b81398f05a4e63b2f7207ae74de25ecc (patch) | |
tree | 336a14150e8b59d04fdc9b8bae28a8dd99ccc5c0 | |
parent | efe1476d4293170496f0e933a4d3c955f0559b03 (diff) | |
download | rneovim-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.c | 13 | ||||
-rw-r--r-- | src/nvim/eval.c | 30 | ||||
-rw-r--r-- | src/nvim/eval/encode.c | 131 | ||||
-rw-r--r-- | src/nvim/eval/typval_encode.h | 131 |
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, \ |