diff --git a/src/lua/utils.c b/src/lua/utils.c index ccaa94e2c837f..73797986129a2 100644 --- a/src/lua/utils.c +++ b/src/lua/utils.c @@ -40,6 +40,7 @@ #include #include "tt_uuid.h" #include "core/datetime.h" +#include "core/say.h" int luaL_nil_ref = LUA_REFNIL; @@ -285,6 +286,19 @@ luaL_setcdatagc(struct lua_State *L, int idx) lua_pop(L, 1); } +int +luaT_setfuncs(struct lua_State *L, const struct luaL_Reg *funcs) +{ + assert(lua_gettop(L) >= 1); + assert(lua_istable(L, -1)); + + for (const struct luaL_Reg *f = funcs; f->name != NULL; ++f) { + lua_pushcclosure(L, f->func, 0); + lua_setfield(L, -2, f->name); + } + + return 0; +} /** * A helper to register a single type metatable. @@ -303,7 +317,7 @@ luaL_register_type(struct lua_State *L, const char *type_name, lua_setfield(L, -2, "__index"); lua_pushstring(L, type_name); lua_setfield(L, -2, "__metatable"); - luaL_register(L, NULL, methods); + luaT_setfuncs(L, methods); lua_pop(L, 1); } @@ -328,7 +342,43 @@ luaL_register_module(struct lua_State *L, const char *modname, luaL_error(L, "Failed to register library"); } lua_remove(L, -2); /* remove _LOADED table */ - luaL_register(L, NULL, methods); + luaT_setfuncs(L, methods); +} + +int +luaT_newmodule(struct lua_State *L, const char *modname, + const struct luaL_Reg *funcs) +{ + say_debug("%s(%s)", __func__, modname); + assert(modname != NULL && funcs != NULL); + + /* Get package.loaded. */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + + /* Verify that the module is not already registered. */ + lua_getfield(L, -1, modname); + assert(lua_isnil(L, -1)); + lua_pop(L, 1); + + /* Create a module table. */ + lua_newtable(L); + + /* Fill the module table with functions. */ + luaT_setfuncs(L, funcs); + + /* Copy the module table. */ + lua_pushvalue(L, -1); + + /* package.loaded[modname] = */ + lua_setfield(L, -3, modname); + + /* Stack: package.loaded, module table. */ + + /* Drop package.loaded. Stack: module table. */ + lua_remove(L, -2); + + say_debug("%s(%s): success", __func__, modname); + return 1; } /* diff --git a/src/lua/utils.h b/src/lua/utils.h index ce90b977561a0..f6ac95bd07330 100644 --- a/src/lua/utils.h +++ b/src/lua/utils.h @@ -249,6 +249,14 @@ luaL_maplen(struct lua_State *L, int idx) extern int luaL_nil_ref; +/** + * Fill a table on top of the Lua stack with functions. + * + * Leave the table on top of the stack. + */ +int +luaT_setfuncs(struct lua_State *L, const struct luaL_Reg *funcs); + void luaL_register_type(struct lua_State *L, const char *type_name, const struct luaL_Reg *methods); @@ -258,6 +266,31 @@ void luaL_register_module(struct lua_State *L, const char *modname, const struct luaL_Reg *methods); +/** + * Create a table with functions and register it as a built-in + * tarantool module. + * + * Leave the table on top of the stack. + * + * Pseudocode: + * + * | local function newmodule(modname, funcs) + * | assert(modname ~= nil and funcs ~= nil) + * | assert(package.loaded[modname] == nil) + * | local mod = {} + * | setfuncs(mod, funcs) + * | package.loaded[modname] = mod + * | return mod + * | end + * + * Unlike luaL_register() it is very straightforward: no recursive + * search, no _G pollution, no branching around using a stack + * top/find a table/create a new table. + */ +int +luaT_newmodule(struct lua_State *L, const char *modname, + const struct luaL_Reg *funcs); + /** \cond public */ /**