diff options
Diffstat (limited to 'lib/supple/capi.c')
-rw-r--r-- | lib/supple/capi.c | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/lib/supple/capi.c b/lib/supple/capi.c index 139cfe6..c413c20 100644 --- a/lib/supple/capi.c +++ b/lib/supple/capi.c @@ -13,13 +13,156 @@ #include <lua.h> #include <lauxlib.h> +#include <string.h> + /* The API exposed, increment on backward-compatible changes */ #define CAPI_API 1 /* The ABI exposed, increment on backward-incompatible changes */ #define CAPI_ABI 1 +static int +supple_capi_explain(lua_State *L) +{ + int one_type; + if (lua_gettop(L) != 2) { + return luaL_error(L, "Expected 2 args got %d", lua_gettop(L)); + } + one_type = lua_type(L, 1); + if ((one_type != LUA_TTABLE) && + (one_type != LUA_TFUNCTION) && + (one_type != LUA_TUSERDATA)) { + return luaL_error(L, + "Expected one of table, function or userdata. " \ + "Got %s instead", + lua_typename(L, one_type)); + } + if (lua_type(L, 2) != LUA_TSTRING) { + return luaL_error(L, "Expected string, got %s instead", + lua_typename(L, lua_type(L, 2))); + } + lua_newtable(L); + lua_pushvalue(L, 2); + lua_setfield(L, -2, "tag"); + lua_pushstring(L, lua_typename(L, one_type)); + lua_setfield(L, -2, "type"); + if (one_type != LUA_TFUNCTION) { + /* Get the metatable */ + if (lua_getmetatable(L, 1) != 0) { + int idx = 1; + /* Prepare a methods table */ + lua_newtable(L); + lua_pushnil(L); /* For iterating */ + while (lua_next(L, -3) != 0) { /* Iterate the metatable */ + lua_pop(L, 1); /* Don't need the value */ + if (lua_type(L, -1) != LUA_TSTRING) { + /* Not a string key */ + } else if (strcmp(lua_tostring(L, -1), + "__mode") == 0) { + /* mode key is ignored */ + } else { + /* dup */ + lua_pushvalue(L, -1); + /* And copy into methods */ + lua_rawseti(L, -3, idx++); + } + /* Key is left ready for lua_next() */ + } + /* Stuff the methods table into the return */ + lua_setfield(L, -3, "methods"); + /* And pop the metatable */ + lua_pop(L, 1); + } + } + /* And return the new table */ + return 1; +} + +static int +supple_capi_proxy_tostring(lua_State *L) +{ + /* arg 1 is the object, upvalue 1 is the original type */ + const char *orig_type = lua_tostring(L, lua_upvalueindex(1)); + lua_pushfstring(L, "%s: %p", orig_type, lua_touserdata(L, 1)); + return 1; +} + +static int +supple_capi_proxy_type(lua_State *L) +{ + /* arg 1 is the object, upvalue 1 is the original type */ + lua_pushvalue(L, lua_upvalueindex(1)); + return 1; +} + +static int +supple_capi_new_proxy(lua_State *L) +{ + void *proxy_ptr; + /* Input is a type string */ + if (lua_type(L, 1) != LUA_TSTRING) { + return luaL_error(L, "Expected type string, got %s", + lua_typename(L, lua_type(L, 1))); + } + proxy_ptr = lua_newuserdata(L, 1); + if (proxy_ptr == NULL) { + return 0; + } + lua_newtable(L); + /* typestr, proxyobj, metatable */ + /* First job is the tostring metamethod, we always add that */ + lua_pushvalue(L, 1); + lua_pushcclosure(L, supple_capi_proxy_tostring, 1); + lua_setfield(L, -2, "__tostring"); + /* next the __type metamethod for the lua level type call */ + lua_pushvalue(L, 1); + lua_pushcclosure(L, supple_capi_proxy_type, 1); + lua_setfield(L, -2, "__type"); + /* And now prevent anything poking in us later */ + lua_pushboolean(L, 1); + lua_setfield(L, -2, "__metatable"); + + /* Finally set the metatable and return proxy, metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, -3); + + return 2; +} + +/* Replacement for lbaselib.c's type method, honouring __type */ +static int +supple_capi_type(lua_State *L) +{ + lua_settop(L, 1); + if (lua_getmetatable(L, 1) != 0) { + /* There is a metatable, try and find __type */ + lua_getfield(L, 2, "__type"); + if (lua_isfunction(L, 3)) { + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; + } + } + + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + + return 1; +} + +/* Reimplementation of base type in case it's not available */ +static int +supple_capi_rawtype(lua_State *L) +{ + lua_settop(L, 1); + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + return 1; +} + static const struct luaL_Reg supple_capi_functions[] = { + { "explain", supple_capi_explain }, + { "new_proxy", supple_capi_new_proxy }, + { "type", supple_capi_type }, + { "rawtype", supple_capi_rawtype }, { NULL, NULL } }; |