aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/intercopycontext.cpp29
-rw-r--r--src/tools.cpp31
-rw-r--r--src/tools.hpp3
-rw-r--r--unit_tests/UnitTests.vcxproj.filters3
-rw-r--r--unit_tests/linda_tests.cpp1
-rw-r--r--unit_tests/scripts/lane/tasking_send_receive_code.lua18
-rw-r--r--unit_tests/scripts/linda/send_receive_func_and_string.lua13
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
179void InterCopyContext::copyFunction() const 179void 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
48namespace { 48namespace {
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
391MAKE_TEST_CASE(linda, multiple_keepers) 391MAKE_TEST_CASE(linda, multiple_keepers)
392MAKE_TEST_CASE(linda, send_receive) 392MAKE_TEST_CASE(linda, send_receive)
393MAKE_TEST_CASE(linda, send_receive_func_and_string)
393MAKE_TEST_CASE(linda, send_registered_userdata) 394MAKE_TEST_CASE(linda, send_registered_userdata)
394MAKE_TEST_CASE(linda, wake_period) 395MAKE_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")
66end 65end
67 66
68local linda = lanes_linda{name = "auto"} 67local linda = lanes_linda{name = "auto"}
69local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch 68local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch
70linda:send("down", function(linda) linda:send("up", "ready!") end, 69linda: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--
74local k,s= linda:receive(1, "up") 72local k,s= linda:receive(1, "up")
75if t2.status == "error" then 73if 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)
78end 76end
79PRINT(s) 77PRINT(s)
80assert(s=="ready!", s .. " is not 'ready!'") 78assert(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 @@
1local lanes = require "lanes"
2
3-- a newly created linda doesn't contain anything
4local l = lanes.linda()
5
6-- send a function and a string, make sure that's what we read back
7l:send("k", function() end, "str")
8local c = l:count("k")
9assert(c == 2, "got " .. c)
10local k, v1, v2 = l:receive_batched("k", 2)
11local tv1, tv2 = type(v1), type(v2)
12assert(k == "k" and tv1 == "function" and tv2 == "string", "got " .. tv1 .. " " .. tv2)
13assert(l:count("k") == 0)