mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #5826 from ZyX-I/fix-typval_encode
Refactor eval/typval_encode.h
This commit is contained in:
commit
b4c0c61f5c
@ -3001,6 +3001,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
|||||||
include = match.group(2)
|
include = match.group(2)
|
||||||
is_system = (match.group(1) == '<')
|
is_system = (match.group(1) == '<')
|
||||||
if include in include_state:
|
if include in include_state:
|
||||||
|
if is_system or not include.endswith('.c.h'):
|
||||||
error(filename, linenum, 'build/include', 4,
|
error(filename, linenum, 'build/include', 4,
|
||||||
'"%s" already included at %s:%s' %
|
'"%s" already included at %s:%s' %
|
||||||
(include, filename, include_state[include]))
|
(include, filename, include_state[include]))
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
#include "nvim/option_defs.h"
|
#include "nvim/option_defs.h"
|
||||||
#include "nvim/version.h"
|
#include "nvim/version.h"
|
||||||
#include "nvim/eval/typval_encode.h"
|
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
|
|
||||||
/// Helper structure for vim_to_object
|
/// 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_ALLOW_SPECIALS false
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
kv_push(edata->stack, NIL)
|
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)))
|
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)))
|
kv_push(edata->stack, INTEGER_OBJ((Integer)(num)))
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
|
#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)))
|
kv_push(edata->stack, FLOATING_OBJ((Float)(flt)))
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_STRING(str, len) \
|
#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
|
||||||
do { \
|
do { \
|
||||||
const size_t len_ = (size_t)(len); \
|
const size_t len_ = (size_t)(len); \
|
||||||
const char *const str_ = (const char *)(str); \
|
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_STR_STRING TYPVAL_ENCODE_CONV_STRING
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(str, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
TYPVAL_ENCODE_CONV_NIL(tv)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
do { \
|
||||||
|
TYPVAL_ENCODE_CONV_NIL(tv); \
|
||||||
|
goto typval_encode_stop_converting_one_item; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
#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 })))
|
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, \
|
kv_push(edata->stack, \
|
||||||
DICTIONARY_OBJ(((Dictionary) { .capacity = 0, .size = 0 })))
|
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))
|
typval_encode_list_start(edata, (size_t)(len))
|
||||||
|
|
||||||
static inline void typval_encode_between_list_items(EncodedData *const edata)
|
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;
|
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)
|
typval_encode_between_list_items(edata)
|
||||||
|
|
||||||
static inline void typval_encode_list_end(EncodedData *const 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_END() \
|
#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
|
||||||
typval_encode_list_end(edata)
|
typval_encode_list_end(edata)
|
||||||
|
|
||||||
static inline void typval_encode_dict_start(EncodedData *const 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))
|
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)
|
static inline void typval_encode_after_key(EncodedData *const edata)
|
||||||
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL
|
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)
|
typval_encode_after_key(edata)
|
||||||
|
|
||||||
static inline void typval_encode_between_dict_items(EncodedData *const 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;
|
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)
|
typval_encode_between_dict_items(edata)
|
||||||
|
|
||||||
static inline void typval_encode_dict_end(EncodedData *const 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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_END() \
|
#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
|
||||||
typval_encode_dict_end(edata)
|
typval_encode_dict_end(edata)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
TYPVAL_ENCODE_CONV_NIL()
|
||||||
|
|
||||||
// object_convert_one_value()
|
#define TYPVAL_ENCODE_SCOPE static
|
||||||
// encode_vim_to_object()
|
#define TYPVAL_ENCODE_NAME object
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
|
#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_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_END
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
@ -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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
#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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef TYPVAL_ENCODE_CONV_RECURSE
|
#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)
|
Object vim_to_object(typval_T *obj)
|
||||||
{
|
{
|
||||||
EncodedData edata = { .stack = KV_INITIAL_VALUE };
|
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);
|
Object ret = kv_A(edata.stack, 0);
|
||||||
assert(kv_size(edata.stack) == 1);
|
assert(kv_size(edata.stack) == 1);
|
||||||
kv_destroy(edata.stack);
|
kv_destroy(edata.stack);
|
||||||
|
226
src/nvim/eval.c
226
src/nvim/eval.c
@ -19042,124 +19042,200 @@ void free_tv(typval_T *varp)
|
|||||||
|
|
||||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
do { \
|
do { \
|
||||||
tv->vval.v_special = kSpecialVarFalse; \
|
tv->vval.v_special = kSpecialVarFalse; \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_BOOL(ignored) \
|
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
TYPVAL_ENCODE_CONV_NIL(tv)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NUMBER(ignored) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
||||||
do { \
|
do { \
|
||||||
(void)ignored; \
|
(void)num; \
|
||||||
tv->vval.v_number = 0; \
|
tv->vval.v_number = 0; \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(ignored) \
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
|
||||||
assert(false)
|
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FLOAT(ignored) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
|
||||||
do { \
|
do { \
|
||||||
tv->vval.v_float = 0; \
|
tv->vval.v_float = 0; \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_STRING(str, ignored) \
|
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
|
||||||
do { \
|
do { \
|
||||||
xfree(str); \
|
xfree(buf); \
|
||||||
tv->vval.v_string = NULL; \
|
tv->vval.v_string = NULL; \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_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 { \
|
do { \
|
||||||
func_unref(fun); \
|
if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
|
||||||
if (fun != empty_string) { \
|
return OK; \
|
||||||
xfree(fun); \
|
|
||||||
} \
|
} \
|
||||||
tv->vval.v_string = NULL; \
|
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
|
||||||
do { \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
|
||||||
partial_unref(pt); \
|
|
||||||
pt = NULL; \
|
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#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 { \
|
do { \
|
||||||
list_unref(tv->vval.v_list); \
|
list_unref(tv->vval.v_list); \
|
||||||
tv->vval.v_list = NULL; \
|
tv->vval.v_list = NULL; \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
tv->v_lock = VAR_UNLOCKED; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
|
||||||
do { \
|
do { \
|
||||||
dict_unref(tv->vval.v_dict); \
|
assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \
|
||||||
tv->vval.v_dict = NULL; \
|
dict_unref((dict_T *)dict); \
|
||||||
tv->v_lock = VAR_UNLOCKED; \
|
*((dict_T **)&dict) = NULL; \
|
||||||
} while (0)
|
if (tv != NULL) { \
|
||||||
|
((typval_T *)tv)->v_lock = VAR_UNLOCKED; \
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_START(ignored) \
|
|
||||||
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; \
|
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS()
|
static inline int _nothing_conv_list_start(typval_T *const tv)
|
||||||
|
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_END() \
|
{
|
||||||
|
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 { \
|
do { \
|
||||||
typval_T *const cur_tv = cur_mpsv->tv; \
|
if (_nothing_conv_list_start(tv) != NOTDONE) { \
|
||||||
assert(cur_tv->v_type == VAR_LIST); \
|
goto typval_encode_stop_converting_one_item; \
|
||||||
list_unref(cur_tv->vval.v_list); \
|
|
||||||
cur_tv->vval.v_list = NULL; \
|
|
||||||
cur_tv->v_lock = VAR_UNLOCKED; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_START(ignored) \
|
|
||||||
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; \
|
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(ignored1, ignored2)
|
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY()
|
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_BETWEEN_ITEMS()
|
static inline int _nothing_conv_dict_start(typval_T *const tv,
|
||||||
|
dict_T **const dictp,
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_END() \
|
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 { \
|
do { \
|
||||||
typval_T *const cur_tv = cur_mpsv->tv; \
|
if (_nothing_conv_dict_start(tv, (dict_T **)&dict, \
|
||||||
assert(cur_tv->v_type == VAR_DICT); \
|
(void *)&TYPVAL_ENCODE_NODICT_VAR) \
|
||||||
dict_unref(cur_tv->vval.v_dict); \
|
!= NOTDONE) { \
|
||||||
cur_tv->vval.v_dict = NULL; \
|
goto typval_encode_stop_converting_one_item; \
|
||||||
cur_tv->v_lock = VAR_UNLOCKED; \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
|
#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)
|
||||||
|
|
||||||
// nothing_convert_one_value()
|
static inline void _nothing_conv_dict_end(typval_T *const tv,
|
||||||
// encode_vim_to_nothing()
|
dict_T **const dictp,
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
|
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(val, conv_type)
|
||||||
|
|
||||||
|
#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_ALLOW_SPECIALS
|
||||||
#undef TYPVAL_ENCODE_CONV_NIL
|
#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_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_END
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_END
|
#undef TYPVAL_ENCODE_CONV_LIST_END
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_START
|
#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_AFTER_KEY
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_END
|
#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)
|
void clear_tv(typval_T *varp)
|
||||||
{
|
{
|
||||||
if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,9 +108,12 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
|
|||||||
{
|
{
|
||||||
garray_T msg_ga;
|
garray_T msg_ga;
|
||||||
ga_init(&msg_ga, (int)sizeof(char), 80);
|
ga_init(&msg_ga, (int)sizeof(char), 80);
|
||||||
char *const key_msg = _("key %s");
|
const char *const key_msg = _("key %s");
|
||||||
char *const key_pair_msg = _("key %s at index %i from special map");
|
const char *const key_pair_msg = _("key %s at index %i from special map");
|
||||||
char *const idx_msg = _("index %i");
|
const char *const idx_msg = _("index %i");
|
||||||
|
const char *const partial_arg_msg = _("partial");
|
||||||
|
const char *const partial_arg_i_msg = _("argument %i");
|
||||||
|
const char *const partial_self_msg = _("partial self dictionary");
|
||||||
for (size_t i = 0; i < kv_size(*mpstack); i++) {
|
for (size_t i = 0; i < kv_size(*mpstack); i++) {
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
ga_concat(&msg_ga, ", ");
|
ga_concat(&msg_ga, ", ");
|
||||||
@ -154,6 +157,29 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kMPConvPartial: {
|
||||||
|
switch (v.data.p.stage) {
|
||||||
|
case kMPConvPartialArgs: {
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kMPConvPartialSelf: {
|
||||||
|
ga_concat(&msg_ga, partial_arg_msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kMPConvPartialEnd: {
|
||||||
|
ga_concat(&msg_ga, partial_self_msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case kMPConvPartialList: {
|
||||||
|
const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
|
||||||
|
vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
|
||||||
|
ga_concat(&msg_ga, IObuff);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EMSG3(msg, objname, (kv_size(*mpstack) == 0
|
EMSG3(msg, objname, (kv_size(*mpstack) == 0
|
||||||
@ -254,7 +280,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
: OK);
|
: OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
|
||||||
do { \
|
do { \
|
||||||
const char *const buf_ = (const char *) buf; \
|
const char *const buf_ = (const char *) buf; \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
@ -273,19 +299,19 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
|
||||||
TYPVAL_ENCODE_CONV_STRING(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 { \
|
do { \
|
||||||
char numbuf[NUMBUFLEN]; \
|
char numbuf[NUMBUFLEN]; \
|
||||||
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
|
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
|
||||||
ga_concat(gap, numbuf); \
|
ga_concat(gap, numbuf); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
|
||||||
do { \
|
do { \
|
||||||
const float_T flt_ = (flt); \
|
const float_T flt_ = (flt); \
|
||||||
switch (fpclassify(flt_)) { \
|
switch (fpclassify(flt_)) { \
|
||||||
@ -308,103 +334,71 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
do { \
|
do { \
|
||||||
|
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("); \
|
ga_concat(gap, "function("); \
|
||||||
TYPVAL_ENCODE_CONV_STRING(fun, STRLEN(fun)); \
|
TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
|
||||||
ga_append(gap, ')'); \
|
}\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
|
||||||
do { \
|
do { \
|
||||||
int i; \
|
if (len != 0) { \
|
||||||
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, ", "); \
|
ga_concat(gap, ", "); \
|
||||||
} \
|
} \
|
||||||
char *tofree = encode_tv2string(&pt->pt_argv[i], NULL); \
|
|
||||||
ga_concat(gap, tofree); \
|
|
||||||
xfree(tofree); \
|
|
||||||
} \
|
|
||||||
ga_append(gap, ']'); \
|
|
||||||
} \
|
|
||||||
if (pt->pt_dict != NULL) { \
|
|
||||||
typval_T dtv; \
|
|
||||||
ga_concat(gap, ", "); \
|
|
||||||
dtv.v_type = VAR_DICT; \
|
|
||||||
dtv.vval.v_dict = pt->pt_dict; \
|
|
||||||
char *tofree = encode_tv2string(&dtv, NULL); \
|
|
||||||
ga_concat(gap, tofree); \
|
|
||||||
xfree(tofree); \
|
|
||||||
} \
|
|
||||||
ga_append(gap, ')'); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_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, "[]")
|
ga_concat(gap, "[]")
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_START(len) \
|
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
|
||||||
ga_append(gap, '[')
|
ga_append(gap, '[')
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_DICT() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
|
||||||
ga_concat(gap, "{}")
|
ga_concat(gap, "{}")
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
ga_concat(gap, "v:null")
|
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"))
|
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, '{')
|
ga_append(gap, '{')
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_END() \
|
#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
|
||||||
ga_append(gap, '}')
|
ga_append(gap, '}')
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY() \
|
#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
|
||||||
ga_concat(gap, ": ")
|
ga_concat(gap, ": ")
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS() \
|
#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
|
||||||
ga_concat(gap, ", ")
|
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, ']')
|
ga_append(gap, ']')
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS() \
|
#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
|
||||||
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS()
|
TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
do { \
|
do { \
|
||||||
@ -437,9 +431,15 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
|
|
||||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
||||||
|
|
||||||
// string_convert_one_value()
|
#define TYPVAL_ENCODE_SCOPE static
|
||||||
// encode_vim_to_string()
|
#define TYPVAL_ENCODE_NAME string
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
#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
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
#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; \
|
return OK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// echo_convert_one_value()
|
#define TYPVAL_ENCODE_SCOPE
|
||||||
// encode_vim_to_echo()
|
#define TYPVAL_ENCODE_NAME echo
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
#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
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
#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
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_NIL
|
#undef TYPVAL_ENCODE_CONV_NIL
|
||||||
#define TYPVAL_ENCODE_CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
ga_concat(gap, "null")
|
ga_concat(gap, "null")
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_BOOL
|
#undef TYPVAL_ENCODE_CONV_BOOL
|
||||||
#define TYPVAL_ENCODE_CONV_BOOL(num) \
|
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
|
||||||
ga_concat(gap, ((num)? "true": "false"))
|
ga_concat(gap, ((num)? "true": "false"))
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
|
#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
|
||||||
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
|
||||||
do { \
|
do { \
|
||||||
char numbuf[NUMBUFLEN]; \
|
char numbuf[NUMBUFLEN]; \
|
||||||
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
|
vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
|
||||||
@ -505,7 +511,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
|
||||||
do { \
|
do { \
|
||||||
const float_T flt_ = (flt); \
|
const float_T flt_ = (flt); \
|
||||||
switch (fpclassify(flt_)) { \
|
switch (fpclassify(flt_)) { \
|
||||||
@ -694,7 +700,7 @@ static inline int convert_to_json_string(garray_T *const gap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_STRING
|
#undef TYPVAL_ENCODE_CONV_STRING
|
||||||
#define TYPVAL_ENCODE_CONV_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
|
||||||
do { \
|
do { \
|
||||||
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
|
if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
|
||||||
return FAIL; \
|
return FAIL; \
|
||||||
@ -702,25 +708,19 @@ static inline int convert_to_json_string(garray_T *const gap,
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#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 { \
|
do { \
|
||||||
xfree(buf); \
|
xfree(buf); \
|
||||||
EMSG(_("E474: Unable to convert EXT string to JSON")); \
|
EMSG(_("E474: Unable to convert EXT string to JSON")); \
|
||||||
return FAIL; \
|
return FAIL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
return conv_error(_("E474: Error while dumping %s, %s: " \
|
return conv_error(_("E474: Error while dumping %s, %s: " \
|
||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
|
||||||
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
|
|
||||||
return conv_error(_("E474: Error while dumping %s, %s: " \
|
|
||||||
"attempt to dump partial"), \
|
|
||||||
mpstack, objname)
|
|
||||||
|
|
||||||
/// Check whether given key can be used in json_encode()
|
/// Check whether given key can be used in json_encode()
|
||||||
///
|
///
|
||||||
/// @param[in] tv Key to check.
|
/// @param[in] tv Key to check.
|
||||||
@ -759,8 +759,8 @@ bool encode_check_json_key(const typval_T *const tv)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK
|
#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
|
||||||
#define TYPVAL_ENCODE_CONV_SPECIAL_DICT_KEY_CHECK(label, key) \
|
#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
|
||||||
do { \
|
do { \
|
||||||
if (!encode_check_json_key(&key)) { \
|
if (!encode_check_json_key(&key)) { \
|
||||||
EMSG(_("E474: Invalid key in special dictionary")); \
|
EMSG(_("E474: Invalid key in special dictionary")); \
|
||||||
@ -768,17 +768,25 @@ bool encode_check_json_key(const typval_T *const tv)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// json_convert_one_value()
|
#define TYPVAL_ENCODE_SCOPE static
|
||||||
// encode_vim_to_json()
|
#define TYPVAL_ENCODE_NAME json
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
#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_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_END
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
@ -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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
#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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef TYPVAL_ENCODE_CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
@ -807,7 +815,10 @@ char *encode_tv2string(typval_T *tv, size_t *len)
|
|||||||
{
|
{
|
||||||
garray_T ga;
|
garray_T ga;
|
||||||
ga_init(&ga, (int)sizeof(char), 80);
|
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;
|
did_echo_string_emsg = false;
|
||||||
if (len != NULL) {
|
if (len != NULL) {
|
||||||
*len = (size_t) ga.ga_len;
|
*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);
|
ga_concat(&ga, tv->vval.v_string);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (len != NULL) {
|
||||||
*len = (size_t) ga.ga_len;
|
*len = (size_t) ga.ga_len;
|
||||||
@ -854,16 +867,19 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
{
|
{
|
||||||
garray_T ga;
|
garray_T ga;
|
||||||
ga_init(&ga, (int)sizeof(char), 80);
|
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;
|
did_echo_string_emsg = false;
|
||||||
if (len != NULL) {
|
if (len != NULL) {
|
||||||
*len = (size_t) ga.ga_len;
|
*len = (size_t)ga.ga_len;
|
||||||
}
|
}
|
||||||
ga_append(&ga, '\0');
|
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 { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_bin(packer, 0); \
|
msgpack_pack_bin(packer, 0); \
|
||||||
@ -874,7 +890,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_STR_STRING(buf, len) \
|
#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
|
||||||
do { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_str(packer, 0); \
|
msgpack_pack_str(packer, 0); \
|
||||||
@ -885,7 +901,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type) \
|
#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
|
||||||
do { \
|
do { \
|
||||||
if (buf == NULL) { \
|
if (buf == NULL) { \
|
||||||
msgpack_pack_ext(packer, 0, (int8_t) type); \
|
msgpack_pack_ext(packer, 0, (int8_t) type); \
|
||||||
@ -896,35 +912,34 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
|
||||||
msgpack_pack_int64(packer, (int64_t) (num))
|
msgpack_pack_int64(packer, (int64_t)(num))
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FLOAT(flt) \
|
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
|
||||||
msgpack_pack_double(packer, (double) (flt))
|
msgpack_pack_double(packer, (double)(flt))
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
|
||||||
return conv_error(_("E5004: Error while dumping %s, %s: " \
|
return conv_error(_("E5004: Error while dumping %s, %s: " \
|
||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
|
||||||
return conv_error(_("E5004: Error while dumping %s, %s: " \
|
#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
|
||||||
"attempt to dump partial"), \
|
#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
|
||||||
mpstack, objname)
|
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
|
||||||
msgpack_pack_array(packer, 0)
|
msgpack_pack_array(packer, 0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_LIST_START(len) \
|
#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
|
||||||
msgpack_pack_array(packer, (size_t) (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)
|
msgpack_pack_map(packer, 0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_NIL() \
|
#define TYPVAL_ENCODE_CONV_NIL(tv) \
|
||||||
msgpack_pack_nil(packer)
|
msgpack_pack_nil(packer)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_BOOL(num) \
|
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
|
||||||
do { \
|
do { \
|
||||||
if ((num)) { \
|
if ((num)) { \
|
||||||
msgpack_pack_true(packer); \
|
msgpack_pack_true(packer); \
|
||||||
@ -933,23 +948,23 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(num) \
|
#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
|
||||||
msgpack_pack_uint64(packer, (num))
|
msgpack_pack_uint64(packer, (num))
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_DICT_START(len) \
|
#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
|
||||||
msgpack_pack_map(packer, (size_t) (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) \
|
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||||
return conv_error(_("E5005: Unable to dump %s: " \
|
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
|
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||||
|
|
||||||
// msgpack_convert_one_value()
|
#define TYPVAL_ENCODE_SCOPE
|
||||||
// encode_vim_to_msgpack()
|
#define TYPVAL_ENCODE_NAME msgpack
|
||||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
#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_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
#undef TYPVAL_ENCODE_CONV_EXT_STRING
|
||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC_START
|
||||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||||
|
#undef TYPVAL_ENCODE_CONV_FUNC_END
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
@ -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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
|
||||||
#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
|
#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_END
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
|
||||||
#undef TYPVAL_ENCODE_CONV_RECURSE
|
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||||
|
776
src/nvim/eval/typval_encode.c.h
Normal file
776
src/nvim/eval/typval_encode.c.h
Normal file
@ -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
|
@ -1,152 +1,17 @@
|
|||||||
/// @file eval/typval_convert.h
|
/// @file eval/typval_encode.h
|
||||||
///
|
///
|
||||||
/// Contains set of macros used to convert (possibly recursive) typval_T into
|
/// Contains common definitions for eval/typval_encode.c.h. Most of time should
|
||||||
/// something else. For these macros to work the following macros must be
|
/// not be included directly.
|
||||||
/// 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.
|
|
||||||
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
|
#ifndef NVIM_EVAL_TYPVAL_ENCODE_H
|
||||||
#define NVIM_EVAL_TYPVAL_ENCODE_H
|
#define NVIM_EVAL_TYPVAL_ENCODE_H
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "nvim/lib/kvec.h"
|
#include "nvim/lib/kvec.h"
|
||||||
#include "nvim/eval_defs.h"
|
#include "nvim/eval_defs.h"
|
||||||
#include "nvim/eval/encode.h"
|
|
||||||
#include "nvim/func_attr.h"
|
#include "nvim/func_attr.h"
|
||||||
|
|
||||||
/// Type of the stack entry
|
/// Type of the stack entry
|
||||||
@ -154,8 +19,17 @@ typedef enum {
|
|||||||
kMPConvDict, ///< Convert dict_T *dictionary.
|
kMPConvDict, ///< Convert dict_T *dictionary.
|
||||||
kMPConvList, ///< Convert list_T *list.
|
kMPConvList, ///< Convert list_T *list.
|
||||||
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
|
kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs.
|
||||||
|
kMPConvPartial, ///< Convert partial_T* partial.
|
||||||
|
kMPConvPartialList, ///< Convert argc/argv pair coming from a partial.
|
||||||
} MPConvStackValType;
|
} MPConvStackValType;
|
||||||
|
|
||||||
|
/// Stage at which partial is being converted
|
||||||
|
typedef enum {
|
||||||
|
kMPConvPartialArgs, ///< About to convert arguments.
|
||||||
|
kMPConvPartialSelf, ///< About to convert self dictionary.
|
||||||
|
kMPConvPartialEnd, ///< Already converted everything.
|
||||||
|
} MPConvPartialStage;
|
||||||
|
|
||||||
/// Structure representing current VimL to messagepack conversion state
|
/// Structure representing current VimL to messagepack conversion state
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MPConvStackValType type; ///< Type of the stack entry.
|
MPConvStackValType type; ///< Type of the stack entry.
|
||||||
@ -163,6 +37,9 @@ typedef struct {
|
|||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
dict_T *dict; ///< Currently converted dictionary.
|
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.
|
hashitem_T *hi; ///< Currently converted dictionary item.
|
||||||
size_t todo; ///< Amount of items left to process.
|
size_t todo; ///< Amount of items left to process.
|
||||||
} d; ///< State of dictionary conversion.
|
} d; ///< State of dictionary conversion.
|
||||||
@ -170,6 +47,15 @@ typedef struct {
|
|||||||
list_T *list; ///< Currently converted list.
|
list_T *list; ///< Currently converted list.
|
||||||
listitem_T *li; ///< Currently converted list item.
|
listitem_T *li; ///< Currently converted list item.
|
||||||
} l; ///< State of list or generic mapping conversion.
|
} l; ///< State of list or generic mapping conversion.
|
||||||
|
struct {
|
||||||
|
MPConvPartialStage stage; ///< Stage at which partial is being converted.
|
||||||
|
partial_T *pt; ///< Currently converted partial.
|
||||||
|
} p; ///< State of partial conversion.
|
||||||
|
struct {
|
||||||
|
typval_T *arg; ///< Currently converted argument.
|
||||||
|
typval_T *argv; ///< Start of the argument list.
|
||||||
|
size_t todo; ///< Number of items left to process.
|
||||||
|
} a; ///< State of list or generic mapping conversion.
|
||||||
} data; ///< Data to convert.
|
} data; ///< Data to convert.
|
||||||
} MPConvStackVal;
|
} MPConvStackVal;
|
||||||
|
|
||||||
@ -184,21 +70,9 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
|
|||||||
#define _mp_pop kv_pop
|
#define _mp_pop kv_pop
|
||||||
#define _mp_last kv_last
|
#define _mp_last kv_last
|
||||||
|
|
||||||
/// Code for checking whether container references itself
|
static inline size_t tv_strlen(const typval_T *const tv)
|
||||||
///
|
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
|
||||||
/// @param[in,out] val Container to check.
|
REAL_FATTR_NONNULL_ALL;
|
||||||
/// @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)
|
|
||||||
|
|
||||||
/// Length of the string stored in typval_T
|
/// 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
|
/// @return Length of the string stored in typval_T, including 0 for NULL
|
||||||
/// string.
|
/// string.
|
||||||
static inline size_t tv_strlen(const typval_T *const tv)
|
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);
|
assert(tv->v_type == VAR_STRING);
|
||||||
return (tv->vval.v_string == NULL
|
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));
|
: strlen((char *) tv->vval.v_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define functions to convert a VimL value:
|
/// Code for checking whether container references itself
|
||||||
/// `{name}_convert_one_value(...)`
|
|
||||||
/// `encode_vim_to_{name}(...)`
|
|
||||||
///
|
///
|
||||||
/// @param scope Scope of the main function: either nothing or `static`.
|
/// @param[in,out] val Container to check.
|
||||||
/// @param name Name of the target converter.
|
/// @param copyID_attr Name of the container attribute that holds copyID.
|
||||||
/// @param firstargtype Type of the first argument. It will be used to return
|
/// After checking whether value of this attribute is
|
||||||
/// the results.
|
/// copyID (variable) it is set to copyID.
|
||||||
/// @param firstargname Name of the first argument.
|
/// @param[in] copyID CopyID used by the caller.
|
||||||
#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
|
/// @param conv_type Type of the conversion, @see MPConvStackValType.
|
||||||
firstargname) \
|
#define _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
|
||||||
/* Returns OK or FAIL */ \
|
conv_type) \
|
||||||
static int name##_convert_one_value(firstargtype firstargname, \
|
do { \
|
||||||
MPConvStack *const mpstack, \
|
const int te_csr_ret = _TYPVAL_ENCODE_CHECK_SELF_REFERENCE( \
|
||||||
typval_T *const tv, \
|
TYPVAL_ENCODE_FIRST_ARG_NAME, \
|
||||||
const int copyID, \
|
(val), &(val)->copyID_attr, mpstack, copyID, conv_type, objname); \
|
||||||
const char *const objname) \
|
if (te_csr_ret != NOTDONE) { \
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
|
return te_csr_ret; \
|
||||||
{ \
|
|
||||||
switch (tv->v_type) { \
|
|
||||||
case VAR_STRING: { \
|
|
||||||
TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \
|
|
||||||
break; \
|
|
||||||
} \
|
} \
|
||||||
case VAR_NUMBER: { \
|
} while (0)
|
||||||
TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \
|
|
||||||
break; \
|
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) \
|
||||||
} \
|
_typval_encode_##name##_check_self_reference
|
||||||
case VAR_FLOAT: { \
|
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(name) \
|
||||||
TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \
|
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name)
|
||||||
break; \
|
|
||||||
} \
|
/// Self reference checker function name
|
||||||
case VAR_FUNC: { \
|
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \
|
||||||
TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
|
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(TYPVAL_ENCODE_NAME)
|
||||||
break; \
|
|
||||||
} \
|
#define _TYPVAL_ENCODE_ENCODE_INNER_2(name) encode_vim_to_##name
|
||||||
case VAR_PARTIAL: { \
|
#define _TYPVAL_ENCODE_ENCODE_INNER(name) _TYPVAL_ENCODE_ENCODE_INNER_2(name)
|
||||||
TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
|
|
||||||
break; \
|
/// Entry point function name
|
||||||
} \
|
#define _TYPVAL_ENCODE_ENCODE _TYPVAL_ENCODE_ENCODE_INNER(TYPVAL_ENCODE_NAME)
|
||||||
case VAR_LIST: { \
|
|
||||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) \
|
||||||
TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
|
_typval_encode_##name##_convert_one_value
|
||||||
break; \
|
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(name) \
|
||||||
} \
|
_TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name)
|
||||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \
|
|
||||||
kMPConvList); \
|
/// Name of the …convert_one_value function
|
||||||
TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
|
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
|
||||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
_TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(TYPVAL_ENCODE_NAME)
|
||||||
.type = kMPConvList, \
|
|
||||||
.tv = tv, \
|
#define _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) \
|
||||||
.data = { \
|
_typval_encode_##name##_nodict_var
|
||||||
.l = { \
|
#define _TYPVAL_ENCODE_NODICT_VAR_INNER(name) \
|
||||||
.list = tv->vval.v_list, \
|
_TYPVAL_ENCODE_NODICT_VAR_INNER_2(name)
|
||||||
.li = tv->vval.v_list->lv_first, \
|
|
||||||
}, \
|
/// Name of the dummy const dict_T *const variable
|
||||||
}, \
|
#define TYPVAL_ENCODE_NODICT_VAR \
|
||||||
})); \
|
_TYPVAL_ENCODE_NODICT_VAR_INNER(TYPVAL_ENCODE_NAME)
|
||||||
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; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
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; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
|
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
|
||||||
|
@ -672,6 +672,12 @@ describe('json_encode() function', function()
|
|||||||
exc_exec('call json_encode(function("tr"))'))
|
exc_exec('call json_encode(function("tr"))'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails to dump a partial', function()
|
||||||
|
execute('function T() dict\nendfunction')
|
||||||
|
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, itself: attempt to dump function reference',
|
||||||
|
exc_exec('call json_encode(function("T", [1, 2], {}))'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails to dump a function reference in a list', function()
|
it('fails to dump a function reference in a list', function()
|
||||||
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
|
eq('Vim(call):E474: Error while dumping encode_tv2json() argument, index 0: attempt to dump function reference',
|
||||||
exc_exec('call json_encode([function("tr")])'))
|
exc_exec('call json_encode([function("tr")])'))
|
||||||
|
@ -493,6 +493,12 @@ describe('msgpackparse() function', function()
|
|||||||
exc_exec('call msgpackparse(function("tr"))'))
|
exc_exec('call msgpackparse(function("tr"))'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails to parse a partial', function()
|
||||||
|
execute('function T() dict\nendfunction')
|
||||||
|
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
||||||
|
exc_exec('call msgpackparse(function("T", [1, 2], {}))'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails to parse a float', function()
|
it('fails to parse a float', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
eq('Vim(call):E686: Argument of msgpackparse() must be a List',
|
||||||
exc_exec('call msgpackparse(0.0)'))
|
exc_exec('call msgpackparse(0.0)'))
|
||||||
@ -570,6 +576,13 @@ describe('msgpackdump() function', function()
|
|||||||
exc_exec('call msgpackdump([Todump])'))
|
exc_exec('call msgpackdump([Todump])'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails to dump a partial', function()
|
||||||
|
execute('function T() dict\nendfunction')
|
||||||
|
execute('let Todump = function("T", [1, 2], {})')
|
||||||
|
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
|
||||||
|
exc_exec('call msgpackdump([Todump])'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails to dump a function reference in a list', function()
|
it('fails to dump a function reference in a list', function()
|
||||||
execute('let todump = [function("tr")]')
|
execute('let todump = [function("tr")]')
|
||||||
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
|
eq('Vim(call):E5004: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
|
||||||
@ -675,6 +688,12 @@ describe('msgpackdump() function', function()
|
|||||||
exc_exec('call msgpackdump(function("tr"))'))
|
exc_exec('call msgpackdump(function("tr"))'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('fails to dump a partial', function()
|
||||||
|
execute('function T() dict\nendfunction')
|
||||||
|
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
|
||||||
|
exc_exec('call msgpackdump(function("T", [1, 2], {}))'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('fails to dump a float', function()
|
it('fails to dump a float', function()
|
||||||
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
|
eq('Vim(call):E686: Argument of msgpackdump() must be a List',
|
||||||
exc_exec('call msgpackdump(0.0)'))
|
exc_exec('call msgpackdump(0.0)'))
|
||||||
|
@ -9,6 +9,8 @@ local redir_exec = helpers.redir_exec
|
|||||||
local funcs = helpers.funcs
|
local funcs = helpers.funcs
|
||||||
local write_file = helpers.write_file
|
local write_file = helpers.write_file
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
|
local source = helpers.source
|
||||||
|
local dedent = helpers.dedent
|
||||||
|
|
||||||
describe('string() function', function()
|
describe('string() function', function()
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
@ -110,10 +112,10 @@ describe('string() function', function()
|
|||||||
function Test1()
|
function Test1()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function s:Test2()
|
function s:Test2() dict
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function g:Test3()
|
function g:Test3() dict
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let g:Test2_f = function('s:Test2')
|
let g:Test2_f = function('s:Test2')
|
||||||
@ -137,6 +139,85 @@ describe('string() function', function()
|
|||||||
it('dumps references to script functions', function()
|
it('dumps references to script functions', function()
|
||||||
eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
|
eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('dumps partials with self referencing a partial', function()
|
||||||
|
source([[
|
||||||
|
function TestDict() dict
|
||||||
|
endfunction
|
||||||
|
let d = {}
|
||||||
|
let TestDictRef = function('TestDict', d)
|
||||||
|
let d.tdr = TestDictRef
|
||||||
|
]])
|
||||||
|
eq("\nE724: unable to correctly dump variable with self-referencing container\nfunction('TestDict', {'tdr': function('TestDict', {E724@1})})",
|
||||||
|
redir_exec('echo string(d.tdr)'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps automatically created partials', function()
|
||||||
|
eq('function(\'<SNR>1_Test2\', {\'f\': function(\'<SNR>1_Test2\')})',
|
||||||
|
eval('string({"f": Test2_f}.f)'))
|
||||||
|
eq('function(\'<SNR>1_Test2\', [1], {\'f\': function(\'<SNR>1_Test2\', [1])})',
|
||||||
|
eval('string({"f": function(Test2_f, [1])}.f)'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('dumps manually created partials', function()
|
||||||
|
eq('function(\'Test3\', [1, 2], {})',
|
||||||
|
eval('string(function("Test3", [1, 2], {}))'))
|
||||||
|
eq('function(\'Test3\', {})',
|
||||||
|
eval('string(function("Test3", {}))'))
|
||||||
|
eq('function(\'Test3\', [1, 2])',
|
||||||
|
eval('string(function("Test3", [1, 2]))'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not crash or halt when dumping partials with reference cycles in self',
|
||||||
|
function()
|
||||||
|
meths.set_var('d', {v=true})
|
||||||
|
eq(dedent([[
|
||||||
|
|
||||||
|
E724: unable to correctly dump variable with self-referencing container
|
||||||
|
{'p': function('<SNR>1_Test2', {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]]),
|
||||||
|
redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not show errors when dumping partials referencing the same dictionary',
|
||||||
|
function()
|
||||||
|
command('let d = {}')
|
||||||
|
-- Regression for “eval/typval_encode: Dump empty dictionary before
|
||||||
|
-- checking for refcycle”, results in error.
|
||||||
|
eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('string([function("tr", d), function("tr", d)])'))
|
||||||
|
-- Regression for “eval: Work with reference cycles in partials (self)
|
||||||
|
-- properly”, results in crash.
|
||||||
|
eval('extend(d, {"a": 1})')
|
||||||
|
eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('string([function("tr", d), function("tr", d)])'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not crash or halt when dumping partials with reference cycles in arguments',
|
||||||
|
function()
|
||||||
|
meths.set_var('l', {})
|
||||||
|
eval('add(l, l)')
|
||||||
|
-- Regression: the below line used to crash (add returns original list and
|
||||||
|
-- there was error in dumping partials). Tested explicitly in
|
||||||
|
-- test/unit/api/private_helpers_spec.lua.
|
||||||
|
eval('add(l, function("Test1", l))')
|
||||||
|
eq(dedent([=[
|
||||||
|
|
||||||
|
E724: unable to correctly dump variable with self-referencing container
|
||||||
|
function('Test1', [[{E724@2}, function('Test1', [{E724@2}])], function('Test1', [[{E724@4}, function('Test1', [{E724@4}])]])])]=]),
|
||||||
|
redir_exec('echo string(function("Test1", l))'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('does not crash or halt when dumping partials with reference cycles in self and arguments',
|
||||||
|
function()
|
||||||
|
meths.set_var('d', {v=true})
|
||||||
|
meths.set_var('l', {})
|
||||||
|
eval('add(l, l)')
|
||||||
|
eval('add(l, function("Test1", l))')
|
||||||
|
eval('add(l, function("Test1", d))')
|
||||||
|
eq(dedent([=[
|
||||||
|
|
||||||
|
E724: unable to correctly dump variable with self-referencing container
|
||||||
|
{'p': function('<SNR>1_Test2', [[{E724@3}, function('Test1', [{E724@3}]), function('Test1', {E724@0})], function('Test1', [[{E724@5}, function('Test1', [{E724@5}]), function('Test1', {E724@0})]]), function('Test1', {E724@0})], {E724@0}), 'f': function('<SNR>1_Test2'), 'v': v:true}]=]),
|
||||||
|
redir_exec('echo string(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('used to represent lists', function()
|
describe('used to represent lists', function()
|
||||||
@ -174,6 +255,13 @@ describe('string() function', function()
|
|||||||
eq('{}', eval('string({})'))
|
eq('{}', eval('string({})'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('dumps list with two same empty dictionaries, also in partials', function()
|
||||||
|
command('let d = {}')
|
||||||
|
eq('[{}, {}]', eval('string([d, d])'))
|
||||||
|
eq('[function(\'tr\', {}), {}]', eval('string([function("tr", d), d])'))
|
||||||
|
eq('[{}, function(\'tr\', {})]', eval('string([d, function("tr", d)])'))
|
||||||
|
end)
|
||||||
|
|
||||||
it('dumps non-empty dictionary', function()
|
it('dumps non-empty dictionary', function()
|
||||||
eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1}))
|
eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1}))
|
||||||
end)
|
end)
|
||||||
|
@ -7,6 +7,7 @@ local NULL = helpers.NULL
|
|||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
|
||||||
local lua2typvalt = eval_helpers.lua2typvalt
|
local lua2typvalt = eval_helpers.lua2typvalt
|
||||||
|
local typvalt2lua = eval_helpers.typvalt2lua
|
||||||
local typvalt = eval_helpers.typvalt
|
local typvalt = eval_helpers.typvalt
|
||||||
|
|
||||||
local nil_value = api_helpers.nil_value
|
local nil_value = api_helpers.nil_value
|
||||||
@ -14,6 +15,7 @@ local list_type = api_helpers.list_type
|
|||||||
local int_type = api_helpers.int_type
|
local int_type = api_helpers.int_type
|
||||||
local type_key = api_helpers.type_key
|
local type_key = api_helpers.type_key
|
||||||
local obj2lua = api_helpers.obj2lua
|
local obj2lua = api_helpers.obj2lua
|
||||||
|
local func_type = api_helpers.func_type
|
||||||
|
|
||||||
local api = cimport('./src/nvim/api/private/helpers.h')
|
local api = cimport('./src/nvim/api/private/helpers.h')
|
||||||
|
|
||||||
@ -85,4 +87,19 @@ describe('vim_to_object', function()
|
|||||||
eq(nil, tt.vval.v_dict)
|
eq(nil, tt.vval.v_dict)
|
||||||
eq({}, obj2lua(api.vim_to_object(tt)))
|
eq({}, obj2lua(api.vim_to_object(tt)))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('regression: partials in a list', function()
|
||||||
|
local llist = {
|
||||||
|
{
|
||||||
|
[type_key]=func_type,
|
||||||
|
value='printf',
|
||||||
|
args={'%s'},
|
||||||
|
dict={v=1},
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
}
|
||||||
|
local list = lua2typvalt(llist)
|
||||||
|
eq(llist, typvalt2lua(list))
|
||||||
|
eq({nil_value, {}}, obj2lua(api.vim_to_object(list)))
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
@ -9,6 +9,7 @@ local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h')
|
|||||||
|
|
||||||
local null_string = {[true]='NULL string'}
|
local null_string = {[true]='NULL string'}
|
||||||
local null_list = {[true]='NULL list'}
|
local null_list = {[true]='NULL list'}
|
||||||
|
local null_dict = {[true]='NULL dict'}
|
||||||
local type_key = {[true]='type key'}
|
local type_key = {[true]='type key'}
|
||||||
local list_type = {[true]='list type'}
|
local list_type = {[true]='list type'}
|
||||||
local dict_type = {[true]='dict type'}
|
local dict_type = {[true]='dict type'}
|
||||||
@ -18,27 +19,28 @@ local flt_type = {[true]='flt type'}
|
|||||||
|
|
||||||
local nil_value = {[true]='nil'}
|
local nil_value = {[true]='nil'}
|
||||||
|
|
||||||
|
local lua2typvalt
|
||||||
|
|
||||||
|
local function li_alloc(nogc)
|
||||||
|
local gcfunc = eval.listitem_free
|
||||||
|
if nogc then gcfunc = nil end
|
||||||
|
local li = ffi.gc(eval.listitem_alloc(), gcfunc)
|
||||||
|
li.li_next = nil
|
||||||
|
li.li_prev = nil
|
||||||
|
li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED}
|
||||||
|
return li
|
||||||
|
end
|
||||||
|
|
||||||
local function list(...)
|
local function list(...)
|
||||||
local ret = ffi.gc(eval.list_alloc(), eval.list_unref)
|
local ret = ffi.gc(eval.list_alloc(), eval.list_unref)
|
||||||
eq(0, ret.lv_refcount)
|
eq(0, ret.lv_refcount)
|
||||||
ret.lv_refcount = 1
|
ret.lv_refcount = 1
|
||||||
for i = 1, select('#', ...) do
|
for i = 1, select('#', ...) do
|
||||||
local val = select(i, ...)
|
local val = select(i, ...)
|
||||||
local typ = type(val)
|
local li_tv = ffi.gc(lua2typvalt(val), nil)
|
||||||
if typ == 'string' then
|
local li = li_alloc(true)
|
||||||
eval.list_append_string(ret, to_cstr(val))
|
li.li_tv = li_tv
|
||||||
elseif typ == 'table' and val == null_string then
|
eval.tv_list_append(ret, li)
|
||||||
eval.list_append_string(ret, nil)
|
|
||||||
elseif typ == 'table' and val == null_list then
|
|
||||||
eval.list_append_list(ret, nil)
|
|
||||||
elseif typ == 'table' and val[type_key] == list_type then
|
|
||||||
local itemlist = ffi.gc(list(table.unpack(val)), nil)
|
|
||||||
eq(1, itemlist.lv_refcount)
|
|
||||||
itemlist.lv_refcount = 0
|
|
||||||
eval.list_append_list(ret, itemlist)
|
|
||||||
else
|
|
||||||
assert(false, 'Not implemented yet')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
@ -49,9 +51,14 @@ local special_tab = {
|
|||||||
[eval.kSpecialVarTrue] = true,
|
[eval.kSpecialVarTrue] = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local ptr2key = function(ptr)
|
||||||
|
return tostring(ptr)
|
||||||
|
end
|
||||||
|
|
||||||
local lst2tbl
|
local lst2tbl
|
||||||
local dct2tbl
|
local dct2tbl
|
||||||
|
|
||||||
|
local typvalt2lua
|
||||||
local typvalt2lua_tab
|
local typvalt2lua_tab
|
||||||
|
|
||||||
typvalt2lua_tab = {
|
typvalt2lua_tab = {
|
||||||
@ -72,32 +79,64 @@ typvalt2lua_tab = {
|
|||||||
return ffi.string(str)
|
return ffi.string(str)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
[tonumber(eval.VAR_LIST)] = function(t)
|
[tonumber(eval.VAR_LIST)] = function(t, processed)
|
||||||
return lst2tbl(t.vval.v_list)
|
return lst2tbl(t.vval.v_list, processed)
|
||||||
end,
|
end,
|
||||||
[tonumber(eval.VAR_DICT)] = function(t)
|
[tonumber(eval.VAR_DICT)] = function(t, processed)
|
||||||
return dct2tbl(t.vval.v_dict)
|
return dct2tbl(t.vval.v_dict, processed)
|
||||||
end,
|
end,
|
||||||
[tonumber(eval.VAR_FUNC)] = function(t)
|
[tonumber(eval.VAR_FUNC)] = function(t, processed)
|
||||||
return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t)}
|
return {[type_key]=func_type, value=typvalt2lua_tab[eval.VAR_STRING](t, processed or {})}
|
||||||
|
end,
|
||||||
|
[tonumber(eval.VAR_PARTIAL)] = function(t, processed)
|
||||||
|
local p_key = ptr2key(t)
|
||||||
|
if processed[p_key] then
|
||||||
|
return processed[p_key]
|
||||||
|
end
|
||||||
|
local pt = t.vval.v_partial
|
||||||
|
local value, auto, dict, argv = nil, nil, nil, nil
|
||||||
|
if pt ~= nil then
|
||||||
|
value = ffi.string(pt.pt_name)
|
||||||
|
auto = pt.pt_auto and true or nil
|
||||||
|
argv = {}
|
||||||
|
for i = 1, pt.pt_argc do
|
||||||
|
argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed)
|
||||||
|
end
|
||||||
|
if pt.pt_dict ~= nil then
|
||||||
|
dict = dct2tbl(pt.pt_dict)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
[type_key]=func_type,
|
||||||
|
value=value,
|
||||||
|
auto=auto,
|
||||||
|
args=argv,
|
||||||
|
dict=dict,
|
||||||
|
}
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
local typvalt2lua = function(t)
|
typvalt2lua = function(t, processed)
|
||||||
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
|
return ((typvalt2lua_tab[tonumber(t.v_type)] or function(t_inner)
|
||||||
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
|
assert(false, 'Converting ' .. tonumber(t_inner.v_type) .. ' was not implemented yet')
|
||||||
end)(t))
|
end)(t, processed or {}))
|
||||||
end
|
end
|
||||||
|
|
||||||
lst2tbl = function(l)
|
lst2tbl = function(l, processed)
|
||||||
local ret = {[type_key]=list_type}
|
|
||||||
if l == nil then
|
if l == nil then
|
||||||
return ret
|
return null_list
|
||||||
end
|
end
|
||||||
|
processed = processed or {}
|
||||||
|
local p_key = ptr2key(l)
|
||||||
|
if processed[p_key] then
|
||||||
|
return processed[p_key]
|
||||||
|
end
|
||||||
|
local ret = {[type_key]=list_type}
|
||||||
|
processed[p_key] = ret
|
||||||
local li = l.lv_first
|
local li = l.lv_first
|
||||||
-- (listitem_T *) NULL is equal to nil, but yet it is not false.
|
-- (listitem_T *) NULL is equal to nil, but yet it is not false.
|
||||||
while li ~= nil do
|
while li ~= nil do
|
||||||
ret[#ret + 1] = typvalt2lua(li.li_tv)
|
ret[#ret + 1] = typvalt2lua(li.li_tv, processed)
|
||||||
li = li.li_next
|
li = li.li_next
|
||||||
end
|
end
|
||||||
if ret[1] then
|
if ret[1] then
|
||||||
@ -106,16 +145,54 @@ lst2tbl = function(l)
|
|||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
dct2tbl = function(d)
|
local function dict_iter(d)
|
||||||
local ret = {d=d}
|
local init_s = {
|
||||||
assert(false, 'Converting dictionaries is not implemented yet')
|
todo=d.dv_hashtab.ht_used,
|
||||||
|
hi=d.dv_hashtab.ht_array,
|
||||||
|
}
|
||||||
|
local function f(s, _)
|
||||||
|
if s.todo == 0 then return nil end
|
||||||
|
while s.todo > 0 do
|
||||||
|
if s.hi.hi_key ~= nil and s.hi ~= eval.hash_removed then
|
||||||
|
local key = ffi.string(s.hi.hi_key)
|
||||||
|
local di = ffi.cast('dictitem_T*',
|
||||||
|
s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key'))
|
||||||
|
s.todo = s.todo - 1
|
||||||
|
s.hi = s.hi + 1
|
||||||
|
return key, di
|
||||||
|
end
|
||||||
|
s.hi = s.hi + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return f, init_s, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function first_di(d)
|
||||||
|
local f, init_s, v = dict_iter(d)
|
||||||
|
return select(2, f(init_s, v))
|
||||||
|
end
|
||||||
|
|
||||||
|
dct2tbl = function(d, processed)
|
||||||
|
if d == nil then
|
||||||
|
return null_dict
|
||||||
|
end
|
||||||
|
processed = processed or {}
|
||||||
|
local p_key = ptr2key(d)
|
||||||
|
if processed[p_key] then
|
||||||
|
return processed[p_key]
|
||||||
|
end
|
||||||
|
local ret = {}
|
||||||
|
processed[p_key] = ret
|
||||||
|
for k, di in dict_iter(d) do
|
||||||
|
ret[k] = typvalt2lua(di.di_tv, processed)
|
||||||
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
local lua2typvalt
|
|
||||||
|
|
||||||
local typvalt = function(typ, vval)
|
local typvalt = function(typ, vval)
|
||||||
if type(typ) == 'string' then
|
if typ == nil then
|
||||||
|
typ = eval.VAR_UNKNOWN
|
||||||
|
elseif type(typ) == 'string' then
|
||||||
typ = eval[typ]
|
typ = eval[typ]
|
||||||
end
|
end
|
||||||
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv)
|
return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv)
|
||||||
@ -164,12 +241,68 @@ local lua2typvalt_type_tab = {
|
|||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end,
|
end,
|
||||||
|
[func_type] = function(l, processed)
|
||||||
|
if processed[l] then
|
||||||
|
processed[l].pt_refcount = processed[l].pt_refcount + 1
|
||||||
|
return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]})
|
||||||
|
end
|
||||||
|
if l.args or l.dict then
|
||||||
|
local pt = ffi.gc(ffi.cast('partial_T*', eval.xmalloc(ffi.sizeof('partial_T'))), nil)
|
||||||
|
processed[l] = pt
|
||||||
|
local argv = nil
|
||||||
|
if l.args and #l.args > 0 then
|
||||||
|
argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil)
|
||||||
|
for i, arg in ipairs(l.args) do
|
||||||
|
local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil)
|
||||||
|
eval.copy_tv(arg_tv, argv[i - 1])
|
||||||
|
eval.clear_tv(arg_tv)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local dict = nil
|
||||||
|
if l.dict then
|
||||||
|
local dict_tv = ffi.gc(lua2typvalt(l.dict, processed), nil)
|
||||||
|
assert(dict_tv.v_type == eval.VAR_DICT)
|
||||||
|
dict = dict_tv.vval.v_dict
|
||||||
|
end
|
||||||
|
pt.pt_refcount = 1
|
||||||
|
pt.pt_name = eval.xmemdupz(to_cstr(l.value), #l.value)
|
||||||
|
pt.pt_auto = not not l.auto
|
||||||
|
pt.pt_argc = l.args and #l.args or 0
|
||||||
|
pt.pt_argv = argv
|
||||||
|
pt.pt_dict = dict
|
||||||
|
return typvalt(eval.VAR_PARTIAL, {v_partial=pt})
|
||||||
|
else
|
||||||
|
return typvalt(eval.VAR_FUNC, {
|
||||||
|
v_string=eval.xmemdupz(to_cstr(l.value), #l.value)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local special_vals = {
|
||||||
|
[null_string] = {eval.VAR_STRING, {v_string=ffi.cast('char_u*', nil)}},
|
||||||
|
[null_list] = {eval.VAR_LIST, {v_list=ffi.cast('list_T*', nil)}},
|
||||||
|
[null_dict] = {eval.VAR_DICT, {v_dict=ffi.cast('dict_T*', nil)}},
|
||||||
|
[nil_value] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull}},
|
||||||
|
[true] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarTrue}},
|
||||||
|
[false] = {eval.VAR_SPECIAL, {v_special=eval.kSpecialVarFalse}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v in pairs(special_vals) do
|
||||||
|
local tmp = function(typ, vval)
|
||||||
|
special_vals[k] = function()
|
||||||
|
return typvalt(typ, vval)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tmp(v[1], v[2])
|
||||||
|
end
|
||||||
|
|
||||||
lua2typvalt = function(l, processed)
|
lua2typvalt = function(l, processed)
|
||||||
processed = processed or {}
|
processed = processed or {}
|
||||||
if l == nil or l == nil_value then
|
if l == nil or l == nil_value then
|
||||||
return typvalt(eval.VAR_SPECIAL, {v_special=eval.kSpecialVarNull})
|
return special_vals[nil_value]()
|
||||||
|
elseif special_vals[l] then
|
||||||
|
return special_vals[l]()
|
||||||
elseif type(l) == 'table' then
|
elseif type(l) == 'table' then
|
||||||
if l[type_key] then
|
if l[type_key] then
|
||||||
return lua2typvalt_type_tab[l[type_key]](l, processed)
|
return lua2typvalt_type_tab[l[type_key]](l, processed)
|
||||||
@ -182,18 +315,19 @@ lua2typvalt = function(l, processed)
|
|||||||
end
|
end
|
||||||
elseif type(l) == 'number' then
|
elseif type(l) == 'number' then
|
||||||
return typvalt(eval.VAR_FLOAT, {v_float=l})
|
return typvalt(eval.VAR_FLOAT, {v_float=l})
|
||||||
elseif type(l) == 'boolean' then
|
|
||||||
return typvalt(eval.VAR_SPECIAL, {
|
|
||||||
v_special=(l and eval.kSpecialVarTrue or eval.kSpecialVarFalse)
|
|
||||||
})
|
|
||||||
elseif type(l) == 'string' then
|
elseif type(l) == 'string' then
|
||||||
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)})
|
return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)})
|
||||||
|
elseif type(l) == 'cdata' then
|
||||||
|
local tv = typvalt(eval.VAR_UNKNOWN)
|
||||||
|
eval.tv_copy(l, tv)
|
||||||
|
return tv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
null_string=null_string,
|
null_string=null_string,
|
||||||
null_list=null_list,
|
null_list=null_list,
|
||||||
|
null_dict=null_dict,
|
||||||
list_type=list_type,
|
list_type=list_type,
|
||||||
dict_type=dict_type,
|
dict_type=dict_type,
|
||||||
func_type=func_type,
|
func_type=func_type,
|
||||||
@ -212,4 +346,9 @@ return {
|
|||||||
typvalt2lua=typvalt2lua,
|
typvalt2lua=typvalt2lua,
|
||||||
|
|
||||||
typvalt=typvalt,
|
typvalt=typvalt,
|
||||||
|
|
||||||
|
li_alloc=li_alloc,
|
||||||
|
|
||||||
|
dict_iter=dict_iter,
|
||||||
|
first_di=first_di,
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ end
|
|||||||
|
|
||||||
local cstr = ffi.typeof('char[?]')
|
local cstr = ffi.typeof('char[?]')
|
||||||
local function to_cstr(string)
|
local function to_cstr(string)
|
||||||
return cstr((string.len(string)) + 1, string)
|
return cstr(#string + 1, string)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- initialize some global variables, this is still necessary to unit test
|
-- initialize some global variables, this is still necessary to unit test
|
||||||
|
Loading…
Reference in New Issue
Block a user