Make it possible for Lua to emulate arbitrary properties

This makes it possible to add something similar to a __index / __newindex
metamethod to all our C objects. Based on this, Lua can then easily implement
arbitrary properties on our capi objects.
This commit is contained in:
Uli Schlachter 2015-03-14 10:06:53 +01:00
parent bf630de74e
commit a5a106f97f
10 changed files with 160 additions and 0 deletions

View File

@ -251,6 +251,8 @@ luaA_class_setup(lua_State *L, lua_class_t *class,
class->parent = parent;
class->tostring = NULL;
class->instances = 0;
class->index_miss_handler = LUA_REFNIL;
class->newindex_miss_handler = LUA_REFNIL;
signal_add(&class->signals, "new");
@ -361,6 +363,40 @@ luaA_class_property_get(lua_State *L, lua_class_t *lua_class, int fieldidx)
return NULL;
}
/** Call a registered function.
* \param L The Lua VM state.
* \param handler The function to call.
* \return The number of elements pushed on stack.
*/
static
int luaA_class_call_handler(lua_State *L, int handler)
{
/* This is based on luaA_dofunction, but allows multiple return values */
assert(handler != LUA_REFNIL);
int nargs = lua_gettop(L);
/* Push error handling function and move it before args */
lua_pushcfunction(L, luaA_dofunction_error);
lua_insert(L, - nargs - 1);
int error_func_pos = 1;
/* push function and move it before args */
lua_rawgeti(L, LUA_REGISTRYINDEX, handler);
lua_insert(L, - nargs - 1);
if(lua_pcall(L, nargs, LUA_MULTRET, error_func_pos))
{
warn("%s", lua_tostring(L, -1));
/* Remove error function and error string */
lua_pop(L, 2);
return 0;
}
/* Remove error function */
lua_remove(L, error_func_pos);
return lua_gettop(L);
}
/** Generic index meta function for objects.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.
@ -397,6 +433,8 @@ luaA_class_index(lua_State *L)
}
else
{
if(class->index_miss_handler != LUA_REFNIL)
return luaA_class_call_handler(L, class->index_miss_handler);
if(class->index_miss_property)
return class->index_miss_property(L, luaA_checkudata(L, 1, class));
}
@ -427,6 +465,8 @@ luaA_class_newindex(lua_State *L)
}
else
{
if(class->newindex_miss_handler != LUA_REFNIL)
return luaA_class_call_handler(L, class->newindex_miss_handler);
if(class->newindex_miss_property)
return class->newindex_miss_property(L, luaA_checkudata(L, 1, class));
}

View File

@ -74,6 +74,10 @@ struct lua_class_t
unsigned int instances;
/** Class tostring method */
lua_class_propfunc_t tostring;
/** Function to call on index misses */
int index_miss_handler;
/** Function to call on newindex misses */
int newindex_miss_handler;
};
const char * luaA_typename(lua_State *, int);
@ -152,6 +156,18 @@ luaA_checkudataornil(lua_State *L, int udx, lua_class_t *class)
{ \
lua_pushnumber(L, (lua_class).instances); \
return 1; \
} \
\
static inline int \
luaA_##prefix##_set_index_miss_handler(lua_State *L) \
{ \
return luaA_registerfct(L, 1, &(lua_class).index_miss_handler); \
} \
\
static inline int \
luaA_##prefix##_set_newindex_miss_handler(lua_State *L) \
{ \
return luaA_registerfct(L, 1, &(lua_class).newindex_miss_handler); \
}
#define LUA_CLASS_METHODS(class) \
@ -160,6 +176,8 @@ luaA_checkudataornil(lua_State *L, int udx, lua_class_t *class)
{ "disconnect_signal", luaA_##class##_class_disconnect_signal }, \
{ "emit_signal", luaA_##class##_class_emit_signal }, \
{ "instances", luaA_##class##_class_instances }, \
{ "set_index_miss_handler", luaA_##class##_set_index_miss_handler }, \
{ "set_newindex_miss_handler", luaA_##class##_set_newindex_miss_handler }, \
#define LUA_CLASS_META \
{ "__index", luaA_class_index }, \

View File

@ -49,6 +49,16 @@
* @function instances
*/
/** Set a __index metamethod for all button instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all button instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
/** Create a new mouse button bindings.
* \param L The Lua VM state.
* \return The number of elements pushed on stack.

View File

@ -126,6 +126,16 @@
* @function instances
*/
/** Set a __index metamethod for all client instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all client instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
static area_t titlebar_get_area(client_t *c, client_titlebar_t bar);
static drawable_t *titlebar_get_drawable(lua_State *L, client_t *c, int cl_idx, client_titlebar_t bar);

View File

@ -49,6 +49,16 @@
* @function instances
*/
/** Set a __index metamethod for all drawable instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all drawable instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
static lua_class_t drawable_class;
LUA_OBJECT_FUNCS(drawable_class, drawable_t, drawable)

View File

@ -83,6 +83,16 @@
* @function instances
*/
/** Set a __index metamethod for all drawin instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all drawin instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
LUA_OBJECT_FUNCS(drawin_class, drawin_t, drawin)
/** Kick out systray windows.

View File

@ -61,6 +61,16 @@
* @function instances
*/
/** Set a __index metamethod for all key instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all key instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
static void
luaA_keystore(lua_State *L, int ud, const char *str, ssize_t len)
{

View File

@ -54,6 +54,22 @@
* @table screen
*/
/** Get the number of instances.
*
* @return The number of screen objects alive.
* @function instances
*/
/** Set a __index metamethod for all screen instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all screen instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
struct screen_output_t
{
/** The XRandR names of the output */

View File

@ -54,6 +54,16 @@
* @function instances
*/
/** Set a __index metamethod for all tag instances.
* @tparam function cb The meta-method
* @function set_index_miss_handler
*/
/** Set a __newindex metamethod for all tag instances.
* @tparam function cb The meta-method
* @function set_newindex_miss_handler
*/
/** Tag type */
struct tag
{

View File

@ -0,0 +1,26 @@
-- Test set_{,new}index_miss_handler
local class = tag
local obj = class({})
awesome.connect_signal("debug::index::miss", error)
awesome.connect_signal("debug::newindex::miss", error)
class.set_index_miss_handler(function(o, k)
assert(o == obj)
assert(k == "key")
return 42
end)
assert(obj.key == 42)
local called = false
class.set_newindex_miss_handler(function(o, k, v)
assert(o == obj)
assert(k == "key")
assert(v == 42)
called = true
end)
obj.key = 42
assert(called)
require("_runner").run_steps({ function() return true end })