diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-12 11:26:38 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-12 11:26:38 +0200 |
commit | fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945 (patch) | |
tree | 01f9bbebd6aab5f9be47597e4f62dd0f10853d3a | |
parent | f1c3401e80a3228ee3ef5110e73712e1aa879fca (diff) | |
download | lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.gz lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.bz2 lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.zip |
Add support for to-be-closed linda
-rw-r--r-- | CHANGES | 1 | ||||
-rw-r--r-- | deep_test/deep_test.cpp | 3 | ||||
-rw-r--r-- | docs/index.html | 9 | ||||
-rw-r--r-- | src/compat.cpp | 2 | ||||
-rw-r--r-- | src/deep.cpp | 3 | ||||
-rw-r--r-- | src/deep.h | 2 | ||||
-rw-r--r-- | src/linda.cpp | 115 | ||||
-rw-r--r-- | src/lindafactory.cpp | 23 |
8 files changed, 116 insertions, 42 deletions
@@ -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>"<unresolved>"</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>"<unresolved>"</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 |
95 | int lua_getiuservalue(lua_State* L_, int idx_, int n_) | 95 | int 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 | */ |
298 | int DeepFactory::pushDeepUserdata(DestState const L_, int const nuv_) const | 298 | void 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 | // ################################################################################################# |
@@ -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) | ||
287 | static 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) | |||
811 | namespace { | 838 | namespace { |
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 | */ |
844 | LUAG_FUNC(linda) | 874 | LUAG_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 | ||
106 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const | 106 | DeepPrelude* 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") { |