aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/clint.py7
-rw-r--r--src/nvim/api/private/helpers.c72
-rw-r--r--src/nvim/eval.c222
-rw-r--r--src/nvim/eval/encode.c311
-rw-r--r--src/nvim/eval/typval_encode.c.h776
-rw-r--r--src/nvim/eval/typval_encode.h593
6 files changed, 1220 insertions, 761 deletions
diff --git a/src/clint.py b/src/clint.py
index efc5f18378..07733d211e 100755
--- a/src/clint.py
+++ b/src/clint.py
@@ -3001,9 +3001,10 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
include = match.group(2)
is_system = (match.group(1) == '<')
if include in include_state:
- error(filename, linenum, 'build/include', 4,
- '"%s" already included at %s:%s' %
- (include, filename, include_state[include]))
+ if is_system or not include.endswith('.c.h'):
+ error(filename, linenum, 'build/include', 4,
+ '"%s" already included at %s:%s' %
+ (include, filename, include_state[include]))
else:
include_state[include] = linenum
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index b004cfc7a1..701a1cbf2b 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -19,7 +19,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/version.h"
-#include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h"
/// Helper structure for vim_to_object
@@ -327,21 +326,21 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
kv_push(edata->stack, NIL)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
kv_push(edata->stack, BOOLEAN_OBJ((Boolean)(num)))
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
kv_push(edata->stack, INTEGER_OBJ((Integer)(num)))
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
kv_push(edata->stack, FLOATING_OBJ((Float)(flt)))
-#define TYPVAL_ENCODE_CONV_STRING(str, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
do { \
const size_t len_ = (size_t)(len); \
const char *const str_ = (const char *)(str); \
@@ -354,19 +353,23 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+ do { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ goto typval_encode_stop_converting_one_item; \
+ } while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
kv_push(edata->stack, \
DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 })))
@@ -381,7 +384,7 @@ static inline void typval_encode_list_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
typval_encode_list_start(edata, (size_t)(len))
static inline void typval_encode_between_list_items(EncodedData *const edata)
@@ -394,7 +397,7 @@ static inline void typval_encode_between_list_items(EncodedData *const edata)
list->data.array.items[list->data.array.size++] = item;
}
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
typval_encode_between_list_items(edata)
static inline void typval_encode_list_end(EncodedData *const edata)
@@ -407,7 +410,7 @@ static inline void typval_encode_list_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
typval_encode_list_end(edata)
static inline void typval_encode_dict_start(EncodedData *const edata,
@@ -421,10 +424,10 @@ static inline void typval_encode_dict_start(EncodedData *const edata,
})));
}
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
typval_encode_dict_start(edata, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
static inline void typval_encode_after_key(EncodedData *const edata)
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
@@ -443,7 +446,7 @@ static inline void typval_encode_after_key(EncodedData *const edata)
}
}
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
typval_encode_after_key(edata)
static inline void typval_encode_between_dict_items(EncodedData *const edata)
@@ -456,7 +459,7 @@ static inline void typval_encode_between_dict_items(EncodedData *const edata)
dict->data.dictionary.items[dict->data.dictionary.size++].value = val;
}
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
typval_encode_between_dict_items(edata)
static inline void typval_encode_dict_end(EncodedData *const edata)
@@ -469,23 +472,31 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
#endif
}
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
typval_encode_dict_end(edata)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
TYPVAL_ENCODE_CONV_NIL()
-// object_convert_one_value()
-// encode_vim_to_object()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME object
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE EncodedData *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME edata
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#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
@@ -496,7 +507,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -510,7 +521,10 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
Object vim_to_object(typval_T *obj)
{
EncodedData edata = { .stack = KV_INITIAL_VALUE };
- encode_vim_to_object(&edata, obj, "vim_to_object argument");
+ const int evo_ret = encode_vim_to_object(&edata, obj,
+ "vim_to_object argument");
+ (void)evo_ret;
+ assert(evo_ret == OK);
Object ret = kv_A(edata.stack, 0);
assert(kv_size(edata.stack) == 1);
kv_destroy(edata.stack);
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index edc4e2e369..4501c2e0a6 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -19042,124 +19042,200 @@ void free_tv(typval_T *varp)
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
tv->vval.v_special = kSpecialVarFalse; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_BOOL(ignored) \
- TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
+ TYPVAL_ENCODE_CONV_NIL(tv)
-#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
- (void)ignored; \
+ (void)num; \
tv->vval.v_number = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \
- assert(false)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
tv->vval.v_float = 0; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
- xfree(str); \
+ xfree(buf); \
tv->vval.v_string = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(ignored1, ignored2, ignored3)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+static inline int _nothing_conv_func_start(typval_T *const tv,
+ char_u *const fun)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1)
+{
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt_ = tv->vval.v_partial;
+ if (pt_ != NULL && pt_->pt_refcount > 1) {
+ pt_->pt_refcount--;
+ tv->vval.v_partial = NULL;
+ return OK;
+ }
+ } else {
+ func_unref(fun);
+ if (fun != empty_string) {
+ xfree(fun);
+ }
+ tv->vval.v_string = NULL;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
do { \
- func_unref(fun); \
- if (fun != empty_string) { \
- xfree(fun); \
+ if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
+ return OK; \
} \
- 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(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
+{
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt = tv->vval.v_partial;
+ if (pt == NULL) {
+ return;
+ }
+ // Dictionary should already be freed by the time.
+ // If it was not freed then it is a part of the reference cycle.
+ assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID);
+ pt->pt_dict = NULL;
+ // As well as all arguments.
+ pt->pt_argc = 0;
+ assert(pt->pt_refcount <= 1);
+ partial_unref(pt);
+ tv->vval.v_partial = NULL;
+ assert(tv->v_lock == VAR_UNLOCKED);
+ }
+}
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
do { \
list_unref(tv->vval.v_list); \
tv->vval.v_list = NULL; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
do { \
- dict_unref(tv->vval.v_dict); \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
+ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
+ dict_unref((dict_T *)dict); \
+ *((dict_T **)&dict) = NULL; \
+ if (tv != NULL) { \
+ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
+ } \
} while (0)
-#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \
+static inline int _nothing_conv_list_start(typval_T *const tv)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (tv == NULL) {
+ return NOTDONE;
+ }
+ tv->v_lock = VAR_UNLOCKED;
+ if (tv->vval.v_list->lv_refcount > 1) {
+ tv->vval.v_list->lv_refcount--;
+ tv->vval.v_list = NULL;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
do { \
- if (tv->vval.v_list->lv_refcount > 1) { \
- tv->vval.v_list->lv_refcount--; \
- tv->vval.v_list = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ if (_nothing_conv_list_start(tv) != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
-#define TYPVAL_ENCODE_CONV_LIST_END() \
- do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_LIST); \
- list_unref(cur_tv->vval.v_list); \
- cur_tv->vval.v_list = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+static inline void _nothing_conv_list_end(typval_T *const tv)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (tv == NULL) {
+ return;
+ }
+ assert(tv->v_type == VAR_LIST);
+ list_T *const list = tv->vval.v_list;
+ list_unref(list);
+ tv->vval.v_list = NULL;
+}
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv)
-#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \
+static inline int _nothing_conv_dict_start(typval_T *const tv,
+ dict_T **const dictp,
+ const void *const nodictvar)
+ FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (tv != NULL) {
+ tv->v_lock = VAR_UNLOCKED;
+ }
+ if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) {
+ (*dictp)->dv_refcount--;
+ *dictp = NULL;
+ return OK;
+ }
+ return NOTDONE;
+}
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
do { \
- if (tv->vval.v_dict->dv_refcount > 1) { \
- tv->vval.v_dict->dv_refcount--; \
- tv->vval.v_dict = NULL; \
- tv->v_lock = VAR_UNLOCKED; \
- return OK; \
+ if (_nothing_conv_dict_start(tv, (dict_T **)&dict, \
+ (void *)&TYPVAL_ENCODE_NODICT_VAR) \
+ != NOTDONE) { \
+ goto typval_encode_stop_converting_one_item; \
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2)
-
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
-
-#define TYPVAL_ENCODE_CONV_DICT_END() \
- do { \
- typval_T *const cur_tv = cur_mpsv->tv; \
- assert(cur_tv->v_type == VAR_DICT); \
- dict_unref(cur_tv->vval.v_dict); \
- cur_tv->vval.v_dict = NULL; \
- cur_tv->v_lock = VAR_UNLOCKED; \
- } while (0)
+static inline void _nothing_conv_dict_end(typval_T *const tv,
+ dict_T **const dictp,
+ const void *const nodictvar)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if ((const void *)dictp != nodictvar) {
+ dict_unref(*dictp);
+ *dictp = NULL;
+ }
+}
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
+ _nothing_conv_dict_end(tv, (dict_T **)&dict, \
+ (void *)&TYPVAL_ENCODE_NODICT_VAR)
-#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
+#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type)
-// nothing_convert_one_value()
-// encode_vim_to_nothing()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME nothing
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME ignored
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
#undef TYPVAL_ENCODE_CONV_NIL
@@ -19170,15 +19246,17 @@ 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
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_DICT_START
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_DICT_END
@@ -19190,7 +19268,9 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
void clear_tv(typval_T *varp)
{
if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
- encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument");
+ (void)evn_ret;
+ assert(evn_ret == OK);
}
}
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 5af4893975..071fbc3923 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
@@ -254,7 +280,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
: OK);
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
const char *const buf_ = (const char *) buf; \
if (buf == NULL) { \
@@ -273,19 +299,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
- TYPVAL_ENCODE_CONV_STRING(buf, len)
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type)
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
ga_concat(gap, numbuf); \
} while (0)
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -308,103 +334,71 @@ 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(tv, fun) \
do { \
- ga_concat(gap, "function("); \
- TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
- ga_append(gap, ')'); \
+ const char *const fun_ = (const char *)(fun); \
+ if (fun_ == NULL) { \
+ EMSG2(_(e_intern2), "string(): NULL function name"); \
+ ga_concat(gap, "function(NULL"); \
+ } else { \
+ ga_concat(gap, "function("); \
+ TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
+ }\
} while (0)
-#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 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_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
+ do { \
+ if ((ptrdiff_t)len != -1) { \
+ ga_concat(gap, ", "); \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv) \
+ ga_append(gap, ')')
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
ga_concat(gap, "[]")
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
ga_append(gap, '[')
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
ga_concat(gap, "{}")
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "v:null")
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "v:true": "v:false"))
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num)
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
ga_append(gap, '{')
-#define TYPVAL_ENCODE_CONV_DICT_END() \
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
ga_append(gap, '}')
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
ga_concat(gap, ": ")
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
ga_concat(gap, ", ")
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END() \
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
ga_append(gap, ']')
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
do { \
@@ -437,9 +431,15 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
-// string_convert_one_value()
-// encode_vim_to_string()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME string
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -469,9 +469,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
return OK; \
} while (0)
-// echo_convert_one_value()
-// encode_vim_to_echo()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME echo
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_RECURSE
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
@@ -489,15 +495,15 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
#undef TYPVAL_ENCODE_CONV_NIL
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
ga_concat(gap, "null")
#undef TYPVAL_ENCODE_CONV_BOOL
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
ga_concat(gap, ((num)? "true": "false"))
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
do { \
char numbuf[NUMBUFLEN]; \
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
@@ -505,7 +511,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
} while (0)
#undef TYPVAL_ENCODE_CONV_FLOAT
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
do { \
const float_T flt_ = (flt); \
switch (fpclassify(flt_)) { \
@@ -694,7 +700,7 @@ static inline int convert_to_json_string(garray_T *const gap,
}
#undef TYPVAL_ENCODE_CONV_STRING
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
return FAIL; \
@@ -702,25 +708,19 @@ static inline int convert_to_json_string(garray_T *const gap,
} while (0)
#undef TYPVAL_ENCODE_CONV_EXT_STRING
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
xfree(buf); \
EMSG(_("E474: Unable to convert EXT string to JSON")); \
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(tv, fun) \
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.
@@ -759,8 +759,8 @@ bool encode_check_json_key(const typval_T *const tv)
return true;
}
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) \
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
do { \
if (!encode_check_json_key(&key)) { \
EMSG(_("E474: Invalid key in special dictionary")); \
@@ -768,17 +768,25 @@ bool encode_check_json_key(const typval_T *const tv)
} \
} while (0)
-// json_convert_one_value()
-// encode_vim_to_json()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME json
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#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
@@ -789,7 +797,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
@@ -807,7 +815,10 @@ char *encode_tv2string(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
+ const int evs_ret = encode_vim_to_string(&ga, tv,
+ "encode_tv2string() argument");
+ (void)evs_ret;
+ assert(evs_ret == OK);
did_echo_string_emsg = false;
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -833,7 +844,9 @@ char *encode_tv2echo(typval_T *tv, size_t *len)
ga_concat(&ga, tv->vval.v_string);
}
} else {
- encode_vim_to_echo(&ga, tv, ":echo argument");
+ const int eve_ret = encode_vim_to_echo(&ga, tv, ":echo argument");
+ (void)eve_ret;
+ assert(eve_ret == OK);
}
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -854,16 +867,19 @@ char *encode_tv2json(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ const int evj_ret = encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
+ if (!evj_ret) {
+ ga_clear(&ga);
+ }
did_echo_string_emsg = false;
if (len != NULL) {
- *len = (size_t) ga.ga_len;
+ *len = (size_t)ga.ga_len;
}
ga_append(&ga, '\0');
- return (char *) ga.ga_data;
+ return (char *)ga.ga_data;
}
-#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_bin(packer, 0); \
@@ -874,7 +890,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
+#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
do { \
if (buf == NULL) { \
msgpack_pack_str(packer, 0); \
@@ -885,7 +901,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
do { \
if (buf == NULL) { \
msgpack_pack_ext(packer, 0, (int8_t) type); \
@@ -896,35 +912,34 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_NUMBER(num) \
- msgpack_pack_int64(packer, (int64_t) (num))
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
+ msgpack_pack_int64(packer, (int64_t)(num))
-#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
- msgpack_pack_double(packer, (double) (flt))
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
+ msgpack_pack_double(packer, (double)(flt))
-#define TYPVAL_ENCODE_CONV_FUNC(fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
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(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
-#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
msgpack_pack_array(packer, 0)
-#define TYPVAL_ENCODE_CONV_LIST_START(len) \
- msgpack_pack_array(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
+ msgpack_pack_array(packer, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
msgpack_pack_map(packer, 0)
-#define TYPVAL_ENCODE_CONV_NIL() \
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
msgpack_pack_nil(packer)
-#define TYPVAL_ENCODE_CONV_BOOL(num) \
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
do { \
if ((num)) { \
msgpack_pack_true(packer); \
@@ -933,23 +948,23 @@ char *encode_tv2json(typval_T *tv, size_t *len)
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
msgpack_pack_uint64(packer, (num))
-#define TYPVAL_ENCODE_CONV_DICT_START(len) \
- msgpack_pack_map(packer, (size_t) (len))
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
+ msgpack_pack_map(packer, (size_t)(len))
-#define TYPVAL_ENCODE_CONV_DICT_END()
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
-#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
-#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key)
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
-#define TYPVAL_ENCODE_CONV_LIST_END()
+#define TYPVAL_ENCODE_CONV_LIST_END(tv)
-#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
return conv_error(_("E5005: Unable to dump %s: " \
@@ -958,17 +973,25 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
-// msgpack_convert_one_value()
-// encode_vim_to_msgpack()
-TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
+#define TYPVAL_ENCODE_SCOPE
+#define TYPVAL_ENCODE_NAME msgpack
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
#undef TYPVAL_ENCODE_CONV_STRING
#undef TYPVAL_ENCODE_CONV_STR_STRING
#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
@@ -979,7 +1002,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
#undef TYPVAL_ENCODE_CONV_DICT_END
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
#undef TYPVAL_ENCODE_CONV_LIST_END
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
#undef TYPVAL_ENCODE_CONV_RECURSE
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
new file mode 100644
index 0000000000..3e1170b8fa
--- /dev/null
+++ b/src/nvim/eval/typval_encode.c.h
@@ -0,0 +1,776 @@
+/// @file eval/typval_encode.c.h
+///
+/// Contains set of macros used to convert (possibly recursive) typval_T into
+/// something else. For these macros to work the following macros must be
+/// defined:
+
+/// @def TYPVAL_ENCODE_CONV_NIL
+/// @brief Macros used to convert NIL value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:null`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to special dictionary.
+
+/// @def TYPVAL_ENCODE_CONV_BOOL
+/// @brief Macros used to convert boolean value
+///
+/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
+/// is false) and `v:true`/`v:false`.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Boolean value to convert. Value is an expression which
+/// evaluates to some integer.
+
+/// @def TYPVAL_ENCODE_CONV_NUMBER
+/// @brief Macros used to convert integer
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param num Integer to convert, must accept both varnumber_T and int64_t.
+
+/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
+/// @brief Macros used to convert unsigned integer
+///
+/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param num Integer to convert, must accept uint64_t.
+
+/// @def TYPVAL_ENCODE_CONV_FLOAT
+/// @brief Macros used to convert floating-point number
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param flt Number to convert, must accept float_T.
+
+/// @def TYPVAL_ENCODE_CONV_STRING
+/// @brief Macros used to convert plain string
+///
+/// Is used to convert VAR_STRING objects as well as BIN strings represented as
+/// special dictionary.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_STR_STRING
+/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
+///
+/// Is used to convert dictionary keys and STR strings represented as special
+/// dictionaries.
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+
+/// @def TYPVAL_ENCODE_CONV_EXT_STRING
+/// @brief Macros used to convert EXT string
+///
+/// Is used to convert EXT strings represented as special dictionaries. Never
+/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
+/// defined.
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL. Points
+/// to a special dictionary.
+/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
+/// @param len String length.
+/// @param type EXT type.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_START
+/// @brief Macros used when starting to convert a funcref or a partial
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @param fun Function name. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+/// @brief Macros used before starting to convert partial arguments
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+/// @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 tv Pointer to typval where value is stored. May not be NULL.
+/// @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
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
+/// @brief Macros used to convert an empty list
+///
+/// @param tv Pointer to typval where value is stored. May not be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
+/// @brief Macros used to convert an empty dictionary
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_LIST_START
+/// @brief Macros used before starting to convert non-empty list
+///
+/// @param tv Pointer to typval where value is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param len List length. Is an expression which evaluates to an integer.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last list item
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_LIST_END
+/// @brief Macros used after converting non-empty list
+///
+/// @param tv Pointer to typval where list is stored. May be NULL.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_START
+/// @brief Macros used before starting to convert non-empty dictionary
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+/// @param len Dictionary length. Is an expression which evaluates to an
+/// integer.
+
+/// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+/// @brief Macros used to check special dictionary key
+///
+/// @param label Label for goto in case check was not successfull.
+/// @param key typval_T key to check.
+
+/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
+/// @brief Macros used after finishing converting dictionary key
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
+/// @brief Macros used after finishing converting non-last dictionary value
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_DICT_END
+/// @brief Macros used after converting non-empty dictionary
+///
+/// @param tv Pointer to typval where dictionary is stored. May be NULL. May
+/// point to a special dictionary.
+/// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
+/// (for dictionaries represented as special lists).
+
+/// @def TYPVAL_ENCODE_CONV_RECURSE
+/// @brief Macros used when self-containing container is detected
+///
+/// @param val Container for which this situation was detected.
+/// @param conv_type Type of the stack entry, @see MPConvStackValType.
+
+/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
+/// @brief Macros that specifies whether special dictionaries are special
+///
+/// Must be something that evaluates to boolean, most likely `true` or `false`.
+/// If it is false then special dictionaries are not treated specially.
+
+/// @def TYPVAL_ENCODE_SCOPE
+/// @brief Scope of the main function: either nothing or `static`
+
+/// @def TYPVAL_ENCODE_NAME
+/// @brief Name of the target converter
+///
+/// After including this file it will define function
+/// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and
+/// static functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and
+/// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_TYPE
+/// @brief Type of the first argument, which will be used to return the results
+///
+/// Is expected to be a pointer type.
+
+/// @def TYPVAL_ENCODE_FIRST_ARG_NAME
+/// @brief Name of the first argument
+///
+/// This name will only be used by one of the above macros which are defined by
+/// the caller. Functions defined here do not use first argument directly.
+#ifndef NVIM_EVAL_TYPVAL_ENCODE_C_H
+#define NVIM_EVAL_TYPVAL_ENCODE_C_H
+#undef NVIM_EVAL_TYPVAL_ENCODE_C_H
+
+#include <stddef.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "nvim/lib/kvec.h"
+#include "nvim/eval_defs.h"
+#include "nvim/eval/encode.h"
+#include "nvim/func_attr.h"
+#include "nvim/eval/typval_encode.h"
+
+/// Dummy variable used because some macros need lvalue
+///
+/// Must not be written to, if needed one must check that address of the
+/// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`.
+const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL;
+
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_ALWAYS_INLINE;
+
+/// Function for checking whether container references itself
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument.
+/// @param[in,out] val Container to check.
+/// @param val_copyID Pointer to the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] mpstack Stack with values to convert. Read-only, used for error
+/// reporting.
+/// @param[in] copyID CopyID used by the caller.
+/// @param[in] conv_type Type of the conversion, @see MPConvStackValType.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return NOTDONE in case of success, what to return in case of failure.
+static inline int _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ void *const val, int *const val_copyID,
+ const MPConvStack *const mpstack, const int copyID,
+ const MPConvStackValType conv_type,
+ const char *const objname)
+{
+ if (*val_copyID == copyID) {
+ TYPVAL_ENCODE_CONV_RECURSE(val, conv_type);
+ return OK;
+ }
+ *val_copyID = copyID;
+ return NOTDONE;
+}
+
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert single value
+///
+/// Only scalar values are converted immediately, everything else is pushed onto
+/// the stack.
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param mpstack Stack with values to convert. Values which are not
+/// converted completely by this function (i.e.
+/// non-scalars) are pushed here.
+/// @param cur_mpsv Currently converted value from stack.
+/// @param tv Converted value.
+/// @param[in] copyID CopyID.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
+ typval_T *const tv, const int copyID,
+ const char *const objname)
+{
+ switch (tv->v_type) {
+ case VAR_STRING: {
+ TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv));
+ break;
+ }
+ case VAR_NUMBER: {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number);
+ break;
+ }
+ case VAR_FLOAT: {
+ TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
+ break;
+ }
+ case VAR_FUNC: {
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ break;
+ }
+ case VAR_PARTIAL: {
+ partial_T *const pt = tv->vval.v_partial;
+ (void)pt;
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : pt->pt_name));
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartial,
+ .tv = tv,
+ .data = {
+ .p = {
+ .stage = kMPConvPartialArgs,
+ .pt = tv->vval.v_partial,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_LIST: {
+ if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
+ break;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .type = kMPConvList,
+ .tv = tv,
+ .data = {
+ .l = {
+ .list = tv->vval.v_list,
+ .li = tv->vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_SPECIAL: {
+ switch (tv->vval.v_special) {
+ case kSpecialVarNull: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kSpecialVarTrue:
+ case kSpecialVarFalse: {
+ TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
+ break;
+ }
+ }
+ break;
+ }
+ case VAR_DICT: {
+ if (tv->vval.v_dict == NULL
+ || tv->vval.v_dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict);
+ break;
+ }
+ const dictitem_T *type_di;
+ const dictitem_T *val_di;
+ if (TYPVAL_ENCODE_ALLOW_SPECIALS
+ && tv->vval.v_dict->dv_hashtab.ht_used == 2
+ && (type_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_TYPE", -1)) != NULL
+ && type_di->di_tv.v_type == VAR_LIST
+ && (val_di = dict_find((dict_T *)tv->vval.v_dict,
+ (char_u *)"_VAL", -1)) != NULL) {
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
+ if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
+ goto _convert_one_value_regular_dict;
+ }
+ switch ((MessagePackType)i) {
+ case kMPNil: {
+ TYPVAL_ENCODE_CONV_NIL(tv);
+ break;
+ }
+ case kMPBoolean: {
+ if (val_di->di_tv.v_type != VAR_NUMBER) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_BOOL(tv, val_di->di_tv.vval.v_number);
+ break;
+ }
+ case kMPInteger: {
+ const list_T *val_list;
+ varnumber_T sign;
+ varnumber_T highest_bits;
+ varnumber_T high_bits;
+ varnumber_T low_bits;
+ // List of 4 integers; first is signed (should be 1 or -1, but
+ // this is not checked), second is unsigned and have at most
+ // one (sign is -1) or two (sign is 1) non-zero bits (number of
+ // bits is not checked), other unsigned and have at most 31
+ // non-zero bits (number of bits is not checked).
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 4
+ || val_list->lv_first->li_tv.v_type != VAR_NUMBER
+ || (sign = val_list->lv_first->li_tv.vval.v_number) == 0
+ || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER
+ || (highest_bits =
+ val_list->lv_first->li_next->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER
+ || (high_bits =
+ val_list->lv_last->li_prev->li_tv.vval.v_number) < 0
+ || val_list->lv_last->li_tv.v_type != VAR_NUMBER
+ || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) {
+ goto _convert_one_value_regular_dict;
+ }
+ uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
+ | (uint64_t)(((uint64_t)high_bits) << 31)
+ | (uint64_t)low_bits);
+ if (sign > 0) {
+ TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
+ } else {
+ TYPVAL_ENCODE_CONV_NUMBER(tv, -number);
+ }
+ break;
+ }
+ case kMPFloat: {
+ if (val_di->di_tv.v_type != VAR_FLOAT) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_FLOAT(tv, val_di->di_tv.vval.v_float);
+ break;
+ }
+ case kMPString:
+ case kMPBinary: {
+ const bool is_string = ((MessagePackType)i == kMPString);
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len,
+ &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ if (is_string) {
+ TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len);
+ } else {
+ TYPVAL_ENCODE_CONV_STRING(tv, buf, len);
+ }
+ xfree(buf);
+ break;
+ }
+ case kMPArray: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
+ lv_copyID, copyID,
+ kMPConvList);
+ TYPVAL_ENCODE_CONV_LIST_START(tv,
+ val_di->di_tv.vval.v_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvList,
+ .data = {
+ .l = {
+ .list = val_di->di_tv.vval.v_list,
+ .li = val_di->di_tv.vval.v_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPMap: {
+ if (val_di->di_tv.v_type != VAR_LIST) {
+ goto _convert_one_value_regular_dict;
+ }
+ list_T *const val_list = val_di->di_tv.vval.v_list;
+ if (val_list == NULL || val_list->lv_len == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
+ break;
+ }
+ for (const listitem_T *li = val_list->lv_first; li != NULL;
+ li = li->li_next) {
+ if (li->li_tv.v_type != VAR_LIST
+ || li->li_tv.vval.v_list->lv_len != 2) {
+ goto _convert_one_value_regular_dict;
+ }
+ }
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
+ kMPConvPairs);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
+ val_list->lv_len);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvPairs,
+ .data = {
+ .l = {
+ .list = val_list,
+ .li = val_list->lv_first,
+ },
+ },
+ }));
+ break;
+ }
+ case kMPExt: {
+ const list_T *val_list;
+ varnumber_T type;
+ if (val_di->di_tv.v_type != VAR_LIST
+ || (val_list = val_di->di_tv.vval.v_list) == NULL
+ || val_list->lv_len != 2
+ || (val_list->lv_first->li_tv.v_type != VAR_NUMBER)
+ || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX
+ || type < INT8_MIN
+ || (val_list->lv_last->li_tv.v_type != VAR_LIST)) {
+ goto _convert_one_value_regular_dict;
+ }
+ size_t len;
+ char *buf;
+ if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list,
+ &len, &buf)) {
+ goto _convert_one_value_regular_dict;
+ }
+ TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
+ xfree(buf);
+ break;
+ }
+ }
+ break;
+ }
+_convert_one_value_regular_dict:
+ _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
+ kMPConvDict);
+ TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
+ tv->vval.v_dict->dv_hashtab.ht_used);
+ _mp_push(*mpstack, ((MPConvStackVal) {
+ .tv = tv,
+ .type = kMPConvDict,
+ .data = {
+ .d = {
+ .dict = tv->vval.v_dict,
+ .dictp = &tv->vval.v_dict,
+ .hi = tv->vval.v_dict->dv_hashtab.ht_array,
+ .todo = tv->vval.v_dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ break;
+ }
+ case VAR_UNKNOWN: {
+ EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
+ return FAIL;
+ }
+ }
+typval_encode_stop_converting_one_item:
+ return OK;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const tv, const char *const objname)
+ REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
+
+/// Convert the whole typval
+///
+/// @param TYPVAL_ENCODE_FIRST_ARG_NAME First argument, defined by the
+/// includer. Only meaningful to macros
+/// defined by the includer.
+/// @param top_tv Converted value.
+/// @param[in] objname Object name, used for error reporting.
+///
+/// @return OK in case of success, FAIL in case of failure.
+TYPVAL_ENCODE_SCOPE int _TYPVAL_ENCODE_ENCODE(
+ TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
+ typval_T *const top_tv, const char *const objname)
+{
+ const int copyID = get_copyID();
+ MPConvStack mpstack;
+ _mp_init(mpstack);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ NULL,
+ top_tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+/// Label common for this and convert_one_value functions, used for escaping
+/// from macros like TYPVAL_ENCODE_CONV_DICT_START.
+typval_encode_stop_converting_one_item:
+ while (_mp_size(mpstack)) {
+ MPConvStackVal *cur_mpsv = &_mp_last(mpstack);
+ typval_T *tv = NULL;
+ switch (cur_mpsv->type) {
+ case kMPConvDict: {
+ if (!cur_mpsv->data.d.todo) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.d.dict->dv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
+ continue;
+ } else if (cur_mpsv->data.d.todo
+ != cur_mpsv->data.d.dict->dv_hashtab.ht_used) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ }
+ while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
+ cur_mpsv->data.d.hi++;
+ }
+ dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi);
+ cur_mpsv->data.d.todo--;
+ cur_mpsv->data.d.hi++;
+ TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0],
+ strlen((char *)&di->di_key[0]));
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ *cur_mpsv->data.d.dictp);
+ tv = &di->di_tv;
+ break;
+ }
+ case kMPConvList: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
+ }
+ tv = &cur_mpsv->data.l.li->li_tv;
+ cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next;
+ break;
+ }
+ case kMPConvPairs: {
+ if (cur_mpsv->data.l.li == NULL) {
+ (void)_mp_pop(mpstack);
+ cur_mpsv->data.l.list->lv_copyID = copyID - 1;
+ TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ continue;
+ } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) {
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(
+ cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
+ }
+ const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list;
+ TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(
+ encode_vim_to__error_ret, kv_pair->lv_first->li_tv);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME,
+ &mpstack, cur_mpsv,
+ &kv_pair->lv_first->li_tv,
+ copyID,
+ objname) == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
+ TYPVAL_ENCODE_NODICT_VAR);
+ tv = &kv_pair->lv_last->li_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;
+ tv = cur_mpsv->tv;
+ switch (cur_mpsv->data.p.stage) {
+ case kMPConvPartialArgs: {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv,
+ pt == NULL ? 0 : pt->pt_argc);
+ cur_mpsv->data.p.stage = kMPConvPartialSelf;
+ if (pt != NULL && pt->pt_argc > 0) {
+ TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvPartialList,
+ .tv = NULL,
+ .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 == NULL ? NULL : pt->pt_dict;
+ if (dict != NULL) {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used);
+ if (dict->dv_hashtab.ht_used == 0) {
+ TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);
+ continue;
+ }
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
+ TYPVAL_ENCODE_FIRST_ARG_NAME,
+ dict, &dict->dv_copyID, &mpstack, copyID, kMPConvDict,
+ objname);
+ if (te_csr_ret != NOTDONE) {
+ if (te_csr_ret == FAIL) {
+ goto encode_vim_to__error_ret;
+ } else {
+ continue;
+ }
+ }
+ TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
+ dict->dv_hashtab.ht_used);
+ _mp_push(mpstack, ((MPConvStackVal) {
+ .type = kMPConvDict,
+ .tv = NULL,
+ .data = {
+ .d = {
+ .dict = dict,
+ .dictp = &pt->pt_dict,
+ .hi = dict->dv_hashtab.ht_array,
+ .todo = dict->dv_hashtab.ht_used,
+ },
+ },
+ }));
+ } else {
+ TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
+ }
+ break;
+ }
+ case kMPConvPartialEnd: {
+ TYPVAL_ENCODE_CONV_FUNC_END(tv);
+ (void)_mp_pop(mpstack);
+ break;
+ }
+ }
+ continue;
+ }
+ case kMPConvPartialList: {
+ if (!cur_mpsv->data.a.todo) {
+ (void)_mp_pop(mpstack);
+ TYPVAL_ENCODE_CONV_LIST_END(NULL);
+ continue;
+ } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
+ TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL);
+ }
+ tv = cur_mpsv->data.a.arg++;
+ cur_mpsv->data.a.todo--;
+ break;
+ }
+ }
+ assert(tv != NULL);
+ if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
+ cur_mpsv, tv, copyID, objname)
+ == FAIL) {
+ goto encode_vim_to__error_ret;
+ }
+ }
+ _mp_destroy(mpstack);
+ return OK;
+encode_vim_to__error_ret:
+ _mp_destroy(mpstack);
+ return FAIL;
+ // Prevent “unused label” warnings.
+ goto typval_encode_stop_converting_one_item;
+}
+#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H
diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h
index b79158b30c..6517efa961 100644
--- a/src/nvim/eval/typval_encode.h
+++ b/src/nvim/eval/typval_encode.h
@@ -1,161 +1,35 @@
-/// @file eval/typval_convert.h
+/// @file eval/typval_encode.h
///
-/// Contains set of macros used to convert (possibly recursive) typval_T into
-/// something else. For these macros to work the following macros must be
-/// defined:
-
-/// @def TYPVAL_ENCODE_CONV_NIL
-/// @brief Macros used to convert NIL value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:null`. Accepts no arguments, but still must be
-/// a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_BOOL
-/// @brief Macros used to convert boolean value
-///
-/// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
-/// is false) and `v:true`/`v:false`.
-///
-/// @param num Boolean value to convert. Value is an expression which
-/// evaluates to some integer.
-
-/// @def TYPVAL_ENCODE_CONV_NUMBER
-/// @brief Macros used to convert integer
-///
-/// @param num Integer to convert, must accept both varnumber_T and int64_t.
-
-/// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
-/// @brief Macros used to convert unsigned integer
-///
-/// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param num Integer to convert, must accept uint64_t.
-
-/// @def TYPVAL_ENCODE_CONV_FLOAT
-/// @brief Macros used to convert floating-point number
-///
-/// @param flt Number to convert, must accept float_T.
-
-/// @def TYPVAL_ENCODE_CONV_STRING
-/// @brief Macros used to convert plain string
-///
-/// Is used to convert VAR_STRING objects as well as BIN strings represented as
-/// special dictionary.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-
-/// @def TYPVAL_ENCODE_CONV_STR_STRING
-/// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
-///
-/// Is used to convert dictionary keys and STR strings represented as special
-/// dictionaries.
-
-/// @def TYPVAL_ENCODE_CONV_EXT_STRING
-/// @brief Macros used to convert EXT string
-///
-/// Is used to convert EXT strings represented as special dictionaries. Never
-/// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
-/// defined.
-///
-/// @param buf String to convert. Is a char[] buffer, not NUL-terminated.
-/// @param len String length.
-/// @param type EXT type.
-
-/// @def TYPVAL_ENCODE_CONV_FUNC
-/// @brief Macros used to convert a function reference
-///
-/// @param fun Function name.
-
-/// @def TYPVAL_ENCODE_CONV_PARTIAL
-/// @brief Macros used to convert a partial
-///
-/// @param pt Partial name.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
-/// @brief Macros used to convert an empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
-/// @brief Macros used to convert an empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_START
-/// @brief Macros used before starting to convert non-empty list
-///
-/// @param len List length. Is an expression which evaluates to an integer.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last list item
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_LIST_END
-/// @brief Macros used after converting non-empty list
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_START
-/// @brief Macros used before starting to convert non-empty dictionary
-///
-/// @param len Dictionary length. Is an expression which evaluates to an
-/// integer.
-
-/// @def TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
-/// @brief Macros used to check special dictionary key
-///
-/// @param label Label for goto in case check was not successfull.
-/// @param key typval_T key to check.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
-/// @brief Macros used after finishing converting dictionary key
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
-/// @brief Macros used after finishing converting non-last dictionary value
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_DICT_END
-/// @brief Macros used after converting non-empty dictionary
-///
-/// Accepts no arguments, but still must be a function-like macros.
-
-/// @def TYPVAL_ENCODE_CONV_RECURSE
-/// @brief Macros used when self-containing container is detected
-///
-/// @param val Container for which this situation was detected.
-/// @param conv_type Type of the stack entry, @see MPConvStackValType.
-
-/// @def TYPVAL_ENCODE_ALLOW_SPECIALS
-/// @brief Macros that specifies whether special dictionaries are special
-///
-/// Must be something that evaluates to boolean, most likely `true` or `false`.
-/// If it is false then special dictionaries are not treated specially.
+/// Contains common definitions for eval/typval_encode.c.h. Most of time should
+/// not be included directly.
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
#define NVIM_EVAL_TYPVAL_ENCODE_H
#include <stddef.h>
#include <inttypes.h>
+#include <string.h>
#include <assert.h>
#include "nvim/lib/kvec.h"
#include "nvim/eval_defs.h"
-#include "nvim/eval/encode.h"
#include "nvim/func_attr.h"
/// 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.
@@ -163,6 +37,9 @@ typedef struct {
union {
struct {
dict_T *dict; ///< Currently converted dictionary.
+ dict_T **dictp; ///< Location where that dictionary is stored.
+ ///< Normally it is &.tv->vval.v_dict, but not when
+ ///< converting partials.
hashitem_T *hi; ///< Currently converted dictionary item.
size_t todo; ///< Amount of items left to process.
} d; ///< State of dictionary conversion.
@@ -170,6 +47,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;
@@ -184,21 +70,9 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
#define _mp_pop kv_pop
#define _mp_last kv_last
-/// Code for checking whether container references itself
-///
-/// @param[in,out] val Container to check.
-/// @param copyID_attr Name of the container attribute that holds copyID.
-/// After checking whether value of this attribute is
-/// copyID (variable) it is set to copyID.
-/// @param conv_type Type of the conversion, @see MPConvStackValType.
-#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
- do { \
- if ((val)->copyID_attr == copyID) { \
- TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
- return OK; \
- } \
- (val)->copyID_attr = copyID; \
- } while (0)
+static inline size_t tv_strlen(const typval_T *const tv)
+ REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
+ REAL_FATTR_NONNULL_ALL;
/// Length of the string stored in typval_T
///
@@ -208,8 +82,6 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
/// @return Length of the string stored in typval_T, including 0 for NULL
/// string.
static inline size_t tv_strlen(const typval_T *const tv)
- FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
- FUNC_ATTR_NONNULL_ALL
{
assert(tv->v_type == VAR_STRING);
return (tv->vval.v_string == NULL
@@ -217,363 +89,56 @@ static inline size_t tv_strlen(const typval_T *const tv)
: strlen((char *) tv->vval.v_string));
}
-/// Define functions to convert a VimL value:
-/// `{name}_convert_one_value(...)`
-/// `encode_vim_to_{name}(...)`
+/// Code for checking whether container references itself
///
-/// @param scope Scope of the main function: either nothing or `static`.
-/// @param name Name of the target converter.
-/// @param firstargtype Type of the first argument. It will be used to return
-/// the results.
-/// @param firstargname Name of the first argument.
-#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
- firstargname) \
-/* Returns OK or FAIL */ \
-static int name##_convert_one_value(firstargtype firstargname, \
- MPConvStack *const mpstack, \
- typval_T *const tv, \
- const int copyID, \
- const char *const objname) \
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- switch (tv->v_type) { \
- case VAR_STRING: { \
- TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \
- break; \
- } \
- case VAR_NUMBER: { \
- TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \
- break; \
- } \
- case VAR_FLOAT: { \
- TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \
- break; \
- } \
- case VAR_FUNC: { \
- TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
- break; \
- } \
- case VAR_PARTIAL: { \
- TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
- break; \
- } \
- case VAR_LIST: { \
- if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
- break; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \
- kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .type = kMPConvList, \
- .tv = tv, \
- .data = { \
- .l = { \
- .list = tv->vval.v_list, \
- .li = tv->vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_SPECIAL: { \
- switch (tv->vval.v_special) { \
- case kSpecialVarNull: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kSpecialVarTrue: \
- case kSpecialVarFalse: { \
- TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
- break; \
- } \
- } \
- break; \
- } \
- case VAR_DICT: { \
- if (tv->vval.v_dict == NULL \
- || tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- const dictitem_T *type_di; \
- const dictitem_T *val_di; \
- if (TYPVAL_ENCODE_ALLOW_SPECIALS \
- && tv->vval.v_dict->dv_hashtab.ht_used == 2 \
- && (type_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_TYPE", -1)) != NULL \
- && type_di->di_tv.v_type == VAR_LIST \
- && (val_di = dict_find((dict_T *) tv->vval.v_dict, \
- (char_u *) "_VAL", -1)) != NULL) { \
- size_t i; \
- for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
- if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
- break; \
- } \
- } \
- if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- switch ((MessagePackType) i) { \
- case kMPNil: { \
- TYPVAL_ENCODE_CONV_NIL(); \
- break; \
- } \
- case kMPBoolean: { \
- if (val_di->di_tv.v_type != VAR_NUMBER) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \
- break; \
- } \
- case kMPInteger: { \
- const list_T *val_list; \
- varnumber_T sign; \
- varnumber_T highest_bits; \
- varnumber_T high_bits; \
- varnumber_T low_bits; \
- /* List of 4 integers; first is signed (should be 1 or -1, but */ \
- /* this is not checked), second is unsigned and have at most */ \
- /* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
- /* bits is not checked), other unsigned and have at most 31 */ \
- /* non-zero bits (number of bits is not checked).*/ \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 4 \
- || val_list->lv_first->li_tv.v_type != VAR_NUMBER \
- || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
- || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
- || (highest_bits = \
- val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
- || (high_bits = \
- val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
- || val_list->lv_last->li_tv.v_type != VAR_NUMBER \
- || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
- | (uint64_t) (((uint64_t) high_bits) << 31) \
- | (uint64_t) low_bits); \
- if (sign > 0) { \
- TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \
- } else { \
- TYPVAL_ENCODE_CONV_NUMBER(-number); \
- } \
- break; \
- } \
- case kMPFloat: { \
- if (val_di->di_tv.v_type != VAR_FLOAT) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \
- break; \
- } \
- case kMPString: \
- case kMPBinary: { \
- const bool is_string = ((MessagePackType) i == kMPString); \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
- &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- if (is_string) { \
- TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \
- } else { \
- TYPVAL_ENCODE_CONV_STRING(buf, len); \
- } \
- xfree(buf); \
- break; \
- } \
- case kMPArray: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \
- lv_copyID, kMPConvList); \
- TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvList, \
- .data = { \
- .l = { \
- .list = val_di->di_tv.vval.v_list, \
- .li = val_di->di_tv.vval.v_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPMap: { \
- if (val_di->di_tv.v_type != VAR_LIST) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- list_T *const val_list = val_di->di_tv.vval.v_list; \
- if (val_list == NULL || val_list->lv_len == 0) { \
- TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
- break; \
- } \
- for (const listitem_T *li = val_list->lv_first; li != NULL; \
- li = li->li_next) { \
- if (li->li_tv.v_type != VAR_LIST \
- || li->li_tv.vval.v_list->lv_len != 2) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- } \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \
- kMPConvPairs); \
- TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvPairs, \
- .data = { \
- .l = { \
- .list = val_list, \
- .li = val_list->lv_first, \
- }, \
- }, \
- })); \
- break; \
- } \
- case kMPExt: { \
- const list_T *val_list; \
- varnumber_T type; \
- if (val_di->di_tv.v_type != VAR_LIST \
- || (val_list = val_di->di_tv.vval.v_list) == NULL \
- || val_list->lv_len != 2 \
- || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
- || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
- || type < INT8_MIN \
- || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- size_t len; \
- char *buf; \
- if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
- &len, &buf)) { \
- goto name##_convert_one_value_regular_dict; \
- } \
- TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \
- xfree(buf); \
- break; \
- } \
- } \
- break; \
- } \
-name##_convert_one_value_regular_dict: \
- _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \
- kMPConvDict); \
- TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
- _mp_push(*mpstack, ((MPConvStackVal) { \
- .tv = tv, \
- .type = kMPConvDict, \
- .data = { \
- .d = { \
- .dict = tv->vval.v_dict, \
- .hi = tv->vval.v_dict->dv_hashtab.ht_array, \
- .todo = tv->vval.v_dict->dv_hashtab.ht_used, \
- }, \
- }, \
- })); \
- break; \
- } \
- case VAR_UNKNOWN: { \
- EMSG2(_(e_intern2), #name "_convert_one_value()"); \
- return FAIL; \
- } \
- } \
- return OK; \
-} \
-\
-scope int encode_vim_to_##name(firstargtype firstargname, typval_T *const tv, \
- const char *const objname) \
- FUNC_ATTR_WARN_UNUSED_RESULT \
-{ \
- const int copyID = get_copyID(); \
- MPConvStack mpstack; \
- _mp_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
- == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- while (_mp_size(mpstack)) { \
- MPConvStackVal *cur_mpsv = &_mp_last(mpstack); \
- typval_T *cur_tv = NULL; \
- switch (cur_mpsv->type) { \
- case kMPConvDict: { \
- if (!cur_mpsv->data.d.todo) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.d.dict->dv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.d.todo \
- != cur_mpsv->data.d.dict->dv_hashtab.ht_used) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { \
- cur_mpsv->data.d.hi++; \
- } \
- dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); \
- cur_mpsv->data.d.todo--; \
- cur_mpsv->data.d.hi++; \
- TYPVAL_ENCODE_CONV_STR_STRING(&di->di_key[0], \
- strlen((char *) &di->di_key[0])); \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &di->di_tv; \
- break; \
- } \
- case kMPConvList: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_LIST_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \
- } \
- cur_tv = &cur_mpsv->data.l.li->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
- } \
- case kMPConvPairs: { \
- if (cur_mpsv->data.l.li == NULL) { \
- (void) _mp_pop(mpstack); \
- cur_mpsv->data.l.list->lv_copyID = copyID - 1; \
- TYPVAL_ENCODE_CONV_DICT_END(); \
- continue; \
- } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { \
- TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(); \
- } \
- const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
- TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK( \
- encode_vim_to_##name##_error_ret, kv_pair->lv_first->li_tv); \
- if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(); \
- cur_tv = &kv_pair->lv_last->li_tv; \
- cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; \
- break; \
+/// @param[in,out] val Container to check.
+/// @param copyID_attr Name of the container attribute that holds copyID.
+/// After checking whether value of this attribute is
+/// copyID (variable) it is set to copyID.
+/// @param[in] copyID CopyID used by the caller.
+/// @param conv_type Type of the conversion, @see MPConvStackValType.
+#define _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
+ conv_type) \
+ do { \
+ const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( \
+ TYPVAL_ENCODE_FIRST_ARG_NAME, \
+ (val), &(val)->copyID_attr, mpstack, copyID, conv_type, objname); \
+ if (te_csr_ret != NOTDONE) { \
+ return te_csr_ret; \
} \
- } \
- assert(cur_tv != NULL); \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
- objname) == FAIL) { \
- goto encode_vim_to_##name##_error_ret; \
- } \
- } \
- _mp_destroy(mpstack); \
- return OK; \
-encode_vim_to_##name##_error_ret: \
- _mp_destroy(mpstack); \
- return FAIL; \
-}
+ } while (0)
+
+#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) \
+ _typval_encode_##name##_check_self_reference
+#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(name) \
+ _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name)
+
+/// Self reference checker function name
+#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
+ _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(TYPVAL_ENCODE_NAME)
+
+#define _TYPVAL_ENCODE_ENCODE_INNER_2(name) encode_vim_to_##name
+#define _TYPVAL_ENCODE_ENCODE_INNER(name) _TYPVAL_ENCODE_ENCODE_INNER_2(name)
+
+/// Entry point function name
+#define _TYPVAL_ENCODE_ENCODE _TYPVAL_ENCODE_ENCODE_INNER(TYPVAL_ENCODE_NAME)
+
+#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) \
+ _typval_encode_##name##_convert_one_value
+#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(name) \
+ _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name)
+
+/// Name of the …convert_one_value function
+#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
+ _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(TYPVAL_ENCODE_NAME)
+
+#define _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) \
+ _typval_encode_##name##_nodict_var
+#define _TYPVAL_ENCODE_NODICT_VAR_INNER(name) \
+ _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name)
+
+/// Name of the dummy const dict_T *const variable
+#define TYPVAL_ENCODE_NODICT_VAR \
+ _TYPVAL_ENCODE_NODICT_VAR_INNER(TYPVAL_ENCODE_NAME)
#endif // NVIM_EVAL_TYPVAL_ENCODE_H