Pioneer
LuaMetaType.h
Go to the documentation of this file.
1 // Copyright © 2008-2021 Pioneer Developers. See AUTHORS.txt for details
2 // Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
3 
4 #pragma once
5 
6 #include "Lua.h"
7 #include "LuaCall.h"
8 #include "LuaManager.h"
9 #include "LuaPushPull.h"
10 #include "LuaTable.h"
11 
13 public:
14  LuaMetaTypeBase(const char *name) :
15  m_typeName(name)
16  {}
17 
18  // Creates and registers the lua-side object for this type.
19  void CreateMetaType(lua_State *l);
20 
21  const char *GetTypeName() const { return m_typeName.c_str(); }
22 
23  const char *GetParent() const { return m_parent.c_str(); }
24 
25  void SetParent(const char *parent) { m_parent = parent; }
26 
27  // Push the metatable to the lua stack.
28  void GetMetatable() const;
29 
30  // Returns true if the metatype has been correctly set up.
31  bool IsValid() const { return m_lua && m_ref != LUA_NOREF; }
32 
33  // Call this function to set the lua stack up to begin recording members
34  // and methods into the metatype.
35  // It is invalid to call other functions outside a StartRecording / StopRecording pair.
37  {
38  assert(IsValid());
39  assert(m_index == 0);
40 
41  GetMetatable();
42  m_index = lua_gettop(m_lua);
43  }
44 
45  // Stop recording and remove the metatype from the stack
47  {
48  assert(IsValid());
49  assert(m_index != 0);
50  lua_remove(m_lua, m_index);
51  m_index = 0;
52  }
53 
54  // Get all valid method/attribute names for the object on the top of the stack.
55  // Mainly intended to be used by the console, though it can also be used for
56  // debug dumping of properties, attributes, and methods
57  static void GetNames(std::vector<std::string> &names, const std::string &prefix = "", bool methodsOnly = false);
58 
59  // Get the lua-side metatable from a type name instead of a LuaRef.
60  static bool GetMetatableFromName(lua_State *l, const char *name);
61 
62 protected:
63  template <typename T, typename Dt>
64  using member_pointer = Dt T::*;
65 
66  template <typename T>
67  using free_function = int (*)(lua_State *, T *);
68 
69  template <typename T, typename Rt, typename... Args>
70  using member_function = Rt (T::*)(Args...);
71 
72  template <typename T, typename Rt, typename... Args>
73  using const_member_function = Rt (T::*)(Args...) const;
74 
75  template <typename T, typename Dt>
76  static int member_wrapper_(lua_State *L)
77  {
78  T *ptr = LuaPull<T *>(L, 1);
79  const char *name = lua_tostring(L, lua_upvalueindex(1));
80 
81  if (!ptr)
82  return luaL_error(L, "Null or invalid userdata accessed for property %s", name);
83 
84  if (lua_gettop(L) > 2)
85  return luaL_error(L, "Invalid number of arguments for property getter/setter %s", name);
86 
87  auto &t = PullPointerToMember<member_pointer<T, Dt>>(L, lua_upvalueindex(2));
88  if (lua_gettop(L) == 1) {
89  LuaPush<Dt>(L, ptr->*(t));
90  return 1;
91  } else {
92  (ptr->*(t)) = LuaPull<Dt>(L, 2);
93  return 0;
94  }
95  }
96 
97  template <typename T>
98  static int getter_member_wrapper_(lua_State *L)
99  {
100  T *ptr = LuaPull<T *>(L, 1);
101  const char *name = lua_tostring(L, lua_upvalueindex(1));
102  free_function<T> getter = PullFreeFunction<T>(L, lua_upvalueindex(2));
103 
104  if (!ptr)
105  return luaL_error(L, "Null or invalid userdata accessed for property %s", name);
106 
107  if (lua_gettop(L) > 2)
108  return luaL_error(L, "Invalid number of arguments for property getter/setter %s", name);
109 
110  if (lua_gettop(L) > 1) {
111  free_function<T> setter = PullFreeFunction<T>(L, lua_upvalueindex(3));
112  if (setter != nullptr)
113  return setter(L, ptr);
114  else
115  return luaL_error(L, "Attempt to call undefined setter for property %s", name);
116  }
117 
118  return getter(L, ptr);
119  }
120 
121  template <typename T, typename Dt, typename Dt2>
122  static int getter_member_fn_wrapper_(lua_State *L)
123  {
124  T *ptr = LuaPull<T *>(L, 1);
125  const char *name = lua_tostring(L, lua_upvalueindex(1));
126  auto &getter = PullPointerToMember<member_function<T, Dt>>(L, lua_upvalueindex(2));
127 
128  if (!ptr)
129  return luaL_error(L, "Null or invalid userdata accessed for property %s", name);
130 
131  if (lua_gettop(L) > 2)
132  return luaL_error(L, "Invalid number of arguments for property getter/setter %s", name);
133 
134  if (lua_gettop(L) > 1) {
135  auto &setter = PullPointerToMember<member_function<T, void, Dt2>>(L, lua_upvalueindex(3));
136  if (setter != nullptr) {
137  pi_lua_multiple_call(L, 1, ptr, setter);
138  return 0;
139  } else
140  return luaL_error(L, "Attempt to call undefined setter for property %s", name);
141  } else {
142  Dt value = pi_lua_multiple_call(L, 1, ptr, getter);
143  LuaPush<Dt>(L, value);
144  return 1;
145  }
146  }
147 
148  template <typename T>
149  static int fn_wrapper_(lua_State *L)
150  {
151  T *ptr = LuaPull<T *>(L, 1);
152  const char *name = lua_tostring(L, lua_upvalueindex(1));
153  luaL_checktype(L, lua_upvalueindex(2), LUA_TLIGHTUSERDATA);
154 
155  if (!ptr)
156  return luaL_error(L, "Invalid userdata accessed for function %s", name);
157 
158  free_function<T> fn = PullFreeFunction<T>(L, lua_upvalueindex(2));
159  return fn(L, ptr);
160  }
161 
162  template <typename T, typename Rt, typename... Args>
163  typename std::enable_if<!std::is_same<Rt, void>::value, int>::type static member_fn_wrapper_(lua_State *L)
164  {
165  T *ptr = LuaPull<T *>(L, 1);
166  const char *name = lua_tostring(L, lua_upvalueindex(1));
167 
168  if (!ptr)
169  return luaL_error(L, "Invalid userdata accessed for function %s", name);
170 
171  if (size_t(lua_gettop(L) - 1) < sizeof...(Args))
172  return luaL_error(L, "Invalid number of arguments for function %s", name);
173 
174  auto &fn = PullPointerToMember<member_function<T, Rt, Args...>>(L, lua_upvalueindex(2));
175  Rt ret = pi_lua_multiple_call(L, 1, ptr, fn);
176  LuaPush<Rt>(L, ret);
177 
178  return 1;
179  }
180 
181  template <typename T, typename Rt, typename... Args>
182  typename std::enable_if<std::is_same<Rt, void>::value, int>::type static member_fn_wrapper_(lua_State *L)
183  {
184  T *ptr = LuaPull<T *>(L, 1);
185  const char *name = lua_tostring(L, lua_upvalueindex(1));
186 
187  if (!ptr)
188  return luaL_error(L, "Invalid userdata accessed for function %s", name);
189 
190  if (size_t(lua_gettop(L) - 1) < sizeof...(Args))
191  return luaL_error(L, "Invalid number of arguments for function %s", name);
192 
193  auto &fn = PullPointerToMember<member_function<T, Rt, Args...>>(L, lua_upvalueindex(2));
194  pi_lua_multiple_call(L, 1, ptr, fn);
195 
196  return 0;
197  }
198 
199  template <typename MemT>
200  static void PushPointerToMember(lua_State *L, MemT obj)
201  {
202  *reinterpret_cast<MemT *>(lua_newuserdata(L, sizeof(MemT))) = obj;
203  }
204 
205  template <typename MemT>
206  static MemT &PullPointerToMember(lua_State *L, int idx)
207  {
208  return *reinterpret_cast<MemT *>(lua_touserdata(L, idx));
209  }
210 
211  template <typename T>
212  static void PushFreeFunction(lua_State *L, free_function<T> obj)
213  {
214  static_assert(sizeof(free_function<T>) == sizeof(void *), "Free functions cannot be 'fat' lambdas!");
215  lua_pushlightuserdata(L, reinterpret_cast<void *>(obj));
216  }
217 
218  template <typename T>
219  static free_function<T> PullFreeFunction(lua_State *L, int index)
220  {
221  return reinterpret_cast<free_function<T>>(lua_touserdata(L, index));
222  }
223 
224  // Pushes a copy of the metatable's attribute table to the stack
225  void GetAttrTable(lua_State *L, int index)
226  {
227  luaL_getsubtable(L, index, "attrs");
228  }
229 
230  // Pushes a copy of the metatable's attribute table to the stack
231  void GetMethodTable(lua_State *L, int index)
232  {
233  luaL_getsubtable(L, index, "methods");
234  }
235 
236  std::string m_typeName;
237  std::string m_parent;
238 
239  lua_State *m_lua;
240  // the reference id of the metatable
241  int m_ref = LUA_NOREF;
242  // the position of the metatable on the stack while recording
243  int m_index = 0;
244 };
245 
247 public:
249 
250  LuaMetaTypeGeneric(const char *name) :
251  LuaMetaTypeBase(name)
252  {}
253 
255  {
257  return *this;
258  }
259 
261  {
263  return *this;
264  }
265 
266  Self &SetProtected(bool enabled)
267  {
268  m_protected = enabled;
269  return *this;
270  }
271 
272  Self &AddMember(const char *name, lua_CFunction getter)
273  {
275 
276  lua_pushcfunction(m_lua, getter);
277  if (m_protected)
278  lua_pushcclosure(m_lua, secure_trampoline, 1);
279 
280  lua_setfield(m_lua, -2, name);
281  lua_pop(m_lua, 1);
282  return *this;
283  }
284 
285  Self &AddFunction(const char *name, lua_CFunction func)
286  {
288 
289  lua_pushcfunction(m_lua, func);
290  if (m_protected)
291  lua_pushcclosure(m_lua, secure_trampoline, 1);
292 
293  lua_setfield(m_lua, -2, name);
294  lua_pop(m_lua, 1);
295  return *this;
296  }
297 
298 private:
299  bool m_protected = false;
300 };
301 
302 template <typename T>
303 class LuaMetaType : public LuaMetaTypeBase {
304 public:
305  LuaMetaType(const char *name) :
306  LuaMetaTypeBase(name)
307  {}
308 
310  {
312  return *this;
313  }
314 
316  {
318  return *this;
319  }
320 
321  // All functions and members pushed while protection is enabled will error
322  // when accessed by a non-trusted lua script.
323  void SetProtected(bool enabled) { m_protected = enabled; }
324 
325  // Bind a raw C++ data member to Lua.
326  // Obviously, the member in question must be publically accessible, or
327  // LuaMetaTypeBase must be marked as a friend class.
328  template <typename Dt>
330  {
331  lua_State *L = m_lua;
332  GetAttrTable(L, m_index);
333 
334  lua_pushstring(L, (m_typeName + "." + name).c_str());
335  PushPointerToMember(L, t);
336  lua_pushcclosure(L, &member_wrapper_<T, Dt>, 2);
337  if (m_protected)
338  lua_pushcclosure(L, &secure_trampoline, 1);
339 
340  lua_setfield(L, -2, name);
341  lua_pop(L, 1);
342 
343  return *this;
344  }
345 
346  // Bind a pseudo-member to Lua via a free-function getter and setter.
347  // The getter and setter are responsible for pulling parameters from Lua.
348  LuaMetaType &AddMember(const char *name, free_function<T> getter, free_function<T> setter = nullptr)
349  {
350  lua_State *L = m_lua;
351  GetAttrTable(L, m_index);
352 
353  lua_pushstring(L, (m_typeName + "." + name).c_str());
354  PushFreeFunction(L, getter);
355  PushFreeFunction(L, setter);
356  lua_pushcclosure(L, &getter_member_wrapper_<T>, 3);
357  if (m_protected)
358  lua_pushcclosure(L, &secure_trampoline, 1);
359 
360  lua_setfield(L, -2, name);
361  lua_pop(L, 1);
362 
363  return *this;
364  }
365 
366  // Magic to allow binding a const function to Lua. Take care to ensure that you do not
367  // push a const object to lua, or this code will become undefined behavior.
368  template <typename Dt, typename Dt2 = Dt>
370  {
371  return AddMember(name, reinterpret_cast<member_function<T, Dt>>(getter), setter);
372  }
373 
374  // Bind a pseudo-member to Lua via a member-function getter and setter.
375  // The parameter will automatically be pulled from Lua and passed to the setter.
376  template <typename Dt, typename Dt2 = Dt>
377  LuaMetaType &AddMember(const char *name, member_function<T, Dt> getter, member_function<T, void, Dt2> setter = nullptr)
378  {
379  lua_State *L = m_lua;
380  GetAttrTable(L, m_index);
381 
382  lua_pushstring(L, (m_typeName + "." + name).c_str());
383  PushPointerToMember(L, getter);
384  PushPointerToMember(L, setter);
385  lua_pushcclosure(L, &getter_member_fn_wrapper_<T, Dt, Dt2>, 3);
386  if (m_protected)
387  lua_pushcclosure(L, &secure_trampoline, 1);
388 
389  lua_setfield(L, -2, name);
390  lua_pop(L, 1);
391 
392  return *this;
393  }
394 
395  // Magic to allow binding a const function to Lua. Take care to ensure that you do not
396  // push a const object to lua, or this code will become undefined behavior.
397  template <typename Rt, typename... Args>
399  {
400  return AddFunction(name, reinterpret_cast<member_function<T, Rt, Args...>>(fn));
401  }
402 
403  // Bind a member function to Lua.
404  // Parameters will automatically be pulled from Lua and be passed to the function.
405  // It is the responsiblity of the programmer to ensure a valid LuaPull implementation
406  // is available for each parameter's time.
407  // If the function has a non-void return type, its return value will automatically be
408  // pushed to Lua.
409  template <typename Rt, typename... Args>
411  {
412  lua_State *L = m_lua;
414 
415  lua_pushstring(L, (m_typeName + "." + name).c_str());
416  PushPointerToMember(L, fn);
417  lua_pushcclosure(L, &member_fn_wrapper_<T, Rt, Args...>, 2);
418  if (m_protected)
419  lua_pushcclosure(L, &secure_trampoline, 1);
420 
421  lua_setfield(L, -2, name);
422  lua_pop(L, 1);
423 
424  return *this;
425  }
426 
427  // Bind a free function to Lua.
428  // The self parameter will be automatically provided, but the function
429  // is responsible for pulling the rest of its parameters and pushing the
430  // appropriate number of return values.
431  LuaMetaType &AddFunction(const char *name, free_function<T> fn)
432  {
433  lua_State *L = m_lua;
435 
436  lua_pushstring(L, (m_typeName + "." + name).c_str());
437  PushFreeFunction(L, fn);
438  lua_pushcclosure(L, &fn_wrapper_<T>, 2);
439  if (m_protected)
440  lua_pushcclosure(L, &secure_trampoline, 1);
441 
442  lua_setfield(L, -2, name);
443  lua_pop(L, 1);
444 
445  return *this;
446  }
447 
448  LuaMetaType &RegisterFuncs(const luaL_Reg *functions)
449  {
450  lua_State *L = m_lua;
452 
453  for (const luaL_Reg *func = functions; func->name; ++func) {
454  lua_pushcfunction(L, func->func);
455  if (m_protected)
456  lua_pushcclosure(L, secure_trampoline, 1);
457 
458  lua_setfield(L, -2, func->name);
459  }
460 
461  lua_pop(L, 1);
462  }
463 
464 private:
465  bool m_protected = false;
466 };
Ret pi_lua_multiple_call(lua_State *l, int index, T *ptr, Ret(T::*fn)())
Definition: LuaCall.h:20
int secure_trampoline(lua_State *l)
Definition: Sandbox.cpp:365
Definition: LuaMetaType.h:12
static free_function< T > PullFreeFunction(lua_State *L, int index)
Definition: LuaMetaType.h:219
Rt(T::*)(Args...) const const_member_function
Definition: LuaMetaType.h:73
void StopRecording()
Definition: LuaMetaType.h:46
static void PushFreeFunction(lua_State *L, free_function< T > obj)
Definition: LuaMetaType.h:212
Dt T::* member_pointer
Definition: LuaMetaType.h:64
const char * GetTypeName() const
Definition: LuaMetaType.h:21
static void PushPointerToMember(lua_State *L, MemT obj)
Definition: LuaMetaType.h:200
int(*)(lua_State *, T *) free_function
Definition: LuaMetaType.h:67
static void GetNames(std::vector< std::string > &names, const std::string &prefix="", bool methodsOnly=false)
Definition: LuaMetaType.cpp:275
static std::enable_if< std::is_same< Rt, void >::value, int >::type member_fn_wrapper_(lua_State *L)
Definition: LuaMetaType.h:182
std::string m_typeName
Definition: LuaMetaType.h:236
lua_State * m_lua
Definition: LuaMetaType.h:239
int m_ref
Definition: LuaMetaType.h:241
static std::enable_if<!std::is_same< Rt, void >::value, int >::type member_fn_wrapper_(lua_State *L)
Definition: LuaMetaType.h:163
std::string m_parent
Definition: LuaMetaType.h:237
void StartRecording()
Definition: LuaMetaType.h:36
const char * GetParent() const
Definition: LuaMetaType.h:23
void GetMetatable() const
Definition: LuaMetaType.cpp:399
LuaMetaTypeBase(const char *name)
Definition: LuaMetaType.h:14
static MemT & PullPointerToMember(lua_State *L, int idx)
Definition: LuaMetaType.h:206
static int fn_wrapper_(lua_State *L)
Definition: LuaMetaType.h:149
static bool GetMetatableFromName(lua_State *l, const char *name)
Definition: LuaMetaType.cpp:386
Rt(T::*)(Args...) member_function
Definition: LuaMetaType.h:70
void SetParent(const char *parent)
Definition: LuaMetaType.h:25
static int getter_member_fn_wrapper_(lua_State *L)
Definition: LuaMetaType.h:122
void GetMethodTable(lua_State *L, int index)
Definition: LuaMetaType.h:231
static int getter_member_wrapper_(lua_State *L)
Definition: LuaMetaType.h:98
void GetAttrTable(lua_State *L, int index)
Definition: LuaMetaType.h:225
static int member_wrapper_(lua_State *L)
Definition: LuaMetaType.h:76
void CreateMetaType(lua_State *l)
Definition: LuaMetaType.cpp:412
int m_index
Definition: LuaMetaType.h:243
bool IsValid() const
Definition: LuaMetaType.h:31
Definition: LuaMetaType.h:246
Self & StopRecording()
Definition: LuaMetaType.h:260
Self & StartRecording()
Definition: LuaMetaType.h:254
LuaMetaTypeGeneric(const char *name)
Definition: LuaMetaType.h:250
Self & SetProtected(bool enabled)
Definition: LuaMetaType.h:266
Self & AddMember(const char *name, lua_CFunction getter)
Definition: LuaMetaType.h:272
Self & AddFunction(const char *name, lua_CFunction func)
Definition: LuaMetaType.h:285
Definition: LuaMetaType.h:303
LuaMetaType(const char *name)
Definition: LuaMetaType.h:305
LuaMetaType & AddMember(const char *name, const_member_function< T, Dt > getter, member_function< T, void, Dt2 > setter=nullptr)
Definition: LuaMetaType.h:369
LuaMetaType & AddFunction(const char *name, member_function< T, Rt, Args... > fn)
Definition: LuaMetaType.h:410
LuaMetaType & AddMember(const char *name, member_pointer< T, Dt > t)
Definition: LuaMetaType.h:329
LuaMetaType & StopRecording()
Definition: LuaMetaType.h:315
LuaMetaType & StartRecording()
Definition: LuaMetaType.h:309
LuaMetaType & RegisterFuncs(const luaL_Reg *functions)
Definition: LuaMetaType.h:448
LuaMetaType & AddMember(const char *name, member_function< T, Dt > getter, member_function< T, void, Dt2 > setter=nullptr)
Definition: LuaMetaType.h:377
LuaMetaType & AddFunction(const char *name, free_function< T > fn)
Definition: LuaMetaType.h:431
LuaMetaType & AddFunction(const char *name, const_member_function< T, Rt, Args... > fn)
Definition: LuaMetaType.h:398
LuaMetaType & AddMember(const char *name, free_function< T > getter, free_function< T > setter=nullptr)
Definition: LuaMetaType.h:348
void SetProtected(bool enabled)
Definition: LuaMetaType.h:323