diff options
| -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") { |
