[tarantool-patches] [PATCH tarantool 1/2] Introduce luaT_tolstring

Eugene Blikh bigbes at gmail.com
Mon Aug 6 10:16:59 MSK 2018


From: Eugine Blikh <bigbes at gmail.com>

`lua_tostring`/`lua_tolstring` ignores metatable/boolean/nil and return NULL,
but sometimes it's needed to have similar behaviour, like lua functions
tostring. Lua 5.1 and LuaJIT ignores it by default, Lua 5.2 introduced
auxilary function luaL_to(l)string with supporting of __tostring. This
function is backport of Lua 5.1 "lauxlib.h"s luaL_tostring in the luaT
namespace.
---
 extra/exports                    |  1 +
 src/lua/init.h                   |  8 --------
 src/lua/utils.c                  | 31 +++++++++++++++++++++++++++++++
 src/lua/utils.h                  |  6 ++++++
 test/app-tap/module_api.c        | 39 +++++++++++++++++++++++++++++++++++++++
 test/app-tap/module_api.test.lua | 13 +++++++++++--
 6 files changed, 88 insertions(+), 10 deletions(-)

diff --git a/extra/exports b/extra/exports
index 61a2e0a54..a27722910 100644
--- a/extra/exports
+++ b/extra/exports
@@ -130,6 +130,7 @@ luaT_error
 luaT_call
 luaT_cpcall
 luaT_state
+luaT_tolstring
 box_txn
 box_txn_begin
 box_txn_commit
diff --git a/src/lua/init.h b/src/lua/init.h
index ddf3e9195..1257ef510 100644
--- a/src/lua/init.h
+++ b/src/lua/init.h
@@ -59,14 +59,6 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv);
 void
 tarantool_lua_free();
 
-/**
- * This function exists because lua_tostring does not use
- * __tostring metamethod, and this metamethod has to be used
- * if we want to print Lua userdata correctly.
- */
-const char *
-tarantool_lua_tostring(struct lua_State *L, int index);
-
 /**
  * Load and execute start-up file
  *
diff --git a/src/lua/utils.c b/src/lua/utils.c
index 2f0f4dcf8..afc44b581 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -941,6 +941,37 @@ luaT_cpcall(lua_State *L, lua_CFunction func, void *ud)
 	return 0;
 }
 
+/**
+ * This function exists because lua_tostring does not use
+ * __tostring metamethod, and this metamethod has to be used
+ * if we want to print Lua userdata correctly.
+ */
+const char *
+luaT_tolstring(lua_State *L, int idx, size_t *len)
+{
+	if (!luaL_callmeta(L, idx, "__tostring")) {
+		switch (lua_type(L, idx)) {
+		case LUA_TNUMBER:
+		case LUA_TSTRING:
+			lua_pushvalue(L, idx);
+			break;
+		case LUA_TBOOLEAN: {
+			int val = lua_toboolean(L, idx);
+			lua_pushstring(L, val ? "true" : "false");
+			break;
+		}
+		case LUA_TNIL:
+			lua_pushliteral(L, "nil");
+			break;
+		default:
+			lua_pushfstring(L, "%s: %p", luaL_typename(L, idx),
+						     lua_topointer(L, idx));
+		}
+	}
+
+	return lua_tolstring(L, -1, len);
+}
+
 lua_State *
 luaT_state(void)
 {
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 6b057af3e..fdfa95805 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -430,6 +430,12 @@ luaT_cpcall(lua_State *L, lua_CFunction func, void *ud);
 LUA_API lua_State *
 luaT_state(void);
 
+/**
+ * Like lua_tolstring, but supports metatables, booleans and nil properly.
+ */
+LUA_API const char *
+luaT_tolstring(lua_State *L, int idx, size_t *ssize);
+
 /** \endcond public */
 
 void
diff --git a/test/app-tap/module_api.c b/test/app-tap/module_api.c
index 01c1ee35b..4abe1af48 100644
--- a/test/app-tap/module_api.c
+++ b/test/app-tap/module_api.c
@@ -402,6 +402,44 @@ test_state(lua_State *L)
 	return 1;
 }
 
+static int table_tostring(lua_State *L) {
+	lua_pushstring(L, "123");
+	return 1;
+}
+
+static int
+test_tostring(lua_State *L)
+{
+	/* original table */
+	lua_createtable(L, 0, 0);
+	/* meta-table */
+	lua_createtable(L, 0, 0);
+	/* pushing __tostring function */
+	lua_pushcfunction(L, table_tostring);
+	lua_setfield(L, -2, "__tostring");
+	/* setting metatable */
+	lua_setmetatable(L, -2);
+	assert(strcmp(luaT_tolstring(L, -1, NULL), "123") == 0);
+
+	lua_pushnumber(L, 1);
+	assert(strcmp(luaT_tolstring(L, -1, NULL), "1") == 0);
+
+	lua_createtable(L, 0, 0);
+	assert(strncmp(luaT_tolstring(L, -1, NULL), "table: ", 7) == 0);
+
+	lua_pushboolean(L, true);
+	assert(strcmp(luaT_tolstring(L, -1, NULL), "true") == 0);
+
+	lua_pushboolean(L, false);
+	assert(strcmp(luaT_tolstring(L, -1, NULL), "false") == 0);
+
+	lua_pushnil(L);
+	assert(strcmp(luaT_tolstring(L, -1, NULL), "nil") == 0);
+
+	lua_pushboolean(L, true);
+	return 1;
+}
+
 LUA_API int
 luaopen_module_api(lua_State *L)
 {
@@ -428,6 +466,7 @@ luaopen_module_api(lua_State *L)
 		{"test_call", test_call},
 		{"test_cpcall", test_cpcall},
 		{"test_state", test_state},
+		{"test_tostring", test_tostring},
 		{NULL, NULL}
 	};
 	luaL_register(L, "module_api", lib);
diff --git a/test/app-tap/module_api.test.lua b/test/app-tap/module_api.test.lua
index d55f67c3b..f93257236 100755
--- a/test/app-tap/module_api.test.lua
+++ b/test/app-tap/module_api.test.lua
@@ -1,8 +1,12 @@
 #!/usr/bin/env tarantool
 
+local fio = require('fio')
+
 box.cfg{log = "tarantool.log"}
 build_path = os.getenv("BUILDDIR")
-package.cpath = build_path .. '/test/app-tap/?.so;' .. build_path .. '/test/app-tap/?.dylib;'
+package.cpath = fio.pathjoin(build_path, 'test/app-tap/?.so'   ) .. ';' ..
+                fio.pathjoin(build_path, 'test/app-tap/?.dylib') .. ';' ..
+                package.cpath
 
 local function test_pushcdata(test, module)
     test:plan(6)
@@ -33,11 +37,15 @@ local function test_pushcdata(test, module)
 end
 
 local test = require('tap').test("module_api", function(test)
-    test:plan(22)
+    test:plan(23)
     local status, module = pcall(require, 'module_api')
     test:is(status, true, "module")
     test:ok(status, "module is loaded")
     if not status then
+        test:diag("Failed to load library:")
+        for _, line in ipairs(module:split("\n")) do
+            test:diag("%s", line)
+        end
         return
     end
 
@@ -57,4 +65,5 @@ local test = require('tap').test("module_api", function(test)
 
     space:drop()
 end)
+
 os.exit(0)
-- 
2.16.2





More information about the Tarantool-patches mailing list