neovim

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

converter.c (43557B)


      1 #include <assert.h>
      2 #include <lauxlib.h>
      3 #include <lua.h>
      4 #include <stdbool.h>
      5 #include <stddef.h>
      6 #include <stdint.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include "klib/kvec.h"
     11 #include "nvim/api/private/defs.h"
     12 #include "nvim/api/private/helpers.h"
     13 #include "nvim/ascii_defs.h"
     14 #include "nvim/eval/decode.h"
     15 #include "nvim/eval/typval.h"
     16 #include "nvim/eval/typval_defs.h"
     17 #include "nvim/eval/typval_encode.h"
     18 #include "nvim/eval/userfunc.h"
     19 #include "nvim/gettext_defs.h"
     20 #include "nvim/highlight_group.h"
     21 #include "nvim/lua/converter.h"
     22 #include "nvim/lua/executor.h"
     23 #include "nvim/macros_defs.h"
     24 #include "nvim/memory.h"
     25 #include "nvim/memory_defs.h"
     26 #include "nvim/message.h"
     27 #include "nvim/types_defs.h"
     28 #include "nvim/vim_defs.h"
     29 
     30 /// Determine, which keys Lua table contains
     31 typedef struct {
     32  size_t maxidx;  ///< Maximum positive integral value found.
     33  size_t string_keys_num;  ///< Number of string keys.
     34  bool has_string_with_nul;  ///< True if there is string key with NUL byte.
     35  ObjectType type;  ///< If has_type_key is true then attached value. Otherwise
     36                    ///< either kObjectTypeNil, kObjectTypeDict or
     37                    ///< kObjectTypeArray, depending on other properties.
     38  lua_Number val;  ///< If has_val_key and val_type == LUA_TNUMBER: value.
     39  bool has_type_key;  ///< True if type key is present.
     40 } LuaTableProps;
     41 
     42 #include "lua/converter.c.generated.h"
     43 
     44 #define TYPE_IDX_VALUE true
     45 #define VAL_IDX_VALUE false
     46 
     47 #define LUA_PUSH_STATIC_STRING(lstate, s) \
     48  lua_pushlstring(lstate, s, sizeof(s) - 1)
     49 
     50 static LuaTableProps nlua_traverse_table(lua_State *const lstate)
     51  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
     52 {
     53  size_t tsize = 0;  // Total number of keys.
     54  int val_type = 0;  // If has_val_key: Lua type of the value.
     55  bool has_val_key = false;  // True if val key was found,
     56                             // @see nlua_push_val_idx().
     57  size_t other_keys_num = 0;  // Number of keys that are not string, integral
     58                              // or type keys.
     59  LuaTableProps ret;
     60  CLEAR_FIELD(ret);
     61  if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
     62    semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2);
     63    ret.type = kObjectTypeNil;
     64    return ret;
     65  }
     66  lua_pushnil(lstate);
     67  while (lua_next(lstate, -2)) {
     68    switch (lua_type(lstate, -2)) {
     69    case LUA_TSTRING: {
     70      size_t len;
     71      const char *s = lua_tolstring(lstate, -2, &len);
     72      if (memchr(s, NUL, len) != NULL) {
     73        ret.has_string_with_nul = true;
     74      }
     75      ret.string_keys_num++;
     76      break;
     77    }
     78    case LUA_TNUMBER: {
     79      const lua_Number n = lua_tonumber(lstate, -2);
     80      if (n > (lua_Number)SIZE_MAX || n <= 0
     81          || ((lua_Number)((size_t)n)) != n) {
     82        other_keys_num++;
     83      } else {
     84        const size_t idx = (size_t)n;
     85        if (idx > ret.maxidx) {
     86          ret.maxidx = idx;
     87        }
     88      }
     89      break;
     90    }
     91    case LUA_TBOOLEAN: {
     92      const bool b = lua_toboolean(lstate, -2);
     93      if (b == TYPE_IDX_VALUE) {
     94        if (lua_type(lstate, -1) == LUA_TNUMBER) {
     95          lua_Number n = lua_tonumber(lstate, -1);
     96          if (n == (lua_Number)kObjectTypeFloat
     97              || n == (lua_Number)kObjectTypeArray
     98              || n == (lua_Number)kObjectTypeDict) {
     99            ret.has_type_key = true;
    100            ret.type = (ObjectType)n;
    101          } else {
    102            other_keys_num++;
    103          }
    104        } else {
    105          other_keys_num++;
    106        }
    107      } else {
    108        has_val_key = true;
    109        val_type = lua_type(lstate, -1);
    110        if (val_type == LUA_TNUMBER) {
    111          ret.val = lua_tonumber(lstate, -1);
    112        }
    113      }
    114      break;
    115    }
    116    default:
    117      other_keys_num++;
    118      break;
    119    }
    120    tsize++;
    121    lua_pop(lstate, 1);
    122  }
    123  if (ret.has_type_key) {
    124    assert(tsize > 0);
    125    if (ret.type == kObjectTypeFloat
    126        && (!has_val_key || val_type != LUA_TNUMBER)) {
    127      ret.type = kObjectTypeNil;
    128    } else if (ret.type == kObjectTypeArray) {
    129      // Determine what is the last number in a *sequence* of keys.
    130      // This condition makes sure that Neovim will not crash when it gets table
    131      // {[vim.type_idx]=vim.types.array, [SIZE_MAX]=1}: without it maxidx will
    132      // be SIZE_MAX, with this condition it should be zero and [SIZE_MAX] key
    133      // should be ignored.
    134      if (ret.maxidx != 0
    135          && ret.maxidx != (tsize
    136                            - ret.has_type_key
    137                            - other_keys_num
    138                            - has_val_key
    139                            - ret.string_keys_num)) {
    140        for (ret.maxidx = 0;; ret.maxidx++) {
    141          lua_rawgeti(lstate, -1, (int)ret.maxidx + 1);
    142          if (lua_isnil(lstate, -1)) {
    143            lua_pop(lstate, 1);
    144            break;
    145          }
    146          lua_pop(lstate, 1);
    147        }
    148      }
    149    }
    150  } else {
    151    if (tsize == 0
    152        || (tsize <= ret.maxidx
    153            && other_keys_num == 0
    154            && ret.string_keys_num == 0)) {
    155      ret.type = kObjectTypeArray;
    156      if (tsize == 0 && lua_getmetatable(lstate, -1)) {
    157        nlua_pushref(lstate, nlua_global_refs->empty_dict_ref);
    158        if (lua_rawequal(lstate, -2, -1)) {
    159          ret.type = kObjectTypeDict;
    160        }
    161        lua_pop(lstate, 2);
    162      }
    163    } else if (ret.string_keys_num == tsize) {
    164      ret.type = kObjectTypeDict;
    165    } else {
    166      ret.type = kObjectTypeNil;
    167    }
    168  }
    169  return ret;
    170 }
    171 
    172 /// Helper structure for nlua_pop_typval
    173 typedef struct {
    174  typval_T *tv;     ///< Location where conversion result is saved.
    175  size_t list_len;  ///< Maximum length when tv is a list.
    176  bool container;   ///< True if tv is a container.
    177  bool special;     ///< If true then tv is a _VAL part of special dict.
    178                    ///< that represents mapping.
    179  int idx;          ///< Container index (used to detect self-referencing structures).
    180 } TVPopStackItem;
    181 
    182 /// Convert Lua object to Vimscript typval_T
    183 ///
    184 /// Should pop exactly one value from Lua stack.
    185 ///
    186 /// @param  lstate  Lua state.
    187 /// @param[out]  ret_tv Where to put the result.
    188 ///
    189 /// @return `true` in case of success, `false` in case of failure. Error is
    190 ///         reported automatically.
    191 bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
    192 {
    193  bool ret = true;
    194  const int initial_size = lua_gettop(lstate);
    195  kvec_withinit_t(TVPopStackItem, 2) stack = KV_INITIAL_VALUE;
    196  kvi_init(stack);
    197  kvi_push(stack, ((TVPopStackItem){ .tv = ret_tv }));
    198  while (ret && kv_size(stack)) {
    199    if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
    200      semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
    201      ret = false;
    202      break;
    203    }
    204    TVPopStackItem cur = kv_pop(stack);
    205    if (cur.container) {
    206      if (cur.special || cur.tv->v_type == VAR_DICT) {
    207        assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT));
    208        bool next_key_found = false;
    209        while (lua_next(lstate, -2)) {
    210          if (lua_type(lstate, -2) == LUA_TSTRING) {
    211            next_key_found = true;
    212            break;
    213          }
    214          lua_pop(lstate, 1);
    215        }
    216        if (next_key_found) {
    217          size_t len;
    218          const char *s = lua_tolstring(lstate, -2, &len);
    219          if (cur.special) {
    220            list_T *const kv_pair = tv_list_alloc(2);
    221 
    222            typval_T s_tv = decode_string(s, len, true, false);
    223            tv_list_append_owned_tv(kv_pair, s_tv);
    224 
    225            // Value: not populated yet, need to create list item to push.
    226            tv_list_append_owned_tv(kv_pair, (typval_T) {
    227              .v_type = VAR_UNKNOWN,
    228            });
    229            kvi_push(stack, cur);
    230            tv_list_append_list(cur.tv->vval.v_list, kv_pair);
    231            cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)) };
    232          } else {
    233            dictitem_T *const di = tv_dict_item_alloc_len(s, len);
    234            if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
    235              abort();
    236            }
    237            kvi_push(stack, cur);
    238            cur = (TVPopStackItem){ .tv = &di->di_tv };
    239          }
    240        } else {
    241          lua_pop(lstate, 1);
    242          continue;
    243        }
    244      } else {
    245        assert(cur.tv->v_type == VAR_LIST);
    246        if ((size_t)tv_list_len(cur.tv->vval.v_list) == cur.list_len) {
    247          lua_pop(lstate, 1);
    248          continue;
    249        }
    250        lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1);
    251        // Not populated yet, need to create list item to push.
    252        tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) {
    253          .v_type = VAR_UNKNOWN,
    254        });
    255        kvi_push(stack, cur);
    256        // TODO(ZyX-I): Use indexes, here list item *will* be reallocated.
    257        cur = (TVPopStackItem){ .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)) };
    258      }
    259    }
    260    assert(!cur.container);
    261    *cur.tv = (typval_T) {
    262      .v_type = VAR_NUMBER,
    263      .v_lock = VAR_UNLOCKED,
    264      .vval = { .v_number = 0 },
    265    };
    266    switch (lua_type(lstate, -1)) {
    267    case LUA_TNIL:
    268      cur.tv->v_type = VAR_SPECIAL;
    269      cur.tv->vval.v_special = kSpecialVarNull;
    270      break;
    271    case LUA_TBOOLEAN:
    272      cur.tv->v_type = VAR_BOOL;
    273      cur.tv->vval.v_bool = (lua_toboolean(lstate, -1) ? kBoolVarTrue : kBoolVarFalse);
    274      break;
    275    case LUA_TSTRING: {
    276      size_t len;
    277      const char *s = lua_tolstring(lstate, -1, &len);
    278      *cur.tv = decode_string(s, len, false, false);
    279      break;
    280    }
    281    case LUA_TNUMBER: {
    282      const lua_Number n = lua_tonumber(lstate, -1);
    283      if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN
    284          || ((lua_Number)((varnumber_T)n)) != n) {
    285        cur.tv->v_type = VAR_FLOAT;
    286        cur.tv->vval.v_float = (float_T)n;
    287      } else {
    288        cur.tv->v_type = VAR_NUMBER;
    289        cur.tv->vval.v_number = (varnumber_T)n;
    290      }
    291      break;
    292    }
    293    case LUA_TTABLE: {
    294      // Only need to track table refs if we have a metatable associated.
    295      LuaRef table_ref = LUA_NOREF;
    296      if (lua_getmetatable(lstate, -1)) {
    297        lua_pop(lstate, 1);
    298        table_ref = nlua_ref_global(lstate, -1);
    299      }
    300 
    301      const LuaTableProps table_props = nlua_traverse_table(lstate);
    302 
    303      for (size_t i = 0; i < kv_size(stack); i++) {
    304        const TVPopStackItem item = kv_A(stack, i);
    305        if (item.container && lua_rawequal(lstate, -1, item.idx)) {
    306          tv_copy(item.tv, cur.tv);
    307          cur.container = false;
    308          goto nlua_pop_typval_table_processing_end;
    309        }
    310      }
    311 
    312      switch (table_props.type) {
    313      case kObjectTypeArray:
    314        cur.tv->v_type = VAR_LIST;
    315        cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
    316        cur.tv->vval.v_list->lua_table_ref = table_ref;
    317        tv_list_ref(cur.tv->vval.v_list);
    318        cur.list_len = table_props.maxidx;
    319        if (table_props.maxidx != 0) {
    320          cur.container = true;
    321          cur.idx = lua_gettop(lstate);
    322          kvi_push(stack, cur);
    323        }
    324        break;
    325      case kObjectTypeDict:
    326        if (table_props.string_keys_num == 0) {
    327          cur.tv->v_type = VAR_DICT;
    328          cur.tv->vval.v_dict = tv_dict_alloc();
    329          cur.tv->vval.v_dict->dv_refcount++;
    330          cur.tv->vval.v_dict->lua_table_ref = table_ref;
    331        } else {
    332          cur.special = table_props.has_string_with_nul;
    333          if (table_props.has_string_with_nul) {
    334            decode_create_map_special_dict(cur.tv, (ptrdiff_t)table_props.string_keys_num);
    335            assert(cur.tv->v_type == VAR_DICT);
    336            dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict,
    337                                                    S_LEN("_VAL"));
    338            assert(val_di != NULL);
    339            cur.tv = &val_di->di_tv;
    340            cur.tv->vval.v_list->lua_table_ref = table_ref;
    341            assert(cur.tv->v_type == VAR_LIST);
    342            cur.list_len = table_props.string_keys_num;
    343          } else {
    344            cur.tv->v_type = VAR_DICT;
    345            cur.tv->vval.v_dict = tv_dict_alloc();
    346            cur.tv->vval.v_dict->dv_refcount++;
    347            cur.tv->vval.v_dict->lua_table_ref = table_ref;
    348          }
    349          cur.container = true;
    350          cur.idx = lua_gettop(lstate);
    351          kvi_push(stack, cur);
    352          lua_pushnil(lstate);
    353        }
    354        break;
    355      case kObjectTypeFloat:
    356        cur.tv->v_type = VAR_FLOAT;
    357        cur.tv->vval.v_float = (float_T)table_props.val;
    358        break;
    359      case kObjectTypeNil:
    360        emsg(_("E5100: Cannot convert given Lua table: table should "
    361               "contain either only integer keys or only string keys"));
    362        ret = false;
    363        break;
    364      default:
    365        abort();
    366      }
    367 nlua_pop_typval_table_processing_end:
    368      break;
    369    }
    370    case LUA_TFUNCTION: {
    371      LuaRef func = nlua_ref_global(lstate, -1);
    372 
    373      char *name = register_luafunc(func);
    374 
    375      cur.tv->v_type = VAR_FUNC;
    376      cur.tv->vval.v_string = xstrdup(name);
    377      break;
    378    }
    379    case LUA_TUSERDATA: {
    380      // TODO(bfredl): check mt.__call and convert to function?
    381      nlua_pushref(lstate, nlua_global_refs->nil_ref);
    382      bool is_nil = lua_rawequal(lstate, -2, -1);
    383      lua_pop(lstate, 1);
    384      if (is_nil) {
    385        cur.tv->v_type = VAR_SPECIAL;
    386        cur.tv->vval.v_special = kSpecialVarNull;
    387      } else {
    388        emsg(_("E5101: Cannot convert given Lua type"));
    389        ret = false;
    390      }
    391      break;
    392    }
    393    default:
    394      emsg(_("E5101: Cannot convert given Lua type"));
    395      ret = false;
    396      break;
    397    }
    398    if (!cur.container) {
    399      lua_pop(lstate, 1);
    400    }
    401  }
    402  kvi_destroy(stack);
    403  if (!ret) {
    404    tv_clear(ret_tv);
    405    *ret_tv = (typval_T) {
    406      .v_type = VAR_NUMBER,
    407      .v_lock = VAR_UNLOCKED,
    408      .vval = { .v_number = 0 },
    409    };
    410    lua_pop(lstate, lua_gettop(lstate) - initial_size + 1);
    411  }
    412  assert(lua_gettop(lstate) == initial_size - 1);
    413  return ret;
    414 }
    415 
    416 static bool typval_conv_special = false;
    417 
    418 #define TYPVAL_ENCODE_ALLOW_SPECIALS true
    419 
    420 #define TYPVAL_ENCODE_CHECK_BEFORE
    421 
    422 #define TYPVAL_ENCODE_CONV_NIL(tv) \
    423  do { \
    424    if (typval_conv_special) { \
    425      lua_pushnil(lstate); \
    426    } else { \
    427      nlua_pushref(lstate, nlua_global_refs->nil_ref); \
    428    } \
    429  } while (0)
    430 
    431 #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
    432  lua_pushboolean(lstate, (bool)(num))
    433 
    434 #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
    435  lua_pushnumber(lstate, (lua_Number)(num))
    436 
    437 #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
    438 
    439 #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
    440  TYPVAL_ENCODE_CONV_NUMBER(tv, flt)
    441 
    442 #define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
    443  lua_pushlstring(lstate, (str), (len))
    444 
    445 #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
    446 
    447 #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
    448  TYPVAL_ENCODE_CONV_NIL(tv)
    449 
    450 #define TYPVAL_ENCODE_CONV_BLOB(tv, blob, len) \
    451  do { \
    452    const blob_T *const blob_ = (blob); \
    453    lua_pushlstring(lstate, blob_ != NULL ? blob_->bv_ga.ga_data : "", (size_t)(len)); \
    454  } while (0)
    455 
    456 #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
    457  do { \
    458    const char *const fun_ = (fun); \
    459    ufunc_T *fp; \
    460    if (fun_ != NULL && (fp = find_func(fun_)) != NULL && fp->uf_flags & FC_LUAREF) { \
    461      nlua_pushref(lstate, fp->uf_luaref); \
    462    } else { \
    463      TYPVAL_ENCODE_CONV_NIL(tv); \
    464    } \
    465    goto typval_encode_stop_converting_one_item; \
    466  } while (0)
    467 
    468 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
    469 #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
    470 #define TYPVAL_ENCODE_CONV_FUNC_END(tv)
    471 
    472 #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
    473  lua_createtable(lstate, 0, 0)
    474 
    475 #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
    476  do { \
    477    if (typval_conv_special) { \
    478      nlua_create_typed_table(lstate, 0, 0, kObjectTypeDict); \
    479    } else { \
    480      lua_createtable(lstate, 0, 0); \
    481      nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); \
    482      lua_setmetatable(lstate, -2); \
    483    } \
    484  } while (0)
    485 
    486 #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
    487  do { \
    488    if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
    489      semsg(_("E5102: Lua failed to grow stack to %i"), \
    490            lua_gettop(lstate) + 3); \
    491      return false; \
    492    } \
    493    lua_createtable(lstate, (int)(len), 0); \
    494    lua_pushnumber(lstate, 1); \
    495  } while (0)
    496 
    497 #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
    498 
    499 #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
    500  do { \
    501    lua_Number idx = lua_tonumber(lstate, -2); \
    502    lua_rawset(lstate, -3); \
    503    lua_pushnumber(lstate, idx + 1); \
    504  } while (0)
    505 
    506 #define TYPVAL_ENCODE_CONV_LIST_END(tv) \
    507  lua_rawset(lstate, -3)
    508 
    509 #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
    510  do { \
    511    if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
    512      semsg(_("E5102: Lua failed to grow stack to %i"), \
    513            lua_gettop(lstate) + 3); \
    514      return false; \
    515    } \
    516    lua_createtable(lstate, 0, (int)(len)); \
    517  } while (0)
    518 
    519 #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
    520 
    521 #define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
    522 
    523 #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
    524 
    525 #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
    526  lua_rawset(lstate, -3)
    527 
    528 #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
    529  TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
    530 
    531 #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
    532  do { \
    533    for (size_t backref = kv_size(*mpstack); backref; backref--) { \
    534      const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \
    535      if (mpval.type == conv_type) { \
    536        if (conv_type == kMPConvDict \
    537            ? (void *)mpval.data.d.dict == (void *)(val) \
    538            : (void *)mpval.data.l.list == (void *)(val)) { \
    539          lua_pushvalue(lstate, \
    540                        -((int)((kv_size(*mpstack) - backref + 1) * 2))); \
    541          break; \
    542        } \
    543      } \
    544    } \
    545  } while (0)
    546 
    547 #define TYPVAL_ENCODE_SCOPE static
    548 #define TYPVAL_ENCODE_NAME lua
    549 #define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const
    550 #define TYPVAL_ENCODE_FIRST_ARG_NAME lstate
    551 #include "nvim/eval/typval_encode.c.h"
    552 
    553 #undef TYPVAL_ENCODE_SCOPE
    554 #undef TYPVAL_ENCODE_NAME
    555 #undef TYPVAL_ENCODE_FIRST_ARG_TYPE
    556 #undef TYPVAL_ENCODE_FIRST_ARG_NAME
    557 
    558 #undef TYPVAL_ENCODE_CONV_STRING
    559 #undef TYPVAL_ENCODE_CONV_STR_STRING
    560 #undef TYPVAL_ENCODE_CONV_EXT_STRING
    561 #undef TYPVAL_ENCODE_CONV_BLOB
    562 #undef TYPVAL_ENCODE_CONV_NUMBER
    563 #undef TYPVAL_ENCODE_CONV_FLOAT
    564 #undef TYPVAL_ENCODE_CONV_FUNC_START
    565 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
    566 #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
    567 #undef TYPVAL_ENCODE_CONV_FUNC_END
    568 #undef TYPVAL_ENCODE_CONV_EMPTY_LIST
    569 #undef TYPVAL_ENCODE_CONV_LIST_START
    570 #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
    571 #undef TYPVAL_ENCODE_CONV_EMPTY_DICT
    572 #undef TYPVAL_ENCODE_CHECK_BEFORE
    573 #undef TYPVAL_ENCODE_CONV_NIL
    574 #undef TYPVAL_ENCODE_CONV_BOOL
    575 #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
    576 #undef TYPVAL_ENCODE_CONV_DICT_START
    577 #undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
    578 #undef TYPVAL_ENCODE_CONV_DICT_END
    579 #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
    580 #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
    581 #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
    582 #undef TYPVAL_ENCODE_CONV_LIST_END
    583 #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
    584 #undef TYPVAL_ENCODE_CONV_RECURSE
    585 #undef TYPVAL_ENCODE_ALLOW_SPECIALS
    586 
    587 /// Convert Vimscript typval_T to Lua value
    588 ///
    589 /// Should leave single value in Lua stack. May only fail if Lua failed to grow stack.
    590 ///
    591 /// @param  lstate  Lua interpreter state.
    592 /// @param[in]  tv  typval_T to convert.
    593 ///
    594 /// @return true in case of success, false otherwise.
    595 bool nlua_push_typval(lua_State *lstate, typval_T *const tv, int flags)
    596 {
    597  typval_conv_special = (flags & kNluaPushSpecial);
    598  const int initial_size = lua_gettop(lstate);
    599 
    600  if (!lua_checkstack(lstate, initial_size + 2)) {
    601    semsg(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
    602    return false;
    603  }
    604  if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) {
    605    return false;
    606  }
    607  assert(lua_gettop(lstate) == initial_size + 1);
    608  return true;
    609 }
    610 
    611 /// Push value which is a type index
    612 ///
    613 /// Used for all “typed” tables: i.e. for all tables which represent Vimscript values.
    614 static inline void nlua_push_type_idx(lua_State *lstate)
    615  FUNC_ATTR_NONNULL_ALL
    616 {
    617  lua_pushboolean(lstate, TYPE_IDX_VALUE);
    618 }
    619 
    620 /// Push value which is a value index
    621 ///
    622 /// Used for tables which represent scalar values, like float value.
    623 static inline void nlua_push_val_idx(lua_State *lstate)
    624  FUNC_ATTR_NONNULL_ALL
    625 {
    626  lua_pushboolean(lstate, VAL_IDX_VALUE);
    627 }
    628 
    629 /// Push type
    630 ///
    631 /// Type is a value in vim.types table.
    632 ///
    633 /// @param[out]  lstate  Lua state.
    634 /// @param[in]   type    Type to push.
    635 static inline void nlua_push_type(lua_State *lstate, ObjectType type)
    636  FUNC_ATTR_NONNULL_ALL
    637 {
    638  lua_pushnumber(lstate, (lua_Number)type);
    639 }
    640 
    641 /// Create Lua table which has an entry that determines its Vimscript type
    642 ///
    643 /// @param[out]  lstate  Lua state.
    644 /// @param[in]   narr    Number of “array” entries to be populated later.
    645 /// @param[in]   nrec    Number of “dictionary” entries to be populated later.
    646 /// @param[in]   type    Type of the table.
    647 static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr, const size_t nrec,
    648                                           const ObjectType type)
    649  FUNC_ATTR_NONNULL_ALL
    650 {
    651  lua_createtable(lstate, (int)narr, (int)(1 + nrec));
    652  nlua_push_type_idx(lstate);
    653  nlua_push_type(lstate, type);
    654  lua_rawset(lstate, -3);
    655 }
    656 
    657 /// Convert given String to Lua string
    658 ///
    659 /// Leaves converted string on top of the stack.
    660 void nlua_push_String(lua_State *lstate, const String s, int flags)
    661  FUNC_ATTR_NONNULL_ALL
    662 {
    663  lua_pushlstring(lstate, s.size ? s.data : "", s.size);
    664 }
    665 
    666 /// Convert given Integer to Lua number
    667 ///
    668 /// Leaves converted number on top of the stack.
    669 void nlua_push_Integer(lua_State *lstate, const Integer n, int flags)
    670  FUNC_ATTR_NONNULL_ALL
    671 {
    672  lua_pushnumber(lstate, (lua_Number)n);
    673 }
    674 
    675 /// Convert given Float to Lua table
    676 ///
    677 /// Leaves converted table on top of the stack.
    678 void nlua_push_Float(lua_State *lstate, const Float f, int flags)
    679  FUNC_ATTR_NONNULL_ALL
    680 {
    681  if (flags & kNluaPushSpecial) {
    682    nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat);
    683    nlua_push_val_idx(lstate);
    684    lua_pushnumber(lstate, (lua_Number)f);
    685    lua_rawset(lstate, -3);
    686  } else {
    687    lua_pushnumber(lstate, (lua_Number)f);
    688  }
    689 }
    690 
    691 /// Convert given Float to Lua boolean
    692 ///
    693 /// Leaves converted value on top of the stack.
    694 void nlua_push_Boolean(lua_State *lstate, const Boolean b, int flags)
    695  FUNC_ATTR_NONNULL_ALL
    696 {
    697  lua_pushboolean(lstate, b);
    698 }
    699 
    700 /// Convert given Dict to Lua table
    701 ///
    702 /// Leaves converted table on top of the stack.
    703 void nlua_push_Dict(lua_State *lstate, const Dict dict, int flags)
    704  FUNC_ATTR_NONNULL_ALL
    705 {
    706  lua_createtable(lstate, 0, (int)dict.size);
    707  if (dict.size == 0) {
    708    nlua_pushref(lstate, nlua_global_refs->empty_dict_ref);
    709    lua_setmetatable(lstate, -2);
    710  }
    711  for (size_t i = 0; i < dict.size; i++) {
    712    nlua_push_String(lstate, dict.items[i].key, flags);
    713    nlua_push_Object(lstate, &dict.items[i].value, flags);
    714    lua_rawset(lstate, -3);
    715  }
    716 }
    717 
    718 /// Convert given Array to Lua table
    719 ///
    720 /// Leaves converted table on top of the stack.
    721 void nlua_push_Array(lua_State *lstate, const Array array, int flags)
    722  FUNC_ATTR_NONNULL_ALL
    723 {
    724  lua_createtable(lstate, (int)array.size, 0);
    725  for (size_t i = 0; i < array.size; i++) {
    726    nlua_push_Object(lstate, &array.items[i], flags);
    727    lua_rawseti(lstate, -2, (int)i + 1);
    728  }
    729 }
    730 
    731 void nlua_push_handle(lua_State *lstate, const handle_T item, int flags)
    732  FUNC_ATTR_NONNULL_ALL
    733 {
    734  lua_pushnumber(lstate, (lua_Number)(item));
    735 }
    736 
    737 /// Convert given Object to Lua value
    738 ///
    739 /// Leaves converted value on top of the stack.
    740 void nlua_push_Object(lua_State *lstate, Object *obj, int flags)
    741  FUNC_ATTR_NONNULL_ALL
    742 {
    743  switch (obj->type) {
    744  case kObjectTypeNil:
    745    if (flags & kNluaPushSpecial) {
    746      lua_pushnil(lstate);
    747    } else {
    748      nlua_pushref(lstate, nlua_global_refs->nil_ref);
    749    }
    750    break;
    751  case kObjectTypeLuaRef: {
    752    nlua_pushref(lstate, obj->data.luaref);
    753    if (flags & kNluaPushFreeRefs) {
    754      api_free_luaref(obj->data.luaref);
    755      obj->data.luaref = LUA_NOREF;
    756    }
    757    break;
    758  }
    759 #define ADD_TYPE(type, data_key) \
    760  case kObjectType##type: { \
    761      nlua_push_##type(lstate, obj->data.data_key, flags); \
    762      break; \
    763  }
    764    ADD_TYPE(Boolean, boolean)
    765    ADD_TYPE(Integer, integer)
    766    ADD_TYPE(Float,   floating)
    767    ADD_TYPE(String,  string)
    768    ADD_TYPE(Array,   array)
    769    ADD_TYPE(Dict,    dict)
    770 #undef ADD_TYPE
    771 #define ADD_REMOTE_TYPE(type) \
    772  case kObjectType##type: { \
    773      nlua_push_##type(lstate, (type)obj->data.integer, flags); \
    774      break; \
    775  }
    776    ADD_REMOTE_TYPE(Buffer)
    777    ADD_REMOTE_TYPE(Window)
    778    ADD_REMOTE_TYPE(Tabpage)
    779 #undef ADD_REMOTE_TYPE
    780  }
    781 }
    782 
    783 /// Convert Lua value to string
    784 ///
    785 /// Always pops one value from the stack.
    786 String nlua_pop_String(lua_State *lstate, Arena *arena, Error *err)
    787  FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    788 {
    789  if (lua_type(lstate, -1) != LUA_TSTRING) {
    790    lua_pop(lstate, 1);
    791    api_set_error(err, kErrorTypeValidation, "Expected Lua string");
    792    return (String) { .size = 0, .data = NULL };
    793  }
    794  String ret;
    795 
    796  ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size));
    797  assert(ret.data != NULL);
    798  // TODO(bfredl): it would be "nice" to just use the memory of the Lua string
    799  // directly, although ensuring the lifetime of such strings is a bit tricky
    800  // (an API call could invoke nested Lua, which triggers GC, and kaboom?)
    801  ret.data = arena_memdupz(arena, ret.data, ret.size);
    802  lua_pop(lstate, 1);
    803 
    804  return ret;
    805 }
    806 
    807 /// Convert Lua value to integer
    808 ///
    809 /// Always pops one value from the stack.
    810 Integer nlua_pop_Integer(lua_State *lstate, Arena *arena, Error *err)
    811  FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    812 {
    813  if (lua_type(lstate, -1) != LUA_TNUMBER) {
    814    lua_pop(lstate, 1);
    815    api_set_error(err, kErrorTypeValidation, "Expected Lua number");
    816    return 0;
    817  }
    818  const lua_Number n = lua_tonumber(lstate, -1);
    819  lua_pop(lstate, 1);
    820  if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN
    821      || ((lua_Number)((Integer)n)) != n) {
    822    api_set_error(err, kErrorTypeException, "Number is not integral");
    823    return 0;
    824  }
    825  return (Integer)n;
    826 }
    827 
    828 /// Convert Lua value to boolean
    829 ///
    830 /// Despite the name of the function, this uses Lua semantics for booleans.
    831 /// thus `err` is never set as any Lua value can be co-erced into a Lua bool
    832 ///
    833 /// Always pops one value from the stack.
    834 Boolean nlua_pop_Boolean(lua_State *lstate, Arena *arena, Error *err)
    835  FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    836 {
    837  const Boolean ret = lua_toboolean(lstate, -1);
    838  lua_pop(lstate, 1);
    839  return ret;
    840 }
    841 
    842 /// Convert Lua value to boolean
    843 ///
    844 /// This follows API conventions for a Boolean value, compare api_object_to_bool
    845 ///
    846 /// Always pops one value from the stack.
    847 Boolean nlua_pop_Boolean_strict(lua_State *lstate, Error *err)
    848  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    849 {
    850  Boolean ret = false;
    851  switch (lua_type(lstate, -1)) {
    852  case LUA_TBOOLEAN:
    853    ret = lua_toboolean(lstate, -1);
    854    break;
    855 
    856  case LUA_TNUMBER:
    857    ret = (lua_tonumber(lstate, -1) != 0);
    858    break;
    859 
    860  case LUA_TNIL:
    861    ret = false;
    862    break;
    863 
    864  default:
    865    api_set_error(err, kErrorTypeValidation, "not a boolean");
    866  }
    867 
    868  lua_pop(lstate, 1);
    869  return ret;
    870 }
    871 
    872 /// Check whether typed table on top of the stack has given type
    873 ///
    874 /// @param[in]  lstate  Lua state.
    875 /// @param[out]  err  Location where error will be saved. May be NULL.
    876 /// @param[in]  type  Type to check.
    877 ///
    878 /// @return @see nlua_traverse_table().
    879 static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *const err,
    880                                            const ObjectType type)
    881  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
    882 {
    883  if (lua_type(lstate, -1) != LUA_TTABLE) {
    884    if (err) {
    885      api_set_error(err, kErrorTypeValidation, "Expected Lua %s",
    886                    (type == kObjectTypeFloat) ? "number" : "table");
    887    }
    888    return (LuaTableProps) { .type = kObjectTypeNil };
    889  }
    890  LuaTableProps table_props = nlua_traverse_table(lstate);
    891 
    892  if (type == kObjectTypeDict && table_props.type == kObjectTypeArray
    893      && table_props.maxidx == 0 && !table_props.has_type_key) {
    894    table_props.type = kObjectTypeDict;
    895  }
    896 
    897  if (table_props.type != type) {
    898    if (err) {
    899      api_set_error(err, kErrorTypeValidation, "Expected %s-like Lua table", api_typename(type));
    900    }
    901  }
    902 
    903  return table_props;
    904 }
    905 
    906 /// Convert Lua table to float
    907 ///
    908 /// Always pops one value from the stack.
    909 Float nlua_pop_Float(lua_State *lstate, Arena *arena, Error *err)
    910  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    911 {
    912  if (lua_type(lstate, -1) == LUA_TNUMBER) {
    913    const Float ret = (Float)lua_tonumber(lstate, -1);
    914    lua_pop(lstate, 1);
    915    return ret;
    916  }
    917 
    918  const LuaTableProps table_props = nlua_check_type(lstate, err,
    919                                                    kObjectTypeFloat);
    920  lua_pop(lstate, 1);
    921  if (table_props.type != kObjectTypeFloat) {
    922    return 0;
    923  }
    924  return (Float)table_props.val;
    925 }
    926 
    927 /// Convert Lua table to array without determining whether it is array
    928 ///
    929 /// @param  lstate  Lua state.
    930 /// @param[in]  table_props  nlua_traverse_table() output.
    931 /// @param[out]  err  Location where error will be saved.
    932 static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTableProps table_props,
    933                                      Arena *arena, Error *const err)
    934 {
    935  Array ret = arena_array(arena, table_props.maxidx);
    936 
    937  if (table_props.maxidx == 0) {
    938    lua_pop(lstate, 1);
    939    return ret;
    940  }
    941 
    942  for (size_t i = 1; i <= table_props.maxidx; i++) {
    943    Object val;
    944 
    945    lua_rawgeti(lstate, -1, (int)i);
    946 
    947    val = nlua_pop_Object(lstate, false, arena, err);
    948    if (ERROR_SET(err)) {
    949      lua_pop(lstate, 1);
    950      if (!arena) {
    951        api_free_array(ret);
    952      }
    953      return (Array) { .size = 0, .items = NULL };
    954    }
    955    ADD_C(ret, val);
    956  }
    957  lua_pop(lstate, 1);
    958 
    959  return ret;
    960 }
    961 
    962 /// Convert Lua table to array
    963 ///
    964 /// Always pops one value from the stack.
    965 Array nlua_pop_Array(lua_State *lstate, Arena *arena, Error *err)
    966  FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
    967 {
    968  const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeArray);
    969  if (table_props.type != kObjectTypeArray) {
    970    return (Array) { .size = 0, .items = NULL };
    971  }
    972  return nlua_pop_Array_unchecked(lstate, table_props, arena, err);
    973 }
    974 
    975 /// Convert Lua table to dictionary
    976 ///
    977 /// Always pops one value from the stack. Does not check whether whether topmost
    978 /// value on the stack is a table.
    979 ///
    980 /// @param  lstate  Lua interpreter state.
    981 /// @param[in]  table_props  nlua_traverse_table() output.
    982 /// @param[out]  err  Location where error will be saved.
    983 static Dict nlua_pop_Dict_unchecked(lua_State *lstate, const LuaTableProps table_props, bool ref,
    984                                    Arena *arena, Error *err)
    985  FUNC_ATTR_NONNULL_ARG(1, 5) FUNC_ATTR_WARN_UNUSED_RESULT
    986 {
    987  Dict ret = arena_dict(arena, table_props.string_keys_num);
    988 
    989  if (table_props.string_keys_num == 0) {
    990    lua_pop(lstate, 1);
    991    return ret;
    992  }
    993 
    994  lua_pushnil(lstate);
    995  for (size_t i = 0; lua_next(lstate, -2) && i < table_props.string_keys_num;) {
    996    // stack: dict, key, value
    997 
    998    if (lua_type(lstate, -2) == LUA_TSTRING) {
    999      lua_pushvalue(lstate, -2);
   1000      // stack: dict, key, value, key
   1001 
   1002      String key = nlua_pop_String(lstate, arena, err);
   1003      // stack: dict, key, value
   1004 
   1005      if (!ERROR_SET(err)) {
   1006        Object value = nlua_pop_Object(lstate, ref, arena, err);
   1007        kv_push_c(ret, ((KeyValuePair) { .key = key, .value = value }));
   1008        // stack: dict, key
   1009      } else {
   1010        lua_pop(lstate, 1);
   1011        // stack: dict, key
   1012      }
   1013 
   1014      if (ERROR_SET(err)) {
   1015        if (!arena) {
   1016          api_free_dict(ret);
   1017        }
   1018        lua_pop(lstate, 2);
   1019        // stack:
   1020        return (Dict) { .size = 0, .items = NULL };
   1021      }
   1022      i++;
   1023    } else {
   1024      lua_pop(lstate, 1);
   1025      // stack: dict, key
   1026    }
   1027  }
   1028  lua_pop(lstate, 1);
   1029 
   1030  return ret;
   1031 }
   1032 
   1033 /// Convert Lua table to dictionary
   1034 ///
   1035 /// Always pops one value from the stack.
   1036 Dict nlua_pop_Dict(lua_State *lstate, bool ref, Arena *arena, Error *err)
   1037  FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT
   1038 {
   1039  const LuaTableProps table_props = nlua_check_type(lstate, err,
   1040                                                    kObjectTypeDict);
   1041  if (table_props.type != kObjectTypeDict) {
   1042    lua_pop(lstate, 1);
   1043    return (Dict) { .size = 0, .items = NULL };
   1044  }
   1045 
   1046  return nlua_pop_Dict_unchecked(lstate, table_props, ref, arena, err);
   1047 }
   1048 
   1049 /// Helper structure for nlua_pop_Object
   1050 typedef struct {
   1051  Object *obj;  ///< Location where conversion result is saved.
   1052  bool container;  ///< True if tv is a container.
   1053 } ObjPopStackItem;
   1054 
   1055 /// Convert Lua table to object
   1056 ///
   1057 /// Always pops one value from the stack.
   1058 Object nlua_pop_Object(lua_State *const lstate, bool ref, Arena *arena, Error *const err)
   1059  FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT
   1060 {
   1061  Object ret = NIL;
   1062  const int initial_size = lua_gettop(lstate);
   1063  kvec_withinit_t(ObjPopStackItem, 2) stack = KV_INITIAL_VALUE;
   1064  kvi_init(stack);
   1065  kvi_push(stack, ((ObjPopStackItem){ .obj = &ret }));
   1066  while (!ERROR_SET(err) && kv_size(stack)) {
   1067    ObjPopStackItem cur = kv_pop(stack);
   1068    if (cur.container) {
   1069      if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
   1070        api_set_error(err, kErrorTypeException, "Lua failed to grow stack");
   1071        break;
   1072      }
   1073      if (cur.obj->type == kObjectTypeDict) {
   1074        // stack: …, dict, key
   1075        if (cur.obj->data.dict.size == cur.obj->data.dict.capacity) {
   1076          lua_pop(lstate, 2);
   1077          continue;
   1078        }
   1079        bool next_key_found = false;
   1080        while (lua_next(lstate, -2)) {
   1081          // stack: …, dict, new key, val
   1082          if (lua_type(lstate, -2) == LUA_TSTRING) {
   1083            next_key_found = true;
   1084            break;
   1085          }
   1086          lua_pop(lstate, 1);
   1087          // stack: …, dict, new key
   1088        }
   1089        if (next_key_found) {
   1090          // stack: …, dict, new key, val
   1091          size_t len;
   1092          const char *s = lua_tolstring(lstate, -2, &len);
   1093          const size_t idx = cur.obj->data.dict.size++;
   1094          cur.obj->data.dict.items[idx].key = CBUF_TO_ARENA_STR(arena, s, len);
   1095          kvi_push(stack, cur);
   1096          cur = (ObjPopStackItem){ .obj = &cur.obj->data.dict.items[idx].value };
   1097        } else {
   1098          // stack: …, dict
   1099          lua_pop(lstate, 1);
   1100          // stack: …
   1101          continue;
   1102        }
   1103      } else {
   1104        if (cur.obj->data.array.size == cur.obj->data.array.capacity) {
   1105          lua_pop(lstate, 1);
   1106          continue;
   1107        }
   1108        const size_t idx = cur.obj->data.array.size++;
   1109        lua_rawgeti(lstate, -1, (int)idx + 1);
   1110        kvi_push(stack, cur);
   1111        cur = (ObjPopStackItem){ .obj = &cur.obj->data.array.items[idx] };
   1112      }
   1113    }
   1114    assert(!cur.container);
   1115    *cur.obj = NIL;
   1116    switch (lua_type(lstate, -1)) {
   1117    case LUA_TNIL:
   1118      break;
   1119    case LUA_TBOOLEAN:
   1120      *cur.obj = BOOLEAN_OBJ(lua_toboolean(lstate, -1));
   1121      break;
   1122    case LUA_TSTRING: {
   1123      size_t len;
   1124      const char *s = lua_tolstring(lstate, -1, &len);
   1125      *cur.obj = STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len));
   1126      break;
   1127    }
   1128    case LUA_TNUMBER: {
   1129      const lua_Number n = lua_tonumber(lstate, -1);
   1130      if (n > (lua_Number)API_INTEGER_MAX || n < (lua_Number)API_INTEGER_MIN
   1131          || ((lua_Number)((Integer)n)) != n) {
   1132        *cur.obj = FLOAT_OBJ((Float)n);
   1133      } else {
   1134        *cur.obj = INTEGER_OBJ((Integer)n);
   1135      }
   1136      break;
   1137    }
   1138    case LUA_TTABLE: {
   1139      const LuaTableProps table_props = nlua_traverse_table(lstate);
   1140 
   1141      switch (table_props.type) {
   1142      case kObjectTypeArray:
   1143        *cur.obj = ARRAY_OBJ(((Array)ARRAY_DICT_INIT));
   1144        if (table_props.maxidx != 0) {
   1145          cur.obj->data.array = arena_array(arena, table_props.maxidx);
   1146          cur.container = true;
   1147          assert(kv_size(stack) < SIZE_MAX);
   1148          kvi_push(stack, cur);
   1149        }
   1150        break;
   1151      case kObjectTypeDict:
   1152        *cur.obj = DICT_OBJ(((Dict)ARRAY_DICT_INIT));
   1153        if (table_props.string_keys_num != 0) {
   1154          cur.obj->data.dict = arena_dict(arena, table_props.string_keys_num);
   1155          cur.container = true;
   1156          assert(kv_size(stack) < SIZE_MAX);
   1157          kvi_push(stack, cur);
   1158          lua_pushnil(lstate);
   1159        }
   1160        break;
   1161      case kObjectTypeFloat:
   1162        *cur.obj = FLOAT_OBJ((Float)table_props.val);
   1163        break;
   1164      case kObjectTypeNil:
   1165        api_set_error(err, kErrorTypeValidation,
   1166                      "Cannot convert given Lua table");
   1167        break;
   1168      default:
   1169        abort();
   1170      }
   1171      break;
   1172    }
   1173 
   1174    case LUA_TFUNCTION:
   1175      if (ref) {
   1176        *cur.obj = LUAREF_OBJ(nlua_ref_global(lstate, -1));
   1177      } else {
   1178        goto type_error;
   1179      }
   1180      break;
   1181 
   1182    case LUA_TUSERDATA: {
   1183      nlua_pushref(lstate, nlua_global_refs->nil_ref);
   1184      bool is_nil = lua_rawequal(lstate, -2, -1);
   1185      lua_pop(lstate, 1);
   1186      if (is_nil) {
   1187        *cur.obj = NIL;
   1188      } else {
   1189        api_set_error(err, kErrorTypeValidation, "Cannot convert userdata");
   1190      }
   1191      break;
   1192    }
   1193 
   1194    default:
   1195 type_error:
   1196      api_set_error(err, kErrorTypeValidation, "Cannot convert given Lua type");
   1197      break;
   1198    }
   1199    if (!cur.container) {
   1200      lua_pop(lstate, 1);
   1201    }
   1202  }
   1203  kvi_destroy(stack);
   1204  if (ERROR_SET(err)) {
   1205    if (!arena) {
   1206      api_free_object(ret);
   1207    }
   1208    ret = NIL;
   1209    lua_pop(lstate, lua_gettop(lstate) - initial_size + 1);
   1210  }
   1211  assert(lua_gettop(lstate) == initial_size - 1);
   1212  return ret;
   1213 }
   1214 
   1215 LuaRef nlua_pop_LuaRef(lua_State *const lstate, Arena *arena, Error *err)
   1216 {
   1217  LuaRef rv = nlua_ref_global(lstate, -1);
   1218  lua_pop(lstate, 1);
   1219  return rv;
   1220 }
   1221 
   1222 handle_T nlua_pop_handle(lua_State *lstate, Arena *arena, Error *err)
   1223  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
   1224 {
   1225  handle_T ret;
   1226  if (lua_type(lstate, -1) != LUA_TNUMBER) {
   1227    api_set_error(err, kErrorTypeValidation, "Expected Lua number");
   1228    ret = (handle_T)(-1);
   1229  } else {
   1230    ret = (handle_T)lua_tonumber(lstate, -1);
   1231  }
   1232  lua_pop(lstate, 1);
   1233  return ret;
   1234 }
   1235 
   1236 /// Record some auxiliary values in vim module
   1237 ///
   1238 /// Assumes that module table is on top of the stack.
   1239 ///
   1240 /// Recorded values:
   1241 ///
   1242 /// `vim.type_idx`: @see nlua_push_type_idx()
   1243 /// `vim.val_idx`: @see nlua_push_val_idx()
   1244 /// `vim.types`: table mapping possible values of `vim.type_idx` to string
   1245 ///              names (i.e. `array`, `float`, `dictionary`) and back.
   1246 void nlua_init_types(lua_State *const lstate)
   1247 {
   1248  LUA_PUSH_STATIC_STRING(lstate, "type_idx");
   1249  nlua_push_type_idx(lstate);
   1250  lua_rawset(lstate, -3);
   1251 
   1252  LUA_PUSH_STATIC_STRING(lstate, "val_idx");
   1253  nlua_push_val_idx(lstate);
   1254  lua_rawset(lstate, -3);
   1255 
   1256  LUA_PUSH_STATIC_STRING(lstate, "types");
   1257  lua_createtable(lstate, 0, 3);
   1258 
   1259  LUA_PUSH_STATIC_STRING(lstate, "float");
   1260  lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat);
   1261  lua_rawset(lstate, -3);
   1262  lua_pushnumber(lstate, (lua_Number)kObjectTypeFloat);
   1263  LUA_PUSH_STATIC_STRING(lstate, "float");
   1264  lua_rawset(lstate, -3);
   1265 
   1266  LUA_PUSH_STATIC_STRING(lstate, "array");
   1267  lua_pushnumber(lstate, (lua_Number)kObjectTypeArray);
   1268  lua_rawset(lstate, -3);
   1269  lua_pushnumber(lstate, (lua_Number)kObjectTypeArray);
   1270  LUA_PUSH_STATIC_STRING(lstate, "array");
   1271  lua_rawset(lstate, -3);
   1272 
   1273  LUA_PUSH_STATIC_STRING(lstate, "dictionary");
   1274  lua_pushnumber(lstate, (lua_Number)kObjectTypeDict);
   1275  lua_rawset(lstate, -3);
   1276  lua_pushnumber(lstate, (lua_Number)kObjectTypeDict);
   1277  LUA_PUSH_STATIC_STRING(lstate, "dictionary");
   1278  lua_rawset(lstate, -3);
   1279 
   1280  lua_rawset(lstate, -3);
   1281 }
   1282 
   1283 // Lua specific variant of api_dict_to_keydict
   1284 void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Arena *arena,
   1285                      Error *err)
   1286 {
   1287  if (!lua_istable(L, -1)) {
   1288    api_set_error(err, kErrorTypeValidation, "Expected Lua table");
   1289    lua_pop(L, -1);
   1290    return;
   1291  }
   1292 
   1293  lua_pushnil(L);  // [dict, nil]
   1294  while (lua_next(L, -2)) {
   1295    // [dict, key, value]
   1296    size_t len;
   1297    const char *s = lua_tolstring(L, -2, &len);
   1298    KeySetLink *field = hashy(s, len);
   1299    if (!field) {
   1300      api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s);
   1301      lua_pop(L, 3);  // []
   1302      return;
   1303    }
   1304 
   1305    if (field->opt_index >= 0) {
   1306      OptKeySet *ks = (OptKeySet *)retval;
   1307      ks->is_set_ |= (1ULL << field->opt_index);
   1308    }
   1309    char *mem = ((char *)retval + field->ptr_off);
   1310 
   1311    if (field->type == kObjectTypeNil) {
   1312      *(Object *)mem = nlua_pop_Object(L, true, arena, err);
   1313    } else if (field->type == kObjectTypeInteger) {
   1314      if (field->is_hlgroup && lua_type(L, -1) == LUA_TSTRING) {
   1315        size_t name_len;
   1316        const char *name = lua_tolstring(L, -1, &name_len);
   1317        lua_pop(L, 1);
   1318        *(Integer *)mem = name_len > 0 ? syn_check_group(name, name_len) : 0;
   1319      } else {
   1320        *(Integer *)mem = nlua_pop_Integer(L, arena, err);
   1321      }
   1322    } else if (field->type == kObjectTypeBoolean) {
   1323      *(Boolean *)mem = nlua_pop_Boolean_strict(L, err);
   1324    } else if (field->type == kObjectTypeString) {
   1325      *(String *)mem = nlua_pop_String(L, arena, err);
   1326    } else if (field->type == kObjectTypeFloat) {
   1327      *(Float *)mem = nlua_pop_Float(L, arena, err);
   1328    } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
   1329               || field->type == kObjectTypeTabpage) {
   1330      *(handle_T *)mem = nlua_pop_handle(L, arena, err);
   1331    } else if (field->type == kObjectTypeArray) {
   1332      *(Array *)mem = nlua_pop_Array(L, arena, err);
   1333    } else if (field->type == kObjectTypeDict) {
   1334      *(Dict *)mem = nlua_pop_Dict(L, false, arena, err);
   1335    } else if (field->type == kObjectTypeLuaRef) {
   1336      *(LuaRef *)mem = nlua_pop_LuaRef(L, arena, err);
   1337    } else {
   1338      abort();
   1339    }
   1340    if (ERROR_SET(err)) {
   1341      *err_opt = field->str;
   1342      break;
   1343    }
   1344  }
   1345  // [dict]
   1346  lua_pop(L, 1);
   1347  // []
   1348 }
   1349 
   1350 void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table)
   1351 {
   1352  lua_createtable(L, 0, 0);
   1353  for (size_t i = 0; table[i].str; i++) {
   1354    KeySetLink *field = &table[i];
   1355    bool is_set = true;
   1356    if (field->opt_index >= 0) {
   1357      OptKeySet *ks = (OptKeySet *)value;
   1358      is_set = ks->is_set_ & (1ULL << field->opt_index);
   1359    }
   1360 
   1361    if (!is_set) {
   1362      continue;
   1363    }
   1364 
   1365    char *mem = ((char *)value + field->ptr_off);
   1366 
   1367    lua_pushstring(L, field->str);
   1368    if (field->type == kObjectTypeNil) {
   1369      nlua_push_Object(L, (Object *)mem, 0);
   1370    } else if (field->type == kObjectTypeInteger) {
   1371      lua_pushinteger(L, *(Integer *)mem);
   1372    } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
   1373               || field->type == kObjectTypeTabpage) {
   1374      lua_pushinteger(L, *(handle_T *)mem);
   1375    } else if (field->type == kObjectTypeFloat) {
   1376      lua_pushnumber(L, *(Float *)mem);
   1377    } else if (field->type == kObjectTypeBoolean) {
   1378      lua_pushboolean(L, *(Boolean *)mem);
   1379    } else if (field->type == kObjectTypeString) {
   1380      nlua_push_String(L, *(String *)mem, 0);
   1381    } else if (field->type == kObjectTypeArray) {
   1382      nlua_push_Array(L, *(Array *)mem, 0);
   1383    } else if (field->type == kObjectTypeDict) {
   1384      nlua_push_Dict(L, *(Dict *)mem, 0);
   1385    } else if (field->type == kObjectTypeLuaRef) {
   1386      nlua_pushref(L, *(LuaRef *)mem);
   1387    } else {
   1388      abort();
   1389    }
   1390 
   1391    lua_rawset(L, -3);
   1392  }
   1393 }