mirror of
https://github.com/neovim/neovim.git
synced 2025-09-14 15:28:17 +00:00
eval/typval_encode: Refactor big-big macros into .c.h file
This makes gdb backtraces much more meaningful: specifically I now know at which line it crashes in place of seeing that it crashes at TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS macros invocation.
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/option_defs.h"
|
||||
#include "nvim/version.h"
|
||||
#include "nvim/eval/typval_encode.h"
|
||||
#include "nvim/lib/kvec.h"
|
||||
|
||||
/// Helper structure for vim_to_object
|
||||
@@ -476,9 +475,17 @@ static inline void typval_encode_dict_end(EncodedData *const edata)
|
||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||
TYPVAL_ENCODE_CONV_NIL()
|
||||
|
||||
// object_convert_one_value()
|
||||
#define TYPVAL_ENCODE_SCOPE static
|
||||
#define TYPVAL_ENCODE_NAME object
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_TYPE EncodedData *const
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_NAME edata
|
||||
// _object_convert_one_value()
|
||||
// encode_vim_to_object()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_CONV_STRING
|
||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||
@@ -513,7 +520,9 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
|
||||
Object vim_to_object(typval_T *obj)
|
||||
{
|
||||
EncodedData edata = { .stack = KV_INITIAL_VALUE };
|
||||
encode_vim_to_object(&edata, obj, "vim_to_object argument");
|
||||
const int evo_ret = encode_vim_to_object(&edata, obj,
|
||||
"vim_to_object argument");
|
||||
assert(evo_ret == OK);
|
||||
Object ret = kv_A(edata.stack, 0);
|
||||
assert(kv_size(edata.stack) == 1);
|
||||
kv_destroy(edata.stack);
|
||||
|
@@ -19151,9 +19151,17 @@ void free_tv(typval_T *varp)
|
||||
|
||||
#define TYPVAL_ENCODE_CONV_RECURSE(ignored1, ignored2)
|
||||
|
||||
// nothing_convert_one_value()
|
||||
#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
|
||||
// _nothing_convert_one_value()
|
||||
// encode_vim_to_nothing()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_ALLOW_SPECIALS
|
||||
#undef TYPVAL_ENCODE_CONV_NIL
|
||||
@@ -19186,7 +19194,8 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, nothing, void *, ignored)
|
||||
void clear_tv(typval_T *varp)
|
||||
{
|
||||
if (varp != NULL && varp->v_type != VAR_UNKNOWN) {
|
||||
encode_vim_to_nothing(varp, varp, "clear_tv argument");
|
||||
const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument");
|
||||
assert(evn_ret == OK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -425,9 +425,17 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
||||
|
||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS false
|
||||
|
||||
// string_convert_one_value()
|
||||
#define TYPVAL_ENCODE_SCOPE static
|
||||
#define TYPVAL_ENCODE_NAME string
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
|
||||
// _string_convert_one_value()
|
||||
// encode_vim_to_string()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||
@@ -457,9 +465,17 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, string, garray_T *const, gap)
|
||||
return OK; \
|
||||
} while (0)
|
||||
|
||||
// echo_convert_one_value()
|
||||
#define TYPVAL_ENCODE_SCOPE
|
||||
#define TYPVAL_ENCODE_NAME echo
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
|
||||
// _echo_convert_one_value()
|
||||
// encode_vim_to_echo()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, echo, garray_T *const, gap)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_CONV_RECURSE
|
||||
#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
|
||||
@@ -750,9 +766,17 @@ bool encode_check_json_key(const typval_T *const tv)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// json_convert_one_value()
|
||||
#define TYPVAL_ENCODE_SCOPE static
|
||||
#define TYPVAL_ENCODE_NAME json
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
|
||||
// _json_convert_one_value()
|
||||
// encode_vim_to_json()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_CONV_STRING
|
||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||
@@ -791,7 +815,9 @@ char *encode_tv2string(typval_T *tv, size_t *len)
|
||||
{
|
||||
garray_T ga;
|
||||
ga_init(&ga, (int)sizeof(char), 80);
|
||||
encode_vim_to_string(&ga, tv, "encode_tv2string() argument");
|
||||
const int evs_ret = encode_vim_to_string(&ga, tv,
|
||||
"encode_tv2string() argument");
|
||||
(void)evs_ret;
|
||||
did_echo_string_emsg = false;
|
||||
if (len != NULL) {
|
||||
*len = (size_t) ga.ga_len;
|
||||
@@ -817,7 +843,8 @@ char *encode_tv2echo(typval_T *tv, size_t *len)
|
||||
ga_concat(&ga, tv->vval.v_string);
|
||||
}
|
||||
} else {
|
||||
encode_vim_to_echo(&ga, tv, ":echo argument");
|
||||
const int eve_ret = encode_vim_to_echo(&ga, tv, ":echo argument");
|
||||
(void)eve_ret;
|
||||
}
|
||||
if (len != NULL) {
|
||||
*len = (size_t) ga.ga_len;
|
||||
@@ -838,7 +865,8 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
||||
{
|
||||
garray_T ga;
|
||||
ga_init(&ga, (int)sizeof(char), 80);
|
||||
encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
|
||||
const int evj_ret = encode_vim_to_json(&ga, tv, "encode_tv2json() argument");
|
||||
(void)evj_ret;
|
||||
did_echo_string_emsg = false;
|
||||
if (len != NULL) {
|
||||
*len = (size_t) ga.ga_len;
|
||||
@@ -941,9 +969,17 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
||||
|
||||
#define TYPVAL_ENCODE_ALLOW_SPECIALS true
|
||||
|
||||
// msgpack_convert_one_value()
|
||||
#define TYPVAL_ENCODE_SCOPE
|
||||
#define TYPVAL_ENCODE_NAME msgpack
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
|
||||
#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
|
||||
// _msgpack_convert_one_value()
|
||||
// encode_vim_to_msgpack()
|
||||
TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
||||
#include "nvim/eval/typval_encode.c.h"
|
||||
#undef TYPVAL_ENCODE_SCOPE
|
||||
#undef TYPVAL_ENCODE_NAME
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
|
||||
#undef TYPVAL_ENCODE_FIRST_ARG_NAME
|
||||
|
||||
#undef TYPVAL_ENCODE_CONV_STRING
|
||||
#undef TYPVAL_ENCODE_CONV_STR_STRING
|
||||
|
656
src/nvim/eval/typval_encode.c.h
Normal file
656
src/nvim/eval/typval_encode.c.h
Normal file
@@ -0,0 +1,656 @@
|
||||
/// @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`. 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_FUNC_START
|
||||
/// @brief Macros used when starting to convert a funcref or a partial
|
||||
///
|
||||
/// @param fun Function name.
|
||||
/// @param is_partial True if converted function is a partial.
|
||||
/// @param pt Pointer to partial or NULL.
|
||||
|
||||
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
|
||||
/// @brief Macros used before starting to convert partial arguments
|
||||
///
|
||||
/// @param len Number of arguments. Zero for absent arguments or when
|
||||
/// converting a funcref.
|
||||
|
||||
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
|
||||
/// @brief Macros used before starting to convert self dictionary
|
||||
///
|
||||
/// @param len Number of arguments. May be zero for empty dictionary or -1 for
|
||||
/// missing self dictionary, also when converting function
|
||||
/// reference.
|
||||
|
||||
/// @def TYPVAL_ENCODE_CONV_FUNC_END
|
||||
/// @brief Macros used after converting a funcref or a partial
|
||||
///
|
||||
/// Accepts no arguments, but still must be a function-like macros.
|
||||
|
||||
/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||
/// @brief Macros used to convert an empty list
|
||||
///
|
||||
/// 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.
|
||||
|
||||
/// @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 function `_{TYPVAL_ENCODE_NAME}_convert_one_value`.
|
||||
|
||||
/// @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"
|
||||
|
||||
static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
|
||||
MPConvStack *const mpstack, typval_T *const tv, const int copyID,
|
||||
const char *const objname)
|
||||
REAL_FATTR_NONNULL_ALL 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[out] mpstack Stack with values to convert. Values which are not
|
||||
/// converted completely by this function (i.e.
|
||||
/// non-scalars) are pushed here.
|
||||
/// @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, typval_T *const tv, const int copyID,
|
||||
const char *const objname)
|
||||
{
|
||||
switch (tv->v_type) {
|
||||
case VAR_STRING: {
|
||||
TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv));
|
||||
break;
|
||||
}
|
||||
case VAR_NUMBER: {
|
||||
TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number);
|
||||
break;
|
||||
}
|
||||
case VAR_FLOAT: {
|
||||
TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float);
|
||||
break;
|
||||
}
|
||||
case VAR_FUNC: {
|
||||
TYPVAL_ENCODE_CONV_FUNC_START(tv->vval.v_string, false, NULL);
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(0);
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1);
|
||||
TYPVAL_ENCODE_CONV_FUNC_END();
|
||||
break;
|
||||
}
|
||||
case VAR_PARTIAL: {
|
||||
partial_T *const pt = tv->vval.v_partial;
|
||||
(void)pt;
|
||||
TYPVAL_ENCODE_CONV_FUNC_START(pt->pt_name, true, pt);
|
||||
_mp_push(*mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvPartial,
|
||||
.tv = tv,
|
||||
.data = {
|
||||
.p = {
|
||||
.stage = kMPConvPartialArgs,
|
||||
.pt = tv->vval.v_partial,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case VAR_LIST: {
|
||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) {
|
||||
TYPVAL_ENCODE_CONV_EMPTY_LIST();
|
||||
break;
|
||||
}
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
|
||||
kMPConvList);
|
||||
TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len);
|
||||
_mp_push(*mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvList,
|
||||
.tv = tv,
|
||||
.data = {
|
||||
.l = {
|
||||
.list = tv->vval.v_list,
|
||||
.li = tv->vval.v_list->lv_first,
|
||||
},
|
||||
},
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case VAR_SPECIAL: {
|
||||
switch (tv->vval.v_special) {
|
||||
case kSpecialVarNull: {
|
||||
TYPVAL_ENCODE_CONV_NIL();
|
||||
break;
|
||||
}
|
||||
case kSpecialVarTrue:
|
||||
case kSpecialVarFalse: {
|
||||
TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case VAR_DICT: {
|
||||
if (tv->vval.v_dict == NULL
|
||||
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) {
|
||||
TYPVAL_ENCODE_CONV_EMPTY_DICT();
|
||||
break;
|
||||
}
|
||||
const dictitem_T *type_di;
|
||||
const dictitem_T *val_di;
|
||||
if (TYPVAL_ENCODE_ALLOW_SPECIALS
|
||||
&& tv->vval.v_dict->dv_hashtab.ht_used == 2
|
||||
&& (type_di = dict_find((dict_T *) tv->vval.v_dict,
|
||||
(char_u *) "_TYPE", -1)) != NULL
|
||||
&& type_di->di_tv.v_type == VAR_LIST
|
||||
&& (val_di = dict_find((dict_T *) tv->vval.v_dict,
|
||||
(char_u *) "_VAL", -1)) != NULL) {
|
||||
size_t i;
|
||||
for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
|
||||
if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
|
||||
goto _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 _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 _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 _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 _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(buf, len);
|
||||
} else {
|
||||
TYPVAL_ENCODE_CONV_STRING(buf, len);
|
||||
}
|
||||
xfree(buf);
|
||||
break;
|
||||
}
|
||||
case kMPArray: {
|
||||
if (val_di->di_tv.v_type != VAR_LIST) {
|
||||
goto _convert_one_value_regular_dict;
|
||||
}
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
|
||||
lv_copyID, 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 _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 _convert_one_value_regular_dict;
|
||||
}
|
||||
}
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, 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 _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(buf, len, type);
|
||||
xfree(buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
_convert_one_value_regular_dict:
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, 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), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
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_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 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 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,
|
||||
tv, copyID, objname)
|
||||
== FAIL) {
|
||||
goto encode_vim_to__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__error_ret, kv_pair->lv_first->li_tv);
|
||||
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
|
||||
&kv_pair->lv_first->li_tv, copyID, objname) == FAIL) {
|
||||
goto encode_vim_to__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;
|
||||
}
|
||||
case kMPConvPartial: {
|
||||
partial_T *const pt = cur_mpsv->data.p.pt;
|
||||
switch (cur_mpsv->data.p.stage) {
|
||||
case kMPConvPartialArgs: {
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(pt->pt_argc);
|
||||
cur_mpsv->data.p.stage = kMPConvPartialSelf;
|
||||
if (pt->pt_argc > 0) {
|
||||
TYPVAL_ENCODE_CONV_LIST_START(pt->pt_argc);
|
||||
_mp_push(mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvPartialList,
|
||||
.tv = tv,
|
||||
.data = {
|
||||
.a = {
|
||||
.arg = pt->pt_argv,
|
||||
.argv = pt->pt_argv,
|
||||
.todo = (size_t)pt->pt_argc,
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kMPConvPartialSelf: {
|
||||
cur_mpsv->data.p.stage = kMPConvPartialEnd;
|
||||
dict_T *const dict = pt->pt_dict;
|
||||
if (dict != NULL) {
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(dict->dv_hashtab.ht_used);
|
||||
TYPVAL_ENCODE_CONV_DICT_START(dict->dv_hashtab.ht_used);
|
||||
_mp_push(mpstack, ((MPConvStackVal) {
|
||||
.type = kMPConvDict,
|
||||
.tv = tv,
|
||||
.data = {
|
||||
.d = {
|
||||
.dict = dict,
|
||||
.hi = dict->dv_hashtab.ht_array,
|
||||
.todo = dict->dv_hashtab.ht_used,
|
||||
},
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kMPConvPartialEnd: {
|
||||
TYPVAL_ENCODE_CONV_FUNC_END();
|
||||
(void) _mp_pop(mpstack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case kMPConvPartialList: {
|
||||
if (!cur_mpsv->data.a.todo) {
|
||||
(void) _mp_pop(mpstack);
|
||||
TYPVAL_ENCODE_CONV_LIST_END();
|
||||
continue;
|
||||
} else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
|
||||
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS();
|
||||
}
|
||||
cur_tv = cur_mpsv->data.a.arg++;
|
||||
cur_mpsv->data.a.todo--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(cur_tv != NULL);
|
||||
if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(
|
||||
TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_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;
|
||||
}
|
||||
#endif // NVIM_EVAL_TYPVAL_ENCODE_C_H
|
@@ -1,4 +1,4 @@
|
||||
/// @file eval/typval_convert.h
|
||||
/// @file eval/typval_encode.h
|
||||
///
|
||||
/// Contains set of macros used to convert (possibly recursive) typval_T into
|
||||
/// something else. For these macros to work the following macros must be
|
||||
@@ -162,11 +162,11 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/eval_defs.h"
|
||||
#include "nvim/eval/encode.h"
|
||||
#include "nvim/func_attr.h"
|
||||
|
||||
/// Type of the stack entry
|
||||
@@ -222,21 +222,9 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
|
||||
#define _mp_pop kv_pop
|
||||
#define _mp_last kv_last
|
||||
|
||||
/// Code for checking whether container references itself
|
||||
///
|
||||
/// @param[in,out] val Container to check.
|
||||
/// @param copyID_attr Name of the container attribute that holds copyID.
|
||||
/// After checking whether value of this attribute is
|
||||
/// copyID (variable) it is set to copyID.
|
||||
/// @param conv_type Type of the conversion, @see MPConvStackValType.
|
||||
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, conv_type) \
|
||||
do { \
|
||||
if ((val)->copyID_attr == copyID) { \
|
||||
TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
|
||||
return OK; \
|
||||
} \
|
||||
(val)->copyID_attr = copyID; \
|
||||
} while (0)
|
||||
static inline size_t tv_strlen(const typval_T *const tv)
|
||||
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT
|
||||
REAL_FATTR_NONNULL_ALL;
|
||||
|
||||
/// Length of the string stored in typval_T
|
||||
///
|
||||
@@ -246,8 +234,6 @@ typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack;
|
||||
/// @return Length of the string stored in typval_T, including 0 for NULL
|
||||
/// string.
|
||||
static inline size_t tv_strlen(const typval_T *const tv)
|
||||
FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
assert(tv->v_type == VAR_STRING);
|
||||
return (tv->vval.v_string == NULL
|
||||
@@ -255,442 +241,37 @@ static inline size_t tv_strlen(const typval_T *const tv)
|
||||
: strlen((char *) tv->vval.v_string));
|
||||
}
|
||||
|
||||
/// Define functions to convert a VimL value:
|
||||
/// `{name}_convert_one_value(...)`
|
||||
/// `encode_vim_to_{name}(...)`
|
||||
/// Code for checking whether container references itself
|
||||
///
|
||||
/// @param scope Scope of the main function: either nothing or `static`.
|
||||
/// @param name Name of the target converter.
|
||||
/// @param firstargtype Type of the first argument. It will be used to return
|
||||
/// the results.
|
||||
/// @param firstargname Name of the first argument.
|
||||
#define TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(scope, name, firstargtype, \
|
||||
firstargname) \
|
||||
/* Returns OK or FAIL */ \
|
||||
static int name##_convert_one_value(firstargtype firstargname, \
|
||||
MPConvStack *const mpstack, \
|
||||
typval_T *const tv, \
|
||||
const int copyID, \
|
||||
const char *const objname) \
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
|
||||
{ \
|
||||
switch (tv->v_type) { \
|
||||
case VAR_STRING: { \
|
||||
TYPVAL_ENCODE_CONV_STRING(tv->vval.v_string, tv_strlen(tv)); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_NUMBER: { \
|
||||
TYPVAL_ENCODE_CONV_NUMBER(tv->vval.v_number); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_FLOAT: { \
|
||||
TYPVAL_ENCODE_CONV_FLOAT(tv->vval.v_float); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_FUNC: { \
|
||||
TYPVAL_ENCODE_CONV_FUNC_START(tv->vval.v_string, false, NULL); \
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(0); \
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1); \
|
||||
TYPVAL_ENCODE_CONV_FUNC_END(); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_PARTIAL: { \
|
||||
partial_T *const pt = tv->vval.v_partial; \
|
||||
(void)pt; \
|
||||
TYPVAL_ENCODE_CONV_FUNC_START(pt->pt_name, true, pt); \
|
||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
||||
.type = kMPConvPartial, \
|
||||
.tv = tv, \
|
||||
.data = { \
|
||||
.p = { \
|
||||
.stage = kMPConvPartialArgs, \
|
||||
.pt = tv->vval.v_partial, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_LIST: { \
|
||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
||||
TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
|
||||
break; \
|
||||
} \
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, \
|
||||
kMPConvList); \
|
||||
TYPVAL_ENCODE_CONV_LIST_START(tv->vval.v_list->lv_len); \
|
||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
||||
.type = kMPConvList, \
|
||||
.tv = tv, \
|
||||
.data = { \
|
||||
.l = { \
|
||||
.list = tv->vval.v_list, \
|
||||
.li = tv->vval.v_list->lv_first, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_SPECIAL: { \
|
||||
switch (tv->vval.v_special) { \
|
||||
case kSpecialVarNull: { \
|
||||
TYPVAL_ENCODE_CONV_NIL(); \
|
||||
break; \
|
||||
} \
|
||||
case kSpecialVarTrue: \
|
||||
case kSpecialVarFalse: { \
|
||||
TYPVAL_ENCODE_CONV_BOOL(tv->vval.v_special == kSpecialVarTrue); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case VAR_DICT: { \
|
||||
if (tv->vval.v_dict == NULL \
|
||||
|| tv->vval.v_dict->dv_hashtab.ht_used == 0) { \
|
||||
TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
|
||||
break; \
|
||||
} \
|
||||
const dictitem_T *type_di; \
|
||||
const dictitem_T *val_di; \
|
||||
if (TYPVAL_ENCODE_ALLOW_SPECIALS \
|
||||
&& tv->vval.v_dict->dv_hashtab.ht_used == 2 \
|
||||
&& (type_di = dict_find((dict_T *) tv->vval.v_dict, \
|
||||
(char_u *) "_TYPE", -1)) != NULL \
|
||||
&& type_di->di_tv.v_type == VAR_LIST \
|
||||
&& (val_di = dict_find((dict_T *) tv->vval.v_dict, \
|
||||
(char_u *) "_VAL", -1)) != NULL) { \
|
||||
size_t i; \
|
||||
for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { \
|
||||
if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if (i == ARRAY_SIZE(eval_msgpack_type_lists)) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
switch ((MessagePackType) i) { \
|
||||
case kMPNil: { \
|
||||
TYPVAL_ENCODE_CONV_NIL(); \
|
||||
break; \
|
||||
} \
|
||||
case kMPBoolean: { \
|
||||
if (val_di->di_tv.v_type != VAR_NUMBER) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
TYPVAL_ENCODE_CONV_BOOL(val_di->di_tv.vval.v_number); \
|
||||
break; \
|
||||
} \
|
||||
case kMPInteger: { \
|
||||
const list_T *val_list; \
|
||||
varnumber_T sign; \
|
||||
varnumber_T highest_bits; \
|
||||
varnumber_T high_bits; \
|
||||
varnumber_T low_bits; \
|
||||
/* List of 4 integers; first is signed (should be 1 or -1, but */ \
|
||||
/* this is not checked), second is unsigned and have at most */ \
|
||||
/* one (sign is -1) or two (sign is 1) non-zero bits (number of */ \
|
||||
/* bits is not checked), other unsigned and have at most 31 */ \
|
||||
/* non-zero bits (number of bits is not checked).*/ \
|
||||
if (val_di->di_tv.v_type != VAR_LIST \
|
||||
|| (val_list = val_di->di_tv.vval.v_list) == NULL \
|
||||
|| val_list->lv_len != 4 \
|
||||
|| val_list->lv_first->li_tv.v_type != VAR_NUMBER \
|
||||
|| (sign = val_list->lv_first->li_tv.vval.v_number) == 0 \
|
||||
|| val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER \
|
||||
|| (highest_bits = \
|
||||
val_list->lv_first->li_next->li_tv.vval.v_number) < 0 \
|
||||
|| val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER \
|
||||
|| (high_bits = \
|
||||
val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 \
|
||||
|| val_list->lv_last->li_tv.v_type != VAR_NUMBER \
|
||||
|| (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) \
|
||||
| (uint64_t) (((uint64_t) high_bits) << 31) \
|
||||
| (uint64_t) low_bits); \
|
||||
if (sign > 0) { \
|
||||
TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(number); \
|
||||
} else { \
|
||||
TYPVAL_ENCODE_CONV_NUMBER(-number); \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case kMPFloat: { \
|
||||
if (val_di->di_tv.v_type != VAR_FLOAT) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
TYPVAL_ENCODE_CONV_FLOAT(val_di->di_tv.vval.v_float); \
|
||||
break; \
|
||||
} \
|
||||
case kMPString: \
|
||||
case kMPBinary: { \
|
||||
const bool is_string = ((MessagePackType) i == kMPString); \
|
||||
if (val_di->di_tv.v_type != VAR_LIST) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
size_t len; \
|
||||
char *buf; \
|
||||
if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len, \
|
||||
&buf)) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
if (is_string) { \
|
||||
TYPVAL_ENCODE_CONV_STR_STRING(buf, len); \
|
||||
} else { \
|
||||
TYPVAL_ENCODE_CONV_STRING(buf, len); \
|
||||
} \
|
||||
xfree(buf); \
|
||||
break; \
|
||||
} \
|
||||
case kMPArray: { \
|
||||
if (val_di->di_tv.v_type != VAR_LIST) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, \
|
||||
lv_copyID, kMPConvList); \
|
||||
TYPVAL_ENCODE_CONV_LIST_START(val_di->di_tv.vval.v_list->lv_len); \
|
||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
||||
.tv = tv, \
|
||||
.type = kMPConvList, \
|
||||
.data = { \
|
||||
.l = { \
|
||||
.list = val_di->di_tv.vval.v_list, \
|
||||
.li = val_di->di_tv.vval.v_list->lv_first, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case kMPMap: { \
|
||||
if (val_di->di_tv.v_type != VAR_LIST) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
list_T *const val_list = val_di->di_tv.vval.v_list; \
|
||||
if (val_list == NULL || val_list->lv_len == 0) { \
|
||||
TYPVAL_ENCODE_CONV_EMPTY_DICT(); \
|
||||
break; \
|
||||
} \
|
||||
for (const listitem_T *li = val_list->lv_first; li != NULL; \
|
||||
li = li->li_next) { \
|
||||
if (li->li_tv.v_type != VAR_LIST \
|
||||
|| li->li_tv.vval.v_list->lv_len != 2) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
} \
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val_list, lv_copyID, \
|
||||
kMPConvPairs); \
|
||||
TYPVAL_ENCODE_CONV_DICT_START(val_list->lv_len); \
|
||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
||||
.tv = tv, \
|
||||
.type = kMPConvPairs, \
|
||||
.data = { \
|
||||
.l = { \
|
||||
.list = val_list, \
|
||||
.li = val_list->lv_first, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case kMPExt: { \
|
||||
const list_T *val_list; \
|
||||
varnumber_T type; \
|
||||
if (val_di->di_tv.v_type != VAR_LIST \
|
||||
|| (val_list = val_di->di_tv.vval.v_list) == NULL \
|
||||
|| val_list->lv_len != 2 \
|
||||
|| (val_list->lv_first->li_tv.v_type != VAR_NUMBER) \
|
||||
|| (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX \
|
||||
|| type < INT8_MIN \
|
||||
|| (val_list->lv_last->li_tv.v_type != VAR_LIST)) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
size_t len; \
|
||||
char *buf; \
|
||||
if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, \
|
||||
&len, &buf)) { \
|
||||
goto name##_convert_one_value_regular_dict; \
|
||||
} \
|
||||
TYPVAL_ENCODE_CONV_EXT_STRING(buf, len, type); \
|
||||
xfree(buf); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
name##_convert_one_value_regular_dict: \
|
||||
_TYPVAL_ENCODE_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, \
|
||||
kMPConvDict); \
|
||||
TYPVAL_ENCODE_CONV_DICT_START(tv->vval.v_dict->dv_hashtab.ht_used); \
|
||||
_mp_push(*mpstack, ((MPConvStackVal) { \
|
||||
.tv = tv, \
|
||||
.type = kMPConvDict, \
|
||||
.data = { \
|
||||
.d = { \
|
||||
.dict = tv->vval.v_dict, \
|
||||
.hi = tv->vval.v_dict->dv_hashtab.ht_array, \
|
||||
.todo = tv->vval.v_dict->dv_hashtab.ht_used, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_UNKNOWN: { \
|
||||
EMSG2(_(e_intern2), #name "_convert_one_value()"); \
|
||||
return FAIL; \
|
||||
} \
|
||||
} \
|
||||
/// @param[in,out] val Container to check.
|
||||
/// @param copyID_attr Name of the container attribute that holds copyID.
|
||||
/// After checking whether value of this attribute is
|
||||
/// copyID (variable) it is set to copyID.
|
||||
/// @param[in] copyID CopyID used by the caller.
|
||||
/// @param conv_type Type of the conversion, @see MPConvStackValType.
|
||||
#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE(val, copyID_attr, copyID, \
|
||||
conv_type) \
|
||||
do { \
|
||||
if ((val)->copyID_attr == (copyID)) { \
|
||||
TYPVAL_ENCODE_CONV_RECURSE((val), conv_type); \
|
||||
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; \
|
||||
} \
|
||||
case kMPConvPartial: { \
|
||||
partial_T *const pt = cur_mpsv->data.p.pt; \
|
||||
switch (cur_mpsv->data.p.stage) { \
|
||||
case kMPConvPartialArgs: { \
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(pt->pt_argc); \
|
||||
cur_mpsv->data.p.stage = kMPConvPartialSelf; \
|
||||
if (pt->pt_argc > 0) { \
|
||||
TYPVAL_ENCODE_CONV_LIST_START(pt->pt_argc); \
|
||||
_mp_push(mpstack, ((MPConvStackVal) { \
|
||||
.type = kMPConvPartialList, \
|
||||
.tv = tv, \
|
||||
.data = { \
|
||||
.a = { \
|
||||
.arg = pt->pt_argv, \
|
||||
.argv = pt->pt_argv, \
|
||||
.todo = (size_t)pt->pt_argc, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case kMPConvPartialSelf: { \
|
||||
cur_mpsv->data.p.stage = kMPConvPartialEnd; \
|
||||
dict_T *const dict = pt->pt_dict; \
|
||||
if (dict != NULL) { \
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(dict->dv_hashtab.ht_used); \
|
||||
TYPVAL_ENCODE_CONV_DICT_START(dict->dv_hashtab.ht_used); \
|
||||
_mp_push(mpstack, ((MPConvStackVal) { \
|
||||
.type = kMPConvDict, \
|
||||
.tv = tv, \
|
||||
.data = { \
|
||||
.d = { \
|
||||
.dict = dict, \
|
||||
.hi = dict->dv_hashtab.ht_array, \
|
||||
.todo = dict->dv_hashtab.ht_used, \
|
||||
}, \
|
||||
}, \
|
||||
})); \
|
||||
} else { \
|
||||
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(-1); \
|
||||
} \
|
||||
break; \
|
||||
} \
|
||||
case kMPConvPartialEnd: { \
|
||||
TYPVAL_ENCODE_CONV_FUNC_END(); \
|
||||
(void) _mp_pop(mpstack); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
continue; \
|
||||
} \
|
||||
case kMPConvPartialList: { \
|
||||
if (!cur_mpsv->data.a.todo) { \
|
||||
(void) _mp_pop(mpstack); \
|
||||
TYPVAL_ENCODE_CONV_LIST_END(); \
|
||||
continue; \
|
||||
} else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) { \
|
||||
TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(); \
|
||||
} \
|
||||
cur_tv = cur_mpsv->data.a.arg++; \
|
||||
cur_mpsv->data.a.todo--; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
assert(cur_tv != NULL); \
|
||||
if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
|
||||
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; \
|
||||
}
|
||||
(val)->copyID_attr = (copyID); \
|
||||
} while (0)
|
||||
|
||||
#define _TYPVAL_ENCODE_ENCODE_INNER_2(name) encode_vim_to_##name
|
||||
#define _TYPVAL_ENCODE_ENCODE_INNER(name) _TYPVAL_ENCODE_ENCODE_INNER_2(name)
|
||||
|
||||
/// Entry point function name
|
||||
#define _TYPVAL_ENCODE_ENCODE _TYPVAL_ENCODE_ENCODE_INNER(TYPVAL_ENCODE_NAME)
|
||||
|
||||
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) \
|
||||
_typval_encode_##name##_convert_one_value
|
||||
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(name) \
|
||||
_TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name)
|
||||
|
||||
/// Name of the …convert_one_value function
|
||||
#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \
|
||||
_TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(TYPVAL_ENCODE_NAME)
|
||||
|
||||
#endif // NVIM_EVAL_TYPVAL_ENCODE_H
|
||||
|
Reference in New Issue
Block a user