]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | #pragma once |
2 | ||
3 | #include <memory> | |
4 | #include <string> | |
5 | #include <string_view> | |
6 | #include <ctime> | |
7 | #include <lua.hpp> | |
8 | ||
9 | #include "include/common_fwd.h" | |
10 | ||
11 | namespace rgw::lua { | |
12 | ||
13 | // push ceph time in string format: "%Y-%m-%d %H:%M:%S" | |
14 | template <typename CephTime> | |
15 | void pushtime(lua_State* L, const CephTime& tp) | |
16 | { | |
17 | const auto tt = CephTime::clock::to_time_t(tp); | |
18 | const auto tm = *std::localtime(&tt); | |
19 | char buff[64]; | |
20 | std::strftime(buff, sizeof(buff), "%Y-%m-%d %H:%M:%S", &tm); | |
21 | lua_pushstring(L, buff); | |
22 | } | |
23 | ||
24 | static inline void pushstring(lua_State* L, std::string_view str) | |
25 | { | |
26 | lua_pushlstring(L, str.data(), str.size()); | |
27 | } | |
28 | ||
29 | static inline void unsetglobal(lua_State* L, const char* name) | |
30 | { | |
31 | lua_pushnil(L); | |
32 | lua_setglobal(L, name); | |
33 | } | |
34 | ||
35 | // dump the lua stack to stdout | |
36 | void stack_dump(lua_State* L); | |
37 | ||
38 | class lua_state_guard { | |
39 | lua_State* l; | |
40 | public: | |
41 | lua_state_guard(lua_State* _l) : l(_l) {} | |
42 | ~lua_state_guard() {lua_close(l);} | |
43 | void reset(lua_State* _l=nullptr) {l = _l;} | |
44 | }; | |
45 | ||
46 | constexpr auto ONE_UPVAL = 1; | |
47 | constexpr auto TWO_UPVALS = 2; | |
48 | constexpr auto THREE_UPVALS = 3; | |
49 | constexpr auto FOUR_UPVALS = 4; | |
50 | constexpr auto FIVE_UPVALS = 5; | |
51 | ||
52 | constexpr auto NO_RETURNVAL = 0; | |
53 | constexpr auto ONE_RETURNVAL = 1; | |
54 | constexpr auto TWO_RETURNVALS = 2; | |
55 | constexpr auto THREE_RETURNVALS = 3; | |
56 | constexpr auto FOUR_RETURNVALS = 4; | |
57 | // utility functions to create a metatable | |
58 | // and tie it to an unnamed table | |
59 | // | |
60 | // add an __index method to it, to allow reading values | |
61 | // if "readonly" parameter is set to "false", it will also add | |
62 | // a __newindex method to it, to allow writing values | |
63 | // if the "toplevel" parameter is set to "true", it will name the | |
64 | // table as well as the metatable, this would allow direct access from | |
65 | // the lua script. | |
66 | // | |
67 | // The MetaTable is expected to be a class with the following members: | |
68 | // Name (static function returning the unique name of the metatable) | |
69 | // TableName (static function returning the unique name of the table - needed only for "toplevel" tables) | |
70 | // Type (typename) - the type of the "upvalue" (the type that the meta table represent) | |
71 | // IndexClosure (static function return "int" and accept "lua_State*") | |
72 | // NewIndexClosure (static function return "int" and accept "lua_State*") | |
73 | // e.g. | |
74 | // struct MyStructMetaTable { | |
75 | // static std::string TableName() { | |
76 | // return "MyStruct"; | |
77 | // } | |
78 | // | |
79 | // using Type = MyStruct; | |
80 | // | |
81 | // static int IndexClosure(lua_State* L) { | |
82 | // const auto value = reinterpret_cast<const Type*>(lua_touserdata(L, lua_upvalueindex(1))); | |
83 | // ... | |
84 | // } | |
85 | ||
86 | // static int NewIndexClosure(lua_State* L) { | |
87 | // auto value = reinterpret_cast<Type*>(lua_touserdata(L, lua_upvalueindex(1))); | |
88 | // ... | |
89 | // } | |
90 | // }; | |
91 | // | |
92 | ||
93 | template<typename MetaTable, typename... Upvalues> | |
94 | void create_metatable(lua_State* L, bool toplevel, Upvalues... upvalues) | |
95 | { | |
96 | constexpr auto upvals_size = sizeof...(upvalues); | |
97 | const std::array<void*, upvals_size> upvalue_arr = {upvalues...}; | |
98 | // create table | |
99 | lua_newtable(L); | |
100 | if (toplevel) { | |
101 | // duplicate the table to make sure it remain in the stack | |
102 | lua_pushvalue(L, -1); | |
103 | // give table a name (in cae of "toplevel") | |
104 | lua_setglobal(L, MetaTable::TableName().c_str()); | |
105 | } | |
106 | // create metatable | |
107 | [[maybe_unused]] const auto rc = luaL_newmetatable(L, MetaTable::Name().c_str()); | |
108 | lua_pushliteral(L, "__index"); | |
109 | for (const auto upvalue : upvalue_arr) { | |
110 | lua_pushlightuserdata(L, upvalue); | |
111 | } | |
112 | lua_pushcclosure(L, MetaTable::IndexClosure, upvals_size); | |
113 | lua_rawset(L, -3); | |
114 | lua_pushliteral(L, "__newindex"); | |
115 | for (const auto upvalue : upvalue_arr) { | |
116 | lua_pushlightuserdata(L, upvalue); | |
117 | } | |
118 | lua_pushcclosure(L, MetaTable::NewIndexClosure, upvals_size); | |
119 | lua_rawset(L, -3); | |
120 | lua_pushliteral(L, "__pairs"); | |
121 | for (const auto upvalue : upvalue_arr) { | |
122 | lua_pushlightuserdata(L, upvalue); | |
123 | } | |
124 | lua_pushcclosure(L, MetaTable::PairsClosure, upvals_size); | |
125 | lua_rawset(L, -3); | |
126 | lua_pushliteral(L, "__len"); | |
127 | for (const auto upvalue : upvalue_arr) { | |
128 | lua_pushlightuserdata(L, upvalue); | |
129 | } | |
130 | lua_pushcclosure(L, MetaTable::LenClosure, upvals_size); | |
131 | lua_rawset(L, -3); | |
132 | // tie metatable and table | |
133 | lua_setmetatable(L, -2); | |
134 | } | |
135 | ||
136 | template<typename MetaTable> | |
137 | void create_metatable(lua_State* L, bool toplevel, std::unique_ptr<typename MetaTable::Type>& ptr) | |
138 | { | |
139 | if (ptr) { | |
140 | create_metatable<MetaTable>(L, toplevel, reinterpret_cast<void*>(ptr.get())); | |
141 | } else { | |
142 | lua_pushnil(L); | |
143 | } | |
144 | } | |
145 | ||
146 | // following struct may be used as a base class for other MetaTable classes | |
147 | // note, however, this is not mandatory to use it as a base | |
148 | struct EmptyMetaTable { | |
149 | // by default everythinmg is "readonly" | |
150 | // to change, overload this function in the derived | |
151 | static int NewIndexClosure(lua_State* L) { | |
20effc67 | 152 | return luaL_error(L, "trying to write to readonly field"); |
f67539c2 TL |
153 | } |
154 | ||
155 | // by default nothing is iterable | |
156 | // to change, overload this function in the derived | |
157 | static int PairsClosure(lua_State* L) { | |
20effc67 | 158 | return luaL_error(L, "trying to iterate over non-iterable field"); |
f67539c2 TL |
159 | } |
160 | ||
161 | // by default nothing is iterable | |
162 | // to change, overload this function in the derived | |
163 | static int LenClosure(lua_State* L) { | |
20effc67 | 164 | return luaL_error(L, "trying to get length of non-iterable field"); |
f67539c2 TL |
165 | } |
166 | ||
20effc67 TL |
167 | static int error_unknown_field(lua_State* L, const std::string& index, const std::string& table) { |
168 | return luaL_error(L, "unknown field name: %s provided to: %s", | |
169 | index.c_str(), table.c_str()); | |
f67539c2 TL |
170 | } |
171 | }; | |
172 | ||
173 | // create a debug log action | |
174 | // it expects CephContext to be captured | |
175 | // it expects one string parameter, which is the message to log | |
176 | // could be executed from any context that has CephContext | |
177 | // e.g. | |
178 | // RGWDebugLog("hello world from lua") | |
179 | // | |
180 | void create_debug_action(lua_State* L, CephContext* cct); | |
181 | ||
182 | // set the packages search path according to: | |
183 | // package.path = "<install_dir>/share/lua/5.3/?.lua" │ LuaRocks. | |
184 | // package.cpath= "<install_dir>/lib/lua/5.3/?.so" | |
185 | void set_package_path(lua_State* L, const std::string& install_dir); | |
186 | ||
187 | // open standard lua libs and remove the following functions: | |
188 | // os.exit() | |
189 | // load() | |
190 | // loadfile() | |
191 | // loadstring() | |
192 | // dofile() | |
193 | // and the "debug" library | |
194 | void open_standard_libs(lua_State* L); | |
195 | ||
196 | } | |
197 |