diff options
| -rw-r--r-- | src/intercopycontext.cpp | 29 | ||||
| -rw-r--r-- | src/tools.cpp | 31 | ||||
| -rw-r--r-- | src/tools.hpp | 3 | ||||
| -rw-r--r-- | unit_tests/UnitTests.vcxproj.filters | 3 | ||||
| -rw-r--r-- | unit_tests/linda_tests.cpp | 1 | ||||
| -rw-r--r-- | unit_tests/scripts/lane/tasking_send_receive_code.lua | 18 | ||||
| -rw-r--r-- | unit_tests/scripts/linda/send_receive_func_and_string.lua | 13 |
7 files changed, 53 insertions, 45 deletions
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index 653eeb6..6e9b66c 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp | |||
| @@ -178,9 +178,10 @@ static lua_Integer get_mt_id(Universe* const U_, lua_State* const L_, StackIndex | |||
| 178 | // L2 has the cache key for this function at the top of the stack | 178 | // L2 has the cache key for this function at the top of the stack |
| 179 | void InterCopyContext::copyFunction() const | 179 | void InterCopyContext::copyFunction() const |
| 180 | { | 180 | { |
| 181 | LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p | 181 | LUA_ASSERT(L1, L2_cache_i != 0); // L1: ... f L2: ... {cache} ... p |
| 182 | STACK_GROW(L1, 2); | 182 | STACK_GROW(L1, 2); |
| 183 | STACK_CHECK_START_REL(L1, 0); | 183 | STACK_CHECK_START_REL(L1, 0); |
| 184 | STACK_CHECK_START_REL(L2, 0); | ||
| 184 | 185 | ||
| 185 | // 'luaW_dump()' needs the function at top of stack | 186 | // 'luaW_dump()' needs the function at top of stack |
| 186 | // if already on top of the stack, no need to push again | 187 | // if already on top of the stack, no need to push again |
| @@ -194,17 +195,16 @@ void InterCopyContext::copyFunction() const | |||
| 194 | // to the writer" (and we only return 0) | 195 | // to the writer" (and we only return 0) |
| 195 | // not sure this could ever fail but for memory shortage reasons | 196 | // not sure this could ever fail but for memory shortage reasons |
| 196 | // last argument is Lua 5.4-specific (no stripping) | 197 | // last argument is Lua 5.4-specific (no stripping) |
| 197 | if (tools::PushFunctionBytecode(L1, U->stripFunctions) != 0) { // L1: ... f "<bytecode>" | 198 | tools::PushFunctionBytecode(L1, L2, U->stripFunctions); // L1: ... f L2: ... {cache} ... p "<bytecode>" |
| 198 | raise_luaL_error(getErrL(), "internal error: function dump failed."); | ||
| 199 | } | ||
| 200 | 199 | ||
| 201 | // if pushed, we need to pop | 200 | // if pushed, we need to pop |
| 202 | if (_needToPush) { | 201 | if (_needToPush) { |
| 203 | lua_remove(L1, -2); // L1: ... "<bytecode>" | 202 | lua_pop(L1, 1); // L1: ... |
| 204 | } | 203 | } |
| 205 | 204 | ||
| 206 | // When we are done, the stack should be the original one, with the bytecode string added on top | 205 | // When we are done, the stack of L1 should be the original one, with the bytecode string added on top of L2 |
| 207 | STACK_CHECK(L1, 1); | 206 | STACK_CHECK(L1, 0); |
| 207 | STACK_CHECK(L2, 1); | ||
| 208 | 208 | ||
| 209 | // transfer the bytecode, then the upvalues, to create a similar closure | 209 | // transfer the bytecode, then the upvalues, to create a similar closure |
| 210 | { | 210 | { |
| @@ -213,16 +213,16 @@ void InterCopyContext::copyFunction() const | |||
| 213 | if constexpr (LOG_FUNC_INFO) | 213 | if constexpr (LOG_FUNC_INFO) |
| 214 | { | 214 | { |
| 215 | lua_Debug _ar; | 215 | lua_Debug _ar; |
| 216 | lua_pushvalue(L1, L1_i); // L1: ... "<bytecode>" f | 216 | lua_pushvalue(L1, L1_i); // L1: ... f |
| 217 | // "To get information about a function you push it onto the stack and start the what string with the character '>'." | 217 | // "To get information about a function you push it onto the stack and start the what string with the character '>'." |
| 218 | // fills 'fname' 'namewhat' and 'linedefined', pops function | 218 | // fills 'fname' 'namewhat' and 'linedefined', pops function |
| 219 | lua_getinfo(L1, ">nS", &_ar); // L1: ... "<bytecode>" | 219 | lua_getinfo(L1, ">nS", &_ar); // L1: ... |
| 220 | _fname = _ar.namewhat; | 220 | _fname = _ar.namewhat; |
| 221 | DEBUGSPEW_CODE(DebugSpew(U) << "FNAME: " << _ar.short_src << " @ " << _ar.linedefined << std::endl); | 221 | DEBUGSPEW_CODE(DebugSpew(U) << "FNAME: " << _ar.short_src << " @ " << _ar.linedefined << std::endl); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | { | 224 | { |
| 225 | std::string_view const _bytecode{ luaW_tostring(L1, kIdxTop) }; // L1: ... "<bytecode>" | 225 | std::string_view const _bytecode{ luaW_tostring(L2, kIdxTop) }; // L2: ... {cache} ... p "<bytecode>" |
| 226 | LUA_ASSERT(L1, !_bytecode.empty()); | 226 | LUA_ASSERT(L1, !_bytecode.empty()); |
| 227 | STACK_GROW(L2, 2); | 227 | STACK_GROW(L2, 2); |
| 228 | // Note: Line numbers seem to be taken precisely from the | 228 | // Note: Line numbers seem to be taken precisely from the |
| @@ -231,15 +231,15 @@ void InterCopyContext::copyFunction() const | |||
| 231 | // | 231 | // |
| 232 | // TBD: Can we get the function's original name through, as well? | 232 | // TBD: Can we get the function's original name through, as well? |
| 233 | // | 233 | // |
| 234 | if (luaL_loadbuffer(L2, _bytecode.data(), _bytecode.size(), _fname) != 0) { // L2: ... {cache} ... p function | 234 | if (luaL_loadbuffer(L2, _bytecode.data(), _bytecode.size(), _fname) != 0) { // L2: ... {cache} ... p "<bytecode>" function |
| 235 | // chunk is precompiled so only LUA_ERRMEM can happen | 235 | // chunk is precompiled so only LUA_ERRMEM can happen |
| 236 | // "Otherwise, it pushes an error message" | 236 | // "Otherwise, it pushes an error message" |
| 237 | // | 237 | // |
| 238 | STACK_GROW(L1, 1); | 238 | STACK_GROW(L1, 1); |
| 239 | raise_luaL_error(getErrL(), "%s: %s", _fname, lua_tostring(L2, -1)); | 239 | raise_luaL_error(getErrL(), "%s: %s", _fname, lua_tostring(L2, kIdxTop)); |
| 240 | } | 240 | } |
| 241 | // remove the dumped string | 241 | // remove the dumped string |
| 242 | lua_pop(L1, 1); // L1: ... | 242 | lua_replace(L2, -2); // L2: ... {cache} ... p function |
| 243 | // now set the cache as soon as we can. | 243 | // now set the cache as soon as we can. |
| 244 | // this is necessary if one of the function's upvalues references it indirectly | 244 | // this is necessary if one of the function's upvalues references it indirectly |
| 245 | // we need to find it in the cache even if it isn't fully transfered yet | 245 | // we need to find it in the cache even if it isn't fully transfered yet |
| @@ -249,6 +249,7 @@ void InterCopyContext::copyFunction() const | |||
| 249 | lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function | 249 | lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function |
| 250 | } | 250 | } |
| 251 | STACK_CHECK(L1, 0); | 251 | STACK_CHECK(L1, 0); |
| 252 | STACK_CHECK(L2, 0); // cache key is replaced by the function, so no stack level change | ||
| 252 | 253 | ||
| 253 | /* push over any upvalues; references to this function will come from | 254 | /* push over any upvalues; references to this function will come from |
| 254 | * cache so we don't end up in eternal loop. | 255 | * cache so we don't end up in eternal loop. |
| @@ -279,6 +280,7 @@ void InterCopyContext::copyFunction() const | |||
| 279 | lua_pop(L1, 1); // L1: ... | 280 | lua_pop(L1, 1); // L1: ... |
| 280 | } // L2: ... {cache} ... function + 'n' upvalues (>=0) | 281 | } // L2: ... {cache} ... function + 'n' upvalues (>=0) |
| 281 | STACK_CHECK(L1, 0); | 282 | STACK_CHECK(L1, 0); |
| 283 | STACK_CHECK(L2, _n); | ||
| 282 | 284 | ||
| 283 | // Set upvalues (originally set to 'nil' by 'lua_load') | 285 | // Set upvalues (originally set to 'nil' by 'lua_load') |
| 284 | for (StackIndex const _func_index{ lua_gettop(L2) - _n }; _n > 0; --_n) { | 286 | for (StackIndex const _func_index{ lua_gettop(L2) - _n }; _n > 0; --_n) { |
| @@ -289,6 +291,7 @@ void InterCopyContext::copyFunction() const | |||
| 289 | // once all upvalues have been set we are left | 291 | // once all upvalues have been set we are left |
| 290 | // with the function at the top of the stack // L2: ... {cache} ... function | 292 | // with the function at the top of the stack // L2: ... {cache} ... function |
| 291 | } | 293 | } |
| 294 | STACK_CHECK(L2, 0); | ||
| 292 | STACK_CHECK(L1, 0); | 295 | STACK_CHECK(L1, 0); |
| 293 | } | 296 | } |
| 294 | 297 | ||
diff --git a/src/tools.cpp b/src/tools.cpp index c6e9cc3..cd1c593 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
| @@ -47,13 +47,9 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull }; | |||
| 47 | 47 | ||
| 48 | namespace { | 48 | namespace { |
| 49 | namespace local { | 49 | namespace local { |
| 50 | static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_) | 50 | static int buf_writer([[maybe_unused]] lua_State* const L_, void const* b_, size_t size_, void* ud_) |
| 51 | { | 51 | { |
| 52 | auto* const _B{ static_cast<luaL_Buffer*>(ud_) }; | 52 | auto* const _B{ static_cast<luaL_Buffer*>(ud_) }; |
| 53 | if (!_B->L) { | ||
| 54 | luaL_buffinit(L_, _B); | ||
| 55 | } | ||
| 56 | // starting with Lua 5.5, the writer is called one last time with nullptr, 0 at the end | ||
| 57 | if (b_ && size_) { | 53 | if (b_ && size_) { |
| 58 | luaL_addlstring(_B, static_cast<char const*>(b_), size_); | 54 | luaL_addlstring(_B, static_cast<char const*>(b_), size_); |
| 59 | } | 55 | } |
| @@ -79,9 +75,7 @@ namespace { | |||
| 79 | * +-------------------+------------+----------+ | 75 | * +-------------------+------------+----------+ |
| 80 | * | bytecode | C function | JIT-fast | | 76 | * | bytecode | C function | JIT-fast | |
| 81 | * +-----------------+-------------------+------------+----------+ | 77 | * +-----------------+-------------------+------------+----------+ |
| 82 | * | lua_topointer | | | | | 78 | * | lua_tocfunction | nullptr | <some p> | nullptr | |
| 83 | * +-----------------+-------------------+------------+----------+ | ||
| 84 | * | lua_tocfunction | nullptr | | nullptr | | ||
| 85 | * +-----------------+-------------------+------------+----------+ | 79 | * +-----------------+-------------------+------------+----------+ |
| 86 | * | luaW_dump | kWriterReturnCode | 1 | 1 | | 80 | * | luaW_dump | kWriterReturnCode | 1 | 1 | |
| 87 | * +-----------------+-------------------+------------+----------+ | 81 | * +-----------------+-------------------+------------+----------+ |
| @@ -139,20 +133,17 @@ namespace tools { | |||
| 139 | 133 | ||
| 140 | // ############################################################################################# | 134 | // ############################################################################################# |
| 141 | 135 | ||
| 142 | [[nodiscard]] | 136 | void PushFunctionBytecode(SourceState const L1_, DestState const L2_, int const strip_) |
| 143 | int PushFunctionBytecode(lua_State* const L_, int const strip_) | ||
| 144 | { | 137 | { |
| 145 | luaL_Buffer B{}; | 138 | luaL_Buffer B{}; |
| 146 | // WORKAROUND FOR Lua 5.5 beta: lua_dump followed by luaL_pushresult pops the function from the stack before adding the bytecode string | 139 | STACK_CHECK_START_REL(L1_, 0); |
| 147 | // so I need to duplicate it so that I end up with the original stack and the bytecode string pushed on top | 140 | STACK_CHECK_START_REL(L2_, 0); |
| 148 | if constexpr (LUA_VERSION_NUM == 505) { | 141 | STACK_GROW(L2_, 2); |
| 149 | lua_pushvalue(L_, kIdxTop); | 142 | luaL_buffinit(L2_, &B); // L1_: ... f L2_: ... <B stuff> |
| 150 | } | 143 | luaW_dump(L1_, local::buf_writer, &B, strip_); |
| 151 | int const result_{ luaW_dump(L_, local::buf_writer, &B, strip_) }; | 144 | luaL_pushresult(&B); // L2_: ... "<bytecode>" |
| 152 | if (result_ == 0) { // documentation says it should always be the case (because our writer only ever returns 0), but better safe than sorry | 145 | STACK_CHECK(L2_, 1); |
| 153 | luaL_pushresult(&B); | 146 | STACK_CHECK(L1_, 0); |
| 154 | } | ||
| 155 | return result_; | ||
| 156 | } | 147 | } |
| 157 | } // namespace tools | 148 | } // namespace tools |
| 158 | 149 | ||
diff --git a/src/tools.hpp b/src/tools.hpp index 51cbb9b..c555344 100644 --- a/src/tools.hpp +++ b/src/tools.hpp | |||
| @@ -37,7 +37,6 @@ namespace tools { | |||
| 37 | void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_); | 37 | void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_); |
| 38 | [[nodiscard]] | 38 | [[nodiscard]] |
| 39 | std::string_view PushFQN(lua_State* L_, StackIndex t_); | 39 | std::string_view PushFQN(lua_State* L_, StackIndex t_); |
| 40 | [[nodiscard]] | 40 | void PushFunctionBytecode(SourceState L1_, DestState L2_, int strip_); |
| 41 | int PushFunctionBytecode(lua_State* L_, int strip_); | ||
| 42 | void SerializeRequire(lua_State* L_); | 41 | void SerializeRequire(lua_State* L_); |
| 43 | } // namespace tools | 42 | } // namespace tools |
diff --git a/unit_tests/UnitTests.vcxproj.filters b/unit_tests/UnitTests.vcxproj.filters index a1db461..33d484a 100644 --- a/unit_tests/UnitTests.vcxproj.filters +++ b/unit_tests/UnitTests.vcxproj.filters | |||
| @@ -146,5 +146,8 @@ | |||
| 146 | <None Include="scripts\coro\cancelling_suspended.lua"> | 146 | <None Include="scripts\coro\cancelling_suspended.lua"> |
| 147 | <Filter>Scripts\coro</Filter> | 147 | <Filter>Scripts\coro</Filter> |
| 148 | </None> | 148 | </None> |
| 149 | <None Include="scripts\linda\send_receive_func_and_string.lua"> | ||
| 150 | <Filter>Scripts\linda</Filter> | ||
| 151 | </None> | ||
| 149 | </ItemGroup> | 152 | </ItemGroup> |
| 150 | </Project> \ No newline at end of file | 153 | </Project> \ No newline at end of file |
diff --git a/unit_tests/linda_tests.cpp b/unit_tests/linda_tests.cpp index a36723a..8d3cd50 100644 --- a/unit_tests/linda_tests.cpp +++ b/unit_tests/linda_tests.cpp | |||
| @@ -390,6 +390,7 @@ TEST_CASE("scripted_tests." #DIR "." #FILE) \ | |||
| 390 | 390 | ||
| 391 | MAKE_TEST_CASE(linda, multiple_keepers) | 391 | MAKE_TEST_CASE(linda, multiple_keepers) |
| 392 | MAKE_TEST_CASE(linda, send_receive) | 392 | MAKE_TEST_CASE(linda, send_receive) |
| 393 | MAKE_TEST_CASE(linda, send_receive_func_and_string) | ||
| 393 | MAKE_TEST_CASE(linda, send_registered_userdata) | 394 | MAKE_TEST_CASE(linda, send_registered_userdata) |
| 394 | MAKE_TEST_CASE(linda, wake_period) | 395 | MAKE_TEST_CASE(linda, wake_period) |
| 395 | 396 | ||
diff --git a/unit_tests/scripts/lane/tasking_send_receive_code.lua b/unit_tests/scripts/lane/tasking_send_receive_code.lua index cb3663f..fdc2602 100644 --- a/unit_tests/scripts/lane/tasking_send_receive_code.lua +++ b/unit_tests/scripts/lane/tasking_send_receive_code.lua | |||
| @@ -53,28 +53,26 @@ local function chunk2(linda) | |||
| 53 | assert(info.linedefined == 32, "bad linedefined") -- start of 'chunk2' | 53 | assert(info.linedefined == 32, "bad linedefined") -- start of 'chunk2' |
| 54 | assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo' | 54 | assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo' |
| 55 | assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2' | 55 | assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2' |
| 56 | local k,func= linda:receive("down") | 56 | assert(linda:count("down") == 2, "bad linda contents") -- function, "ok" |
| 57 | assert(type(func)=="function", "not a function") | 57 | local k,func,str= linda:receive_batched("down", 2) |
| 58 | assert(k=="down") | 58 | assert(k=="down") |
| 59 | assert(type(func)=="function", "not a function") | ||
| 60 | assert(str=="ok", "bad receive result: " .. tostring(k) .. " -> ".. tostring(str)) | ||
| 61 | assert(linda:count("down") == 0, "bad linda contents") -- nothing | ||
| 59 | 62 | ||
| 60 | func(linda) | 63 | func(linda) |
| 61 | |||
| 62 | local k,str= linda:receive("down") | ||
| 63 | assert(str=="ok", "bad receive result") | ||
| 64 | |||
| 65 | linda:send("up", function() return ":)" end, "ok2") | 64 | linda:send("up", function() return ":)" end, "ok2") |
| 66 | end | 65 | end |
| 67 | 66 | ||
| 68 | local linda = lanes_linda{name = "auto"} | 67 | local linda = lanes_linda{name = "auto"} |
| 69 | local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch | 68 | local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch |
| 70 | linda:send("down", function(linda) linda:send("up", "ready!") end, | 69 | linda:send("down", function(linda) linda:send("up", "ready!") end, "ok") |
| 71 | "ok") | ||
| 72 | -- wait to see if the tiny function gets executed | 70 | -- wait to see if the tiny function gets executed |
| 73 | -- | 71 | -- |
| 74 | local k,s= linda:receive(1, "up") | 72 | local k,s= linda:receive(1, "up") |
| 75 | if t2.status == "error" then | 73 | if t2.status == "error" then |
| 76 | PRINT("t2 error: " , t2:join()) | 74 | local n,err,s = t2:join() |
| 77 | assert(false) | 75 | assert(false, "t2 error: " .. err) |
| 78 | end | 76 | end |
| 79 | PRINT(s) | 77 | PRINT(s) |
| 80 | assert(s=="ready!", s .. " is not 'ready!'") | 78 | assert(s=="ready!", s .. " is not 'ready!'") |
diff --git a/unit_tests/scripts/linda/send_receive_func_and_string.lua b/unit_tests/scripts/linda/send_receive_func_and_string.lua new file mode 100644 index 0000000..188cfcd --- /dev/null +++ b/unit_tests/scripts/linda/send_receive_func_and_string.lua | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | -- a newly created linda doesn't contain anything | ||
| 4 | local l = lanes.linda() | ||
| 5 | |||
| 6 | -- send a function and a string, make sure that's what we read back | ||
| 7 | l:send("k", function() end, "str") | ||
| 8 | local c = l:count("k") | ||
| 9 | assert(c == 2, "got " .. c) | ||
| 10 | local k, v1, v2 = l:receive_batched("k", 2) | ||
| 11 | local tv1, tv2 = type(v1), type(v2) | ||
| 12 | assert(k == "k" and tv1 == "function" and tv2 == "string", "got " .. tv1 .. " " .. tv2) | ||
| 13 | assert(l:count("k") == 0) | ||
