summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-05 10:26:40 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-08-05 10:26:40 +0100
commit7b0db2a989f71608f399ae93cf717031ffb0ff94 (patch)
treebc0b2a8ae3b00887952aa945eded432c4e18bd60
parent04d7d87d6ad709fa62e5ecd200a8d2d2871227c7 (diff)
downloadsupple-7b0db2a989f71608f399ae93cf717031ffb0ff94.tar.gz
CAPI: Add pairs/ipairs/next to CAPI to support proxies
-rw-r--r--lib/supple/capi.c81
-rw-r--r--test/test-supple.capi.lua68
2 files changed, 149 insertions, 0 deletions
diff --git a/lib/supple/capi.c b/lib/supple/capi.c
index 125a415..38c4f27 100644
--- a/lib/supple/capi.c
+++ b/lib/supple/capi.c
@@ -349,6 +349,80 @@ supple_capi_raw_getmm(lua_State *L)
return 1;
}
+static int
+supple_capi_next(lua_State *L)
+{
+ int type;
+
+ lua_settop(L, 2);
+
+ type = lua_type(L, 1);
+ if (type == LUA_TTABLE) {
+ /* It is a table, so do the equiv of luaB_next */
+ if (lua_next(L, 1))
+ /* return the k/v pair we got */
+ return 2;
+ /* didn't get a k/v pair, so push a nil key and return */
+ lua_pushnil(L);
+ return 1;
+ } else {
+ /* Do we have a metatable? */
+ if (lua_getmetatable(L, 1)) {
+ /* We do, do we have a __next entry? */
+ lua_getfield(L, -1, "__next");
+ if (!lua_isnil(L, -1)) {
+ /* we do, so let's call that instead */
+ lua_insert(L, 1);
+ lua_settop(L, 3);
+ lua_call(L, 2, LUA_MULTRET);
+ return lua_gettop(L);
+ }
+ }
+ }
+ /* Fell out of the ladders so return an error */
+ return luaL_error(L, "Expected table (or iterable) as argument 1");
+}
+
+static int
+supple_capi_ipairs_iterator(lua_State *L)
+{
+ /* Obj Int -> (NewInt Value|nothing) */
+ int i = luaL_checkint(L, 2) + 1;
+ lua_pushinteger(L, i);
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_rawgeti(L, 1, i);
+ } else {
+ lua_pushinteger(L, i);
+ lua_gettable(L, 1); /* Use this so __index is used */
+ }
+ return lua_isnil(L, -1) ? 0 : 2;
+}
+
+static int
+supple_capi_ipairs(lua_State *L)
+{
+ /* Replacement ipairs which uses our iterator so that fetches
+ * cross the proxy boundary transparently if needed.
+ */
+ /* We register this with the iterator as the first upvalue */
+ lua_pushvalue(L, lua_upvalueindex(1)); /* iterator */
+ lua_pushvalue(L, 1); /* state (table?) */
+ lua_pushinteger(L, 0); /* initial value */
+ return 3;
+}
+
+static int
+supple_capi_pairs(lua_State *L)
+{
+ /* Replacement pairs which uses our next function. It is
+ * provided as our upvalue
+ */
+ lua_pushvalue(L, lua_upvalueindex(1));
+ lua_pushvalue(L, 1);
+ lua_pushnil(L);
+ return 3;
+}
+
static const struct luaL_Reg
supple_capi_functions[] = {
{ "explain", supple_capi_explain },
@@ -357,6 +431,7 @@ supple_capi_functions[] = {
{ "rawtype", supple_capi_rawtype },
{ "lockdown", supple_capi_lockdown },
{ "raw_getmm", supple_capi_raw_getmm },
+ { "next", supple_capi_next },
{ NULL, NULL }
};
@@ -368,5 +443,11 @@ luaopen_supple_capi(lua_State *L)
lua_setfield(L, -2, "_API");
lua_pushnumber(L, CAPI_ABI);
lua_setfield(L, -2, "_ABI");
+ lua_pushcclosure(L, supple_capi_ipairs_iterator, 0);
+ lua_pushcclosure(L, supple_capi_ipairs, 1);
+ lua_setfield(L, -2, "ipairs");
+ lua_pushcclosure(L, supple_capi_next, 0);
+ lua_pushcclosure(L, supple_capi_pairs, 1);
+ lua_setfield(L, -2, "pairs");
return 1;
}
diff --git a/test/test-supple.capi.lua b/test/test-supple.capi.lua
index 7438874..f46c3eb 100644
--- a/test/test-supple.capi.lua
+++ b/test/test-supple.capi.lua
@@ -170,6 +170,74 @@ function suite.capi_new_proxy_table()
assert(tostring(proxy):match("^userdata: "), "Proxy didn't tostring nicely")
end
+function suite.capi_test_next_on_table()
+ local t = { foo = "bar" }
+ local k, v = capi.next(t)
+ assert(k == "foo", "Key was not 'foo'")
+ assert(v == "bar", "Value was not 'bar'")
+ assert(capi.next(t, "foo") == nil, "Next after 'foo' was not nil")
+end
+
+function suite.capi_test_next_on_proxied_table()
+ local proxied = { foo = "bar" }
+ local proxy, mt = capi.new_proxy("table")
+ mt.__next = function(self, key)
+ return next(proxied, key)
+ end
+ local k, v = capi.next(proxy)
+ assert(k == "foo", "Key was not 'foo'")
+ assert(v == "bar", "Value was not 'bar'")
+ assert(capi.next(proxy, "foo") == nil, "Next after 'foo' was not nil")
+end
+
+function suite.capi_test_ipairs_on_table()
+ local tab = { "a", "b" }
+ for i, v in capi.ipairs(tab) do
+ assert(i > 0 and i < 3, "Index out of range")
+ assert((i == 1 and v == "a") or
+ (i == 2 and v == "b"),
+ "Key/value pair wrong")
+ end
+end
+
+function suite.capi_test_ipairs_on_proxied_table()
+ local proxied = { "a", "b" }
+ local tab, mt = capi.new_proxy("table")
+ mt.__index = function(self, idx)
+ return proxied[idx]
+ end
+ for i, v in capi.ipairs(tab) do
+ assert(i > 0 and i < 3, "Index out of range")
+ assert((i == 1 and v == "a") or
+ (i == 2 and v == "b"),
+ "Key/value pair wrong")
+ end
+end
+
+function suite.capi_test_pairs_on_table()
+ local tab = { foo = "bar", baz = "meta" }
+ for k, v in capi.pairs(tab) do
+ assert(k == "foo" or k == "baz", "Key not right")
+ assert((k == "foo" and v == "bar") or
+ (k == "baz" and v == "meta"),
+ "key/value pair wrong")
+ end
+end
+
+function suite.capi_test_pairs_on_proxied_table()
+ local proxied = { foo = "bar", baz = "meta" }
+ local tab, mt = capi.new_proxy("table")
+ mt.__next = function(self, idx)
+ return next(proxied, idx)
+ end
+ for k, v in capi.pairs(tab) do
+ assert(k == "foo" or k == "baz", "Key not right")
+ assert((k == "foo" and v == "bar") or
+ (k == "baz" and v == "meta"),
+ "key/value pair wrong")
+ end
+end
+
local count_ok = 0
for _, testname in ipairs(testnames) do
-- print("Run: " .. testname)