From 7b0db2a989f71608f399ae93cf717031ffb0ff94 Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sun, 5 Aug 2012 10:26:40 +0100 Subject: CAPI: Add pairs/ipairs/next to CAPI to support proxies --- lib/supple/capi.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++ test/test-supple.capi.lua | 68 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) 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) -- cgit v1.2.1