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.
This commit is contained in:
bfredl 2024-02-11 15:46:14 +01:00
parent 89135cff03
commit 0353dd3029
33 changed files with 318 additions and 230 deletions

View File

@ -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()|

View File

@ -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*

View File

@ -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;

View File

@ -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');

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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--;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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(
[[

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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 value ? BOOLEAN_OBJ(true) : NIL;
return BOOLEAN_OBJ(bool_value);
}
case kRetLuaref: {
LuaRef ref = nlua_ref_global(lstate, -1);
lua_pop(lstate, 1);
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);
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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 };

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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;

View File

@ -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);

View File

@ -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()