summaryrefslogtreecommitdiffstats
path: root/src/if_lua.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-06-25 19:27:56 +0200
committerBram Moolenaar <Bram@vim.org>2020-06-25 19:27:56 +0200
commit801ab069341c8652680d63c174530fd4feb2911e (patch)
treeff8d365fe4c9ea1dbd5b5918b3a58568a77ec18e /src/if_lua.c
parent832adf9bb8cd39d8e982d8a35ed8a6d39b974494 (diff)
patch 8.2.1054: not so easy to pass a lua function to Vimv8.2.1054
Problem: Not so easy to pass a lua function to Vim. Solution: Convert a Lua function and closure to a Vim funcref. (Prabir Shrestha, closes #6246)
Diffstat (limited to 'src/if_lua.c')
-rw-r--r--src/if_lua.c101
1 files changed, 100 insertions, 1 deletions
diff --git a/src/if_lua.c b/src/if_lua.c
index 75231b4c27..ce0901a20e 100644
--- a/src/if_lua.c
+++ b/src/if_lua.c
@@ -35,6 +35,13 @@ typedef struct {
} luaV_Funcref;
typedef void (*msgfunc_T)(char_u *);
+typedef struct {
+ int lua_funcref; // ref to a lua func
+ int lua_tableref; // ref to a lua table if metatable else LUA_NOREF. used
+ // for __call
+ lua_State *L;
+} luaV_CFuncState;
+
static const char LUAVIM_DICT[] = "dict";
static const char LUAVIM_LIST[] = "list";
static const char LUAVIM_BLOB[] = "blob";
@@ -45,6 +52,8 @@ static const char LUAVIM_FREE[] = "luaV_free";
static const char LUAVIM_LUAEVAL[] = "luaV_luaeval";
static const char LUAVIM_SETREF[] = "luaV_setref";
+static const char LUA___CALL[] = "__call";
+
// most functions are closures with a cache table as first upvalue;
// get/setudata manage references to vim userdata in cache table through
// object pointers (light userdata)
@@ -64,7 +73,7 @@ static const char LUAVIM_SETREF[] = "luaV_setref";
#define luaV_emsg(L) luaV_msgfunc((L), (msgfunc_T) emsg)
#define luaV_checktypval(L, a, v, msg) \
do { \
- if (luaV_totypval(L, a, v) == FAIL) \
+ if (luaV_totypval(L, a, v) == FAIL) \
luaL_error(L, msg ": cannot convert value"); \
} while (0)
@@ -72,6 +81,8 @@ static luaV_List *luaV_pushlist(lua_State *L, list_T *lis);
static luaV_Dict *luaV_pushdict(lua_State *L, dict_T *dic);
static luaV_Blob *luaV_pushblob(lua_State *L, blob_T *blo);
static luaV_Funcref *luaV_pushfuncref(lua_State *L, char_u *name);
+static int luaV_call_lua_func(int argcount, typval_T *argvars, typval_T *rettv, void *state);
+static void luaV_call_lua_func_free(void *state);
#if LUA_VERSION_NUM <= 501
#define luaV_openlib(L, l, n) luaL_openlib(L, NULL, l, n)
@@ -591,6 +602,45 @@ luaV_totypval(lua_State *L, int pos, typval_T *tv)
tv->vval.v_number = (varnumber_T) lua_tointeger(L, pos);
#endif
break;
+ case LUA_TFUNCTION:
+ {
+ char_u *name;
+ lua_pushvalue(L, pos);
+ luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState);
+ state->lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX);
+ state->L = L;
+ state->lua_tableref = LUA_NOREF;
+ name = register_cfunc(&luaV_call_lua_func,
+ &luaV_call_lua_func_free, state);
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = vim_strsave(name);
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ lua_pushvalue(L, pos);
+ int lua_tableref = luaL_ref(L, LUA_REGISTRYINDEX);
+ if (lua_getmetatable(L, pos)) {
+ lua_getfield(L, -1, LUA___CALL);
+ if (lua_isfunction(L, -1)) {
+ char_u *name;
+ int lua_funcref = luaL_ref(L, LUA_REGISTRYINDEX);
+ luaV_CFuncState *state = ALLOC_CLEAR_ONE(luaV_CFuncState);
+ state->lua_funcref = lua_funcref;
+ state->L = L;
+ state->lua_tableref = lua_tableref;
+ name = register_cfunc(&luaV_call_lua_func,
+ &luaV_call_lua_func_free, state);
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = vim_strsave(name);
+ break;
+ }
+ }
+ tv->v_type = VAR_NUMBER;
+ tv->vval.v_number = 0;
+ status = FAIL;
+ break;
+ }
case LUA_TUSERDATA:
{
void *p = lua_touserdata(L, pos);
@@ -2415,4 +2465,53 @@ update_package_paths_in_lua()
}
}
+/*
+ * Native C function callback
+ */
+ static int
+luaV_call_lua_func(
+ int argcount,
+ typval_T *argvars,
+ typval_T *rettv,
+ void *state)
+{
+ int i;
+ int luaargcount = argcount;
+ luaV_CFuncState *funcstate = (luaV_CFuncState*)state;
+ lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_funcref);
+
+ if (funcstate->lua_tableref != LUA_NOREF)
+ {
+ // First arg for metatable __call method is a table
+ luaargcount += 1;
+ lua_rawgeti(funcstate->L, LUA_REGISTRYINDEX, funcstate->lua_tableref);
+ }
+
+ for (i = 0; i < argcount; ++i)
+ luaV_pushtypval(funcstate->L, &argvars[i]);
+
+ if (lua_pcall(funcstate->L, luaargcount, 1, 0))
+ {
+ luaV_emsg(funcstate->L);
+ return FCERR_OTHER;
+ }
+
+ luaV_checktypval(funcstate->L, -1, rettv, "get return value");
+ return FCERR_NONE;
+}
+
+/*
+ * Free up any lua references held by the func state.
+ */
+ static void
+luaV_call_lua_func_free(void *state)
+{
+ luaV_CFuncState *funcstate = (luaV_CFuncState*)state;
+ luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_funcref);
+ funcstate->L = NULL;
+ if (funcstate->lua_tableref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, funcstate->lua_tableref);
+ VIM_CLEAR(funcstate);
+}
+
#endif