aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2024-02-11 15:46:14 +0100
committerbfredl <bjorn.linse@gmail.com>2024-02-13 11:54:44 +0100
commit0353dd3029f9ce31c3894530385443a90f6677ee (patch)
treefa288427461ee2c1ce1c271d01a760977a161bf5
parent89135cff030b06f60cd596a9ae81cd9583987517 (diff)
downloadrneovim-0353dd3029f9ce31c3894530385443a90f6677ee.tar.gz
rneovim-0353dd3029f9ce31c3894530385443a90f6677ee.tar.bz2
rneovim-0353dd3029f9ce31c3894530385443a90f6677ee.zip
refactor(lua): use Arena when converting from lua stack to API args
and for return value of nlua_exec/nlua_call_ref, as this uses the same family of functions. NB: the handling of luaref:s is a bit of a mess. add api_luarefs_free_XX functions as a stop-gap as refactoring luarefs is a can of worms for another PR:s. as a minor feature/bug-fix, nvim_buf_call and nvim_win_call now preserves arbitrary return values.
-rw-r--r--runtime/doc/api.txt6
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--src/nvim/api/autocmd.c3
-rw-r--r--src/nvim/api/buffer.c7
-rw-r--r--src/nvim/api/command.c14
-rw-r--r--src/nvim/api/deprecated.c4
-rw-r--r--src/nvim/api/private/helpers.c50
-rw-r--r--src/nvim/api/private/helpers.h3
-rw-r--r--src/nvim/api/vim.c60
-rw-r--r--src/nvim/api/win_config.c2
-rw-r--r--src/nvim/api/window.c5
-rw-r--r--src/nvim/autocmd.c26
-rw-r--r--src/nvim/buffer_updates.c14
-rw-r--r--src/nvim/cmdexpand.c2
-rw-r--r--src/nvim/decoration_provider.c2
-rw-r--r--src/nvim/eval.c4
-rw-r--r--src/nvim/eval/funcs.c11
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua36
-rw-r--r--src/nvim/getchar.c2
-rw-r--r--src/nvim/highlight.c6
-rw-r--r--src/nvim/lua/converter.c124
-rw-r--r--src/nvim/lua/executor.c80
-rw-r--r--src/nvim/lua/executor.h13
-rw-r--r--src/nvim/lua/xdiff.c3
-rw-r--r--src/nvim/main.c16
-rw-r--r--src/nvim/mapping.c2
-rw-r--r--src/nvim/msgpack_rpc/channel.c2
-rw-r--r--src/nvim/runtime.c13
-rw-r--r--src/nvim/ui.c4
-rw-r--r--src/nvim/ui_client.c2
-rw-r--r--src/nvim/version.c4
-rw-r--r--test/functional/lua/vim_spec.lua24
33 files changed, 318 insertions, 230 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 95bcb31db9..85b3dffb4b 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -2183,8 +2183,7 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()*
only)
Return: ~
- Return value of function. NB: will deepcopy Lua values currently, use
- upvalues to send Lua references in and out.
+ Return value of function.
nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()*
Unmaps a buffer-local |mapping| for the given mode.
@@ -2879,8 +2878,7 @@ nvim_win_call({window}, {fun}) *nvim_win_call()*
only)
Return: ~
- Return value of function. NB: will deepcopy Lua values currently, use
- upvalues to send Lua references in and out.
+ Return value of function.
See also: ~
• |win_execute()|
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 573a9f43b8..9891926632 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -422,6 +422,8 @@ The following changes to existing APIs or features add new behavior.
• |--startuptime| reports the startup times for both processes (TUI + server) as separate sections.
+• |nvim_buf_call()| and |nvim_win_call()| now preserves any return value (NB: not multiple return values)
+
==============================================================================
REMOVED FEATURES *news-removed*
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 78628c4154..4f67f682f1 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -431,7 +431,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
});
cb.type = kCallbackLua;
- cb.data.luaref = api_new_luaref(callback->data.luaref);
+ cb.data.luaref = callback->data.luaref;
+ callback->data.luaref = LUA_NOREF;
break;
case kObjectTypeString:
cb.type = kCallbackFuncref;
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 183483b329..993e290b2d 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1227,8 +1227,7 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err)
/// @param fun Function to call inside the buffer (currently Lua callable
/// only)
/// @param[out] err Error details, if any
-/// @return Return value of function. NB: will deepcopy Lua values
-/// currently, use upvalues to send Lua references in and out.
+/// @return Return value of function.
Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
FUNC_API_SINCE(7)
FUNC_API_LUA_ONLY
@@ -1242,7 +1241,7 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
aucmd_prepbuf(&aco, buf);
Array args = ARRAY_DICT_INIT;
- Object res = nlua_call_ref(fun, NULL, args, true, err);
+ Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
aucmd_restbuf(&aco);
try_end(err);
@@ -1419,7 +1418,7 @@ static void push_linestr(lua_State *lstate, Array *a, const char *s, size_t len,
} else {
String str = STRING_INIT;
if (len > 0) {
- str = arena_string(arena, cbuf_as_string((char *)s, len));
+ str = CBUF_TO_ARENA_STR(arena, s, len);
if (replace_nl) {
// Vim represents NULs as NLs, but this may confuse clients.
strchrsub(str.data, '\n', '\0');
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index bafc45e543..0ac3d42231 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -99,7 +99,7 @@
Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(10) FUNC_API_FAST
{
- Dict(cmd) result = { 0 };
+ Dict(cmd) result = KEYDICT_INIT;
// Parse command line
exarg_T ea;
@@ -514,7 +514,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
VALIDATE_MOD((!ea.forceit || (ea.argt & EX_BANG)), "bang", cmd->cmd.data);
if (HAS_KEY(cmd, cmd, magic)) {
- Dict(cmd_magic) magic[1] = { 0 };
+ Dict(cmd_magic) magic[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(magic, KeyDict_cmd_magic_get_field, cmd->magic, err)) {
goto end;
}
@@ -532,13 +532,13 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
}
if (HAS_KEY(cmd, cmd, mods)) {
- Dict(cmd_mods) mods[1] = { 0 };
+ Dict(cmd_mods) mods[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(mods, KeyDict_cmd_mods_get_field, cmd->mods, err)) {
goto end;
}
if (HAS_KEY(mods, cmd_mods, filter)) {
- Dict(cmd_mods_filter) filter[1] = { 0 };
+ Dict(cmd_mods_filter) filter[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(&filter, KeyDict_cmd_mods_filter_get_field,
mods->filter, err)) {
@@ -1103,7 +1103,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
if (opts->complete.type == kObjectTypeLuaRef) {
context = EXPAND_USER_LUA;
- compl_luaref = api_new_luaref(opts->complete.data.luaref);
+ compl_luaref = opts->complete.data.luaref;
+ opts->complete.data.luaref = LUA_NOREF;
} else if (opts->complete.type == kObjectTypeString) {
VALIDATE_S(OK == parse_compl_arg(opts->complete.data.string.data,
(int)opts->complete.data.string.size, &context, &argt,
@@ -1123,7 +1124,8 @@ void create_user_command(uint64_t channel_id, String name, Object command, Dict(
});
argt |= EX_PREVIEW;
- preview_luaref = api_new_luaref(opts->preview.data.luaref);
+ preview_luaref = opts->preview.data.luaref;
+ opts->preview.data.luaref = LUA_NOREF;
}
switch (command.type) {
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index dccaeb6922..c9a6036b8f 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -51,12 +51,12 @@ String nvim_command_output(uint64_t channel_id, String command, Error *err)
/// @deprecated Use nvim_exec_lua() instead.
/// @see nvim_exec_lua
-Object nvim_execute_lua(String code, Array args, Error *err)
+Object nvim_execute_lua(String code, Array args, Arena *arena, Error *err)
FUNC_API_SINCE(3)
FUNC_API_DEPRECATED_SINCE(7)
FUNC_API_REMOTE_ONLY
{
- return nlua_exec(code, args, err);
+ return nlua_exec(code, args, kRetObject, arena, err);
}
/// Gets the buffer number
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 8b45af7c71..7bf0d87603 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -576,7 +576,7 @@ String arena_string(Arena *arena, String str)
if (str.size) {
return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size);
} else {
- return (String)STRING_INIT;
+ return (String){ .data = arena ? "" : xstrdup(""), .size = 0 };
}
}
@@ -1062,24 +1062,56 @@ Dictionary api_keydict_to_dict(void *value, KeySetLink *table, size_t max_size,
return rv;
}
-void api_free_keydict(void *dict, KeySetLink *table)
+void api_luarefs_free_object(Object value)
+{
+ // TODO(bfredl): this is more complicated than it needs to be.
+ // we should be able to lock down more specifically where luarefs can be
+ switch (value.type) {
+ case kObjectTypeLuaRef:
+ api_free_luaref(value.data.luaref);
+ break;
+
+ case kObjectTypeArray:
+ api_luarefs_free_array(value.data.array);
+ break;
+
+ case kObjectTypeDictionary:
+ api_luarefs_free_dict(value.data.dictionary);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void api_luarefs_free_keydict(void *dict, KeySetLink *table)
{
for (size_t i = 0; table[i].str; i++) {
char *mem = ((char *)dict + table[i].ptr_off);
if (table[i].type == kObjectTypeNil) {
- api_free_object(*(Object *)mem);
- } else if (table[i].type == kObjectTypeString) {
- api_free_string(*(String *)mem);
- } else if (table[i].type == kObjectTypeArray) {
- api_free_array(*(Array *)mem);
- } else if (table[i].type == kObjectTypeDictionary) {
- api_free_dictionary(*(Dictionary *)mem);
+ api_luarefs_free_object(*(Object *)mem);
} else if (table[i].type == kObjectTypeLuaRef) {
api_free_luaref(*(LuaRef *)mem);
+ } else if (table[i].type == kObjectTypeDictionary) {
+ api_luarefs_free_dict(*(Dictionary *)mem);
}
}
}
+void api_luarefs_free_array(Array value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_luarefs_free_object(value.items[i]);
+ }
+}
+
+void api_luarefs_free_dict(Dictionary value)
+{
+ for (size_t i = 0; i < value.size; i++) {
+ api_luarefs_free_object(value.items[i].value);
+ }
+}
+
/// Set a named mark
/// buffer and mark name must be validated already
/// @param buffer Buffer to set the mark on
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 9ee812f45c..11abb8f801 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -34,6 +34,7 @@
#define CSTR_TO_OBJ(s) STRING_OBJ(cstr_to_string(s))
#define CSTR_TO_ARENA_STR(arena, s) arena_string(arena, cstr_as_string(s))
#define CSTR_TO_ARENA_OBJ(arena, s) STRING_OBJ(CSTR_TO_ARENA_STR(arena, s))
+#define CBUF_TO_ARENA_STR(arena, s, len) arena_string(arena, cbuf_as_string((char *)(s), len))
#define BUFFER_OBJ(s) ((Object) { \
.type = kObjectTypeBuffer, \
@@ -119,6 +120,8 @@
#define api_init_array = ARRAY_DICT_INIT
#define api_init_dictionary = ARRAY_DICT_INIT
+#define KEYDICT_INIT { 0 }
+
#define api_free_boolean(value)
#define api_free_integer(value)
#define api_free_float(value)
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 78c2561bbd..769537ac98 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -496,11 +496,12 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Bool
/// or executing the Lua code.
///
/// @return Return value of Lua code if present or NIL.
-Object nvim_exec_lua(String code, Array args, Error *err)
+Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err)
FUNC_API_SINCE(7)
FUNC_API_REMOTE_ONLY
{
- return nlua_exec(code, args, err);
+ // TODO(bfredl): convert directly from msgpack to lua and then back again
+ return nlua_exec(code, args, kRetObject, arena, err);
}
/// Notify the user with a message
@@ -512,7 +513,7 @@ Object nvim_exec_lua(String code, Array args, Error *err)
/// @param log_level The log level
/// @param opts Reserved for future use.
/// @param[out] err Error details, if any
-Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
+Object nvim_notify(String msg, Integer log_level, Dictionary opts, Arena *arena, Error *err)
FUNC_API_SINCE(7)
{
MAXSIZE_TEMP_ARRAY(args, 3);
@@ -520,7 +521,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
ADD_C(args, INTEGER_OBJ(log_level));
ADD_C(args, DICTIONARY_OBJ(opts));
- return NLUA_EXEC_STATIC("return vim.notify(...)", args, err);
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
}
/// Calculates the number of display cells occupied by `text`.
@@ -603,7 +604,8 @@ String nvim__get_lib_dir(void)
/// @param all whether to return all matches or only the first
/// @param opts is_lua: only search Lua subdirs
/// @return list of absolute paths to the found files
-ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err)
+ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Arena *arena,
+ Error *err)
FUNC_API_SINCE(8)
FUNC_API_FAST
{
@@ -613,7 +615,7 @@ ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, E
return (Array)ARRAY_DICT_INIT;
}
- ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all);
+ ArrayOf(String) res = runtime_get_named(opts->is_lua, pat, all, arena);
if (opts->do_source) {
for (size_t i = 0; i < res.size; i++) {
@@ -1068,7 +1070,7 @@ static void term_write(const char *buf, size_t size, void *data)
ADD_C(args, BUFFER_OBJ(terminal_buf(chan->term)));
ADD_C(args, STRING_OBJ(((String){ .data = (char *)buf, .size = size })));
textlock++;
- nlua_call_ref(cb, "input", args, false, NULL);
+ nlua_call_ref(cb, "input", args, kRetNilBool, NULL, NULL);
textlock--;
}
@@ -1189,7 +1191,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
/// @return
/// - true: Client may continue pasting.
/// - false: Client must cancel the paste.
-Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
+Boolean nvim_paste(String data, Boolean crlf, Integer phase, Arena *arena, Error *err)
FUNC_API_SINCE(6)
FUNC_API_TEXTLOCK_ALLOW_CMDWIN
{
@@ -1199,19 +1201,18 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
VALIDATE_INT((phase >= -1 && phase <= 3), "phase", phase, {
return false;
});
- Array args = ARRAY_DICT_INIT;
- Object rv = OBJECT_INIT;
+ Array lines = ARRAY_DICT_INIT;
if (phase == -1 || phase == 1) { // Start of paste-stream.
draining = false;
} else if (draining) {
// Skip remaining chunks. Report error only once per "stream".
goto theend;
}
- Array lines = string_to_array(data, crlf);
- ADD(args, ARRAY_OBJ(lines));
- ADD(args, INTEGER_OBJ(phase));
- rv = nvim_exec_lua(STATIC_CSTR_AS_STRING("return vim.paste(...)"), args,
- err);
+ lines = string_to_array(data, crlf);
+ MAXSIZE_TEMP_ARRAY(args, 2);
+ ADD_C(args, ARRAY_OBJ(lines));
+ ADD_C(args, INTEGER_OBJ(phase));
+ Object rv = NLUA_EXEC_STATIC("return vim.paste(...)", args, kRetNilBool, arena, err);
if (ERROR_SET(err)) {
draining = true;
goto theend;
@@ -1238,8 +1239,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err)
AppendCharToRedobuff(ESC); // Dot-repeat.
}
theend:
- api_free_object(rv);
- api_free_array(args);
+ api_free_array(lines);
if (cancel || phase == -1 || phase == 3) { // End of paste-stream.
draining = false;
}
@@ -1875,7 +1875,7 @@ Array nvim_list_uis(Arena *arena)
/// Gets the immediate children of process `pid`.
///
/// @return Array of child process ids, empty if process not found.
-Array nvim_get_proc_children(Integer pid, Error *err)
+Array nvim_get_proc_children(Integer pid, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
Array rvobj = ARRAY_DICT_INIT;
@@ -1892,7 +1892,7 @@ Array nvim_get_proc_children(Integer pid, Error *err)
DLOG("fallback to vim._os_proc_children()");
MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, kRetObject, arena, err);
if (o.type == kObjectTypeArray) {
rvobj = o.data.array;
} else if (!ERROR_SET(err)) {
@@ -1900,11 +1900,11 @@ Array nvim_get_proc_children(Integer pid, Error *err)
"Failed to get process children. pid=%" PRId64 " error=%d",
pid, rv);
}
- goto end;
- }
-
- for (size_t i = 0; i < proc_count; i++) {
- ADD(rvobj, INTEGER_OBJ(proc_list[i]));
+ } else {
+ rvobj = arena_array(arena, proc_count);
+ for (size_t i = 0; i < proc_count; i++) {
+ ADD(rvobj, INTEGER_OBJ(proc_list[i]));
+ }
}
end:
@@ -1915,19 +1915,17 @@ end:
/// Gets info describing process `pid`.
///
/// @return Map of process properties, or NIL if process not found.
-Object nvim_get_proc(Integer pid, Error *err)
+Object nvim_get_proc(Integer pid, Arena *arena, Error *err)
FUNC_API_SINCE(4)
{
- Object rvobj = OBJECT_INIT;
- rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT;
- rvobj.type = kObjectTypeDictionary;
+ Object rvobj = NIL;
VALIDATE_INT((pid > 0 && pid <= INT_MAX), "pid", pid, {
return NIL;
});
#ifdef MSWIN
- rvobj.data.dictionary = os_proc_info((int)pid);
+ rvobj = DICTIONARY_OBJ(os_proc_info((int)pid));
if (rvobj.data.dictionary.size == 0) { // Process not found.
return NIL;
}
@@ -1935,11 +1933,11 @@ Object nvim_get_proc(Integer pid, Error *err)
// Cross-platform process info APIs are miserable, so use `ps` instead.
MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, kRetObject, arena, err);
if (o.type == kObjectTypeArray && o.data.array.size == 0) {
return NIL; // Process not found.
} else if (o.type == kObjectTypeDictionary) {
- rvobj.data.dictionary = o.data.dictionary;
+ rvobj = o;
} else if (!ERROR_SET(err)) {
api_set_error(err, kErrorTypeException,
"Failed to get process info. pid=%" PRId64, pid);
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 52372b838e..e76db82c61 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -600,7 +600,7 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
/// Keep in sync with WinSplit in buffer_defs.h
static const char *const win_split_str[] = { "left", "right", "above", "below" };
- Dict(win_config) rv = { 0 };
+ Dict(win_config) rv = KEYDICT_INIT;
win_T *wp = find_window_by_handle(window, err);
if (!wp) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 4ac7e47832..93c9dfa049 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -421,8 +421,7 @@ void nvim_win_close(Window window, Boolean force, Error *err)
/// @param fun Function to call inside the window (currently Lua callable
/// only)
/// @param[out] err Error details, if any
-/// @return Return value of function. NB: will deepcopy Lua values
-/// currently, use upvalues to send Lua references in and out.
+/// @return Return value of function.
Object nvim_win_call(Window window, LuaRef fun, Error *err)
FUNC_API_SINCE(7)
FUNC_API_LUA_ONLY
@@ -438,7 +437,7 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
win_execute_T win_execute_args;
if (win_execute_before(&win_execute_args, win, tabpage)) {
Array args = ARRAY_DICT_INIT;
- res = nlua_call_ref(fun, NULL, args, true, err);
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
}
win_execute_after(&win_execute_args);
try_end(err);
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index 1edc60f230..3b747e0920 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -2002,15 +2002,15 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
{
Callback callback = ac->exec.callable.cb;
if (callback.type == kCallbackLua) {
- Dictionary data = ARRAY_DICT_INIT;
- PUT(data, "id", INTEGER_OBJ(ac->id));
- PUT(data, "event", CSTR_TO_OBJ(event_nr2name(apc->event)));
- PUT(data, "match", CSTR_TO_OBJ(autocmd_match));
- PUT(data, "file", CSTR_TO_OBJ(autocmd_fname));
- PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr));
+ MAXSIZE_TEMP_DICT(data, 7);
+ PUT_C(data, "id", INTEGER_OBJ(ac->id));
+ PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event)));
+ PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match));
+ PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname));
+ PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
- PUT(data, "data", copy_object(*apc->data, NULL));
+ PUT_C(data, "data", *apc->data);
}
int group = ac->pat->group;
@@ -2023,21 +2023,15 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
// omit group in these cases
break;
default:
- PUT(data, "group", INTEGER_OBJ(group));
+ PUT_C(data, "group", INTEGER_OBJ(group));
break;
}
MAXSIZE_TEMP_ARRAY(args, 1);
ADD_C(args, DICTIONARY_OBJ(data));
- Object result = nlua_call_ref(callback.data.luaref, NULL, args, true, NULL);
- bool ret = false;
- if (result.type == kObjectTypeBoolean) {
- ret = result.data.boolean;
- }
- api_free_dictionary(data);
- api_free_object(result);
- return ret;
+ Object result = nlua_call_ref(callback.data.luaref, NULL, args, kRetNilBool, NULL, NULL);
+ return LUARET_TRUTHY(result);
} else {
typval_T argsin = TV_INITIAL_VALUE;
typval_T rettv = TV_INITIAL_VALUE;
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index 1a02ac78d7..e725678937 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -179,7 +179,7 @@ void buf_updates_unload(buf_T *buf, bool can_reload)
ADD_C(args, BUFFER_OBJ(buf->handle));
TEXTLOCK_WRAP({
- nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL);
+ nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL, NULL);
});
}
@@ -295,10 +295,10 @@ void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added,
Object res;
TEXTLOCK_WRAP({
- res = nlua_call_ref(cb.on_lines, "lines", args, false, NULL);
+ res = nlua_call_ref(cb.on_lines, "lines", args, kRetNilBool, NULL, NULL);
});
- if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
+ if (LUARET_TRUTHY(res)) {
buffer_update_callbacks_free(cb);
keep = false;
}
@@ -345,10 +345,10 @@ void buf_updates_send_splice(buf_T *buf, int start_row, colnr_T start_col, bcoun
Object res;
TEXTLOCK_WRAP({
- res = nlua_call_ref(cb.on_bytes, "bytes", args, false, NULL);
+ res = nlua_call_ref(cb.on_bytes, "bytes", args, kRetNilBool, NULL, NULL);
});
- if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
+ if (LUARET_TRUTHY(res)) {
buffer_update_callbacks_free(cb);
keep = false;
}
@@ -381,10 +381,10 @@ void buf_updates_changedtick(buf_T *buf)
Object res;
TEXTLOCK_WRAP({
- res = nlua_call_ref(cb.on_changedtick, "changedtick", args, false, NULL);
+ res = nlua_call_ref(cb.on_changedtick, "changedtick", args, kRetNilBool, NULL, NULL);
});
- if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
+ if (LUARET_TRUTHY(res)) {
buffer_update_callbacks_free(cb);
keep = false;
}
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index c5f1bfeb87..f172646edf 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -2591,7 +2591,7 @@ static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
if (last_gen != get_cmdline_last_prompt_id() || last_gen == 0) {
Array a = ARRAY_DICT_INIT;
Error err = ERROR_INIT;
- Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err);
+ Object res = NLUA_EXEC_STATIC("return vim.health._complete()", a, kRetObject, NULL, &err);
api_clear_error(&err);
api_free_object(names);
names = res;
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index cd55219bf6..2417c14f7f 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -48,7 +48,7 @@ static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref
textlock++;
provider_active = true;
- Object ret = nlua_call_ref(ref, name, args, true, &err);
+ Object ret = nlua_call_ref(ref, name, args, kRetNilBool, NULL, &err);
provider_active = false;
textlock--;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 3647bde952..a4da582dca 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6143,8 +6143,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
break;
case kCallbackLua:
- rv = nlua_call_ref(callback->data.luaref, NULL, args, false, NULL);
- return (rv.type == kObjectTypeBoolean && rv.data.boolean == true);
+ rv = nlua_call_ref(callback->data.luaref, NULL, args, kRetNilBool, NULL, NULL);
+ return LUARET_TRUTHY(rv);
case kCallbackNone:
return false;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index e4d2a219d9..726b9ce758 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -3278,13 +3278,11 @@ static bool has_wsl(void)
static TriState has_wsl = kNone;
if (has_wsl == kNone) {
Error err = ERROR_INIT;
- Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()"
- ":match('microsoft') and true or false"),
- (Array)ARRAY_DICT_INIT, &err);
+ Object o = NLUA_EXEC_STATIC("return vim.uv.os_uname()['release']:lower()"
+ ":match('microsoft')",
+ (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err);
assert(!ERROR_SET(&err));
- assert(o.type == kObjectTypeBoolean);
- has_wsl = o.data.boolean ? kTrue : kFalse;
- api_free_object(o);
+ has_wsl = LUARET_TRUTHY(o) ? kTrue : kFalse;
}
return has_wsl == kTrue;
}
@@ -6963,6 +6961,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
ArenaMem res_mem = NULL;
Object result = rpc_send_call(chan_id, method, args, &res_mem, &err);
+ api_free_array(args);
if (l_provider_call_nesting) {
current_sctx = save_current_sctx;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 12e746d49e..2913f6d4e9 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7445,7 +7445,7 @@ static void ex_checkhealth(exarg_T *eap)
ADD_C(args, STRING_OBJ(((String){ .data = mods, .size = mods_len })));
ADD_C(args, CSTR_AS_OBJ(eap->arg));
- NLUA_EXEC_STATIC("return vim.health._check(...)", args, &err);
+ NLUA_EXEC_STATIC("vim.health._check(...)", args, kRetNilBool, NULL, &err);
if (!ERROR_SET(&err)) {
return;
}
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index 04fe43b712..c16ea29a01 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -333,9 +333,6 @@ KeySetLink *KeyDict_]] .. k.name .. [[_get_field(const char *str, size_t len)
}
]])
- keysets_defs:write(
- '#define api_free_keydict_' .. k.name .. '(x) api_free_keydict(x, ' .. k.name .. '_table)\n'
- )
end
local function real_type(type)
@@ -687,6 +684,7 @@ local function process_function(fn)
static int %s(lua_State *lstate)
{
Error err = ERROR_INIT;
+ Arena arena = ARENA_EMPTY;
char *err_param = 0;
if (lua_gettop(lstate) != %i) {
api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s");
@@ -736,18 +734,24 @@ local function process_function(fn)
local param = fn.parameters[j]
local cparam = string.format('arg%u', j)
local param_type = real_type(param[1])
- local lc_param_type = real_type(param[1]):lower()
local extra = param_type == 'Dictionary' and 'false, ' or ''
- if param[1] == 'Object' or param[1] == 'DictionaryOf(LuaRef)' then
+ local arg_free_code = ''
+ if param[1] == 'Object' then
+ extra = 'true, '
+ arg_free_code = 'api_luarefs_free_object(' .. cparam .. ');'
+ elseif param[1] == 'DictionaryOf(LuaRef)' then
extra = 'true, '
+ arg_free_code = 'api_luarefs_free_dict(' .. cparam .. ');'
+ elseif param[1] == 'LuaRef' then
+ arg_free_code = 'api_free_luaref(' .. cparam .. ');'
end
local errshift = 0
local seterr = ''
if string.match(param_type, '^KeyDict_') then
write_shifted_output(
[[
- %s %s = { 0 };
- nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &err);
+ %s %s = KEYDICT_INIT;
+ nlua_pop_keydict(lstate, &%s, %s_get_field, &err_param, &arena, &err);
]],
param_type,
cparam,
@@ -756,10 +760,15 @@ local function process_function(fn)
)
cparam = '&' .. cparam
errshift = 1 -- free incomplete dict on error
+ arg_free_code = 'api_luarefs_free_keydict('
+ .. cparam
+ .. ', '
+ .. string.sub(param_type, 9)
+ .. '_table);'
else
write_shifted_output(
[[
- const %s %s = nlua_pop_%s(lstate, %s&err);]],
+ const %s %s = nlua_pop_%s(lstate, %s&arena, &err);]],
param[1],
cparam,
param_type,
@@ -776,7 +785,7 @@ local function process_function(fn)
}
]], #fn.parameters - j + errshift)
- free_code[#free_code + 1] = ('api_free_%s(%s);'):format(lc_param_type, cparam)
+ free_code[#free_code + 1] = arg_free_code
cparams = cparam .. ', ' .. cparams
end
if fn.receives_channel_id then
@@ -784,7 +793,6 @@ local function process_function(fn)
end
if fn.arena_return then
cparams = cparams .. '&arena, '
- write_shifted_output(' Arena arena = ARENA_EMPTY;\n')
end
if fn.has_lua_imp then
@@ -809,6 +817,7 @@ local function process_function(fn)
local err_throw_code = [[
exit_0:
+ arena_mem_free(arena_finish(&arena));
if (ERROR_SET(&err)) {
luaL_where(lstate, 1);
if (err_param) {
@@ -829,10 +838,8 @@ exit_0:
else
return_type = fn.return_type
end
- local free_retval
- if fn.arena_return then
- free_retval = ' arena_mem_free(arena_finish(&arena));'
- else
+ local free_retval = ''
+ if not fn.arena_return then
free_retval = ' api_free_' .. return_type:lower() .. '(ret);'
end
write_shifted_output(' %s ret = %s(%s);\n', fn.return_type, fn.name, cparams)
@@ -858,6 +865,7 @@ exit_0:
write_shifted_output(' nlua_push_%s(lstate, ret, %s);\n', return_type, tostring(special))
end
+ -- NOTE: we currently assume err_throw needs nothing from arena
write_shifted_output(
[[
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index d3411850fd..a010687001 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -3027,7 +3027,7 @@ bool map_execute_lua(bool may_repeat)
Error err = ERROR_INIT;
Array args = ARRAY_DICT_INIT;
- nlua_call_ref(ref, NULL, args, false, &err);
+ nlua_call_ref(ref, NULL, args, kRetNilBool, NULL, &err);
if (err.type != kErrorTypeNone) {
semsg_multiline("E5108: %s", err.msg);
api_clear_error(&err);
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index e74ae817d0..5723ea1198 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -212,7 +212,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
Error err = ERROR_INIT;
recursive++;
- Object ret = nlua_call_ref(p->hl_def, "hl_def", args, true, &err);
+ Object ret = nlua_call_ref(p->hl_def, "hl_def", args, kRetObject, NULL, &err);
recursive--;
// TODO(bfredl): or "inherit", combine with global value?
@@ -221,7 +221,7 @@ int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
HlAttrs attrs = HLATTRS_INIT;
if (ret.type == kObjectTypeDictionary) {
fallback = false;
- Dict(highlight) dict = { 0 };
+ Dict(highlight) dict = KEYDICT_INIT;
if (api_dict_to_keydict(&dict, KeyDict_highlight_get_field,
ret.data.dictionary, &err)) {
attrs = dict2hlattrs(&dict, true, &it.link_id, &err);
@@ -1086,7 +1086,7 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
// Handle cterm attrs
if (dict->cterm.type == kObjectTypeDictionary) {
- Dict(highlight_cterm) cterm[1] = { 0 };
+ Dict(highlight_cterm) cterm[1] = KEYDICT_INIT;
if (!api_dict_to_keydict(cterm, KeyDict_highlight_cterm_get_field,
dict->cterm.data.dictionary, err)) {
return hlattrs;
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 3cf5b0e94f..423d2fa775 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -795,8 +795,8 @@ void nlua_push_Object(lua_State *lstate, const Object obj, bool special)
/// Convert lua value to string
///
/// Always pops one value from the stack.
-String nlua_pop_String(lua_State *lstate, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+String nlua_pop_String(lua_State *lstate, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
if (lua_type(lstate, -1) != LUA_TSTRING) {
lua_pop(lstate, 1);
@@ -807,7 +807,10 @@ String nlua_pop_String(lua_State *lstate, Error *err)
ret.data = (char *)lua_tolstring(lstate, -1, &(ret.size));
assert(ret.data != NULL);
- ret.data = xmemdupz(ret.data, ret.size);
+ // TODO(bfredl): it would be "nice" to just use the memory of the lua string
+ // directly, although ensuring the lifetime of such strings is a bit tricky
+ // (an API call could invoke nested lua, which triggers GC, and kaboom?)
+ ret.data = arena_memdupz(arena, ret.data, ret.size);
lua_pop(lstate, 1);
return ret;
@@ -816,8 +819,8 @@ String nlua_pop_String(lua_State *lstate, Error *err)
/// Convert lua value to integer
///
/// Always pops one value from the stack.
-Integer nlua_pop_Integer(lua_State *lstate, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+Integer nlua_pop_Integer(lua_State *lstate, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
if (lua_type(lstate, -1) != LUA_TNUMBER) {
lua_pop(lstate, 1);
@@ -840,8 +843,8 @@ Integer nlua_pop_Integer(lua_State *lstate, Error *err)
/// thus `err` is never set as any lua value can be co-erced into a lua bool
///
/// Always pops one value from the stack.
-Boolean nlua_pop_Boolean(lua_State *lstate, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+Boolean nlua_pop_Boolean(lua_State *lstate, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
const Boolean ret = lua_toboolean(lstate, -1);
lua_pop(lstate, 1);
@@ -915,7 +918,7 @@ static inline LuaTableProps nlua_check_type(lua_State *const lstate, Error *cons
/// Convert lua table to float
///
/// Always pops one value from the stack.
-Float nlua_pop_Float(lua_State *lstate, Error *err)
+Float nlua_pop_Float(lua_State *lstate, Arena *arena, Error *err)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
if (lua_type(lstate, -1) == LUA_TNUMBER) {
@@ -939,29 +942,29 @@ Float nlua_pop_Float(lua_State *lstate, Error *err)
/// @param[in] table_props nlua_traverse_table() output.
/// @param[out] err Location where error will be saved.
static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTableProps table_props,
- Error *const err)
+ Arena *arena, Error *const err)
{
- Array ret = { .size = table_props.maxidx, .items = NULL };
+ Array ret = arena_array(arena, table_props.maxidx);
- if (ret.size == 0) {
+ if (table_props.maxidx == 0) {
lua_pop(lstate, 1);
return ret;
}
- ret.items = xcalloc(ret.size, sizeof(*ret.items));
- for (size_t i = 1; i <= ret.size; i++) {
+ for (size_t i = 1; i <= table_props.maxidx; i++) {
Object val;
lua_rawgeti(lstate, -1, (int)i);
- val = nlua_pop_Object(lstate, false, err);
+ val = nlua_pop_Object(lstate, false, arena, err);
if (ERROR_SET(err)) {
- ret.size = i - 1;
lua_pop(lstate, 1);
- api_free_array(ret);
+ if (!arena) {
+ api_free_array(ret);
+ }
return (Array) { .size = 0, .items = NULL };
}
- ret.items[i - 1] = val;
+ ADD_C(ret, val);
}
lua_pop(lstate, 1);
@@ -971,15 +974,14 @@ static Array nlua_pop_Array_unchecked(lua_State *const lstate, const LuaTablePro
/// Convert lua table to array
///
/// Always pops one value from the stack.
-Array nlua_pop_Array(lua_State *lstate, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+Array nlua_pop_Array(lua_State *lstate, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 3) FUNC_ATTR_WARN_UNUSED_RESULT
{
- const LuaTableProps table_props = nlua_check_type(lstate, err,
- kObjectTypeArray);
+ const LuaTableProps table_props = nlua_check_type(lstate, err, kObjectTypeArray);
if (table_props.type != kObjectTypeArray) {
return (Array) { .size = 0, .items = NULL };
}
- return nlua_pop_Array_unchecked(lstate, table_props, err);
+ return nlua_pop_Array_unchecked(lstate, table_props, arena, err);
}
/// Convert lua table to dictionary
@@ -991,30 +993,30 @@ Array nlua_pop_Array(lua_State *lstate, Error *err)
/// @param[in] table_props nlua_traverse_table() output.
/// @param[out] err Location where error will be saved.
static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTableProps table_props,
- bool ref, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+ bool ref, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 5) FUNC_ATTR_WARN_UNUSED_RESULT
{
- Dictionary ret = { .size = table_props.string_keys_num, .items = NULL };
+ Dictionary ret = arena_dict(arena, table_props.string_keys_num);
- if (ret.size == 0) {
+ if (table_props.string_keys_num == 0) {
lua_pop(lstate, 1);
return ret;
}
- ret.items = xcalloc(ret.size, sizeof(*ret.items));
lua_pushnil(lstate);
- for (size_t i = 0; lua_next(lstate, -2) && i < ret.size;) {
+ for (size_t i = 0; lua_next(lstate, -2) && i < table_props.string_keys_num;) {
// stack: dict, key, value
if (lua_type(lstate, -2) == LUA_TSTRING) {
lua_pushvalue(lstate, -2);
// stack: dict, key, value, key
- ret.items[i].key = nlua_pop_String(lstate, err);
+ String key = nlua_pop_String(lstate, arena, err);
// stack: dict, key, value
if (!ERROR_SET(err)) {
- ret.items[i].value = nlua_pop_Object(lstate, ref, err);
+ Object value = nlua_pop_Object(lstate, ref, arena, err);
+ kv_push_c(ret, ((KeyValuePair) { .key = key, .value = value }));
// stack: dict, key
} else {
lua_pop(lstate, 1);
@@ -1022,8 +1024,9 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl
}
if (ERROR_SET(err)) {
- ret.size = i;
- api_free_dictionary(ret);
+ if (!arena) {
+ api_free_dictionary(ret);
+ }
lua_pop(lstate, 2);
// stack:
return (Dictionary) { .size = 0, .items = NULL };
@@ -1042,8 +1045,8 @@ static Dictionary nlua_pop_Dictionary_unchecked(lua_State *lstate, const LuaTabl
/// Convert lua table to dictionary
///
/// Always pops one value from the stack.
-Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Arena *arena, Error *err)
+ FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT
{
const LuaTableProps table_props = nlua_check_type(lstate, err,
kObjectTypeDictionary);
@@ -1052,7 +1055,7 @@ Dictionary nlua_pop_Dictionary(lua_State *lstate, bool ref, Error *err)
return (Dictionary) { .size = 0, .items = NULL };
}
- return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, err);
+ return nlua_pop_Dictionary_unchecked(lstate, table_props, ref, arena, err);
}
/// Helper structure for nlua_pop_Object
@@ -1064,7 +1067,8 @@ typedef struct {
/// Convert lua table to object
///
/// Always pops one value from the stack.
-Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
+Object nlua_pop_Object(lua_State *const lstate, bool ref, Arena *arena, Error *const err)
+ FUNC_ATTR_NONNULL_ARG(1, 4) FUNC_ATTR_WARN_UNUSED_RESULT
{
Object ret = NIL;
const int initial_size = lua_gettop(lstate);
@@ -1099,10 +1103,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
size_t len;
const char *s = lua_tolstring(lstate, -2, &len);
const size_t idx = cur.obj->data.dictionary.size++;
- cur.obj->data.dictionary.items[idx].key = (String) {
- .data = xmemdupz(s, len),
- .size = len,
- };
+ cur.obj->data.dictionary.items[idx].key = CBUF_TO_ARENA_STR(arena, s, len);
kvi_push(stack, cur);
cur = (ObjPopStackItem){ .obj = &cur.obj->data.dictionary.items[idx].value };
} else {
@@ -1133,7 +1134,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
case LUA_TSTRING: {
size_t len;
const char *s = lua_tolstring(lstate, -1, &len);
- *cur.obj = STRING_OBJ(((String) { .data = xmemdupz(s, len), .size = len }));
+ *cur.obj = STRING_OBJ(CBUF_TO_ARENA_STR(arena, s, len));
break;
}
case LUA_TNUMBER: {
@@ -1151,23 +1152,17 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err)
switch (table_props.type) {
case kObjectTypeArray:
- *cur.obj = ARRAY_OBJ(((Array) { .items = NULL, .size = 0, .capacity = 0 }));
+ *cur.obj = ARRAY_OBJ(((Array)ARRAY_DICT_INIT));
if (table_props.maxidx != 0) {
- cur.obj->data.array.items =
- xcalloc(table_props.maxidx,
- sizeof(cur.obj->data.array.items[0]));
- cur.obj->data.array.capacity = table_props.maxidx;
+ cur.obj->data.array = arena_array(arena, table_props.maxidx);
cur.container = true;
kvi_push(stack, cur);
}
break;
case kObjectTypeDictionary:
- *cur.obj = DICTIONARY_OBJ(((Dictionary) { .items = NULL, .size = 0, .capacity = 0 }));
+ *cur.obj = DICTIONARY_OBJ(((Dictionary)ARRAY_DICT_INIT));
if (table_props.string_keys_num != 0) {
- cur.obj->data.dictionary.items =
- xcalloc(table_props.string_keys_num,
- sizeof(cur.obj->data.dictionary.items[0]));
- cur.obj->data.dictionary.capacity = table_props.string_keys_num;
+ cur.obj->data.dictionary = arena_dict(arena, table_props.string_keys_num);
cur.container = true;
kvi_push(stack, cur);
lua_pushnil(lstate);
@@ -1219,7 +1214,9 @@ type_error:
}
kvi_destroy(stack);
if (ERROR_SET(err)) {
- api_free_object(ret);
+ if (!arena) {
+ api_free_object(ret);
+ }
ret = NIL;
lua_pop(lstate, lua_gettop(lstate) - initial_size + 1);
}
@@ -1227,14 +1224,14 @@ type_error:
return ret;
}
-LuaRef nlua_pop_LuaRef(lua_State *const lstate, Error *err)
+LuaRef nlua_pop_LuaRef(lua_State *const lstate, Arena *arena, Error *err)
{
LuaRef rv = nlua_ref_global(lstate, -1);
lua_pop(lstate, 1);
return rv;
}
-handle_T nlua_pop_handle(lua_State *lstate, Error *err)
+handle_T nlua_pop_handle(lua_State *lstate, Arena *arena, Error *err)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
handle_T ret;
@@ -1296,7 +1293,8 @@ void nlua_init_types(lua_State *const lstate)
}
// lua specific variant of api_dict_to_keydict
-void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Error *err)
+void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_opt, Arena *arena,
+ Error *err)
{
if (!lua_istable(L, -1)) {
api_set_error(err, kErrorTypeValidation, "Expected Lua table");
@@ -1323,7 +1321,7 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_
char *mem = ((char *)retval + field->ptr_off);
if (field->type == kObjectTypeNil) {
- *(Object *)mem = nlua_pop_Object(L, true, err);
+ *(Object *)mem = nlua_pop_Object(L, true, arena, err);
} else if (field->type == kObjectTypeInteger) {
if (field->is_hlgroup && lua_type(L, -1) == LUA_TSTRING) {
size_t name_len;
@@ -1331,23 +1329,23 @@ void nlua_pop_keydict(lua_State *L, void *retval, FieldHashfn hashy, char **err_
lua_pop(L, 1);
*(Integer *)mem = name_len > 0 ? syn_check_group(name, name_len) : 0;
} else {
- *(Integer *)mem = nlua_pop_Integer(L, err);
+ *(Integer *)mem = nlua_pop_Integer(L, arena, err);
}
} else if (field->type == kObjectTypeBoolean) {
*(Boolean *)mem = nlua_pop_Boolean_strict(L, err);
} else if (field->type == kObjectTypeString) {
- *(String *)mem = nlua_pop_String(L, err);
+ *(String *)mem = nlua_pop_String(L, arena, err);
} else if (field->type == kObjectTypeFloat) {
- *(Float *)mem = nlua_pop_Float(L, err);
+ *(Float *)mem = nlua_pop_Float(L, arena, err);
} else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
|| field->type == kObjectTypeTabpage) {
- *(handle_T *)mem = nlua_pop_handle(L, err);
+ *(handle_T *)mem = nlua_pop_handle(L, arena, err);
} else if (field->type == kObjectTypeArray) {
- *(Array *)mem = nlua_pop_Array(L, err);
+ *(Array *)mem = nlua_pop_Array(L, arena, err);
} else if (field->type == kObjectTypeDictionary) {
- *(Dictionary *)mem = nlua_pop_Dictionary(L, false, err);
+ *(Dictionary *)mem = nlua_pop_Dictionary(L, false, arena, err);
} else if (field->type == kObjectTypeLuaRef) {
- *(LuaRef *)mem = nlua_pop_LuaRef(L, err);
+ *(LuaRef *)mem = nlua_pop_LuaRef(L, arena, err);
} else {
abort();
}
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 85d614fe0d..be55bde202 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -311,7 +311,10 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
lua_pop(lstate, 1);
Error err = ERROR_INIT;
- const Array pat = nlua_pop_Array(lstate, &err);
+ // TODO(bfredl): we could use an arena here for both "pat" and "ret", but then
+ // we need a path to not use the freelist but a private block local to the thread.
+ // We do not want mutex contentionery for the main arena freelist.
+ const Array pat = nlua_pop_Array(lstate, NULL, &err);
if (ERROR_SET(&err)) {
luaL_where(lstate, 1);
lua_pushstring(lstate, err.msg);
@@ -1242,13 +1245,13 @@ static int nlua_rpc(lua_State *lstate, bool request)
const char *name = luaL_checklstring(lstate, 2, &name_len);
int nargs = lua_gettop(lstate) - 2;
Error err = ERROR_INIT;
- Array args = ARRAY_DICT_INIT;
+ Arena arena = ARENA_EMPTY;
+ Array args = arena_array(&arena, (size_t)nargs);
for (int i = 0; i < nargs; i++) {
lua_pushvalue(lstate, i + 3);
- ADD(args, nlua_pop_Object(lstate, false, &err));
+ ADD(args, nlua_pop_Object(lstate, false, &arena, &err));
if (ERROR_SET(&err)) {
- api_free_array(args);
goto check_err;
}
}
@@ -1265,10 +1268,11 @@ static int nlua_rpc(lua_State *lstate, bool request)
api_set_error(&err, kErrorTypeValidation,
"Invalid channel: %" PRIu64, chan_id);
}
- api_free_array(args); // TODO(bfredl): no
}
check_err:
+ arena_mem_free(arena_finish(&arena));
+
if (ERROR_SET(&err)) {
lua_pushstring(lstate, err.msg);
api_clear_error(&err);
@@ -1541,10 +1545,12 @@ int typval_exec_lua_callable(LuaRef lua_cb, int argcount, typval_T *argvars, typ
///
/// @param[in] str String to execute.
/// @param[in] args array of ... args
+/// @param[in] mode Whether and how the the return value should be converted to Object
+/// @param[in] arena can be NULL, then nested allocations are used
/// @param[out] err Location where error will be saved.
///
/// @return Return value of the execution.
-Object nlua_exec(const String str, const Array args, Error *err)
+Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *arena, Error *err)
{
lua_State *const lstate = global_lstate;
@@ -1568,7 +1574,7 @@ Object nlua_exec(const String str, const Array args, Error *err)
return NIL;
}
- return nlua_pop_Object(lstate, false, err);
+ return nlua_call_pop_retval(lstate, mode, arena, err);
}
bool nlua_ref_is_function(LuaRef ref)
@@ -1589,12 +1595,12 @@ bool nlua_ref_is_function(LuaRef ref)
/// @param ref the reference to call (not consumed)
/// @param name if non-NULL, sent to callback as first arg
/// if NULL, only args are used
-/// @param retval if true, convert return value to Object
-/// if false, only check if return value is truthy
+/// @param mode Whether and how the the return value should be converted to Object
+/// @param arena can be NULL, then nested allocations are used
/// @param err Error details, if any (if NULL, errors are echoed)
-/// @return Return value of function, if retval was set. Otherwise
-/// BOOLEAN_OBJ(true) or NIL.
-Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Error *err)
+/// @return Return value of function, as per mode
+Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode, Arena *arena,
+ Error *err)
{
lua_State *const lstate = global_lstate;
nlua_pushref(lstate, ref);
@@ -1620,18 +1626,34 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, bool retval, Erro
return NIL;
}
- if (retval) {
- Error dummy = ERROR_INIT;
- if (err == NULL) {
- err = &dummy;
- }
- return nlua_pop_Object(lstate, false, err);
- } else {
- bool value = lua_toboolean(lstate, -1);
+ return nlua_call_pop_retval(lstate, mode, arena, err);
+}
+
+static Object nlua_call_pop_retval(lua_State *lstate, LuaRetMode mode, Arena *arena, Error *err)
+{
+ if (lua_isnil(lstate, -1)) {
+ lua_pop(lstate, 1);
+ return NIL;
+ }
+ Error dummy = ERROR_INIT;
+
+ switch (mode) {
+ case kRetNilBool: {
+ bool bool_value = lua_toboolean(lstate, -1);
+ lua_pop(lstate, 1);
+
+ return BOOLEAN_OBJ(bool_value);
+ }
+ case kRetLuaref: {
+ LuaRef ref = nlua_ref_global(lstate, -1);
lua_pop(lstate, 1);
- return value ? BOOLEAN_OBJ(true) : NIL;
+ return LUAREF_OBJ(ref);
+ }
+ case kRetObject:
+ return nlua_pop_Object(lstate, false, arena, err ? err : &dummy);
}
+ UNREACHABLE;
}
/// check if the current execution context is safe for calling deferred API
@@ -1930,13 +1952,14 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results)
*num_results = 0;
*results = NULL;
- int prefix_len = (int)nlua_pop_Integer(lstate, &err);
+ Arena arena = ARENA_EMPTY;
+ int prefix_len = (int)nlua_pop_Integer(lstate, &arena, &err);
if (ERROR_SET(&err)) {
ret = FAIL;
goto cleanup;
}
- Array completions = nlua_pop_Array(lstate, &err);
+ Array completions = nlua_pop_Array(lstate, &arena, &err);
if (ERROR_SET(&err)) {
ret = FAIL;
goto cleanup_array;
@@ -1960,7 +1983,7 @@ int nlua_expand_pat(expand_T *xp, char *pat, int *num_results, char ***results)
*num_results = result_array.ga_len;
cleanup_array:
- api_free_array(completions);
+ arena_mem_free(arena_finish(&arena));
cleanup:
@@ -2354,13 +2377,10 @@ bool nlua_func_exists(const char *lua_funcname)
vim_snprintf(str, length, "return %s", lua_funcname);
ADD_C(args, CSTR_AS_OBJ(str));
Error err = ERROR_INIT;
- Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args, &err);
+ Object result = NLUA_EXEC_STATIC("return type(loadstring(...)()) == 'function'", args,
+ kRetNilBool, NULL, &err);
xfree(str);
api_clear_error(&err);
- if (result.type != kObjectTypeBoolean) {
- api_free_object(result);
- return false;
- }
- return result.data.boolean;
+ return LUARET_TRUTHY(result);
}
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 0b4623cbd3..ebcd62122f 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -24,7 +24,8 @@ typedef struct {
#endif
} nlua_ref_state_t;
-#define NLUA_EXEC_STATIC(cstr, arg, err) nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, err)
+#define NLUA_EXEC_STATIC(cstr, arg, mode, arena, err) \
+ nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, mode, arena, err)
#define NLUA_CLEAR_REF(x) \
do { \
@@ -35,6 +36,16 @@ typedef struct {
} \
} while (0)
+typedef enum {
+ kRetObject, ///< any object, but doesn't preserve nested luarefs
+ kRetNilBool, ///< NIL preserved as such, other values return their booleanness
+ ///< Should also be used when return value is ignored, as it is allocation-free
+ kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL)
+} LuaRetMode;
+
+/// To use with kRetNilBool for quick thuthyness check
+#define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true)
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/executor.h.generated.h"
#endif
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index 5285c1187d..f5a5949ab6 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -209,7 +209,8 @@ static bool check_xdiff_opt(ObjectType actType, ObjectType expType, const char *
static NluaXdiffMode process_xdl_diff_opts(lua_State *lstate, xdemitconf_t *cfg, xpparam_t *params,
int64_t *linematch, Error *err)
{
- const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, err);
+ // TODO: this is very much a keydict..
+ const DictionaryOf(LuaRef) opts = nlua_pop_Dictionary(lstate, true, NULL, err);
NluaXdiffMode mode = kNluaXdiffModeUnified;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index f2893dc9e3..d712a45b81 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -940,20 +940,20 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
}
Array args = ARRAY_DICT_INIT;
+ kv_resize(args, (size_t)(argc - remote_args));
for (int t_argc = remote_args; t_argc < argc; t_argc++) {
- String arg_s = cstr_to_string(argv[t_argc]);
- ADD(args, STRING_OBJ(arg_s));
+ ADD_C(args, CSTR_AS_OBJ(argv[t_argc]));
}
Error err = ERROR_INIT;
- Array a = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(a, 4);
ADD(a, INTEGER_OBJ((int)chan));
- ADD(a, CSTR_TO_OBJ(server_addr));
- ADD(a, CSTR_TO_OBJ(connect_error));
+ ADD(a, CSTR_AS_OBJ(server_addr));
+ ADD(a, CSTR_AS_OBJ(connect_error));
ADD(a, ARRAY_OBJ(args));
String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)");
- Object o = nlua_exec(s, a, &err);
- api_free_array(a);
+ Object o = nlua_exec(s, a, kRetObject, NULL, &err);
+ kv_destroy(args);
if (ERROR_SET(&err)) {
fprintf(stderr, "%s\n", err.msg);
os_exit(2);
@@ -2085,7 +2085,7 @@ static void do_exrc_initialization(void)
str = nlua_read_secure(VIMRC_LUA_FILE);
if (str != NULL) {
Error err = ERROR_INIT;
- nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, &err);
+ nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err);
xfree(str);
if (ERROR_SET(&err)) {
semsg("Error detected while processing %s:", VIMRC_LUA_FILE);
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index cb7b8aab33..59022e8be6 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -1644,7 +1644,7 @@ char *eval_map_expr(mapblock_T *mp, int c)
if (mp->m_luaref != LUA_NOREF) {
Error err = ERROR_INIT;
Array args = ARRAY_DICT_INIT;
- Object ret = nlua_call_ref(mp->m_luaref, NULL, args, true, &err);
+ Object ret = nlua_call_ref(mp->m_luaref, NULL, args, kRetObject, NULL, &err);
if (ret.type == kObjectTypeString) {
p = string_to_cstr(ret.data.string);
}
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 36fa7e77fc..2224403fa2 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -192,7 +192,6 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
if (!(channel = find_rpc_channel(id))) {
api_set_error(err, kErrorTypeException, "Invalid channel: %" PRIu64, id);
- api_free_array(args);
return NIL;
}
@@ -201,7 +200,6 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
uint32_t request_id = rpc->next_request_id++;
// Send the msgpack-rpc request
send_request(channel, request_id, method_name, args);
- api_free_array(args);
// Push the frame
ChannelCallFrame frame = { request_id, false, false, NIL, NULL };
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 98e9d6c9e6..44fcc29ad1 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -592,13 +592,13 @@ Array runtime_inspect(void)
return rv;
}
-ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all)
+ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all, Arena *arena)
{
int ref;
RuntimeSearchPath path = runtime_search_path_get_cached(&ref);
static char buf[MAXPATHL];
- ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf);
+ ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, path, buf, sizeof buf, arena);
runtime_search_path_unref(path, &ref);
return rv;
@@ -610,15 +610,16 @@ ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all)
uv_mutex_lock(&runtime_search_path_mutex);
static char buf[MAXPATHL];
ArrayOf(String) rv = runtime_get_named_common(lua, pat, all, runtime_search_path_thread,
- buf, sizeof buf);
+ buf, sizeof buf, NULL);
uv_mutex_unlock(&runtime_search_path_mutex);
return rv;
}
static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
- RuntimeSearchPath path, char *buf, size_t buf_len)
+ RuntimeSearchPath path, char *buf, size_t buf_len,
+ Arena *arena)
{
- ArrayOf(String) rv = ARRAY_DICT_INIT;
+ ArrayOf(String) rv = arena_array(arena, kv_size(path) * pat.size);
for (size_t i = 0; i < kv_size(path); i++) {
SearchPathItem *item = &kv_A(path, i);
if (lua) {
@@ -638,7 +639,7 @@ static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
item->path, pat_item.data.string.data);
if (size < buf_len) {
if (os_file_is_readable(buf)) {
- ADD(rv, CSTR_TO_OBJ(buf));
+ ADD_C(rv, CSTR_TO_ARENA_OBJ(arena, buf));
if (!all) {
goto done;
}
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index debd2fe511..3708a87ddf 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -710,8 +710,8 @@ void ui_call_event(char *name, Array args)
bool handled = false;
map_foreach_value(&ui_event_cbs, event_cb, {
Error err = ERROR_INIT;
- Object res = nlua_call_ref(event_cb->cb, name, args, false, &err);
- if (res.type == kObjectTypeBoolean && res.data.boolean == true) {
+ Object res = nlua_call_ref(event_cb->cb, name, args, kRetNilBool, NULL, &err);
+ if (LUARET_TRUTHY(res)) {
handled = true;
}
if (ERROR_SET(&err)) {
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index d4dec7db83..2bb5ee16b3 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -170,7 +170,7 @@ Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Er
static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
{
Error err = ERROR_INIT;
- Dict(highlight) dict = { 0 };
+ Dict(highlight) dict = KEYDICT_INIT;
if (!api_dict_to_keydict(&dict, KeyDict_highlight_get_field, d, &err)) {
// TODO(bfredl): log "err"
return HLATTRS_INIT;
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 22101531ef..8568f67aa1 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -2681,9 +2681,9 @@ void list_in_columns(char **items, int size, int current)
void list_lua_version(void)
{
- char *code = "return ((jit and jit.version) and jit.version or _VERSION)";
Error err = ERROR_INIT;
- Object ret = nlua_exec(cstr_as_string(code), (Array)ARRAY_DICT_INIT, &err);
+ Object ret = NLUA_EXEC_STATIC("return ((jit and jit.version) and jit.version or _VERSION)",
+ (Array)ARRAY_DICT_INIT, kRetObject, NULL, &err);
assert(!ERROR_SET(&err));
assert(ret.type == kObjectTypeString);
msg(ret.data.string.data, 0);
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index c7490756d4..a262d239e8 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -3543,6 +3543,18 @@ describe('lua stdlib', function()
]])
)
end)
+
+ it('can return values by reference', function()
+ eq(
+ { 4, 7 },
+ exec_lua [[
+ local val = {4, 10}
+ local ref = vim.api.nvim_buf_call(0, function() return val end)
+ ref[2] = 7
+ return val
+ ]]
+ )
+ end)
end)
describe('vim.api.nvim_win_call', function()
@@ -3640,6 +3652,18 @@ describe('lua stdlib', function()
|
]]
end)
+
+ it('can return values by reference', function()
+ eq(
+ { 7, 10 },
+ exec_lua [[
+ local val = {4, 10}
+ local ref = vim.api.nvim_win_call(0, function() return val end)
+ ref[1] = 7
+ return val
+ ]]
+ )
+ end)
end)
describe('vim.iconv', function()