Hi, Sergey! Thanks for the patch! LGTM     -- Best regards, Maxim Kokryashkin     >Пятница, 6 октября 2023, 17:16 +03:00 от Sergey Bronnikov via Tarantool-patches : >  >From: Sergey Bronnikov < sergeyb@tarantool.org > > >Reported by Mathias Westerdahl. > >(cherry picked from commit 633f265f67f322cbe2c5fd11d3e46d968ac220f7) > >Lua 5.1 Reference Manual [1] defines a function `lua_concat`, that: >  >> void lua_concat (lua_State *L, int n); >> >> Concatenates the n values at the top of the stack, pops them, and leaves >> the result at the top. >Without the patch `lua_concat()` behaved incorrectly with userdata with >defined `__concat` metamethod. The problem is GC64-specific. > >Assuming we have three literals and a userdata with defined "__concat" >metamethod on top of the Lua stack: > >1 [string] >2 [string] >3 [string] >4 [string] >5 [userdata] <--- top > >On attempt to concatenate *two* items on top of the Lua stack, >`lua_concat()` concatenates *four* items and leaves result on the top: > >1 [string] >2 [string][string][string][userdata] <--- top > >The problem is in incorrect calculation of `n` counter in the loop in >implementation of function `lua_concat`. Without the fix `n` is equal to >3 at the end of the first iteration and therefore it goes to the next >iteration of concatenation. In the fixed implementation of >`lua_concat()` `n` is equal to 1 at the end of the first loop iteration, >decremented in a loop postcondition and breaks the loop. > >The patch fixes incorrect behaviour. > >1. https://www.lua.org/manual/5.1/manual.html > >Sergey Bronnikov: >* added the description and the test for the problem > >Part of tarantool/tarantool#8825 >--- >PR: https://github.com/tarantool/tarantool/pull/9176 >Branch: https://github.com/tarantool/luajit/commits/ligurio/lj-881-fix-concat >Issues: >- https://github.com/LuaJIT/LuaJIT/issues/881 >- https://github.com/tarantool/tarantool/issues/8825 > >Changes: >- fixed typos >- updated assertions (bool asserts replaced with int assertions) >- replaced userdata with struct with userdata with integer per Max's request >that's all, folks > > src/lj_api.c | 2 +- > .../lj-881-fix-lua-concat.test.c | 95 +++++++++++++++++++ > 2 files changed, 96 insertions(+), 1 deletion(-) > create mode 100644 test/tarantool-c-tests/lj-881-fix-lua-concat.test.c > >diff --git a/src/lj_api.c b/src/lj_api.c >index 05e02029..3bacad33 100644 >--- a/src/lj_api.c >+++ b/src/lj_api.c >@@ -801,7 +801,7 @@ LUA_API void lua_concat(lua_State *L, int n) >  L->top -= n; >  break; >       } >- n -= (int)(L->top - top); >+ n -= (int)(L->top - (top - 2*LJ_FR2)); >       L->top = top+2; >       jit_secure_call(L, top, 1+1); >       L->top -= 1+LJ_FR2; >diff --git a/test/tarantool-c-tests/lj-881-fix-lua-concat.test.c b/test/tarantool-c-tests/lj-881-fix-lua-concat.test.c >new file mode 100644 >index 00000000..1b1f146f >--- /dev/null >+++ b/test/tarantool-c-tests/lj-881-fix-lua-concat.test.c >@@ -0,0 +1,95 @@ >+#include >+ >+#include "lua.h" >+#include "lauxlib.h" >+ >+#include "test.h" >+#include "utils.h" >+ >+/* >+ * This test demonstrates LuaJIT's incorrect behaviour, when >+ * calling `lua_concat()` with userdata with __concat metamethod. >+ * See https://github.com/LuaJIT/LuaJIT/issues/881 for details. >+ */ >+ >+#define MT_NAME "int" >+ >+static int INFO_concat(lua_State *L) >+{ >+ const char *s = luaL_checkstring(L, 1); >+ int *n = (int *)luaL_checkudata(L, 2, MT_NAME); >+ /* Do non-default concatenation. */ >+ lua_pushfstring(L, "%s + %d", s, *n); >+ return 1; >+} >+ >+static const luaL_Reg mt[] = >+{ >+ {"__concat", INFO_concat}, >+ {NULL, NULL} >+}; >+ >+static int lua_concat_testcase(void *test_state) >+{ >+ /* Setup. */ >+ lua_State *L = test_state; >+ const int top = 4; >+ >+ /* Create metatable and put it to the Lua registry. */ >+ luaL_newmetatable(L, MT_NAME); >+ /* Fill metatable. */ >+ luaL_register(L, 0, mt); >+ lua_pop(L, 1); >+ >+ assert_int_equal(lua_gettop(L), 0); >+ >+ lua_pushliteral(L, "C"); >+ lua_pushliteral(L, "B"); >+ lua_pushliteral(L, "A"); >+ >+ int *n = (int *)lua_newuserdata(L, sizeof(int)); >+ *n = 100; >+ >+ luaL_getmetatable(L, MT_NAME); >+ lua_setmetatable(L, -2); >+ >+ assert_int_equal(lua_gettop(L), top); >+ >+ /* Test body. */ >+ >+ /* >+ * void lua_concat (lua_State *L, int n); >+ * >+ * Concatenates the n values at the top of the stack, >+ * pops them, and leaves the result at the top. If n is 1, >+ * the result is the single value on the stack; if n is 0, >+ * the result is the empty string [1]. >+ * >+ * 1. https://www.lua.org/manual/5.1/manual.html >+ */ >+ >+ /* Concatenate two elements on top. */ >+ lua_concat(L, 2); >+ >+ const char *str = lua_tostring(L, -1); >+ assert_int_equal(lua_gettop(L), top - 2 + 1); >+ const char expected_str[] = "A + 100"; >+ assert_str_equal(str, expected_str, strlen(expected_str)); >+ >+ /* Teardown. */ >+ lua_settop(L, 0); >+ >+ return TEST_EXIT_SUCCESS; >+} >+ >+int main(void) >+{ >+ lua_State *L = utils_lua_init(); >+ const struct test_unit tgroup[] = { >+ test_unit_def(lua_concat_testcase), >+ }; >+ const int test_result = test_run_group(tgroup, L); >+ utils_lua_close(L); >+ >+ return test_result; >+} >-- >2.34.1