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