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