neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

typval_encode.c.h (33679B)


      1 // uncrustify:off
      2 
      3 /// @file eval/typval_encode.c.h
      4 ///
      5 /// Contains set of macros used to convert (possibly recursive) typval_T into
      6 /// something else. For these macros to work the following macros must be
      7 /// defined:
      8 
      9 /// @def TYPVAL_ENCODE_CHECK_BEFORE
     10 /// @brief Macro used before any specific CONV function
     11 ///
     12 /// can be used for a common check, like flushing a buffer if necessary
     13 
     14 /// @def TYPVAL_ENCODE_CONV_NIL
     15 /// @brief Macros used to convert NIL value
     16 ///
     17 /// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
     18 /// is false) and `v:null`.
     19 ///
     20 /// @param  tv  Pointer to typval where value is stored. May not be NULL. May
     21 ///             point to special dictionary.
     22 
     23 /// @def TYPVAL_ENCODE_CONV_BOOL
     24 /// @brief Macros used to convert boolean value
     25 ///
     26 /// Is called both for special dictionary (unless #TYPVAL_ENCODE_ALLOW_SPECIALS
     27 /// is false) and `v:true`/`v:false`.
     28 ///
     29 /// @param  tv  Pointer to typval where value is stored. May not be NULL. May
     30 ///             point to a special dictionary.
     31 /// @param  num  Boolean value to convert. Value is an expression which
     32 ///              evaluates to some integer.
     33 
     34 /// @def TYPVAL_ENCODE_CONV_NUMBER
     35 /// @brief Macros used to convert integer
     36 ///
     37 /// @param  tv  Pointer to typval where value is stored. May not be NULL. May
     38 ///             point to a special dictionary.
     39 /// @param  num  Integer to convert, must accept both varnumber_T and int64_t.
     40 
     41 /// @def TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
     42 /// @brief Macros used to convert unsigned integer
     43 ///
     44 /// Not used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
     45 /// defined.
     46 ///
     47 /// @param  tv  Pointer to typval where value is stored. May not be NULL. Points
     48 ///             to a special dictionary.
     49 /// @param  num  Integer to convert, must accept uint64_t.
     50 
     51 /// @def TYPVAL_ENCODE_CONV_FLOAT
     52 /// @brief Macros used to convert floating-point number
     53 ///
     54 /// @param  tv  Pointer to typval where value is stored. May not be NULL. May
     55 ///             point to a special dictionary.
     56 /// @param  flt  Number to convert, must accept float_T.
     57 
     58 /// @def TYPVAL_ENCODE_CONV_STRING
     59 /// @brief Macros used to convert plain string
     60 ///
     61 /// Is used to convert VAR_STRING objects as well as BIN strings represented as
     62 /// special dictionary.
     63 ///
     64 /// @param  tv  Pointer to typval where value is stored. May not be NULL. May
     65 ///             point to a special dictionary.
     66 /// @param  buf  String to convert. Is a char[] buffer, not NUL-terminated.
     67 /// @param  len  String length.
     68 
     69 /// @def TYPVAL_ENCODE_CONV_STR_STRING
     70 /// @brief Like #TYPVAL_ENCODE_CONV_STRING, but for STR strings
     71 ///
     72 /// Is used to convert dictionary keys and STR strings represented as special
     73 /// dictionaries.
     74 ///
     75 /// @param  tv  Pointer to typval where value is stored. May be NULL. May
     76 ///             point to a special dictionary.
     77 /// @param  buf  String to convert. Is a char[] buffer, not NUL-terminated.
     78 /// @param  len  String length.
     79 
     80 /// @def TYPVAL_ENCODE_CONV_EXT_STRING
     81 /// @brief Macros used to convert EXT string
     82 ///
     83 /// Is used to convert EXT strings represented as special dictionaries. Never
     84 /// actually used if #TYPVAL_ENCODE_ALLOW_SPECIALS is false, but still must be
     85 /// defined.
     86 ///
     87 /// @param  tv  Pointer to typval where value is stored. May not be NULL. Points
     88 ///             to a special dictionary.
     89 /// @param  buf  String to convert. Is a char[] buffer, not NUL-terminated.
     90 /// @param  len  String length.
     91 /// @param  type  EXT type.
     92 
     93 /// @def TYPVAL_ENCODE_CONV_BLOB
     94 /// @brief Macros used to convert a blob
     95 ///
     96 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
     97 /// @param  blob  Pointer to the blob to convert.
     98 /// @param  len  Blob length.
     99 
    100 /// @def TYPVAL_ENCODE_CONV_FUNC_START
    101 /// @brief Macros used when starting to convert a funcref or a partial
    102 ///
    103 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
    104 /// @param  fun  Function name. May be NULL.
    105 /// @param  prefix  Prefix for converting to a string.
    106 
    107 /// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
    108 /// @brief Macros used before starting to convert partial arguments
    109 ///
    110 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
    111 /// @param  len  Number of arguments. Zero for absent arguments or when
    112 ///              converting a funcref.
    113 
    114 /// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
    115 /// @brief Macros used before starting to convert self dictionary
    116 ///
    117 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
    118 /// @param  len  Number of arguments. May be zero for empty dictionary or -1 for
    119 ///              missing self dictionary, also when converting function
    120 ///              reference.
    121 
    122 /// @def TYPVAL_ENCODE_CONV_FUNC_END
    123 /// @brief Macros used after converting a funcref or a partial
    124 ///
    125 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
    126 
    127 /// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
    128 /// @brief Macros used to convert an empty list
    129 ///
    130 /// @param  tv  Pointer to typval where value is stored. May not be NULL.
    131 
    132 /// @def TYPVAL_ENCODE_CONV_EMPTY_DICT
    133 /// @brief Macros used to convert an empty dictionary
    134 ///
    135 /// @param  tv  Pointer to typval where value is stored. May be NULL. May
    136 ///             point to a special dictionary.
    137 /// @param  dict  Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
    138 ///               (for dictionaries represented as special lists).
    139 
    140 /// @def TYPVAL_ENCODE_CONV_LIST_START
    141 /// @brief Macros used before starting to convert non-empty list
    142 ///
    143 /// @param  tv  Pointer to typval where value is stored. May be NULL. May
    144 ///             point to a special dictionary.
    145 /// @param  len  List length. Is an expression which evaluates to an integer.
    146 
    147 /// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
    148 /// @brief Macros used after pushing list onto the stack
    149 ///
    150 /// Only used for real list_T* lists, not for special dictionaries or partial
    151 /// arguments.
    152 ///
    153 /// @param  tv  Pointer to typval where value is stored. May be NULL. May
    154 ///             point to a special dictionary.
    155 /// @param  mpsv  Pushed MPConvStackVal value.
    156 
    157 /// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
    158 /// @brief Macros used after finishing converting non-last list item
    159 ///
    160 /// @param  tv  Pointer to typval where list is stored. May be NULL.
    161 
    162 /// @def TYPVAL_ENCODE_CONV_LIST_END
    163 /// @brief Macros used after converting non-empty list
    164 ///
    165 /// @param  tv  Pointer to typval where list is stored. May be NULL.
    166 
    167 /// @def TYPVAL_ENCODE_CONV_DICT_START
    168 /// @brief Macros used before starting to convert non-empty dictionary
    169 ///
    170 /// Only used for real dict_T* dictionaries, not for special dictionaries. Also
    171 /// used for partial self dictionary.
    172 ///
    173 /// @param  tv  Pointer to typval where dictionary is stored. May be NULL. May
    174 ///             point to a special dictionary.
    175 /// @param  dict  Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
    176 ///               (for dictionaries represented as special lists).
    177 /// @param  len  Dict length. Is an expression which evaluates to an integer.
    178 
    179 /// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
    180 /// @brief Macros used after pushing dictionary onto the stack
    181 ///
    182 /// @param  tv  Pointer to typval where dictionary is stored. May be NULL.
    183 ///             May not point to a special dictionary.
    184 /// @param  dict  Converted dictionary, lvalue.
    185 /// @param  mpsv  Pushed MPConvStackVal value.
    186 
    187 /// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
    188 /// @brief Macros used to check special dictionary key
    189 ///
    190 /// @param  label  Label for goto in case check was not successful.
    191 /// @param  key  typval_T key to check.
    192 
    193 /// @def TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
    194 /// @brief Macros used after finishing converting dictionary key
    195 ///
    196 /// @param  tv  Pointer to typval where dictionary is stored. May be NULL. May
    197 ///             point to a special dictionary.
    198 /// @param  dict  Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
    199 ///               (for dictionaries represented as special lists).
    200 
    201 /// @def TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
    202 /// @brief Macros used after finishing converting non-last dictionary value
    203 ///
    204 /// @param  tv  Pointer to typval where dictionary is stored. May be NULL. May
    205 ///             point to a special dictionary.
    206 /// @param  dict  Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
    207 ///               (for dictionaries represented as special lists).
    208 
    209 /// @def TYPVAL_ENCODE_CONV_DICT_END
    210 /// @brief Macros used after converting non-empty dictionary
    211 ///
    212 /// @param  tv  Pointer to typval where dictionary is stored. May be NULL. May
    213 ///             point to a special dictionary.
    214 /// @param  dict  Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR
    215 ///               (for dictionaries represented as special lists).
    216 
    217 /// @def TYPVAL_ENCODE_CONV_RECURSE
    218 /// @brief Macros used when self-containing container is detected
    219 ///
    220 /// @param  val  Container for which this situation was detected.
    221 /// @param  conv_type  Type of the stack entry, @see MPConvStackValType.
    222 
    223 /// @def TYPVAL_ENCODE_ALLOW_SPECIALS
    224 /// @brief Macros that specifies whether special dictionaries are special
    225 ///
    226 /// Must be something that evaluates to boolean, most likely `true` or `false`.
    227 /// If it is false then special dictionaries are not treated specially.
    228 
    229 /// @def TYPVAL_ENCODE_SCOPE
    230 /// @brief Scope of the main function: either nothing or `static`
    231 
    232 /// @def TYPVAL_ENCODE_NAME
    233 /// @brief Name of the target converter
    234 ///
    235 /// After including this file it will define function
    236 /// `encode_vim_to_{TYPVAL_ENCODE_NAME}` with scope #TYPVAL_ENCODE_SCOPE and
    237 /// static functions `_typval_encode_{TYPVAL_ENCODE_NAME}_convert_one_value` and
    238 /// `_typval_encode_{TYPVAL_ENCODE_NAME}_check_self_reference`.
    239 
    240 /// @def TYPVAL_ENCODE_FIRST_ARG_TYPE
    241 /// @brief Type of the first argument, which will be used to return the results
    242 ///
    243 /// Is expected to be a pointer type.
    244 
    245 /// @def TYPVAL_ENCODE_FIRST_ARG_NAME
    246 /// @brief Name of the first argument
    247 ///
    248 /// This name will only be used by one of the above macros which are defined by
    249 /// the caller. Functions defined here do not use first argument directly.
    250 #include <assert.h>
    251 #include <inttypes.h>
    252 #include <stddef.h>
    253 
    254 #include "klib/kvec.h"
    255 #include "nvim/eval.h"
    256 #include "nvim/eval/encode.h"
    257 #include "nvim/eval/typval.h"
    258 #include "nvim/eval/typval_encode.h"
    259 #include "nvim/eval/vars.h"
    260 #include "nvim/func_attr.h"
    261 
    262 /// Dummy variable used because some macros need lvalue
    263 ///
    264 /// Must not be written to, if needed one must check that address of the
    265 /// macros argument is (not) equal to `&TYPVAL_ENCODE_NODICT_VAR`.
    266 const dict_T *const TYPVAL_ENCODE_NODICT_VAR = NULL;
    267 
    268 static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
    269                                                      TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
    270                                                      void *const val, int *const val_copyID,
    271                                                      const MPConvStack *const mpstack, const int copyID,
    272                                                      const MPConvStackValType conv_type,
    273                                                      const char *const objname)
    274  REAL_FATTR_NONNULL_ARG(2, 3, 4, 7) REAL_FATTR_WARN_UNUSED_RESULT
    275  REAL_FATTR_ALWAYS_INLINE;
    276 
    277 /// Function for checking whether container references itself
    278 ///
    279 /// @param  TYPVAL_ENCODE_FIRST_ARG_NAME  First argument.
    280 /// @param[in,out]  val  Container to check.
    281 /// @param  val_copyID  Pointer to the container attribute that holds copyID.
    282 ///                     After checking whether value of this attribute is
    283 ///                     copyID (variable) it is set to copyID.
    284 /// @param[in]  mpstack  Stack with values to convert. Read-only, used for error
    285 ///                      reporting.
    286 /// @param[in]  copyID  CopyID used by the caller.
    287 /// @param[in]  conv_type  Type of the conversion, @see MPConvStackValType.
    288 /// @param[in]  objname  Object name, used for error reporting.
    289 ///
    290 /// @return NOTDONE in case of success, what to return in case of failure.
    291 static inline int TYPVAL_ENCODE_CHECK_SELF_REFERENCE(
    292                                                     TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, void *const val, int *const val_copyID,
    293                                                     const MPConvStack *const mpstack, const int copyID, const MPConvStackValType conv_type,
    294                                                     const char *const objname)
    295 {
    296  if (*val_copyID == copyID) {
    297    TYPVAL_ENCODE_CONV_RECURSE(val, conv_type);
    298    return OK;
    299  }
    300  *val_copyID = copyID;
    301  return NOTDONE;
    302 }
    303 
    304 static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
    305                                            TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
    306                                            MPConvStack *const mpstack, MPConvStackVal *const cur_mpsv,
    307                                            typval_T *const tv, const int copyID,
    308                                            const char *const objname)
    309  REAL_FATTR_NONNULL_ARG(2, 4, 6) REAL_FATTR_WARN_UNUSED_RESULT;
    310 
    311 /// Convert single value
    312 ///
    313 /// Only scalar values are converted immediately, everything else is pushed onto
    314 /// the stack.
    315 ///
    316 /// @param  TYPVAL_ENCODE_FIRST_ARG_NAME  First argument, defined by the
    317 ///                                       includer. Only meaningful to macros
    318 ///                                       defined by the includer.
    319 /// @param  mpstack  Stack with values to convert. Values which are not
    320 ///                  converted completely by this function (i.e.
    321 ///                  non-scalars) are pushed here.
    322 /// @param  cur_mpsv  Currently converted value from stack.
    323 /// @param  tv  Converted value.
    324 /// @param[in]  copyID  CopyID.
    325 /// @param[in]  objname  Object name, used for error reporting.
    326 ///
    327 /// @return OK in case of success, FAIL in case of failure.
    328 static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
    329                                           TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, MPConvStack *const mpstack,
    330                                           MPConvStackVal *const cur_mpsv, typval_T *const tv, const int copyID, const char *const objname)
    331 {
    332  TYPVAL_ENCODE_CHECK_BEFORE;
    333  switch (tv->v_type) {
    334  case VAR_STRING:
    335    TYPVAL_ENCODE_CONV_STRING(tv, tv->vval.v_string, tv_strlen(tv));
    336    break;
    337  case VAR_NUMBER:
    338    TYPVAL_ENCODE_CONV_NUMBER(tv, tv->vval.v_number);
    339    break;
    340  case VAR_FLOAT:
    341    TYPVAL_ENCODE_CONV_FLOAT(tv, tv->vval.v_float);
    342    break;
    343  case VAR_BLOB:
    344    TYPVAL_ENCODE_CONV_BLOB(tv, tv->vval.v_blob,
    345                            tv_blob_len(tv->vval.v_blob));
    346    break;
    347  case VAR_FUNC:
    348    TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string, "");
    349    TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
    350    TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
    351    TYPVAL_ENCODE_CONV_FUNC_END(tv);
    352    break;
    353  case VAR_PARTIAL: {
    354    partial_T *const pt = tv->vval.v_partial;
    355    char *const fun = pt == NULL ? NULL : partial_name(pt);
    356    // When using uf_name prepend "g:" for a global function.
    357    const char *const prefix = fun != NULL && pt != NULL && pt->pt_name == NULL
    358                               && ASCII_ISUPPER(fun[0]) ? "g:" : "";
    359    (void)prefix;
    360    TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix);
    361    kvi_push(*mpstack, ((MPConvStackVal) {
    362        .type = kMPConvPartial,
    363        .tv = tv,
    364        .saved_copyID = copyID - 1,
    365        .data = {
    366          .p = {
    367            .stage = kMPConvPartialArgs,
    368            .pt = tv->vval.v_partial,
    369          },
    370        },
    371    }));
    372    break;
    373  }
    374  case VAR_LIST: {
    375    if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) {
    376      TYPVAL_ENCODE_CONV_EMPTY_LIST(tv);
    377      break;
    378    }
    379    const int saved_copyID = tv_list_copyid(tv->vval.v_list);
    380    TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
    381                                          kMPConvList);
    382    TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
    383    assert(saved_copyID != copyID);
    384    kvi_push(*mpstack, ((MPConvStackVal) {
    385        .type = kMPConvList,
    386        .tv = tv,
    387        .saved_copyID = saved_copyID,
    388        .data = {
    389          .l = {
    390            .list = tv->vval.v_list,
    391            .li = tv_list_first(tv->vval.v_list),
    392          },
    393        },
    394    }));
    395    TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, kv_last(*mpstack));
    396    break;
    397  }
    398  case VAR_BOOL:
    399    switch (tv->vval.v_bool) {
    400    case kBoolVarTrue:
    401    case kBoolVarFalse:
    402      TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue);
    403      break;
    404    }
    405    break;
    406  case VAR_SPECIAL:
    407    switch (tv->vval.v_special) {
    408    case kSpecialVarNull:
    409      TYPVAL_ENCODE_CONV_NIL(tv);
    410      break;
    411    }
    412    break;
    413  case VAR_DICT: {
    414    if (tv->vval.v_dict == NULL
    415        || tv->vval.v_dict->dv_hashtab.ht_used == 0) {
    416      TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, tv->vval.v_dict);
    417      break;
    418    }
    419    const dictitem_T *type_di;
    420    const dictitem_T *val_di;
    421    if (TYPVAL_ENCODE_ALLOW_SPECIALS
    422        && tv->vval.v_dict->dv_hashtab.ht_used == 2
    423        && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict,
    424                                   S_LEN("_TYPE"))) != NULL
    425        && type_di->di_tv.v_type == VAR_LIST
    426        && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict,
    427                                  S_LEN("_VAL"))) != NULL) {
    428      size_t i;
    429      for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) {
    430        if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) {
    431          break;
    432        }
    433      }
    434      TYPVAL_ENCODE_CHECK_BEFORE;
    435      if (i == ARRAY_SIZE(eval_msgpack_type_lists)) {
    436        goto _convert_one_value_regular_dict;
    437      }
    438      switch ((MessagePackType)i) {
    439      case kMPNil:
    440        TYPVAL_ENCODE_CONV_NIL(tv);
    441        break;
    442      case kMPBoolean:
    443        if (val_di->di_tv.v_type != VAR_NUMBER) {
    444          goto _convert_one_value_regular_dict;
    445        }
    446        TYPVAL_ENCODE_CONV_BOOL(tv, val_di->di_tv.vval.v_number);
    447        break;
    448      case kMPInteger: {
    449        const list_T *val_list;
    450        varnumber_T sign;
    451        varnumber_T highest_bits;
    452        varnumber_T high_bits;
    453        varnumber_T low_bits;
    454        // List of 4 integers; first is signed (should be 1 or -1, but
    455        // this is not checked), second is unsigned and have at most
    456        // one (sign is -1) or two (sign is 1) non-zero bits (number of
    457        // bits is not checked), other unsigned and have at most 31
    458        // non-zero bits (number of bits is not checked).
    459        if (val_di->di_tv.v_type != VAR_LIST
    460            || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) {
    461          goto _convert_one_value_regular_dict;
    462        }
    463 
    464        const listitem_T *const sign_li = tv_list_first(val_list);
    465        if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER
    466            || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) {
    467          goto _convert_one_value_regular_dict;
    468        }
    469 
    470        const listitem_T *const highest_bits_li = (
    471                                                   TV_LIST_ITEM_NEXT(val_list, sign_li));
    472        if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER
    473            || ((highest_bits = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number)
    474                < 0)) {
    475          goto _convert_one_value_regular_dict;
    476        }
    477 
    478        const listitem_T *const high_bits_li =  (
    479                                                 TV_LIST_ITEM_NEXT(val_list, highest_bits_li));
    480        if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER
    481            || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number)
    482                < 0)) {
    483          goto _convert_one_value_regular_dict;
    484        }
    485 
    486        const listitem_T *const low_bits_li = tv_list_last(val_list);
    487        if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER
    488            || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number)
    489                < 0)) {
    490          goto _convert_one_value_regular_dict;
    491        }
    492 
    493        const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62)
    494                                 | (uint64_t)(((uint64_t)high_bits) << 31)
    495                                 | (uint64_t)low_bits);
    496        if (sign > 0) {
    497          TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number);
    498        } else {
    499          TYPVAL_ENCODE_CONV_NUMBER(tv, -number);
    500        }
    501        break;
    502      }
    503      case kMPFloat:
    504        if (val_di->di_tv.v_type != VAR_FLOAT) {
    505          goto _convert_one_value_regular_dict;
    506        }
    507        TYPVAL_ENCODE_CONV_FLOAT(tv, val_di->di_tv.vval.v_float);
    508        break;
    509      case kMPString: {
    510        if (val_di->di_tv.v_type != VAR_LIST) {
    511          goto _convert_one_value_regular_dict;
    512        }
    513        size_t len;
    514        char *buf;
    515        if (!encode_vim_list_to_buf(val_di->di_tv.vval.v_list, &len,
    516                                    &buf)) {
    517          goto _convert_one_value_regular_dict;
    518        }
    519        TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len);
    520        xfree(buf);
    521        break;
    522      }
    523      case kMPArray: {
    524        if (val_di->di_tv.v_type != VAR_LIST) {
    525          goto _convert_one_value_regular_dict;
    526        }
    527        const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
    528        TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list,
    529                                              lv_copyID, copyID,
    530                                              kMPConvList);
    531        TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(val_di->di_tv.vval.v_list));
    532        assert(saved_copyID != copyID && saved_copyID != copyID - 1);
    533        kvi_push(*mpstack, ((MPConvStackVal) {
    534              .tv = tv,
    535              .type = kMPConvList,
    536              .saved_copyID = saved_copyID,
    537              .data = {
    538                .l = {
    539                  .list = val_di->di_tv.vval.v_list,
    540                  .li = tv_list_first(val_di->di_tv.vval.v_list),
    541                },
    542              },
    543        }));
    544        break;
    545      }
    546      case kMPMap: {
    547        if (val_di->di_tv.v_type != VAR_LIST) {
    548          goto _convert_one_value_regular_dict;
    549        }
    550        list_T *const val_list = val_di->di_tv.vval.v_list;
    551        if (val_list == NULL || tv_list_len(val_list) == 0) {
    552          TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR);
    553          break;
    554        }
    555        TV_LIST_ITER_CONST(val_list, li, {
    556              if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
    557                  || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) {
    558                goto _convert_one_value_regular_dict;
    559              }
    560            });
    561        const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list);
    562        TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID,
    563                                              kMPConvPairs);
    564        TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR,
    565                                      tv_list_len(val_list));
    566        assert(saved_copyID != copyID && saved_copyID != copyID - 1);
    567        kvi_push(*mpstack, ((MPConvStackVal) {
    568              .tv = tv,
    569              .type = kMPConvPairs,
    570              .saved_copyID = saved_copyID,
    571              .data = {
    572                .l = {
    573                  .list = val_list,
    574                  .li = tv_list_first(val_list),
    575                },
    576              },
    577        }));
    578        break;
    579      }
    580      case kMPExt: {
    581        const list_T *val_list;
    582        varnumber_T type;
    583        if (val_di->di_tv.v_type != VAR_LIST
    584            || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2
    585            || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type
    586                != VAR_NUMBER)
    587            || ((type = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number)
    588                > INT8_MAX)
    589            || type < INT8_MIN
    590            || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type
    591                != VAR_LIST)) {
    592          goto _convert_one_value_regular_dict;
    593        }
    594        size_t len;
    595        char *buf;
    596        if (!(
    597              encode_vim_list_to_buf(TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len,
    598                                     &buf))) {
    599          goto _convert_one_value_regular_dict;
    600        }
    601        TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type);
    602        xfree(buf);
    603        break;
    604      }
    605      }
    606      break;
    607    }
    608 _convert_one_value_regular_dict: {}
    609    const int saved_copyID = tv->vval.v_dict->dv_copyID;
    610    TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_dict, dv_copyID, copyID,
    611                                          kMPConvDict);
    612    TYPVAL_ENCODE_CONV_DICT_START(tv, tv->vval.v_dict,
    613                                  tv->vval.v_dict->dv_hashtab.ht_used);
    614    assert(saved_copyID != copyID);
    615    kvi_push(*mpstack, ((MPConvStackVal) {
    616        .tv = tv,
    617        .type = kMPConvDict,
    618        .saved_copyID = saved_copyID,
    619        .data = {
    620          .d = {
    621            .dict = tv->vval.v_dict,
    622            .dictp = &tv->vval.v_dict,
    623            .hi = tv->vval.v_dict->dv_hashtab.ht_array,
    624            .todo = tv->vval.v_dict->dv_hashtab.ht_used,
    625          },
    626        },
    627    }));
    628    TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict,
    629                                             kv_last(*mpstack));
    630    break;
    631  }
    632  case VAR_UNKNOWN:
    633    internal_error(STR(TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()");
    634    return FAIL;
    635  }
    636 typval_encode_stop_converting_one_item:
    637  return OK;
    638  // Prevent “unused label” warnings.
    639  goto typval_encode_stop_converting_one_item;
    640 }
    641 
    642 TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE(
    643                                              TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME,
    644                                              typval_T *const tv, const char *const objname)
    645  REAL_FATTR_NONNULL_ARG(2, 3) REAL_FATTR_WARN_UNUSED_RESULT;
    646 
    647 /// Convert the whole typval
    648 ///
    649 /// @param  TYPVAL_ENCODE_FIRST_ARG_NAME  First argument, defined by the
    650 ///                                       includer. Only meaningful to macros
    651 ///                                       defined by the includer.
    652 /// @param  top_tv  Converted value.
    653 /// @param[in]  objname  Object name, used for error reporting.
    654 ///
    655 /// @return OK in case of success, FAIL in case of failure.
    656 TYPVAL_ENCODE_SCOPE int TYPVAL_ENCODE_ENCODE(
    657                                             TYPVAL_ENCODE_FIRST_ARG_TYPE TYPVAL_ENCODE_FIRST_ARG_NAME, typval_T *const top_tv,
    658                                             const char *const objname)
    659 {
    660  const int copyID = get_copyID();
    661  MPConvStack mpstack;
    662  kvi_init(mpstack);
    663  if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
    664                                      NULL,
    665                                      top_tv, copyID, objname)
    666      == FAIL) {
    667    goto encode_vim_to__error_ret;
    668  }
    669 /// Label common for this and convert_one_value functions, used for escaping
    670 /// from macros like TYPVAL_ENCODE_CONV_DICT_START.
    671 typval_encode_stop_converting_one_item:
    672  while (kv_size(mpstack)) {
    673    MPConvStackVal *cur_mpsv = &kv_last(mpstack);
    674    typval_T *tv = NULL;
    675    switch (cur_mpsv->type) {
    676    case kMPConvDict: {
    677      if (!cur_mpsv->data.d.todo) {
    678        (void)kv_pop(mpstack);
    679        cur_mpsv->data.d.dict->dv_copyID = cur_mpsv->saved_copyID;
    680        TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, *cur_mpsv->data.d.dictp);
    681        continue;
    682      } else if (cur_mpsv->data.d.todo
    683                 != cur_mpsv->data.d.dict->dv_hashtab.ht_used) {
    684        TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv,
    685                                              *cur_mpsv->data.d.dictp);
    686      }
    687      while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) {
    688        cur_mpsv->data.d.hi++;
    689      }
    690      dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi);
    691      cur_mpsv->data.d.todo--;
    692      cur_mpsv->data.d.hi++;
    693      TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0],
    694                                    strlen((char *)&di->di_key[0]));
    695      TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
    696                                        *cur_mpsv->data.d.dictp);
    697      tv = &di->di_tv;
    698      break;
    699    }
    700    case kMPConvList:
    701      if (cur_mpsv->data.l.li == NULL) {
    702        (void)kv_pop(mpstack);
    703        tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
    704        TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv);
    705        continue;
    706      } else if (cur_mpsv->data.l.li
    707                 != tv_list_first(cur_mpsv->data.l.list)) {
    708        TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv);
    709      }
    710      tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li);
    711      cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
    712                                              cur_mpsv->data.l.li);
    713      break;
    714    case kMPConvPairs: {
    715      if (cur_mpsv->data.l.li == NULL) {
    716        (void)kv_pop(mpstack);
    717        tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID);
    718        TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
    719        continue;
    720      } else if (cur_mpsv->data.l.li
    721                 != tv_list_first(cur_mpsv->data.l.list)) {
    722        TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR);
    723      }
    724      const list_T *const kv_pair = (
    725                                     TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list);
    726      TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(encode_vim_to__error_ret,
    727                                           *TV_LIST_ITEM_TV(tv_list_first(kv_pair)));
    728      if (
    729          TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv,
    730                                          TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname)
    731          == FAIL) {
    732        goto encode_vim_to__error_ret;
    733      }
    734      TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv,
    735                                        TYPVAL_ENCODE_NODICT_VAR);
    736      tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair));
    737      cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list,
    738                                              cur_mpsv->data.l.li);
    739      break;
    740    }
    741    case kMPConvPartial: {
    742      partial_T *const pt = cur_mpsv->data.p.pt;
    743      tv = cur_mpsv->tv;
    744      (void)tv;
    745      switch (cur_mpsv->data.p.stage) {
    746      case kMPConvPartialArgs:
    747        TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv,
    748                                            pt == NULL ? 0 : pt->pt_argc);
    749        cur_mpsv->data.p.stage = kMPConvPartialSelf;
    750        if (pt != NULL && pt->pt_argc > 0) {
    751          TYPVAL_ENCODE_CONV_LIST_START(NULL, pt->pt_argc);
    752          kvi_push(mpstack, ((MPConvStackVal) {
    753              .type = kMPConvPartialList,
    754              .tv = NULL,
    755              .saved_copyID = copyID - 1,
    756              .data = {
    757                .a = {
    758                  .arg = pt->pt_argv,
    759                  .argv = pt->pt_argv,
    760                  .todo = (size_t)pt->pt_argc,
    761                },
    762              },
    763          }));
    764        }
    765        break;
    766      case kMPConvPartialSelf: {
    767        cur_mpsv->data.p.stage = kMPConvPartialEnd;
    768        dict_T *const dict = pt == NULL ? NULL : pt->pt_dict;
    769        if (dict != NULL) {
    770          TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, dict->dv_hashtab.ht_used);
    771          if (dict->dv_hashtab.ht_used == 0) {
    772            TYPVAL_ENCODE_CONV_EMPTY_DICT(NULL, pt->pt_dict);
    773            continue;
    774          }
    775          const int saved_copyID = dict->dv_copyID;
    776          const int te_csr_ret = TYPVAL_ENCODE_CHECK_SELF_REFERENCE(TYPVAL_ENCODE_FIRST_ARG_NAME,
    777                                                                    dict, &dict->dv_copyID,
    778                                                                    &mpstack, copyID, kMPConvDict,
    779                                                                    objname);
    780          if (te_csr_ret != NOTDONE) {
    781            if (te_csr_ret == FAIL) {
    782              goto encode_vim_to__error_ret;
    783            } else {
    784              continue;
    785            }
    786          }
    787          TYPVAL_ENCODE_CONV_DICT_START(NULL, pt->pt_dict,
    788                                        dict->dv_hashtab.ht_used);
    789          assert(saved_copyID != copyID && saved_copyID != copyID - 1);
    790          kvi_push(mpstack, ((MPConvStackVal) {
    791                .type = kMPConvDict,
    792                .tv = NULL,
    793                .saved_copyID = saved_copyID,
    794                .data = {
    795                  .d = {
    796                    .dict = dict,
    797                    .dictp = &pt->pt_dict,
    798                    .hi = dict->dv_hashtab.ht_array,
    799                    .todo = dict->dv_hashtab.ht_used,
    800                  },
    801                },
    802          }));
    803          TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict,
    804                                                   kv_last(mpstack));
    805        } else {
    806          TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
    807        }
    808        break;
    809      }
    810      case kMPConvPartialEnd:
    811        TYPVAL_ENCODE_CONV_FUNC_END(tv);
    812        (void)kv_pop(mpstack);
    813        break;
    814      }
    815      continue;
    816    }
    817    case kMPConvPartialList:
    818      if (!cur_mpsv->data.a.todo) {
    819        (void)kv_pop(mpstack);
    820        TYPVAL_ENCODE_CONV_LIST_END(NULL);
    821        continue;
    822      } else if (cur_mpsv->data.a.argv != cur_mpsv->data.a.arg) {
    823        TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(NULL);
    824      }
    825      tv = cur_mpsv->data.a.arg++;
    826      cur_mpsv->data.a.todo--;
    827      break;
    828    }
    829    assert(tv != NULL);
    830    if (TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack,
    831                                        cur_mpsv, tv, copyID, objname)
    832        == FAIL) {
    833      goto encode_vim_to__error_ret;
    834    }
    835  }
    836  kvi_destroy(mpstack);
    837  return OK;
    838 encode_vim_to__error_ret:
    839  kvi_destroy(mpstack);
    840  return FAIL;
    841  // Prevent “unused label” warnings.
    842  goto typval_encode_stop_converting_one_item;
    843 }