fix(lua): only free luarefs when returning from API (#28373)

This commit is contained in:
zeertzjq 2024-04-17 05:44:06 +08:00 committed by GitHub
parent 8e5c48b08d
commit f150b62423
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 81 additions and 35 deletions

View File

@ -913,7 +913,7 @@ exit_0:
write_shifted_output(string.format( write_shifted_output(string.format(
[[ [[
if (lua_gettop(lstate) == 0) { if (lua_gettop(lstate) == 0) {
nlua_push_%s(lstate, %sret, true); nlua_push_%s(lstate, %sret, kNluaPushSpecial | kNluaPushFreeRefs);
} }
]], ]],
return_type, return_type,
@ -927,10 +927,10 @@ exit_0:
else else
local special = (fn.since ~= nil and fn.since < 11) local special = (fn.since ~= nil and fn.since < 11)
write_shifted_output( write_shifted_output(
' nlua_push_%s(lstate, %sret, %s);\n', ' nlua_push_%s(lstate, %sret, %s | kNluaPushFreeRefs);\n',
return_type, return_type,
ret_mode, ret_mode,
tostring(special) special and 'kNluaPushSpecial' or '0'
) )
end end

View File

@ -597,9 +597,9 @@ static bool typval_conv_special = false;
/// @param[in] tv typval_T to convert. /// @param[in] tv typval_T to convert.
/// ///
/// @return true in case of success, false otherwise. /// @return true in case of success, false otherwise.
bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) bool nlua_push_typval(lua_State *lstate, typval_T *const tv, int flags)
{ {
typval_conv_special = special; typval_conv_special = (flags & kNluaPushSpecial);
const int initial_size = lua_gettop(lstate); const int initial_size = lua_gettop(lstate);
if (!lua_checkstack(lstate, initial_size + 2)) { if (!lua_checkstack(lstate, initial_size + 2)) {
@ -662,7 +662,7 @@ static inline void nlua_create_typed_table(lua_State *lstate, const size_t narr,
/// Convert given String to lua string /// Convert given String to lua string
/// ///
/// Leaves converted string on top of the stack. /// Leaves converted string on top of the stack.
void nlua_push_String(lua_State *lstate, const String s, bool special) void nlua_push_String(lua_State *lstate, const String s, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
lua_pushlstring(lstate, s.data, s.size); lua_pushlstring(lstate, s.data, s.size);
@ -671,7 +671,7 @@ void nlua_push_String(lua_State *lstate, const String s, bool special)
/// Convert given Integer to lua number /// Convert given Integer to lua number
/// ///
/// Leaves converted number on top of the stack. /// Leaves converted number on top of the stack.
void nlua_push_Integer(lua_State *lstate, const Integer n, bool special) void nlua_push_Integer(lua_State *lstate, const Integer n, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
lua_pushnumber(lstate, (lua_Number)n); lua_pushnumber(lstate, (lua_Number)n);
@ -680,10 +680,10 @@ void nlua_push_Integer(lua_State *lstate, const Integer n, bool special)
/// Convert given Float to lua table /// Convert given Float to lua table
/// ///
/// Leaves converted table on top of the stack. /// Leaves converted table on top of the stack.
void nlua_push_Float(lua_State *lstate, const Float f, bool special) void nlua_push_Float(lua_State *lstate, const Float f, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (special) { if (flags & kNluaPushSpecial) {
nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat); nlua_create_typed_table(lstate, 0, 1, kObjectTypeFloat);
nlua_push_val_idx(lstate); nlua_push_val_idx(lstate);
lua_pushnumber(lstate, (lua_Number)f); lua_pushnumber(lstate, (lua_Number)f);
@ -696,7 +696,7 @@ void nlua_push_Float(lua_State *lstate, const Float f, bool special)
/// Convert given Float to lua boolean /// Convert given Float to lua boolean
/// ///
/// Leaves converted value on top of the stack. /// Leaves converted value on top of the stack.
void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special) void nlua_push_Boolean(lua_State *lstate, const Boolean b, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
lua_pushboolean(lstate, b); lua_pushboolean(lstate, b);
@ -705,21 +705,21 @@ void nlua_push_Boolean(lua_State *lstate, const Boolean b, bool special)
/// Convert given Dictionary to lua table /// Convert given Dictionary to lua table
/// ///
/// Leaves converted table on top of the stack. /// Leaves converted table on top of the stack.
void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special) void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (dict.size == 0 && special) { if (dict.size == 0 && (flags & kNluaPushSpecial)) {
nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary); nlua_create_typed_table(lstate, 0, 0, kObjectTypeDictionary);
} else { } else {
lua_createtable(lstate, 0, (int)dict.size); lua_createtable(lstate, 0, (int)dict.size);
if (dict.size == 0 && !special) { if (dict.size == 0 && !(flags & kNluaPushSpecial)) {
nlua_pushref(lstate, nlua_global_refs->empty_dict_ref); nlua_pushref(lstate, nlua_global_refs->empty_dict_ref);
lua_setmetatable(lstate, -2); lua_setmetatable(lstate, -2);
} }
} }
for (size_t i = 0; i < dict.size; i++) { for (size_t i = 0; i < dict.size; i++) {
nlua_push_String(lstate, dict.items[i].key, special); nlua_push_String(lstate, dict.items[i].key, flags);
nlua_push_Object(lstate, &dict.items[i].value, special); nlua_push_Object(lstate, &dict.items[i].value, flags);
lua_rawset(lstate, -3); lua_rawset(lstate, -3);
} }
} }
@ -727,18 +727,18 @@ void nlua_push_Dictionary(lua_State *lstate, const Dictionary dict, bool special
/// Convert given Array to lua table /// Convert given Array to lua table
/// ///
/// Leaves converted table on top of the stack. /// Leaves converted table on top of the stack.
void nlua_push_Array(lua_State *lstate, const Array array, bool special) void nlua_push_Array(lua_State *lstate, const Array array, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
lua_createtable(lstate, (int)array.size, 0); lua_createtable(lstate, (int)array.size, 0);
for (size_t i = 0; i < array.size; i++) { for (size_t i = 0; i < array.size; i++) {
nlua_push_Object(lstate, &array.items[i], special); nlua_push_Object(lstate, &array.items[i], flags);
lua_rawseti(lstate, -2, (int)i + 1); lua_rawseti(lstate, -2, (int)i + 1);
} }
} }
#define GENERATE_INDEX_FUNCTION(type) \ #define GENERATE_INDEX_FUNCTION(type) \
void nlua_push_##type(lua_State *lstate, const type item, bool special) \ void nlua_push_##type(lua_State *lstate, const type item, int flags) \
FUNC_ATTR_NONNULL_ALL \ FUNC_ATTR_NONNULL_ALL \
{ \ { \
lua_pushnumber(lstate, (lua_Number)(item)); \ lua_pushnumber(lstate, (lua_Number)(item)); \
@ -753,12 +753,12 @@ GENERATE_INDEX_FUNCTION(Tabpage)
/// Convert given Object to lua value /// Convert given Object to lua value
/// ///
/// Leaves converted value on top of the stack. /// Leaves converted value on top of the stack.
void nlua_push_Object(lua_State *lstate, Object *obj, bool special) void nlua_push_Object(lua_State *lstate, Object *obj, int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
switch (obj->type) { switch (obj->type) {
case kObjectTypeNil: case kObjectTypeNil:
if (special) { if (flags & kNluaPushSpecial) {
lua_pushnil(lstate); lua_pushnil(lstate);
} else { } else {
nlua_pushref(lstate, nlua_global_refs->nil_ref); nlua_pushref(lstate, nlua_global_refs->nil_ref);
@ -766,13 +766,15 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special)
break; break;
case kObjectTypeLuaRef: { case kObjectTypeLuaRef: {
nlua_pushref(lstate, obj->data.luaref); nlua_pushref(lstate, obj->data.luaref);
api_free_luaref(obj->data.luaref); if (flags & kNluaPushFreeRefs) {
obj->data.luaref = LUA_NOREF; api_free_luaref(obj->data.luaref);
obj->data.luaref = LUA_NOREF;
}
break; break;
} }
#define ADD_TYPE(type, data_key) \ #define ADD_TYPE(type, data_key) \
case kObjectType##type: { \ case kObjectType##type: { \
nlua_push_##type(lstate, obj->data.data_key, special); \ nlua_push_##type(lstate, obj->data.data_key, flags); \
break; \ break; \
} }
ADD_TYPE(Boolean, boolean) ADD_TYPE(Boolean, boolean)
@ -784,7 +786,7 @@ void nlua_push_Object(lua_State *lstate, Object *obj, bool special)
#undef ADD_TYPE #undef ADD_TYPE
#define ADD_REMOTE_TYPE(type) \ #define ADD_REMOTE_TYPE(type) \
case kObjectType##type: { \ case kObjectType##type: { \
nlua_push_##type(lstate, (type)obj->data.integer, special); \ nlua_push_##type(lstate, (type)obj->data.integer, flags); \
break; \ break; \
} }
ADD_REMOTE_TYPE(Buffer) ADD_REMOTE_TYPE(Buffer)
@ -1380,7 +1382,7 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table)
lua_pushstring(L, field->str); lua_pushstring(L, field->str);
if (field->type == kObjectTypeNil) { if (field->type == kObjectTypeNil) {
nlua_push_Object(L, (Object *)mem, false); nlua_push_Object(L, (Object *)mem, 0);
} else if (field->type == kObjectTypeInteger) { } else if (field->type == kObjectTypeInteger) {
lua_pushinteger(L, *(Integer *)mem); lua_pushinteger(L, *(Integer *)mem);
} else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow } else if (field->type == kObjectTypeBuffer || field->type == kObjectTypeWindow
@ -1391,11 +1393,11 @@ void nlua_push_keydict(lua_State *L, void *value, KeySetLink *table)
} else if (field->type == kObjectTypeBoolean) { } else if (field->type == kObjectTypeBoolean) {
lua_pushboolean(L, *(Boolean *)mem); lua_pushboolean(L, *(Boolean *)mem);
} else if (field->type == kObjectTypeString) { } else if (field->type == kObjectTypeString) {
nlua_push_String(L, *(String *)mem, false); nlua_push_String(L, *(String *)mem, 0);
} else if (field->type == kObjectTypeArray) { } else if (field->type == kObjectTypeArray) {
nlua_push_Array(L, *(Array *)mem, false); nlua_push_Array(L, *(Array *)mem, 0);
} else if (field->type == kObjectTypeDictionary) { } else if (field->type == kObjectTypeDictionary) {
nlua_push_Dictionary(L, *(Dictionary *)mem, false); nlua_push_Dictionary(L, *(Dictionary *)mem, 0);
} else if (field->type == kObjectTypeLuaRef) { } else if (field->type == kObjectTypeLuaRef) {
nlua_pushref(L, *(LuaRef *)mem); nlua_pushref(L, *(LuaRef *)mem);
} else { } else {

View File

@ -9,6 +9,12 @@
#define nlua_pop_Window nlua_pop_handle #define nlua_pop_Window nlua_pop_handle
#define nlua_pop_Tabpage nlua_pop_handle #define nlua_pop_Tabpage nlua_pop_handle
/// Flags for nlua_push_*() functions.
enum {
kNluaPushSpecial = 0x01, ///< Use lua-special-tbl when necessary
kNluaPushFreeRefs = 0x02, ///< Free luarefs to elide an api_luarefs_free_*() later
};
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/converter.h.generated.h" # include "lua/converter.h.generated.h"
#endif #endif

View File

@ -103,7 +103,7 @@ typedef struct {
if (args[i].v_type == VAR_UNKNOWN) { \ if (args[i].v_type == VAR_UNKNOWN) { \
lua_pushnil(lstate); \ lua_pushnil(lstate); \
} else { \ } else { \
nlua_push_typval(lstate, &args[i], special); \ nlua_push_typval(lstate, &args[i], (special) ? kNluaPushSpecial : 0); \
} \ } \
} }
@ -325,7 +325,7 @@ static int nlua_thr_api_nvim__get_runtime(lua_State *lstate)
} }
ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all); ArrayOf(String) ret = runtime_get_named_thread(is_lua, pat, all);
nlua_push_Array(lstate, ret, true); nlua_push_Array(lstate, ret, kNluaPushSpecial);
api_free_array(ret); api_free_array(ret);
api_free_array(pat); api_free_array(pat);
@ -1210,7 +1210,7 @@ int nlua_call(lua_State *lstate)
}); });
if (!ERROR_SET(&err)) { if (!ERROR_SET(&err)) {
nlua_push_typval(lstate, &rettv, false); nlua_push_typval(lstate, &rettv, 0);
} }
tv_clear(&rettv); tv_clear(&rettv);
@ -1261,7 +1261,7 @@ static int nlua_rpc(lua_State *lstate, bool request)
ArenaMem res_mem = NULL; ArenaMem res_mem = NULL;
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err); Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) { if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, &result, false); nlua_push_Object(lstate, &result, 0);
arena_mem_free(res_mem); arena_mem_free(res_mem);
} }
} else { } else {
@ -1564,7 +1564,7 @@ Object nlua_exec(const String str, const Array args, LuaRetMode mode, Arena *are
} }
for (size_t i = 0; i < args.size; i++) { for (size_t i = 0; i < args.size; i++) {
nlua_push_Object(lstate, &args.items[i], false); nlua_push_Object(lstate, &args.items[i], 0);
} }
if (nlua_pcall(lstate, (int)args.size, 1)) { if (nlua_pcall(lstate, (int)args.size, 1)) {
@ -1611,7 +1611,7 @@ Object nlua_call_ref(LuaRef ref, const char *name, Array args, LuaRetMode mode,
nargs++; nargs++;
} }
for (size_t i = 0; i < args.size; i++) { for (size_t i = 0; i < args.size; i++) {
nlua_push_Object(lstate, &args.items[i], false); nlua_push_Object(lstate, &args.items[i], 0);
} }
if (nlua_pcall(lstate, nargs, 1)) { if (nlua_pcall(lstate, nargs, 1)) {

View File

@ -449,7 +449,7 @@ int nlua_getvar(lua_State *lstate)
if (di == NULL) { if (di == NULL) {
return 0; // nil return 0; // nil
} }
nlua_push_typval(lstate, &di->di_tv, false); nlua_push_typval(lstate, &di->di_tv, 0);
return 1; return 1;
} }

View File

@ -355,6 +355,44 @@ describe('autocmd api', function()
test({ 'list' }) test({ 'list' })
test({ foo = 'bar' }) test({ foo = 'bar' })
end) end)
it('function in arbitrary data is passed to all autocmds #28353', function()
eq(
1303,
exec_lua([[
local res = 1
local fun = function(m, x)
res = res * m + x
end
local group = vim.api.nvim_create_augroup('MyTest', { clear = false })
vim.api.nvim_create_autocmd('User', {
group = group,
callback = function(payload)
payload.data.fun(10, payload.data.x)
end,
pattern = 'MyEvent',
})
vim.api.nvim_create_autocmd('User', {
group = group,
callback = function(payload)
payload.data.fun(100, payload.data.x)
end,
pattern = 'MyEvent',
})
vim.api.nvim_exec_autocmds('User', {
group = group,
pattern = 'MyEvent',
data = { x = 3, fun = fun },
})
return res
]])
)
end)
end) end)
describe('nvim_get_autocmds', function() describe('nvim_get_autocmds', function()