Get rid of DECLARE_LUACLASS
[lsnes.git] / src / library / luabase.cpp
blob4cd575b0bba685966ceef2986575da27fc25cc52
1 #include "luabase.hpp"
2 #include "register-queue.hpp"
3 #include <iostream>
4 #include <cassert>
6 std::unordered_map<std::type_index, void*>& lua_class_types()
8 static std::unordered_map<std::type_index, void*> x;
9 return x;
11 namespace
13 int lua_trampoline_function(lua_State* L)
15 void* ptr = lua_touserdata(L, lua_upvalueindex(1));
16 lua_state* state = reinterpret_cast<lua_state*>(lua_touserdata(L, lua_upvalueindex(2)));
17 lua_function* f = reinterpret_cast<lua_function*>(ptr);
18 lua_state _L(*state, L);
19 try {
20 return f->invoke(_L);
21 } catch(std::exception& e) {
22 lua_pushfstring(L, "%s", e.what());
23 lua_error(L);
25 return 0;
28 //Pushes given table to top of stack, creating if needed.
29 void recursive_lookup_table(lua_state& L, const std::string& tab)
31 if(tab == "") {
32 #if LUA_VERSION_NUM == 501
33 L.pushvalue(LUA_GLOBALSINDEX);
34 #endif
35 #if LUA_VERSION_NUM == 502
36 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
37 #endif
38 assert(L.type(-1) == LUA_TTABLE);
39 return;
41 std::string u = tab;
42 size_t split = u.find_last_of(".");
43 std::string u1;
44 std::string u2 = u;
45 if(split < u.length()) {
46 u1 = u.substr(0, split);
47 u2 = u.substr(split + 1);
49 recursive_lookup_table(L, u1);
50 L.getfield(-1, u2.c_str());
51 if(L.type(-1) != LUA_TTABLE) {
52 //Not a table, create a table.
53 L.pop(1);
54 L.newtable();
55 L.setfield(-2, u2.c_str());
56 L.getfield(-1, u2.c_str());
58 //Get rid of previous table.
59 L.insert(-2);
60 L.pop(1);
63 void register_lua_function(lua_state& L, const std::string& name, lua_function* fun)
65 std::string u = name;
66 size_t split = u.find_last_of(".");
67 std::string u1;
68 std::string u2 = u;
69 if(split < u.length()) {
70 u1 = u.substr(0, split);
71 u2 = u.substr(split + 1);
73 recursive_lookup_table(L, u1);
74 if(!fun)
75 L.pushnil();
76 else {
77 void* ptr = reinterpret_cast<void*>(fun);
78 L.pushlightuserdata(ptr);
79 L.pushlightuserdata(&L);
80 L.pushcclosure(lua_trampoline_function, 2);
82 L.setfield(-2, u2.c_str());
83 L.pop(1);
85 typedef register_queue<lua_state, lua_function> regqueue_t;
86 typedef register_queue<lua_state::callback_proxy, lua_state::lua_callback_list> regqueue2_t;
87 typedef register_queue<lua_function_group, lua_function> regqueue3_t;
91 lua_state::lua_state() throw(std::bad_alloc)
92 : cbproxy(*this)
94 master = NULL;
95 lua_handle = NULL;
96 oom_handler = builtin_oom;
97 regqueue2_t::do_ready(cbproxy, true);
100 lua_state::lua_state(lua_state& _master, lua_State* L)
101 : cbproxy(*this)
103 master = &_master;
104 lua_handle = L;
107 lua_state::~lua_state() throw()
109 if(master)
110 return;
111 for(auto i : function_groups)
112 i.first->drop_callback(i.second);
113 regqueue2_t::do_ready(cbproxy, false);
114 if(lua_handle)
115 lua_close(lua_handle);
118 void lua_state::builtin_oom()
120 std::cerr << "PANIC: FATAL: Out of memory" << std::endl;
121 exit(1);
124 void* lua_state::builtin_alloc(void* user, void* old, size_t olds, size_t news)
126 if(news) {
127 void* m = realloc(old, news);
128 if(!m)
129 reinterpret_cast<lua_state*>(user)->oom_handler();
130 return m;
131 } else
132 free(old);
133 return NULL;
137 lua_function::lua_function(lua_function_group& _group, const std::string& func) throw(std::bad_alloc)
138 : group(_group)
140 regqueue3_t::do_register(group, fname = func, *this);
143 lua_function::~lua_function() throw()
145 regqueue3_t::do_unregister(group, fname);
148 void lua_state::reset() throw(std::bad_alloc, std::runtime_error)
150 if(master)
151 return master->reset();
152 if(lua_handle) {
153 lua_State* tmp = lua_newstate(lua_state::builtin_alloc, this);
154 if(!tmp)
155 throw std::runtime_error("Can't re-initialize Lua interpretter");
156 lua_close(lua_handle);
157 for(auto& i : callbacks)
158 i.second->clear();
159 lua_handle = tmp;
160 } else {
161 //Initialize new.
162 lua_handle = lua_newstate(lua_state::builtin_alloc, this);
163 if(!lua_handle)
164 throw std::runtime_error("Can't initialize Lua interpretter");
166 for(auto i : function_groups)
167 i.first->request_callback([this](std::string name, lua_function* func) -> void {
168 register_lua_function(*this, name, func);
172 void lua_state::deinit() throw()
174 if(master)
175 return master->deinit();
176 if(lua_handle)
177 lua_close(lua_handle);
178 lua_handle = NULL;
181 void lua_state::add_function_group(lua_function_group& group)
183 function_groups.insert(std::make_pair(&group, group.add_callback([this](const std::string& name,
184 lua_function* func) -> void {
185 this->function_callback(name, func);
186 }, [this](lua_function_group* x) {
187 for(auto i = this->function_groups.begin(); i != this->function_groups.end();)
188 if(i->first == x)
189 i = this->function_groups.erase(i);
190 else
191 i++;
192 })));
195 void lua_state::function_callback(const std::string& name, lua_function* func)
197 if(master)
198 return master->function_callback(name, func);
199 if(lua_handle)
200 register_lua_function(*this, name, func);
203 bool lua_state::do_once(void* key)
205 if(master)
206 return master->do_once(key);
207 pushlightuserdata(key);
208 rawget(LUA_REGISTRYINDEX);
209 if(type(-1) == LUA_TNIL) {
210 pop(1);
211 pushlightuserdata(key);
212 pushlightuserdata(key);
213 rawset(LUA_REGISTRYINDEX);
214 return true;
215 } else {
216 pop(1);
217 return false;
221 lua_state::lua_callback_list::lua_callback_list(lua_state& _L, const std::string& _name, const std::string& fncbname)
222 : L(_L), name(_name), fn_cbname(fncbname)
224 regqueue2_t::do_register(L.cbproxy, name, *this);
227 lua_state::lua_callback_list::~lua_callback_list()
229 regqueue2_t::do_unregister(L.cbproxy, name);
230 if(!L.handle())
231 return;
232 for(auto& i : callbacks) {
233 L.pushlightuserdata(&i);
234 L.pushnil();
235 L.rawset(LUA_REGISTRYINDEX);
239 void lua_state::lua_callback_list::_register(lua_state& _L)
241 callbacks.push_back(0);
242 _L.pushlightuserdata(&*callbacks.rbegin());
243 _L.pushvalue(-2);
244 _L.rawset(LUA_REGISTRYINDEX);
247 void lua_state::lua_callback_list::_unregister(lua_state& _L)
249 for(auto i = callbacks.begin(); i != callbacks.end();) {
250 _L.pushlightuserdata(&*i);
251 _L.rawget(LUA_REGISTRYINDEX);
252 if(_L.rawequal(-1, -2)) {
253 char* key = &*i;
254 _L.pushlightuserdata(key);
255 _L.pushnil();
256 _L.rawset(LUA_REGISTRYINDEX);
257 i = callbacks.erase(i);
258 } else
259 i++;
260 _L.pop(1);
264 lua_function_group::lua_function_group()
266 next_handle = 0;
267 regqueue3_t::do_ready(*this, true);
270 lua_function_group::~lua_function_group()
272 for(auto i : functions)
273 for(auto j : callbacks)
274 j.second(i.first, NULL);
275 for(auto i : dcallbacks)
276 i.second(this);
277 regqueue3_t::do_ready(*this, false);
280 void lua_function_group::request_callback(std::function<void(std::string, lua_function*)> cb)
282 for(auto i : functions)
283 cb(i.first, i.second);
286 int lua_function_group::add_callback(std::function<void(std::string, lua_function*)> cb,
287 std::function<void(lua_function_group*)> dcb)
289 int handle = next_handle++;
290 callbacks[handle] = cb;
291 dcallbacks[handle] = dcb;
292 for(auto i : functions)
293 cb(i.first, i.second);
294 return handle;
297 void lua_function_group::drop_callback(int handle)
299 callbacks.erase(handle);
302 void lua_function_group::do_register(const std::string& name, lua_function& fun)
304 functions[name] = &fun;
305 for(auto i : callbacks)
306 i.second(name, &fun);
309 void lua_function_group::do_unregister(const std::string& name)
311 functions.erase(name);
312 for(auto i : callbacks)
313 i.second(name, NULL);
316 std::list<luaclass_methods>& userdata_recogn_fns()
318 static std::list<luaclass_methods> x;
319 return x;
322 std::string try_recognize_userdata(lua_state& state, int index)
324 for(auto i : userdata_recogn_fns())
325 if(i.is(state, index))
326 return i.name();
327 //Hack: Lua builtin file objects.
328 state.pushstring("FILE*");
329 state.rawget(LUA_REGISTRYINDEX);
330 if(state.getmetatable(index)) {
331 if(state.rawequal(-1, -2)) {
332 state.pop(2);
333 return "FILE*";
335 state.pop(1);
337 state.pop(1);
338 return "unknown";
341 std::string try_print_userdata(lua_state& L, int index)
343 for(auto i : userdata_recogn_fns())
344 if(i.is(L, index))
345 return i.print(L, index);
346 return "no data available";
349 int lua_state::vararg_tag::pushargs(lua_state& L)
351 int e = 0;
352 for(auto i : args) {
353 if(i == "")
354 L.pushnil();
355 else if(i == "true")
356 L.pushboolean(true);
357 else if(i == "false")
358 L.pushboolean(false);
359 else if(regex_match("[+-]?(|0|[1-9][0-9]*)(.[0-9]+)?([eE][+-]?(0|[1-9][0-9]*))?", i))
360 L.pushnumber(strtod(i.c_str(), NULL));
361 else if(i[0] == ':')
362 L.pushlstring(i.substr(1));
363 else
364 L.pushlstring(i);
365 e++;
367 return e;