summaryrefslogtreecommitdiffstats
path: root/player/lua.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/lua.c')
-rw-r--r--player/lua.c178
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,
};