aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c70
-rw-r--r--src/nvim/eval/decode.c138
-rw-r--r--src/nvim/viml/executor/converter.c388
-rw-r--r--src/nvim/viml/executor/converter.h3
-rw-r--r--src/nvim/viml/executor/executor.c159
5 files changed, 572 insertions, 186 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index c9141fbcbf..248383d2e1 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6480,28 +6480,44 @@ static void dict_free_dict(dict_T *d) {
xfree(d);
}
-void dict_free(dict_T *d) {
+void dict_free(dict_T *d)
+{
if (!in_free_unref_items) {
dict_free_contents(d);
dict_free_dict(d);
}
}
-/*
- * Allocate a Dictionary item.
- * The "key" is copied to the new item.
- * Note that the value of the item "di_tv" still needs to be initialized!
- */
-dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET
+/// Allocate a dictionary item
+///
+/// @note that the value of the item di_tv still needs to be initialized.
+///
+/// @param[in] key Item key.
+/// @param[in] len Key length.
+///
+/// @return [allocated] New dictionary item.
+dictitem_T *dictitem_alloc_len(const char *const key, const size_t len)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
- dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
-#ifndef __clang_analyzer__
- STRCPY(di->di_key, key);
-#endif
+ dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) + len);
+ memcpy(di->di_key, key, len);
di->di_flags = DI_FLAGS_ALLOC;
return di;
}
+/// Allocate a dictionary item
+///
+/// @note that the value of the item di_tv still needs to be initialized.
+///
+/// @param[in] key Item key, NUL-terminated string.
+///
+/// @return [allocated] New dictionary item.
+dictitem_T *dictitem_alloc(const char_u *const key)
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return dictitem_alloc_len((const char *)key, STRLEN(key));
+}
+
/*
* Make a copy of a Dictionary item.
*/
@@ -13386,37 +13402,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- Object arg;
- if (argvars[1].v_type == VAR_UNKNOWN) {
- arg = NIL;
- } else {
- arg = vim_to_object(&argvars[1]);
- }
-
- // TODO(ZyX-I): Create function which converts lua objects directly to VimL
- // objects, not to API objects.
- Error err;
- String err_str;
- Object ret = executor_eval_lua(cstr_as_string(str), arg, &err, &err_str);
- if (err.set) {
- if (err_str.size) {
- EMSG3(_("E971: Failed to eval lua string: %s (%s)"), err.msg,
- err_str.data);
- } else {
- EMSG2(_("E971: Failed to eval lua string: %s"), err.msg);
- }
- }
-
- api_free_string(err_str);
-
- if (!err.set) {
- if (!object_to_vim(ret, rettv, &err)) {
- EMSG2(_("E972: Failed to convert resulting API object to VimL: %s"),
- err.msg);
- }
- }
-
- api_free_object(ret);
+ executor_eval_lua(cstr_as_string(str), &argvars[1], rettv);
}
/*
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 43e9f76c0f..cb5a624f7b 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -7,6 +7,7 @@
#include "nvim/eval/encode.h"
#include "nvim/ascii.h"
#include "nvim/message.h"
+#include "nvim/globals.h"
#include "nvim/charset.h" // vim_str2nr
#include "nvim/lib/kvec.h"
#include "nvim/vim.h" // OK, FAIL
@@ -218,6 +219,69 @@ static inline int json_decoder_pop(ValuesStackItem obj,
} \
} while (0)
+/// Create a new special dictionary that ought to represent a MAP
+///
+/// @param[out] ret_tv Address where new special dictionary is saved.
+///
+/// @return [allocated] list which should contain key-value pairs. Return value
+/// may be safely ignored.
+list_T *decode_create_map_special_dict(typval_T *const ret_tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ create_special_dict(ret_tv, kMPMap, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ return list;
+}
+
+/// Convert char* string to typval_T
+///
+/// Depending on whether string has (no) NUL bytes, it may use a special
+/// dictionary or decode string to VAR_STRING.
+///
+/// @param[in] s String to decode.
+/// @param[in] len String length.
+/// @param[in] hasnul Whether string has NUL byte, not or it was not yet
+/// determined.
+/// @param[in] binary If true, save special string type as kMPBinary,
+/// otherwise kMPString.
+///
+/// @return Decoded string.
+typval_T decode_string(const char *const s, const size_t len,
+ const TriState hasnul, const bool binary)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ assert(s != NULL || len == 0);
+ const bool really_hasnul = (hasnul == kNone
+ ? memchr(s, NUL, len) != NULL
+ : (bool)hasnul);
+ if (really_hasnul) {
+ list_T *const list = list_alloc();
+ list->lv_refcount++;
+ typval_T tv;
+ create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
+ .v_type = VAR_LIST,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_list = list },
+ }));
+ if (encode_list_write((void *)list, s, len) == -1) {
+ clear_tv(&tv);
+ return (typval_T) { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED };
+ }
+ return tv;
+ } else {
+ return (typval_T) {
+ .v_type = VAR_STRING,
+ .v_lock = VAR_UNLOCKED,
+ .vval = { .v_string = xmemdupz(s, len) },
+ };
+ }
+}
+
/// Parse JSON double-quoted string
///
/// @param[in] conv Defines conversion necessary to convert UTF-8 string to
@@ -428,29 +492,13 @@ static inline int parse_json_string(vimconv_T *const conv,
str = new_str;
str_end = new_str + str_len;
}
- if (hasnul) {
- typval_T obj;
- list_T *const list = list_alloc();
- list->lv_refcount++;
- create_special_dict(&obj, kMPString, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
- if (encode_list_write((void *) list, str, (size_t) (str_end - str))
- == -1) {
- clear_tv(&obj);
- goto parse_json_string_fail;
- }
- xfree(str);
- POP(obj, true);
- } else {
- *str_end = NUL;
- POP(((typval_T) {
- .v_type = VAR_STRING,
- .vval = { .v_string = (char_u *) str },
- }), false);
+ typval_T obj;
+ obj = decode_string(str, (size_t)(str_end - str), hasnul ? kTrue : kFalse,
+ false);
+ if (obj.v_type == VAR_UNKNOWN) {
+ goto parse_json_string_fail;
}
+ POP(obj, obj.v_type != VAR_STRING);
goto parse_json_string_ret;
parse_json_string_fail:
ret = FAIL;
@@ -827,13 +875,7 @@ json_decode_string_cycle_start:
list_T *val_list = NULL;
if (next_map_special) {
next_map_special = false;
- val_list = list_alloc();
- val_list->lv_refcount++;
- create_special_dict(&tv, kMPMap, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = val_list },
- }));
+ val_list = decode_create_map_special_dict(&tv);
} else {
dict_T *dict = dict_alloc();
dict->dv_refcount++;
@@ -980,37 +1022,15 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
break;
}
case MSGPACK_OBJECT_STR: {
- list_T *const list = list_alloc();
- list->lv_refcount++;
- create_special_dict(rettv, kMPString, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
- if (encode_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size)
- == -1) {
+ *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kTrue, false);
+ if (rettv->v_type == VAR_UNKNOWN) {
return FAIL;
}
break;
}
case MSGPACK_OBJECT_BIN: {
- if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) {
- *rettv = (typval_T) {
- .v_type = VAR_STRING,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) },
- };
- break;
- }
- list_T *const list = list_alloc();
- list->lv_refcount++;
- create_special_dict(rettv, kMPBinary, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
- if (encode_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size)
- == -1) {
+ *rettv = decode_string(mobj.via.bin.ptr, mobj.via.bin.size, kNone, true);
+ if (rettv->v_type == VAR_UNKNOWN) {
return FAIL;
}
break;
@@ -1067,13 +1087,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
msgpack_to_vim_generic_map: {}
- list_T *const list = list_alloc();
- list->lv_refcount++;
- create_special_dict(rettv, kMPMap, ((typval_T) {
- .v_type = VAR_LIST,
- .v_lock = VAR_UNLOCKED,
- .vval = { .v_list = list },
- }));
+ list_T *const list = decode_create_map_special_dict(rettv);
for (size_t i = 0; i < mobj.via.map.size; i++) {
list_T *const kv_pair = list_alloc();
list_append_list(list, kv_pair);
diff --git a/src/nvim/viml/executor/converter.c b/src/nvim/viml/executor/converter.c
index 2105beb08a..123659aa5c 100644
--- a/src/nvim/viml/executor/converter.c
+++ b/src/nvim/viml/executor/converter.c
@@ -2,12 +2,24 @@
#include <lualib.h>
#include <lauxlib.h>
#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/func_attr.h"
#include "nvim/memory.h"
#include "nvim/assert.h"
+// FIXME: vim.h is not actually needed, but otherwise it states MAXPATHL is
+// redefined
+#include "nvim/vim.h"
+#include "nvim/globals.h"
+#include "nvim/message.h"
+#include "nvim/eval_defs.h"
+#include "nvim/ascii.h"
+
+#include "nvim/lib/kvec.h"
+#include "nvim/eval/decode.h"
#include "nvim/viml/executor/converter.h"
#include "nvim/viml/executor/executor.h"
@@ -16,6 +28,382 @@
# include "viml/executor/converter.c.generated.h"
#endif
+/// Helper structure for nlua_pop_typval
+typedef struct {
+ typval_T *tv; ///< Location where conversion result is saved.
+ bool container; ///< True if tv is a container.
+ bool special; ///< If true then tv is a _VAL part of special dictionary
+ ///< that represents mapping.
+} PopStackItem;
+
+/// Convert lua object to VimL typval_T
+///
+/// Should pop exactly one value from lua stack.
+///
+/// @param lstate Lua state.
+/// @param[out] ret_tv Where to put the result.
+///
+/// @return `true` in case of success, `false` in case of failure. Error is
+/// reported automatically.
+bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
+{
+ bool ret = true;
+#ifndef NDEBUG
+ const int initial_size = lua_gettop(lstate);
+#endif
+ kvec_t(PopStackItem) stack = KV_INITIAL_VALUE;
+ kv_push(stack, ((PopStackItem) { ret_tv, false, false }));
+ while (ret && kv_size(stack)) {
+ if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
+ emsgf(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 3);
+ ret = false;
+ break;
+ }
+ PopStackItem cur = kv_pop(stack);
+ if (cur.container) {
+ if (cur.special || cur.tv->v_type == VAR_DICT) {
+ assert(cur.tv->v_type == (cur.special ? VAR_LIST : VAR_DICT));
+ if (lua_next(lstate, -2)) {
+ assert(lua_type(lstate, -2) == LUA_TSTRING);
+ size_t len;
+ const char *s = lua_tolstring(lstate, -2, &len);
+ if (cur.special) {
+ list_T *const kv_pair = list_alloc();
+ list_append_list(cur.tv->vval.v_list, kv_pair);
+ listitem_T *const key = listitem_alloc();
+ key->li_tv = decode_string(s, len, kTrue, false);
+ list_append(kv_pair, key);
+ if (key->li_tv.v_type == VAR_UNKNOWN) {
+ ret = false;
+ list_unref(kv_pair);
+ continue;
+ }
+ listitem_T *const val = listitem_alloc();
+ list_append(kv_pair, val);
+ kv_push(stack, cur);
+ cur = (PopStackItem) { &val->li_tv, false, false };
+ } else {
+ dictitem_T *const di = dictitem_alloc_len(s, len);
+ if (dict_add(cur.tv->vval.v_dict, di) == FAIL) {
+ assert(false);
+ }
+ kv_push(stack, cur);
+ cur = (PopStackItem) { &di->di_tv, false, false };
+ }
+ } else {
+ lua_pop(lstate, 1);
+ continue;
+ }
+ } else {
+ assert(cur.tv->v_type == VAR_LIST);
+ lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1);
+ if (lua_isnil(lstate, -1)) {
+ lua_pop(lstate, 1);
+ lua_pop(lstate, 1);
+ continue;
+ }
+ listitem_T *li = listitem_alloc();
+ list_append(cur.tv->vval.v_list, li);
+ kv_push(stack, cur);
+ cur = (PopStackItem) { &li->li_tv, false, false };
+ }
+ }
+ assert(!cur.container);
+ memset(cur.tv, 0, sizeof(*cur.tv));
+ switch (lua_type(lstate, -1)) {
+ case LUA_TNIL: {
+ cur.tv->v_type = VAR_SPECIAL;
+ cur.tv->vval.v_special = kSpecialVarNull;
+ break;
+ }
+ case LUA_TBOOLEAN: {
+ cur.tv->v_type = VAR_SPECIAL;
+ cur.tv->vval.v_special = (lua_toboolean(lstate, -1)
+ ? kSpecialVarTrue
+ : kSpecialVarFalse);
+ break;
+ }
+ case LUA_TSTRING: {
+ size_t len;
+ const char *s = lua_tolstring(lstate, -1, &len);
+ *cur.tv = decode_string(s, len, kNone, true);
+ if (cur.tv->v_type == VAR_UNKNOWN) {
+ ret = false;
+ }
+ break;
+ }
+ case LUA_TNUMBER: {
+ const lua_Number n = lua_tonumber(lstate, -1);
+ if (n > (lua_Number)VARNUMBER_MAX || n < (lua_Number)VARNUMBER_MIN
+ || ((lua_Number)((varnumber_T)n)) != n) {
+ cur.tv->v_type = VAR_FLOAT;
+ cur.tv->vval.v_float = (float_T)n;
+ } else {
+ cur.tv->v_type = VAR_NUMBER;
+ cur.tv->vval.v_number = (varnumber_T)n;
+ }
+ break;
+ }
+ case LUA_TTABLE: {
+ bool has_string = false;
+ bool has_string_with_nul = false;
+ bool has_other = false;
+ size_t maxidx = 0;
+ size_t tsize = 0;
+ lua_pushnil(lstate);
+ while (lua_next(lstate, -2)) {
+ switch (lua_type(lstate, -2)) {
+ case LUA_TSTRING: {
+ size_t len;
+ const char *s = lua_tolstring(lstate, -2, &len);
+ if (memchr(s, NUL, len) != NULL) {
+ has_string_with_nul = true;
+ }
+ has_string = true;
+ break;
+ }
+ case LUA_TNUMBER: {
+ const lua_Number n = lua_tonumber(lstate, -2);
+ if (n > (lua_Number)SIZE_MAX || n <= 0
+ || ((lua_Number)((size_t)n)) != n) {
+ has_other = true;
+ } else {
+ const size_t idx = (size_t)n;
+ if (idx > maxidx) {
+ maxidx = idx;
+ }
+ }
+ break;
+ }
+ default: {
+ has_other = true;
+ break;
+ }
+ }
+ tsize++;
+ lua_pop(lstate, 1);
+ }
+
+ if (tsize == 0) {
+ // Assuming empty list
+ cur.tv->v_type = VAR_LIST;
+ cur.tv->vval.v_list = list_alloc();
+ cur.tv->vval.v_list->lv_refcount++;
+ } else if (tsize == maxidx && !has_other && !has_string) {
+ // Assuming array
+ cur.tv->v_type = VAR_LIST;
+ cur.tv->vval.v_list = list_alloc();
+ cur.tv->vval.v_list->lv_refcount++;
+ cur.container = true;
+ kv_push(stack, cur);
+ } else if (has_string && !has_other && maxidx == 0) {
+ // Assuming dictionary
+ cur.special = has_string_with_nul;
+ if (has_string_with_nul) {
+ decode_create_map_special_dict(cur.tv);
+ assert(cur.tv->v_type = VAR_DICT);
+ dictitem_T *const val_di = dict_find(cur.tv->vval.v_dict,
+ (char_u *)"_VAL", 4);
+ assert(val_di != NULL);
+ cur.tv = &val_di->di_tv;
+ assert(cur.tv->v_type == VAR_LIST);
+ } else {
+ cur.tv->v_type = VAR_DICT;
+ cur.tv->vval.v_dict = dict_alloc();
+ cur.tv->vval.v_dict->dv_refcount++;
+ }
+ cur.container = true;
+ kv_push(stack, cur);
+ lua_pushnil(lstate);
+ } else {
+ EMSG(_("E5100: Cannot convert given lua table: table "
+ "should either have a sequence of positive integer keys "
+ "or contain only string keys"));
+ ret = false;
+ }
+ break;
+ }
+ default: {
+ EMSG(_("E5101: Cannot convert given lua type"));
+ ret = false;
+ break;
+ }
+ }
+ if (!cur.container) {
+ lua_pop(lstate, 1);
+ }
+ }
+ kv_destroy(stack);
+ if (!ret) {
+ clear_tv(ret_tv);
+ memset(ret_tv, 0, sizeof(*ret_tv));
+ lua_pop(lstate, lua_gettop(lstate) - initial_size + 1);
+ }
+ assert(lua_gettop(lstate) == initial_size - 1);
+ return ret;
+}
+
+#define TYPVAL_ENCODE_ALLOW_SPECIALS true
+
+#define TYPVAL_ENCODE_CONV_NIL(tv) \
+ lua_pushnil(lstate)
+
+#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
+ lua_pushboolean(lstate, (bool)(num))
+
+#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
+ lua_pushnumber(lstate, (lua_Number)(num))
+
+#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER TYPVAL_ENCODE_CONV_NUMBER
+
+#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
+ TYPVAL_ENCODE_CONV_NUMBER(tv, flt)
+
+#define TYPVAL_ENCODE_CONV_STRING(tv, str, len) \
+ lua_pushlstring(lstate, (const char *)(str), (len))
+
+#define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING
+
+#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \
+ TYPVAL_ENCODE_CONV_NIL()
+
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+ do { \
+ TYPVAL_ENCODE_CONV_NIL(tv); \
+ goto typval_encode_stop_converting_one_item; \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
+#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
+ lua_createtable(lstate, 0, 0)
+
+#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
+ TYPVAL_ENCODE_CONV_EMPTY_LIST()
+
+#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
+ do { \
+ if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
+ emsgf(_("E5102: Lua failed to grow stack to %i"), \
+ lua_gettop(lstate) + 3); \
+ return false; \
+ } \
+ lua_createtable(lstate, (int)(len), 0); \
+ lua_pushnumber(lstate, 1); \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
+
+#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
+ do { \
+ lua_Number idx = lua_tonumber(lstate, -2); \
+ lua_rawset(lstate, -3); \
+ lua_pushnumber(lstate, idx + 1); \
+ } while (0)
+
+#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
+ lua_rawset(lstate, -3)
+
+#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
+ do { \
+ if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) { \
+ emsgf(_("E5102: Lua failed to grow stack to %i"), \
+ lua_gettop(lstate) + 3); \
+ return false; \
+ } \
+ lua_createtable(lstate, 0, (int)(len)); \
+ } while (0)
+
+#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair)
+
+#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
+
+#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
+
+#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
+ lua_rawset(lstate, -3)
+
+#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
+ TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
+
+#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
+ do { \
+ for (size_t backref = kv_size(*mpstack); backref; backref--) { \
+ const MPConvStackVal mpval = kv_A(*mpstack, backref - 1); \
+ if (mpval.type == conv_type) { \
+ if (conv_type == kMPConvDict \
+ ? (void *) mpval.data.d.dict == (void *) (val) \
+ : (void *) mpval.data.l.list == (void *) (val)) { \
+ lua_pushvalue(lstate, \
+ 1 - ((int)((kv_size(*mpstack) - backref + 1) * 2))); \
+ break; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define TYPVAL_ENCODE_SCOPE static
+#define TYPVAL_ENCODE_NAME lua
+#define TYPVAL_ENCODE_FIRST_ARG_TYPE lua_State *const
+#define TYPVAL_ENCODE_FIRST_ARG_NAME lstate
+#include "nvim/eval/typval_encode.c.h"
+#undef TYPVAL_ENCODE_SCOPE
+#undef TYPVAL_ENCODE_NAME
+#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
+#undef TYPVAL_ENCODE_FIRST_ARG_NAME
+
+#undef TYPVAL_ENCODE_CONV_STRING
+#undef TYPVAL_ENCODE_CONV_STR_STRING
+#undef TYPVAL_ENCODE_CONV_EXT_STRING
+#undef TYPVAL_ENCODE_CONV_NUMBER
+#undef TYPVAL_ENCODE_CONV_FLOAT
+#undef TYPVAL_ENCODE_CONV_FUNC_START
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
+#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
+#undef TYPVAL_ENCODE_CONV_FUNC_END
+#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
+#undef TYPVAL_ENCODE_CONV_LIST_START
+#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
+#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
+#undef TYPVAL_ENCODE_CONV_NIL
+#undef TYPVAL_ENCODE_CONV_BOOL
+#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
+#undef TYPVAL_ENCODE_CONV_DICT_START
+#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
+#undef TYPVAL_ENCODE_CONV_DICT_END
+#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
+#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
+#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
+#undef TYPVAL_ENCODE_CONV_LIST_END
+#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
+#undef TYPVAL_ENCODE_CONV_RECURSE
+#undef TYPVAL_ENCODE_ALLOW_SPECIALS
+
+/// Convert VimL typval_T to lua value
+///
+/// Should leave single value in lua stack. May only fail if lua failed to grow
+/// stack.
+///
+/// @param lstate Lua interpreter state.
+/// @param[in] tv typval_T to convert.
+///
+/// @return true in case of success, false otherwise.
+bool nlua_push_typval(lua_State *lstate, typval_T *const tv)
+{
+ const int initial_size = lua_gettop(lstate);
+ if (!lua_checkstack(lstate, initial_size + 1)) {
+ emsgf(_("E1502: Lua failed to grow stack to %i"), initial_size + 4);
+ return false;
+ }
+ if (encode_vim_to_lua(lstate, tv, "nlua_push_typval argument") == FAIL) {
+ return false;
+ }
+ assert(lua_gettop(lstate) == initial_size + 1);
+ return true;
+}
+
#define NLUA_PUSH_IDX(lstate, type, idx) \
do { \
STATIC_ASSERT(sizeof(type) <= sizeof(lua_Number), \
diff --git a/src/nvim/viml/executor/converter.h b/src/nvim/viml/executor/converter.h
index e11d0cef19..dbbaaebf6b 100644
--- a/src/nvim/viml/executor/converter.h
+++ b/src/nvim/viml/executor/converter.h
@@ -3,8 +3,11 @@
#include <lua.h>
#include <stdbool.h>
+#include <stdint.h>
+
#include "nvim/api/private/defs.h"
#include "nvim/func_attr.h"
+#include "nvim/eval.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "viml/executor/converter.h.generated.h"
diff --git a/src/nvim/viml/executor/executor.c b/src/nvim/viml/executor/executor.c
index 6f1e847649..a9d05bcd31 100644
--- a/src/nvim/viml/executor/executor.c
+++ b/src/nvim/viml/executor/executor.c
@@ -36,7 +36,19 @@ typedef struct {
lua_pushcfunction(lstate, &function); \
lua_call(lstate, 0, numret); \
} while (0)
-/// Call C function which expects four arguments
+/// Call C function which expects two arguments
+///
+/// @param function Called function
+/// @param numret Number of returned arguments
+/// @param a… Supplied argument (should be a void* pointer)
+#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \
+ do { \
+ lua_pushcfunction(lstate, &function); \
+ lua_pushlightuserdata(lstate, a1); \
+ lua_pushlightuserdata(lstate, a2); \
+ lua_call(lstate, 2, numret); \
+ } while (0)
+/// Call C function which expects three arguments
///
/// @param function Called function
/// @param numret Number of returned arguments
@@ -64,15 +76,19 @@ typedef struct {
lua_call(lstate, 4, numret); \
} while (0)
-static void set_lua_error(lua_State *lstate, LuaError *lerr)
+/// Convert lua error into a Vim error message
+///
+/// @param lstate Lua interpreter state.
+/// @param[in] msg Message base, must contain one `%s`.
+static void nlua_error(lua_State *const lstate, const char *const msg)
FUNC_ATTR_NONNULL_ALL
{
- const char *const str = lua_tolstring(lstate, -1, &lerr->lua_err_str.size);
- lerr->lua_err_str.data = xmemdupz(str, lerr->lua_err_str.size);
- lua_pop(lstate, 1);
+ size_t len;
+ const char *const str = lua_tolstring(lstate, -1, &len);
+
+ EMSG2(msg, str);
- // FIXME? More specific error?
- set_api_error("Error while executing lua code", &lerr->err);
+ lua_pop(lstate, 1);
}
/// Compare two strings, ignoring case
@@ -94,25 +110,26 @@ static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
/// Evaluate lua string
///
-/// Expects three values on the stack: string to evaluate, pointer to the
-/// location where result is saved, pointer to the location where error is
-/// saved. Always returns nothing (from the lua point of view).
+/// Expects two values on the stack: string to evaluate, pointer to the
+/// location where result is saved. Always returns nothing (from the lua point
+/// of view).
static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
{
- String *str = (String *) lua_touserdata(lstate, 1);
- Object *obj = (Object *) lua_touserdata(lstate, 2);
- LuaError *lerr = (LuaError *) lua_touserdata(lstate, 3);
- lua_pop(lstate, 3);
+ String *str = (String *)lua_touserdata(lstate, 1);
+ typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 2);
+ lua_pop(lstate, 2);
if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) {
- set_lua_error(lstate, lerr);
+ nlua_error(lstate, _("E5104: Error while creating lua chunk: %s"));
return 0;
}
if (lua_pcall(lstate, 0, 1, 0)) {
- set_lua_error(lstate, lerr);
+ nlua_error(lstate, _("E5105: Error while calling lua chunk: %s"));
+ return 0;
+ }
+ if (!nlua_pop_typval(lstate, ret_tv)) {
return 0;
}
- *obj = nlua_pop_Object(lstate, &lerr->err);
return 0;
}
@@ -124,8 +141,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_stricmp);
lua_setglobal(lstate, "stricmp");
if (luaL_dostring(lstate, (char *) &vim_module[0])) {
- LuaError lerr;
- set_lua_error(lstate, &lerr);
+ nlua_error(lstate, _("E5106: Error while creating vim module: %s"));
return 1;
}
nlua_add_api_functions(lstate);
@@ -150,14 +166,6 @@ static lua_State *init_lua(void)
return lstate;
}
-static Object exec_lua_string(lua_State *lstate, String str, LuaError *lerr)
- FUNC_ATTR_NONNULL_ALL
-{
- Object ret = { kObjectTypeNil, { false } };
- NLUA_CALL_C_FUNCTION_3(lstate, nlua_exec_lua_string, 0, &str, &ret, lerr);
- return ret;
-}
-
static lua_State *global_lstate = NULL;
/// Execute lua string
@@ -165,28 +173,17 @@ static lua_State *global_lstate = NULL;
/// Used for :lua.
///
/// @param[in] str String to execute.
-/// @param[out] err Location where error will be saved.
-/// @param[out] err_str Location where lua error string will be saved, if any.
+/// @param[out] ret_tv Location where result will be saved.
///
/// @return Result of the execution.
-Object executor_exec_lua(String str, Error *err, String *err_str)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+void executor_exec_lua(String str, typval_T *ret_tv)
+ FUNC_ATTR_NONNULL_ALL
{
if (global_lstate == NULL) {
global_lstate = init_lua();
}
- LuaError lerr = {
- .err = { .set = false },
- .lua_err_str = STRING_INIT,
- };
-
- Object ret = exec_lua_string(global_lstate, str, &lerr);
-
- *err = lerr.err;
- *err_str = lerr.lua_err_str;
-
- return ret;
+ NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, &str, ret_tv);
}
/// Evaluate lua string
@@ -196,75 +193,73 @@ Object executor_exec_lua(String str, Error *err, String *err_str)
/// 1. String to evaluate.
/// 2. _A value.
/// 3. Pointer to location where result is saved.
-/// 4. Pointer to location where error will be saved.
///
/// @param[in,out] lstate Lua interpreter state.
static int nlua_eval_lua_string(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
- String *str = (String *) lua_touserdata(lstate, 1);
- Object *arg = (Object *) lua_touserdata(lstate, 2);
- Object *ret = (Object *) lua_touserdata(lstate, 3);
- LuaError *lerr = (LuaError *) lua_touserdata(lstate, 4);
+ String *str = (String *)lua_touserdata(lstate, 1);
+ typval_T *arg = (typval_T *)lua_touserdata(lstate, 2);
+ typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 3);
+ lua_pop(lstate, 3);
garray_T str_ga;
ga_init(&str_ga, 1, 80);
-#define EVALHEADER "local _A=select(1,...) return "
- ga_concat_len(&str_ga, EVALHEADER, sizeof(EVALHEADER) - 1);
+#define EVALHEADER "local _A=select(1,...) return ("
+ const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1;
+ char *lcmd;
+ if (lcmd_len < IOSIZE) {
+ lcmd = (char *)IObuff;
+ } else {
+ lcmd = xmalloc(lcmd_len);
+ }
+ memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1);
+ memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size);
+ lcmd[lcmd_len - 1] = ')';
#undef EVALHEADER
- ga_concat_len(&str_ga, str->data, str->size);
- if (luaL_loadbuffer(lstate, str_ga.ga_data, (size_t) str_ga.ga_len,
- NLUA_EVAL_NAME)) {
- set_lua_error(lstate, lerr);
+ if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
+ nlua_error(lstate,
+ _("E5107: Error while creating lua chunk for luaeval(): %s"));
return 0;
}
- ga_clear(&str_ga);
+ if (lcmd != (char *)IObuff) {
+ xfree(lcmd);
+ }
- nlua_push_Object(lstate, *arg);
+ if (arg == NULL || arg->v_type == VAR_UNKNOWN) {
+ lua_pushnil(lstate);
+ } else {
+ nlua_push_typval(lstate, arg);
+ }
if (lua_pcall(lstate, 1, 1, 0)) {
- set_lua_error(lstate, lerr);
+ nlua_error(lstate,
+ _("E5108: Error while calling lua chunk for luaeval(): %s"));
+ return 0;
+ }
+ if (!nlua_pop_typval(lstate, ret_tv)) {
return 0;
}
- *ret = nlua_pop_Object(lstate, &lerr->err);
return 0;
}
-static Object eval_lua_string(lua_State *lstate, String str, Object arg,
- LuaError *lerr)
- FUNC_ATTR_NONNULL_ALL
-{
- Object ret = { kObjectTypeNil, { false } };
- NLUA_CALL_C_FUNCTION_4(lstate, nlua_eval_lua_string, 0,
- &str, &arg, &ret, lerr);
- return ret;
-}
-
/// Evaluate lua string
///
/// Used for luaeval().
///
/// @param[in] str String to execute.
-/// @param[out] err Location where error will be saved.
-/// @param[out] err_str Location where lua error string will be saved, if any.
+/// @param[in] arg Second argument to `luaeval()`.
+/// @param[out] ret_tv Location where result will be saved.
///
/// @return Result of the execution.
-Object executor_eval_lua(String str, Object arg, Error *err, String *err_str)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+void executor_eval_lua(const String str, typval_T *const arg,
+ typval_T *const ret_tv)
+ FUNC_ATTR_NONNULL_ALL
{
if (global_lstate == NULL) {
global_lstate = init_lua();
}
- LuaError lerr = {
- .err = { .set = false },
- .lua_err_str = STRING_INIT,
- };
-
- Object ret = eval_lua_string(global_lstate, str, arg, &lerr);
-
- *err = lerr.err;
- *err_str = lerr.lua_err_str;
-
- return ret;
+ NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0,
+ (void *)&str, arg, ret_tv);
}