1// Aseprite
2// Copyright (C) 2018-2019 Igara Studio S.A.
3// Copyright (C) 2018 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifndef APP_SCRIPT_LUACPP_H_INCLUDED
9#define APP_SCRIPT_LUACPP_H_INCLUDED
10#pragma once
11
12extern "C" {
13 #include "lua.h"
14 #include "lualib.h"
15 #include "lauxlib.h"
16}
17
18#include "base/debug.h"
19
20#include <functional>
21#include <type_traits>
22
23namespace app {
24namespace script {
25
26#if LUA_TNONE != -1
27 #error Invalid LUA_TNONE value
28#endif
29#if LUA_TNIL != 0
30 #error Invalid LUA_TNIL value
31#endif
32#define VALID_LUATYPE(type) ((type) > 0)
33
34// Some of these auxiliary methods are based on code from the Skia
35// library (SkLua.cpp file) by Google Inc.
36
37template <typename T> const char* get_mtname();
38#define DEF_MTNAME(T) \
39 template <> const char* get_mtname<T>() { \
40 return #T; \
41 }
42
43#define DEF_MTNAME_ALIAS(T, ALIAS) \
44 template <> const char* get_mtname<ALIAS>() { \
45 return #T; \
46 }
47
48template <typename T, typename... Args> T* push_new(lua_State* L, Args&&... args) {
49 T* addr = (T*)lua_newuserdata(L, sizeof(T));
50 new (addr) T(std::forward<Args>(args)...);
51 luaL_getmetatable(L, get_mtname<T>());
52 lua_setmetatable(L, -2);
53 return addr;
54}
55
56template <typename T> void push_obj(lua_State* L, const T& obj) {
57 new (lua_newuserdata(L, sizeof(T))) T(obj);
58 luaL_getmetatable(L, get_mtname<T>());
59 lua_setmetatable(L, -2);
60}
61
62template <typename T> T* push_ptr(lua_State* L, T* ptr) {
63 *(T**)lua_newuserdata(L, sizeof(T*)) = ptr;
64 luaL_getmetatable(L, get_mtname<T>());
65 lua_setmetatable(L, -2);
66 return ptr;
67}
68
69template <typename T> T* get_ptr(lua_State* L, int index) {
70 return *(T**)luaL_checkudata(L, index, get_mtname<T>());
71}
72
73template <typename T> T* get_obj(lua_State* L, int index) {
74 T* ptr = (T*)luaL_checkudata(L, index, get_mtname<T>());
75 ASSERT(typeid(*ptr) == typeid(T));
76 return ptr;
77}
78
79// Returns nil if the index doesn't have the given metatable
80template <typename T> T* may_get_ptr(lua_State* L, int index) {
81 T** ptr = (T**)luaL_testudata(L, index, get_mtname<T>());
82 if (ptr) {
83 ASSERT(typeid(**ptr) == typeid(T));
84 return *ptr;
85 }
86 else
87 return nullptr;
88}
89
90// Returns nil if the index doesn't have the given metatable
91template <typename T> T* may_get_obj(lua_State* L, int index) {
92 return (T*)luaL_testudata(L, index, get_mtname<T>());
93}
94
95inline bool lua2bool(lua_State* L, int index) {
96 return !!lua_toboolean(L, index);
97}
98
99template<typename T>
100inline void setfield_integer(lua_State* L, const char* key, const T& value) {
101 lua_pushinteger(L, int(value));
102 lua_setfield(L, -2, key);
103}
104
105#define REG_CLASS(L, T) { \
106 luaL_newmetatable(L, get_mtname<T>()); \
107 lua_getglobal(L, "__generic_mt_index"); \
108 lua_setfield(L, -2, "__index"); \
109 lua_getglobal(L, "__generic_mt_newindex"); \
110 lua_setfield(L, -2, "__newindex"); \
111 luaL_setfuncs(L, T##_methods, 0); \
112 lua_pop(L, 1); \
113 }
114
115#define REG_CLASS_NEW(L, T) { \
116 lua_pushcfunction(L, T##_new); \
117 lua_setglobal(L, #T); \
118 }
119
120struct Property {
121 const char* name;
122 lua_CFunction getter;
123 lua_CFunction setter;
124};
125
126void run_mt_index_code(lua_State* L);
127void create_mt_getters_setters(lua_State* L,
128 const char* tname,
129 const Property* properties);
130
131bool lua_is_key_true(lua_State* L, int tableIndex, const char* keyName);
132
133#define REG_CLASS_PROPERTIES(L, T) { \
134 luaL_getmetatable(L, get_mtname<T>()); \
135 create_mt_getters_setters(L, get_mtname<T>(), T##_properties); \
136 lua_pop(L, 1); \
137 }
138
139} // namespace script
140} // namespace app
141
142#endif
143