diff options
Diffstat (limited to 'player/lua.c')
-rw-r--r-- | player/lua.c | 178 |
1 files changed, 92 insertions, 86 deletions
diff --git a/player/lua.c b/player/lua.c index 363e1a52fd..b2548387f6 100644 --- a/player/lua.c +++ b/player/lua.c @@ -45,7 +45,6 @@ #include "osdep/subprocess.h" #include "osdep/timer.h" #include "osdep/threads.h" -#include "osdep/getpid.h" #include "stream/stream.h" #include "sub/osd.h" #include "core.h" @@ -57,28 +56,31 @@ // All these are generated from player/lua/*.lua static const char * const builtin_lua_scripts[][2] = { {"mp.defaults", -# include "generated/player/lua/defaults.lua.inc" +# include "player/lua/defaults.lua.inc" }, {"mp.assdraw", -# include "generated/player/lua/assdraw.lua.inc" +# include "player/lua/assdraw.lua.inc" + }, + {"mp.input", +# include "player/lua/input.lua.inc" }, {"mp.options", -# include "generated/player/lua/options.lua.inc" +# include "player/lua/options.lua.inc" }, {"@osc.lua", -# include "generated/player/lua/osc.lua.inc" +# include "player/lua/osc.lua.inc" }, {"@ytdl_hook.lua", -# include "generated/player/lua/ytdl_hook.lua.inc" +# include "player/lua/ytdl_hook.lua.inc" }, {"@stats.lua", -# include "generated/player/lua/stats.lua.inc" +# include "player/lua/stats.lua.inc" }, {"@console.lua", -# include "generated/player/lua/console.lua.inc" +# include "player/lua/console.lua.inc" }, {"@auto_profiles.lua", -# include "generated/player/lua/auto_profiles.lua.inc" +# include "player/lua/auto_profiles.lua.inc" }, {0} }; @@ -122,14 +124,30 @@ static void mp_lua_optarg(lua_State *L, int arg) lua_pushnil(L); } +// autofree: avoid leaks if a lua-error occurs between talloc new/free. +// If a lua c-function does a new allocation (not tied to an existing context), +// and an uncaught lua-error occurs before "free" - the allocation is leaked. + // autofree lua C function: same as lua_CFunction but with these differences: -// - It accepts an additional void* argument which is a pre-initialized talloc -// context which it can use, and which is freed with its children once the -// function completes - regardless if a lua error occured or not. If a lua -// error did occur then it's re-thrown after the ctx is freed. -// - At struct fn_entry it's declared with AF_ENTRY instead of FN_ENTRY. +// - It accepts an additional void* argument - a pre-initialized talloc context +// which it can use, and which is freed with its children once the function +// completes - regardless if a lua error occurred or not. If a lua error did +// occur then it's re-thrown after the ctx is freed. +// The stack/arguments/upvalues/return are the same as with lua_CFunction. +// - It's inserted into the lua VM using af_pushc{function,closure} instead of +// lua_pushc{function,closure}, which takes care of wrapping it with the +// automatic talloc allocation + lua-error-handling + talloc release. +// This requires using AF_ENTRY instead of FN_ENTRY at struct fn_entry. +// - The autofree overhead per call is roughly two additional plain lua calls. +// Typically that's up to 20% slower than plain new+free without "auto", +// and at most about twice slower - compared to bare new+free lua_CFunction. +// - The overhead of af_push* is one additional lua-c-closure with two upvalues. typedef int (*af_CFunction)(lua_State *L, void *ctx); +static void af_pushcclosure(lua_State *L, af_CFunction fn, int n); +#define af_pushcfunction(L, fn) af_pushcclosure((L), (fn), 0) + + // add_af_dir, add_af_mpv_alloc take a valid DIR*/char* value respectively, // and closedir/mpv_free it when the parent is freed. @@ -159,7 +177,7 @@ static void add_af_mpv_alloc(void *parent, char *ma) // Perform the equivalent of mpv_free_node_contents(node) when tmp is freed. -static void steal_node_alloctions(void *tmp, mpv_node *node) +static void steal_node_allocations(void *tmp, mpv_node *node) { talloc_steal(tmp, node_get_alloc(node)); } @@ -233,13 +251,16 @@ static void load_file(lua_State *L, const char *fname) { struct script_ctx *ctx = get_ctx(L); MP_DBG(ctx, "loading file %s\n", fname); - struct bstr s = stream_read_file(fname, ctx, ctx->mpctx->global, 100000000); + void *tmp = talloc_new(ctx); + // according to Lua manual chunkname should be '@' plus the filename + char *dispname = talloc_asprintf(tmp, "@%s", fname); + struct bstr s = stream_read_file(fname, tmp, ctx->mpctx->global, 100000000); if (!s.start) luaL_error(L, "Could not read file.\n"); - if (luaL_loadbuffer(L, s.start, s.len, fname)) + if (luaL_loadbuffer(L, s.start, s.len, dispname)) lua_error(L); lua_call(L, 0, 1); - talloc_free(s.start); + talloc_free(tmp); } static int load_builtin(lua_State *L) @@ -469,10 +490,9 @@ error_out: static int check_loglevel(lua_State *L, int arg) { const char *level = luaL_checkstring(L, arg); - for (int n = 0; n < MSGL_MAX; n++) { - if (mp_log_levels[n] && strcasecmp(mp_log_levels[n], level) == 0) - return n; - } + int n = mp_msg_find_level(level); + if (n >= 0) + return n; luaL_error(L, "Invalid log level '%s'", level); abort(); } @@ -524,23 +544,6 @@ static int script_get_script_directory(lua_State *L) return 0; } -static int script_suspend(lua_State *L) -{ - struct script_ctx *ctx = get_ctx(L); - MP_ERR(ctx, "mp.suspend() is deprecated and does nothing.\n"); - return 0; -} - -static int script_resume(lua_State *L) -{ - return 0; -} - -static int script_resume_all(lua_State *L) -{ - return 0; -} - static void pushnode(lua_State *L, mpv_node *node); static int script_raw_wait_event(lua_State *L, void *tmp) @@ -551,7 +554,7 @@ static int script_raw_wait_event(lua_State *L, void *tmp) struct mpv_node rn; mpv_event_to_node(&rn, event); - steal_node_alloctions(tmp, &rn); + steal_node_allocations(tmp, &rn); pushnode(L, &rn); // event @@ -612,6 +615,14 @@ static int script_commandv(lua_State *L) return check_error(L, mpv_command(ctx->client, args)); } +static int script_del_property(lua_State *L) +{ + struct script_ctx *ctx = get_ctx(L); + const char *p = luaL_checkstring(L, 1); + + return check_error(L, mpv_del_property(ctx->client, p)); +} + static int script_set_property(lua_State *L) { struct script_ctx *ctx = get_ctx(L); @@ -655,6 +666,8 @@ static int script_set_property_number(lua_State *L) static void makenode(void *tmp, mpv_node *dst, lua_State *L, int t) { + luaL_checkstack(L, 6, "makenode"); + if (t < 0) t = lua_gettop(L) + (t + 1); switch (lua_type(L, t)) { @@ -854,7 +867,7 @@ static int script_get_property_number(lua_State *L) static void pushnode(lua_State *L, mpv_node *node) { - luaL_checkstack(L, 6, "stack overflow"); + luaL_checkstack(L, 6, "pushnode"); switch (node->format) { case MPV_FORMAT_STRING: @@ -913,7 +926,7 @@ static int script_get_property_native(lua_State *L, void *tmp) mpv_node node; int err = mpv_get_property(ctx->client, name, MPV_FORMAT_NODE, &node); if (err >= 0) { - steal_node_alloctions(tmp, &node); + steal_node_allocations(tmp, &node); pushnode(L, &node); return 1; } @@ -964,7 +977,7 @@ static int script_command_native(lua_State *L, void *tmp) makenode(tmp, &node, L, 1); int err = mpv_command_node(ctx->client, &node, &result); if (err >= 0) { - steal_node_alloctions(tmp, &result); + steal_node_allocations(tmp, &result); pushnode(L, &result); return 1; } @@ -991,16 +1004,6 @@ static int script_raw_abort_async_command(lua_State *L) return 0; } -static int script_get_mouse_pos(lua_State *L) -{ - struct MPContext *mpctx = get_mpctx(L); - int px, py; - mp_input_get_mouse_pos(mpctx->input, &px, &py); - lua_pushnumber(L, px); - lua_pushnumber(L, py); - return 2; -} - static int script_get_time(lua_State *L) { struct script_ctx *ctx = get_ctx(L); @@ -1146,19 +1149,12 @@ static int script_split_path(lua_State *L) return 2; } -static int script_join_path(lua_State *L) +static int script_join_path(lua_State *L, void *tmp) { const char *p1 = luaL_checkstring(L, 1); const char *p2 = luaL_checkstring(L, 2); - char *r = mp_path_join(NULL, p1, p2); + char *r = mp_path_join(tmp, p1, p2); lua_pushstring(L, r); - talloc_free(r); - return 1; -} - -static int script_getpid(lua_State *L) -{ - lua_pushnumber(L, mp_getpid()); return 1; } @@ -1169,7 +1165,7 @@ static int script_parse_json(lua_State *L, void *tmp) bool trail = lua_toboolean(L, 2); bool ok = false; struct mpv_node node; - if (json_parse(tmp, &node, &text, 32) >= 0) { + if (json_parse(tmp, &node, &text, MAX_JSON_DEPTH) >= 0) { json_skip_whitespace(&text); ok = !text[0] || trail; } @@ -1191,11 +1187,11 @@ static int script_format_json(lua_State *L, void *tmp) char *dst = talloc_strdup(tmp, ""); if (json_write(&dst, &node) >= 0) { lua_pushstring(L, dst); - lua_pushnil(L); - } else { - lua_pushnil(L); - lua_pushstring(L, "error"); + return 1; } + + lua_pushnil(L); + lua_pushstring(L, "error"); return 2; } @@ -1219,9 +1215,6 @@ struct fn_entry { static const struct fn_entry main_fns[] = { FN_ENTRY(log), - FN_ENTRY(suspend), - FN_ENTRY(resume), - FN_ENTRY(resume_all), AF_ENTRY(raw_wait_event), FN_ENTRY(request_event), FN_ENTRY(find_config_file), @@ -1236,13 +1229,13 @@ static const struct fn_entry main_fns[] = { FN_ENTRY(get_property_bool), FN_ENTRY(get_property_number), AF_ENTRY(get_property_native), + FN_ENTRY(del_property), FN_ENTRY(set_property), FN_ENTRY(set_property_bool), FN_ENTRY(set_property_number), AF_ENTRY(set_property_native), FN_ENTRY(raw_observe_property), FN_ENTRY(raw_unobserve_property), - FN_ENTRY(get_mouse_pos), FN_ENTRY(get_time), FN_ENTRY(input_set_section_mouse_area), FN_ENTRY(format_time), @@ -1257,8 +1250,7 @@ static const struct fn_entry utils_fns[] = { AF_ENTRY(readdir), FN_ENTRY(file_info), FN_ENTRY(split_path), - FN_ENTRY(join_path), - FN_ENTRY(getpid), + AF_ENTRY(join_path), AF_ENTRY(parse_json), AF_ENTRY(format_json), FN_ENTRY(get_env_list), @@ -1273,39 +1265,53 @@ typedef struct autofree_data { /* runs the target autofree script_* function with the ctx argument */ static int script_autofree_call(lua_State *L) { - autofree_data *data = lua_touserdata(L, lua_upvalueindex(1)); + // n*args &data + autofree_data *data = lua_touserdata(L, -1); + lua_pop(L, 1); // n*args assert(data && data->target && data->ctx); return data->target(L, data->ctx); } static int script_autofree_trampoline(lua_State *L) { + // n*args autofree_data data = { - .target = lua_touserdata(L, lua_upvalueindex(1)), + .target = lua_touserdata(L, lua_upvalueindex(2)), // fn .ctx = NULL, }; assert(data.target); - int nargs = lua_gettop(L); - - lua_pushlightuserdata(L, &data); - lua_pushcclosure(L, script_autofree_call, 1); - lua_insert(L, 1); + lua_pushvalue(L, lua_upvalueindex(1)); // n*args autofree_call (closure) + lua_insert(L, 1); // autofree_call n*args + lua_pushlightuserdata(L, &data); // autofree_call n*args &data data.ctx = talloc_new(NULL); - int r = lua_pcall(L, nargs, LUA_MULTRET, 0); + int r = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); // m*retvals talloc_free(data.ctx); if (r) lua_error(L); - return lua_gettop(L); + return lua_gettop(L); // m (retvals) } -static void mp_push_autofree_fn(lua_State *L, af_CFunction fn) +static void af_pushcclosure(lua_State *L, af_CFunction fn, int n) { + // Instead of pushing a direct closure of fn with n upvalues, we push an + // autofree_trampoline closure with two upvalues: + // 1: autofree_call closure with the n upvalues given here. + // 2: fn + // + // when called the autofree_trampoline closure will pcall the autofree_call + // closure with the current lua call arguments and an additional argument + // which holds ctx and fn. the autofree_call closure (with the n upvalues + // given here) calls fn directly and provides it with the ctx C argument, + // so that fn sees the exact n upvalues and lua call arguments as intended, + // wrapped with ctx init/cleanup. + + lua_pushcclosure(L, script_autofree_call, n); lua_pushlightuserdata(L, fn); - lua_pushcclosure(L, script_autofree_trampoline, 1); + lua_pushcclosure(L, script_autofree_trampoline, 2); } static void register_package_fns(lua_State *L, char *module, @@ -1314,7 +1320,7 @@ static void register_package_fns(lua_State *L, char *module, push_module_table(L, module); // modtable for (int n = 0; e[n].name; n++) { if (e[n].af) { - mp_push_autofree_fn(L, e[n].af); // modtable fn + af_pushcclosure(L, e[n].af, 0); // modtable fn } else { lua_pushcclosure(L, e[n].fn, 0); // modtable fn } @@ -1332,7 +1338,7 @@ static void add_functions(struct script_ctx *ctx) } const struct mp_scripting mp_scripting_lua = { - .name = "lua script", + .name = "lua", .file_ext = "lua", .load = load_lua, }; |