summaryrefslogtreecommitdiff
path: root/lib/supple/capi.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/supple/capi.c')
-rw-r--r--lib/supple/capi.c143
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 }
};