aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-12 11:26:38 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-12 11:26:38 +0200
commitfffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945 (patch)
tree01f9bbebd6aab5f9be47597e4f62dd0f10853d3a
parentf1c3401e80a3228ee3ef5110e73712e1aa879fca (diff)
downloadlanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.gz
lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.bz2
lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.zip
Add support for to-be-closed linda
-rw-r--r--CHANGES1
-rw-r--r--deep_test/deep_test.cpp3
-rw-r--r--docs/index.html9
-rw-r--r--src/compat.cpp2
-rw-r--r--src/deep.cpp3
-rw-r--r--src/deep.h2
-rw-r--r--src/linda.cpp115
-rw-r--r--src/lindafactory.cpp23
8 files changed, 116 insertions, 42 deletions
diff --git a/CHANGES b/CHANGES
index 64794b1..f10f160 100644
--- a/CHANGES
+++ b/CHANGES
@@ -25,6 +25,7 @@ CHANGE 2: BGe 11-Jun-24
25 - providing "auto" as name when constructing a Linda cause Lanes to provide a name built from the source location of the construction. 25 - providing "auto" as name when constructing a Linda cause Lanes to provide a name built from the source location of the construction.
26 - specifying a group to lanes.linda() is mandatory when Lanes is configured with user Keepers. 26 - specifying a group to lanes.linda() is mandatory when Lanes is configured with user Keepers.
27 - linda:deep() result no longer contains the raw C pointer of the Linda object. 27 - linda:deep() result no longer contains the raw C pointer of the Linda object.
28 - Lindas have a __close metamethod that calls any provided suitable handler at Linda creation.
28 - Lane generator settings: 29 - Lane generator settings:
29 - error_trace_level added. Replaces the global verbose_errors setting. 30 - error_trace_level added. Replaces the global verbose_errors setting.
30 - name added. Can be used to set the name early (before the lane body calls set_debug_threadname()). 31 - name added. Can be used to set the name early (before the lane body calls set_debug_threadname()).
diff --git a/deep_test/deep_test.cpp b/deep_test/deep_test.cpp
index 178075d..2497742 100644
--- a/deep_test/deep_test.cpp
+++ b/deep_test/deep_test.cpp
@@ -131,7 +131,8 @@ int luaD_new_deep(lua_State* L)
131{ 131{
132 int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) }; 132 int const nuv{ static_cast<int>(luaL_optinteger(L, 1, 0)) };
133 lua_settop(L, 0); 133 lua_settop(L, 0);
134 return MyDeepFactory::Instance.pushDeepUserdata(DestState{ L }, nuv); 134 MyDeepFactory::Instance.pushDeepUserdata(DestState{ L }, nuv);
135 return 1;
135} 136}
136 137
137// ################################################################################################# 138// #################################################################################################
diff --git a/docs/index.html b/docs/index.html
index 10bf832..be8324c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -70,7 +70,7 @@
70 </p> 70 </p>
71 71
72 <p> 72 <p>
73 This document was revised on 15-May-24, and applies to version <tt>4.0.0</tt>. 73 This document was revised on 12-Jun-24, and applies to version <tt>4.0.0</tt>.
74 </p> 74 </p>
75 </font> 75 </font>
76 </center> 76 </center>
@@ -1196,13 +1196,16 @@
1196</p> 1196</p>
1197 1197
1198<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1198<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
1199 h = lanes.linda([opt_name, [opt_group]]) 1199 h = lanes.linda([name],[group],[close_handler])
1200</pre></td></tr></table> 1200</pre></td></tr></table>
1201 1201
1202<p> 1202<p>
1203 Arguments to <tt>lanes.linda()</tt> can be provided in any order, as long as there is a single string, a single number, and a single callable value (all are optional).<br />
1203 Converting the Linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>.<br /> 1204 Converting the Linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>.<br />
1204 If <tt>opt_name</tt> is omitted, it will evaluate to an hexadecimal number uniquely representing that Linda when the Linda is converted to a string. The value is the same as returned by <tt>linda:deep()</tt>.<br /> 1205 If <tt>opt_name</tt> is omitted, it will evaluate to an hexadecimal number uniquely representing that Linda when the Linda is converted to a string. The value is the same as returned by <tt>linda:deep()</tt>.<br />
1205 If <tt>opt_name</tt> is <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the Linda name will be <tt>"&lt;unresolved&gt;"</tt>. 1206 If <tt>opt_name</tt> is <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the Linda name will be <tt>"&lt;unresolved&gt;"</tt>.<br />
1207 If Lanes is configured with more than one Keeper state, <tt>group</tt> is mandatory.<br />
1208 If the Linda is to-be-closed (Lua 5.4+), and a <tt>close_handler</tt> is provided, it will be called with all the provided arguments. For older Lua versions, its presence will cause an error.
1206</p> 1209</p>
1207 1210
1208<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> 1211<table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre>
diff --git a/src/compat.cpp b/src/compat.cpp
index e1e7488..4076aa6 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -92,7 +92,7 @@ void* lua_newuserdatauv(lua_State* L_, size_t sz_, [[maybe_unused]] int nuvalue_
92// ################################################################################################# 92// #################################################################################################
93 93
94// push on stack uservalue #n of full userdata at idx 94// push on stack uservalue #n of full userdata at idx
95int lua_getiuservalue(lua_State* L_, int idx_, int n_) 95int lua_getiuservalue(lua_State* const L_, int const idx_, int const n_)
96{ 96{
97 STACK_CHECK_START_REL(L_, 0); 97 STACK_CHECK_START_REL(L_, 0);
98 // full userdata can have only 1 uservalue before 5.4 98 // full userdata can have only 1 uservalue before 5.4
diff --git a/src/deep.cpp b/src/deep.cpp
index b6a9a22..249c497 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -295,7 +295,7 @@ void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_,
295 * 295 *
296 * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' 296 * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()'
297 */ 297 */
298int DeepFactory::pushDeepUserdata(DestState const L_, int const nuv_) const 298void DeepFactory::pushDeepUserdata(DestState const L_, int const nuv_) const
299{ 299{
300 STACK_GROW(L_, 1); 300 STACK_GROW(L_, 1);
301 STACK_CHECK_START_REL(L_, 0); 301 STACK_CHECK_START_REL(L_, 0);
@@ -322,7 +322,6 @@ int DeepFactory::pushDeepUserdata(DestState const L_, int const nuv_) const
322 322
323 DeepFactory::PushDeepProxy(L_, _prelude, nuv_, LookupMode::LaneBody, L_); // proxy 323 DeepFactory::PushDeepProxy(L_, _prelude, nuv_, LookupMode::LaneBody, L_); // proxy
324 STACK_CHECK(L_, 1); 324 STACK_CHECK(L_, 1);
325 return 1;
326} 325}
327 326
328// ################################################################################################# 327// #################################################################################################
diff --git a/src/deep.h b/src/deep.h
index fb62276..bb86c20 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -68,7 +68,7 @@ class DeepFactory
68 static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_); 68 static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_);
69 [[nodiscard]] static DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_); 69 [[nodiscard]] static DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_);
70 static void PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_, lua_State* errL_); 70 static void PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_, lua_State* errL_);
71 [[nodiscard]] int pushDeepUserdata(DestState L_, int nuv_) const; 71 void pushDeepUserdata(DestState L_, int nuv_) const;
72 [[nodiscard]] DeepPrelude* toDeep(lua_State* L_, int index_) const; 72 [[nodiscard]] DeepPrelude* toDeep(lua_State* L_, int index_) const;
73}; 73};
74 74
diff --git a/src/linda.cpp b/src/linda.cpp
index 1bd5d16..91138c5 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -282,6 +282,33 @@ LUAG_FUNC(linda_cancel)
282 282
283// ################################################################################################# 283// #################################################################################################
284 284
285#if LUA_VERSION_NUM >= 504
286// linda:__close(err|nil)
287static LUAG_FUNC(linda_close)
288{
289 [[maybe_unused]] Linda* const _linda{ ToLinda<false>(L_, 1) }; // L_: linda err|nil
290
291 // do we have a uservalue? it contains a callback
292 switch (lua_getiuservalue(L_, 1, 1)) {
293 case LUA_TTABLE: // callable table
294 case LUA_TUSERDATA: // callable userdata
295 case LUA_TFUNCTION: // L_: linda err|nil on_close()
296 lua_insert(L_, 1); // L_: on_close() linda err|nil
297 lua_call(L_, lua_gettop(L_) - 1, 0); // L_:
298 return 0;
299
300 case LUA_TNONE:
301 case LUA_TNIL:
302 return 0;
303
304 default:
305 raise_luaL_error(L_, "Invalid __close handler");
306 }
307}
308#endif // LUA_VERSION_NUM >= 504
309
310// #################################################################################################
311
285/* 312/*
286 * string = linda:__concat( a, b) 313 * string = linda:__concat( a, b)
287 * 314 *
@@ -811,6 +838,9 @@ LUAG_FUNC(linda_towatch)
811namespace { 838namespace {
812 namespace local { 839 namespace local {
813 static luaL_Reg const sLindaMT[] = { 840 static luaL_Reg const sLindaMT[] = {
841#if LUA_VERSION_NUM >= 504
842 { "__close", LG_linda_close },
843#endif // LUA_VERSION_NUM >= 504
814 { "__concat", LG_linda_concat }, 844 { "__concat", LG_linda_concat },
815 { "__tostring", LG_linda_tostring }, 845 { "__tostring", LG_linda_tostring },
816#if HAVE_DECODA_SUPPORT() 846#if HAVE_DECODA_SUPPORT()
@@ -837,31 +867,88 @@ namespace {
837// ################################################################################################# 867// #################################################################################################
838 868
839/* 869/*
840 * ud = lanes.linda( [name[,group]]) 870 * ud = lanes.linda( [name[,group[,close_handler]]])
841 * 871 *
842 * returns a linda object, or raises an error if creation failed 872 * returns a linda object, or raises an error if creation failed
843 */ 873 */
844LUAG_FUNC(linda) 874LUAG_FUNC(linda)
845{ 875{
876 static constexpr int kLastArg{ LUA_VERSION_NUM >= 504 ? 3 : 2};
846 int const _top{ lua_gettop(L_) }; 877 int const _top{ lua_gettop(L_) };
878 luaL_argcheck(L_, _top <= kLastArg, _top, "too many arguments");
879 int _closeHandlerIdx{};
880 int _nameIdx{};
847 int _groupIdx{}; 881 int _groupIdx{};
848 luaL_argcheck(L_, _top <= 2, _top, "too many arguments"); 882 for (int const _i : std::ranges::iota_view{ 1, _top + 1 }) {
849 if (_top == 1) { 883 switch (luaG_type(L_, _i)) {
850 LuaType const _t{ luaG_type(L_, 1) }; 884#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4
851 int const _nameIdx{ (_t == LuaType::STRING) ? 1 : 0 }; 885 case LuaType::FUNCTION:
852 _groupIdx = (_t == LuaType::NUMBER) ? 1 : 0; 886 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
853 luaL_argcheck(L_, _nameIdx || _groupIdx, 1, "wrong parameter (should be a string or a number)"); 887 _closeHandlerIdx = _i;
854 } else if (_top == 2) { 888 break;
855 luaL_checktype(L_, 1, LUA_TSTRING); 889
856 luaL_checktype(L_, 2, LUA_TNUMBER); 890 case LuaType::USERDATA:
857 _groupIdx = 2; 891 case LuaType::TABLE:
892 luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
893 luaL_argcheck(L_, luaL_getmetafield(L_, _i, "__call") != 0, _i, "__close handler is not callable");
894 lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it
895 _closeHandlerIdx = _i;
896 break;
897#endif // LUA_VERSION_NUM >= 504
898
899 case LuaType::STRING:
900 luaL_argcheck(L_, _nameIdx == 0, _i, "More than one name");
901 _nameIdx = _i;
902 break;
903
904 case LuaType::NUMBER:
905 luaL_argcheck(L_, _groupIdx == 0, _i, "More than one group");
906 _groupIdx = _i;
907 break;
908
909 default:
910 luaL_argcheck(L_, false, _i, "Bad argument type (should be a string, a number, or a callable type)");
911 }
858 } 912 }
913
859 int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() }; 914 int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() };
860 if (!_groupIdx) { 915 if (!_groupIdx) {
861 luaL_argcheck(L_, _nbKeepers < 2, 0, "there are multiple keepers, you must specify a group"); 916 luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
862 } else { 917 } else {
863 int const _group{ static_cast<int>(lua_tointeger(L_, _groupIdx)) }; 918 int const _group{ static_cast<int>(lua_tointeger(L_, _groupIdx)) };
864 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "group out of range"); 919 luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "Group out of range");
865 } 920 }
866 return LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, 0); 921
922 // done with argument checking, let's proceed
923 if constexpr (LUA_VERSION_NUM >= 504) {
924 // make sure we have kMaxArgs arguments on the stack for processing, with name, group, and handler, in that order
925 lua_settop(L_, kLastArg); // L_: a b c
926 // If either index is 0, lua_settop() adjusted the stack with a nil in slot kLastArg
927 lua_pushvalue(L_, _closeHandlerIdx ? _closeHandlerIdx : kLastArg); // L_: a b c close_handler
928 lua_pushvalue(L_, _groupIdx ? _groupIdx : kLastArg); // L_: a b c close_handler group
929 lua_pushvalue(L_, _nameIdx ? _nameIdx : kLastArg); // L_: a b c close_handler group name
930 lua_replace(L_, 1); // L_: name b c close_handler group
931 lua_replace(L_, 2); // L_: name group c close_handler
932 lua_replace(L_, 3); // L_: name group close_handler
933
934 // if we have a __close handler, we need a uservalue slot to store it
935 int const _nuv{ _closeHandlerIdx ? 1 : 0 };
936 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda
937 if (_closeHandlerIdx != 0) {
938 lua_replace(L_, 2); // L_: name linda close_handler
939 lua_setiuservalue(L_, 2, 1); // L_: name linda
940 }
941 // depending on whether we have a handler or not, the stack is not in the same state at this point
942 // just make sure we have our Linda at the top
943 LUA_ASSERT(L_, ToLinda<true>(L_, -1));
944 return 1;
945 } else { // no to-be-closed support
946 // ensure we have name, group in that order on the stack
947 if (_nameIdx > _groupIdx) {
948 lua_insert(L_, 1); // L_: name group
949 }
950 LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, 0); // L_: name group linda
951 return 1;
952 }
953
867} 954}
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 8622c12..863f16e 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -105,26 +105,9 @@ std::string_view LindaFactory::moduleName() const
105 105
106DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const 106DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const
107{ 107{
108 std::string_view _linda_name{}; 108 // we always expect name and group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified
109 LindaGroup _linda_group{ 0 }; 109 std::string_view _linda_name{ luaG_tostring(L_, 1) };
110 // should have a string and/or a number of the stack as parameters (name and group) 110 LindaGroup _linda_group{ static_cast<int>(lua_tointeger(L_, 2)) };
111 switch (lua_gettop(L_)) {
112 default: // 0
113 break;
114
115 case 1: // 1 parameter, either a name or a group
116 if (luaG_type(L_, -1) == LuaType::STRING) {
117 _linda_name = luaG_tostring(L_, -1);
118 } else {
119 _linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) };
120 }
121 break;
122
123 case 2: // 2 parameters, a name and group, in that order
124 _linda_name = luaG_tostring(L_, -2);
125 _linda_group = LindaGroup{ static_cast<int>(lua_tointeger(L_, -1)) };
126 break;
127 }
128 111
129 // store in the linda the location of the script that created it 112 // store in the linda the location of the script that created it
130 if (_linda_name == "auto") { 113 if (_linda_name == "auto") {