1 // Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16
17 #include <libaegisub/fs.h>
18
19 #include <lua.hpp>
20 #include <string>
21 #include <vector>
22 #include <type_traits>
23
24 #include <boost/config.hpp>
25
26 #ifndef BOOST_NORETURN
27 #include <boost/exception/detail/attribute_noreturn.hpp>
28 #define BOOST_NORETURN BOOST_ATTRIBUTE_NORETURN
29 #endif
30
31 namespace agi { namespace lua {
32 // Exception type for errors where the error details are on the lua stack
33 struct error_tag {};
34
35 // Below are functionally equivalent to the luaL_ functions, but using a C++
36 // exception for stack unwinding
37 int BOOST_NORETURN error(lua_State *L, const char *fmt, ...);
38 int BOOST_NORETURN argerror(lua_State *L, int narg, const char *extramsg);
39 int BOOST_NORETURN typerror(lua_State *L, int narg, const char *tname);
40 void argcheck(lua_State *L, bool cond, int narg, const char *msg);
41
push_value(lua_State * L,bool value)42 inline void push_value(lua_State *L, bool value) { lua_pushboolean(L, value); }
push_value(lua_State * L,const char * value)43 inline void push_value(lua_State *L, const char *value) { lua_pushstring(L, value); }
push_value(lua_State * L,double value)44 inline void push_value(lua_State *L, double value) { lua_pushnumber(L, value); }
push_value(lua_State * L,int value)45 inline void push_value(lua_State *L, int value) { lua_pushinteger(L, value); }
push_value(lua_State * L,void * p)46 inline void push_value(lua_State *L, void *p) { lua_pushlightuserdata(L, p); }
47
48 template<typename Integer>
49 typename std::enable_if<std::is_integral<Integer>::value>::type
push_value(lua_State * L,Integer value)50 push_value(lua_State *L, Integer value) {
51 lua_pushinteger(L, static_cast<lua_Integer>(value));
52 }
53
push_value(lua_State * L,fs::path const & value)54 inline void push_value(lua_State *L, fs::path const& value) {
55 lua_pushstring(L, value.string().c_str());
56 }
57
push_value(lua_State * L,std::string const & value)58 inline void push_value(lua_State *L, std::string const& value) {
59 lua_pushlstring(L, value.c_str(), value.size());
60 }
61
push_value(lua_State * L,lua_CFunction value)62 inline void push_value(lua_State *L, lua_CFunction value) {
63 if (lua_gettop(L) >= 2 && lua_type(L, -2) == LUA_TUSERDATA) {
64 lua_pushvalue(L, -2);
65 lua_pushcclosure(L, value, 1);
66 }
67 else
68 lua_pushcclosure(L, value, 0);
69 }
70
71 template<typename T>
push_value(lua_State * L,std::vector<T> const & value)72 void push_value(lua_State *L, std::vector<T> const& value) {
73 lua_createtable(L, value.size(), 0);
74 for (size_t i = 0; i < value.size(); ++i) {
75 push_value(L, value[i]);
76 lua_rawseti(L, -2, i + 1);
77 }
78 }
79
80 /// Wrap a function which may throw exceptions and make it trigger lua errors
81 /// whenever it throws
82 template<int (*func)(lua_State *L)>
exception_wrapper(lua_State * L)83 int exception_wrapper(lua_State *L) {
84 try {
85 return func(L);
86 }
87 catch (agi::Exception const& e) {
88 push_value(L, e.GetMessage());
89 return lua_error(L);
90 }
91 catch (std::exception const& e) {
92 push_value(L, e.what());
93 return lua_error(L);
94 }
95 catch (error_tag) {
96 // Error message is already on the stack
97 return lua_error(L);
98 }
99 catch (...) {
100 std::terminate();
101 }
102 }
103
104 template<typename T>
set_field(lua_State * L,const char * name,T value)105 void set_field(lua_State *L, const char *name, T value) {
106 push_value(L, value);
107 lua_setfield(L, -2, name);
108 }
109
110 template<int (*func)(lua_State *L)>
set_field(lua_State * L,const char * name)111 void set_field(lua_State *L, const char *name) {
112 push_value(L, exception_wrapper<func>);
113 lua_setfield(L, -2, name);
114 }
115
116 std::string get_string_or_default(lua_State *L, int idx);
117 std::string get_string(lua_State *L, int idx);
118 std::string get_global_string(lua_State *L, const char *name);
119
120 std::string check_string(lua_State *L, int idx);
121 int check_int(lua_State *L, int idx);
122 size_t check_uint(lua_State *L, int idx);
123 void *check_udata(lua_State *L, int idx, const char *mt);
124
125 template<typename T, typename... Args>
make(lua_State * L,const char * mt,Args &&...args)126 T *make(lua_State *L, const char *mt, Args&&... args) {
127 auto obj = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
128 new(obj) T(std::forward<Args>(args)...);
129 luaL_getmetatable(L, mt);
130 lua_setmetatable(L, -2);
131 return obj;
132 }
133
134 template<typename T>
get(lua_State * L,int idx,const char * mt)135 T& get(lua_State *L, int idx, const char *mt) {
136 return *static_cast<T *>(check_udata(L, idx, mt));
137 }
138
139 struct LuaForEachBreak {};
140
141 template<typename Func>
lua_for_each(lua_State * L,Func && func)142 void lua_for_each(lua_State *L, Func&& func) {
143 lua_pushnil(L); // initial key
144 while (lua_next(L, -2)) {
145 try {
146 func();
147 }
148 catch (LuaForEachBreak) {
149 lua_pop(L, 1);
150 break;
151 }
152 lua_pop(L, 1); // pop value, leave key
153 }
154 lua_pop(L, 1); // pop table
155 }
156
157 /// Lua error handler which adds the stack trace to the error message, with
158 /// moonscript line rewriting support
159 int add_stack_trace(lua_State *L);
160
161 #ifdef _DEBUG
162 struct LuaStackcheck {
163 lua_State *L;
164 int startstack;
165
166 void check_stack(int additional);
167 void dump();
168
LuaStackcheckLuaStackcheck169 LuaStackcheck(lua_State *L) : L(L), startstack(lua_gettop(L)) { }
~LuaStackcheckLuaStackcheck170 ~LuaStackcheck() { check_stack(0); }
171 };
172 #else
173 struct LuaStackcheck {
check_stackLuaStackcheck174 void check_stack(int) { }
dumpLuaStackcheck175 void dump() { }
LuaStackcheckLuaStackcheck176 LuaStackcheck(lua_State*) { }
177 };
178 #endif
179
180 } }
181