aboutsummaryrefslogtreecommitdiff
path: root/src/linda.cpp
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 /src/linda.cpp
parentf1c3401e80a3228ee3ef5110e73712e1aa879fca (diff)
downloadlanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.gz
lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.tar.bz2
lanes-fffa17d7e28f4a3147adf1f2ae2a73c4b0f7b945.zip
Add support for to-be-closed linda
Diffstat (limited to 'src/linda.cpp')
-rw-r--r--src/linda.cpp115
1 files changed, 101 insertions, 14 deletions
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}