viml/executor: Directly generate typval_T values

Note: this will *still* crash when using API in cases similar to the one
described in first commit. Just it needs different code to reproduce.
This commit is contained in:
ZyX
2016-07-10 08:03:14 +03:00
parent e7bbd8256b
commit f551df17f3
6 changed files with 715 additions and 186 deletions

View File

@@ -36,7 +36,19 @@ typedef struct {
lua_pushcfunction(lstate, &function); \
lua_call(lstate, 0, numret); \
} while (0)
/// Call C function which expects four arguments
/// Call C function which expects two arguments
///
/// @param function Called function
/// @param numret Number of returned arguments
/// @param a… Supplied argument (should be a void* pointer)
#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \
do { \
lua_pushcfunction(lstate, &function); \
lua_pushlightuserdata(lstate, a1); \
lua_pushlightuserdata(lstate, a2); \
lua_call(lstate, 2, numret); \
} while (0)
/// Call C function which expects three arguments
///
/// @param function Called function
/// @param numret Number of returned arguments
@@ -64,15 +76,19 @@ typedef struct {
lua_call(lstate, 4, numret); \
} while (0)
static void set_lua_error(lua_State *lstate, LuaError *lerr)
/// Convert lua error into a Vim error message
///
/// @param lstate Lua interpreter state.
/// @param[in] msg Message base, must contain one `%s`.
static void nlua_error(lua_State *const lstate, const char *const msg)
FUNC_ATTR_NONNULL_ALL
{
const char *const str = lua_tolstring(lstate, -1, &lerr->lua_err_str.size);
lerr->lua_err_str.data = xmemdupz(str, lerr->lua_err_str.size);
lua_pop(lstate, 1);
size_t len;
const char *const str = lua_tolstring(lstate, -1, &len);
// FIXME? More specific error?
set_api_error("Error while executing lua code", &lerr->err);
EMSG2(msg, str);
lua_pop(lstate, 1);
}
/// Compare two strings, ignoring case
@@ -94,25 +110,26 @@ static int nlua_stricmp(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
/// Evaluate lua string
///
/// Expects three values on the stack: string to evaluate, pointer to the
/// location where result is saved, pointer to the location where error is
/// saved. Always returns nothing (from the lua point of view).
/// Expects two values on the stack: string to evaluate, pointer to the
/// location where result is saved. Always returns nothing (from the lua point
/// of view).
static int nlua_exec_lua_string(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
{
String *str = (String *) lua_touserdata(lstate, 1);
Object *obj = (Object *) lua_touserdata(lstate, 2);
LuaError *lerr = (LuaError *) lua_touserdata(lstate, 3);
lua_pop(lstate, 3);
String *str = (String *)lua_touserdata(lstate, 1);
typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 2);
lua_pop(lstate, 2);
if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) {
set_lua_error(lstate, lerr);
nlua_error(lstate, _("E5104: Error while creating lua chunk: %s"));
return 0;
}
if (lua_pcall(lstate, 0, 1, 0)) {
set_lua_error(lstate, lerr);
nlua_error(lstate, _("E5105: Error while calling lua chunk: %s"));
return 0;
}
if (!nlua_pop_typval(lstate, ret_tv)) {
return 0;
}
*obj = nlua_pop_Object(lstate, &lerr->err);
return 0;
}
@@ -124,8 +141,7 @@ static int nlua_state_init(lua_State *lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, &nlua_stricmp);
lua_setglobal(lstate, "stricmp");
if (luaL_dostring(lstate, (char *) &vim_module[0])) {
LuaError lerr;
set_lua_error(lstate, &lerr);
nlua_error(lstate, _("E5106: Error while creating vim module: %s"));
return 1;
}
nlua_add_api_functions(lstate);
@@ -150,14 +166,6 @@ static lua_State *init_lua(void)
return lstate;
}
static Object exec_lua_string(lua_State *lstate, String str, LuaError *lerr)
FUNC_ATTR_NONNULL_ALL
{
Object ret = { kObjectTypeNil, { false } };
NLUA_CALL_C_FUNCTION_3(lstate, nlua_exec_lua_string, 0, &str, &ret, lerr);
return ret;
}
static lua_State *global_lstate = NULL;
/// Execute lua string
@@ -165,28 +173,17 @@ static lua_State *global_lstate = NULL;
/// Used for :lua.
///
/// @param[in] str String to execute.
/// @param[out] err Location where error will be saved.
/// @param[out] err_str Location where lua error string will be saved, if any.
/// @param[out] ret_tv Location where result will be saved.
///
/// @return Result of the execution.
Object executor_exec_lua(String str, Error *err, String *err_str)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
void executor_exec_lua(String str, typval_T *ret_tv)
FUNC_ATTR_NONNULL_ALL
{
if (global_lstate == NULL) {
global_lstate = init_lua();
}
LuaError lerr = {
.err = { .set = false },
.lua_err_str = STRING_INIT,
};
Object ret = exec_lua_string(global_lstate, str, &lerr);
*err = lerr.err;
*err_str = lerr.lua_err_str;
return ret;
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0, &str, ret_tv);
}
/// Evaluate lua string
@@ -196,75 +193,73 @@ Object executor_exec_lua(String str, Error *err, String *err_str)
/// 1. String to evaluate.
/// 2. _A value.
/// 3. Pointer to location where result is saved.
/// 4. Pointer to location where error will be saved.
///
/// @param[in,out] lstate Lua interpreter state.
static int nlua_eval_lua_string(lua_State *lstate)
FUNC_ATTR_NONNULL_ALL
{
String *str = (String *) lua_touserdata(lstate, 1);
Object *arg = (Object *) lua_touserdata(lstate, 2);
Object *ret = (Object *) lua_touserdata(lstate, 3);
LuaError *lerr = (LuaError *) lua_touserdata(lstate, 4);
String *str = (String *)lua_touserdata(lstate, 1);
typval_T *arg = (typval_T *)lua_touserdata(lstate, 2);
typval_T *ret_tv = (typval_T *)lua_touserdata(lstate, 3);
lua_pop(lstate, 3);
garray_T str_ga;
ga_init(&str_ga, 1, 80);
#define EVALHEADER "local _A=select(1,...) return "
ga_concat_len(&str_ga, EVALHEADER, sizeof(EVALHEADER) - 1);
#define EVALHEADER "local _A=select(1,...) return ("
const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1;
char *lcmd;
if (lcmd_len < IOSIZE) {
lcmd = (char *)IObuff;
} else {
lcmd = xmalloc(lcmd_len);
}
memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1);
memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size);
lcmd[lcmd_len - 1] = ')';
#undef EVALHEADER
ga_concat_len(&str_ga, str->data, str->size);
if (luaL_loadbuffer(lstate, str_ga.ga_data, (size_t) str_ga.ga_len,
NLUA_EVAL_NAME)) {
set_lua_error(lstate, lerr);
if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) {
nlua_error(lstate,
_("E5107: Error while creating lua chunk for luaeval(): %s"));
return 0;
}
ga_clear(&str_ga);
if (lcmd != (char *)IObuff) {
xfree(lcmd);
}
nlua_push_Object(lstate, *arg);
if (arg == NULL || arg->v_type == VAR_UNKNOWN) {
lua_pushnil(lstate);
} else {
nlua_push_typval(lstate, arg);
}
if (lua_pcall(lstate, 1, 1, 0)) {
set_lua_error(lstate, lerr);
nlua_error(lstate,
_("E5108: Error while calling lua chunk for luaeval(): %s"));
return 0;
}
if (!nlua_pop_typval(lstate, ret_tv)) {
return 0;
}
*ret = nlua_pop_Object(lstate, &lerr->err);
return 0;
}
static Object eval_lua_string(lua_State *lstate, String str, Object arg,
LuaError *lerr)
FUNC_ATTR_NONNULL_ALL
{
Object ret = { kObjectTypeNil, { false } };
NLUA_CALL_C_FUNCTION_4(lstate, nlua_eval_lua_string, 0,
&str, &arg, &ret, lerr);
return ret;
}
/// Evaluate lua string
///
/// Used for luaeval().
///
/// @param[in] str String to execute.
/// @param[out] err Location where error will be saved.
/// @param[out] err_str Location where lua error string will be saved, if any.
/// @param[in] arg Second argument to `luaeval()`.
/// @param[out] ret_tv Location where result will be saved.
///
/// @return Result of the execution.
Object executor_eval_lua(String str, Object arg, Error *err, String *err_str)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
void executor_eval_lua(const String str, typval_T *const arg,
typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL
{
if (global_lstate == NULL) {
global_lstate = init_lua();
}
LuaError lerr = {
.err = { .set = false },
.lua_err_str = STRING_INIT,
};
Object ret = eval_lua_string(global_lstate, str, arg, &lerr);
*err = lerr.err;
*err_str = lerr.lua_err_str;
return ret;
NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0,
(void *)&str, arg, ret_tv);
}