Cleanup lua code by introducing lua::functions
[lsnes.git] / src / library / lua.cpp
blobf7058896be1d4fe237b878c8541cf407ace5f06b
1 #include "lua-base.hpp"
2 #include "lua-class.hpp"
3 #include "lua-function.hpp"
4 #include "lua-params.hpp"
5 #include "lua-pin.hpp"
6 #include "register-queue.hpp"
7 #include <iostream>
8 #include <cassert>
10 namespace lua
12 std::unordered_map<std::type_index, void*>& class_types()
14 static std::unordered_map<std::type_index, void*> x;
15 return x;
17 namespace
19 char classtable_key;
20 char classtable_meta_key;
22 struct class_info
24 class_base* obj;
25 static int index(lua_State* L);
26 static int newindex(lua_State* L);
27 static int pairs(lua_State* L);
28 static int pairs_next(lua_State* L);
29 static int smethods(lua_State* L);
30 static int cmethods(lua_State* L);
31 static int trampoline(lua_State* L);
32 static void check(lua_State* L);
35 void class_info::check(lua_State* L)
37 lua_pushlightuserdata(L, &classtable_meta_key);
38 lua_rawget(L, LUA_REGISTRYINDEX);
39 lua_getmetatable(L, 1);
40 if(!lua_rawequal(L, -1, -2)) {
41 lua_pushstring(L, "Bad class table");
42 lua_error(L);
44 lua_pop(L, 2);
47 int class_info::index(lua_State* L)
49 check(L);
51 class_base* ptr = ((class_info*)lua_touserdata(L, 1))->obj;
52 const char* method = lua_tostring(L, 2);
53 if(!method) {
54 lua_pushstring(L, "Indexing invalid element of class table");
55 lua_error(L);
57 if(!strcmp(method, "_static_methods")) {
58 lua_pushlightuserdata(L, ptr);
59 lua_pushcclosure(L, class_info::smethods, 1);
60 return 1;
61 } else if(!strcmp(method, "_class_methods")) {
62 lua_pushlightuserdata(L, ptr);
63 lua_pushcclosure(L, class_info::cmethods, 1);
64 return 1;
65 } else {
66 auto m = ptr->static_methods();
67 for(auto i : m) {
68 if(!strcmp(i.name, method)) {
69 //Hit.
70 std::string name = ptr->get_name() + "::" + method;
71 lua_pushvalue(L, lua_upvalueindex(1)); //State.
72 lua_pushlightuserdata(L, (void*)i.fn);
73 std::string fname = ptr->get_name() + "::" + i.name;
74 lua_pushstring(L, fname.c_str());
75 lua_pushcclosure(L, class_info::trampoline, 3);
76 return 1;
79 std::string err = std::string("Class '") + ptr->get_name() +
80 "' does not have static method '" + method + "'";
81 lua_pushstring(L, err.c_str());
82 lua_error(L);
85 int class_info::newindex(lua_State* L)
87 lua_pushstring(L, "Writing into class table not allowed");
88 lua_error(L);
91 int class_info::pairs(lua_State* _xL)
93 check(_xL);
95 lua::state* _L = (lua::state*)lua_touserdata(_xL, lua_upvalueindex(1));
96 state L(*_L, _xL);
97 class_base* obj = ((class_info*)L.touserdata(1))->obj;
99 L.pushvalue(lua_upvalueindex(1));
100 L.pushcclosure(class_info::pairs_next, 1); //Next
101 L.pushvalue(1); //State.
102 L.pushnil(); //Index.
103 return 3;
106 int class_info::pairs_next(lua_State* _xL)
108 check(_xL);
110 lua::state* _L = (lua::state*)lua_touserdata(_xL, lua_upvalueindex(1));
111 state L(*_L, _xL);
112 class_base* obj = ((class_info*)L.touserdata(1))->obj;
113 auto m = obj->static_methods();
114 std::string key = (L.type(2) == LUA_TSTRING) ? L.tostring(2) : "";
115 std::string lowbound = "\xFF"; //Sorts greater than anything that is valid UTF-8.
116 void* best_fn = NULL;
117 for(auto i : m)
118 if(lowbound > i.name && i.name > key) {
119 lowbound = i.name;
120 best_fn = (void*)i.fn;
122 if(best_fn) {
123 L.pushlstring(lowbound);
124 std::string name = obj->get_name() + "::" + lowbound;
125 L.pushvalue(lua_upvalueindex(1)); //State.
126 L.pushlightuserdata(best_fn);
127 L.pushlstring(name);
128 L.pushcclosure(class_info::trampoline, 3);
129 return 2;
130 } else {
131 L.pushnil();
132 return 1;
136 int class_info::smethods(lua_State* L)
138 class_base* obj = (class_base*)lua_touserdata(L, lua_upvalueindex(1));
139 auto m = obj->static_methods();
140 int rets = 0;
141 for(auto i : m) {
142 lua_pushstring(L, i.name);
143 rets++;
145 return rets;
148 int class_info::cmethods(lua_State* L)
150 class_base* obj = (class_base*)lua_touserdata(L, lua_upvalueindex(1));
151 auto m = obj->class_methods();
152 int rets = 0;
153 for(auto i : m) {
154 lua_pushstring(L, i.c_str());
155 rets++;
157 return rets;
160 typedef int (*fn_t)(state& L, parameters& P);
162 int class_info::trampoline(lua_State* L)
164 state* lstate = reinterpret_cast<state*>(lua_touserdata(L, lua_upvalueindex(1)));
165 void* _fn = lua_touserdata(L, lua_upvalueindex(2));
166 fn_t fn = (fn_t)_fn;
167 std::string name = lua_tostring(L, lua_upvalueindex(3));
168 state _L(*lstate, L);
169 try {
170 parameters P(_L, name);
171 return fn(_L, P);
172 } catch(std::exception& e) {
173 lua_pushfstring(L, "%s", e.what());
174 lua_error(L);
176 return 0;
179 int lua_trampoline_function(lua_State* L)
181 void* ptr = lua_touserdata(L, lua_upvalueindex(1));
182 state* lstate = reinterpret_cast<state*>(lua_touserdata(L, lua_upvalueindex(2)));
183 function* f = reinterpret_cast<function*>(ptr);
184 state _L(*lstate, L);
185 try {
186 return f->invoke(_L);
187 } catch(std::exception& e) {
188 lua_pushfstring(L, "%s", e.what());
189 lua_error(L);
191 return 0;
194 //Pushes given table to top of stack, creating if needed.
195 void recursive_lookup_table(state& L, const std::string& tab)
197 if(tab == "") {
198 #if LUA_VERSION_NUM == 501
199 L.pushvalue(LUA_GLOBALSINDEX);
200 #endif
201 #if LUA_VERSION_NUM == 502
202 L.rawgeti(LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
203 #endif
204 assert(L.type(-1) == LUA_TTABLE);
205 return;
207 std::string u = tab;
208 size_t split = u.find_last_of(".");
209 std::string u1;
210 std::string u2 = u;
211 if(split < u.length()) {
212 u1 = u.substr(0, split);
213 u2 = u.substr(split + 1);
215 recursive_lookup_table(L, u1);
216 L.getfield(-1, u2.c_str());
217 if(L.type(-1) != LUA_TTABLE) {
218 //Not a table, create a table.
219 L.pop(1);
220 L.newtable();
221 L.setfield(-2, u2.c_str());
222 L.getfield(-1, u2.c_str());
224 //Get rid of previous table.
225 L.insert(-2);
226 L.pop(1);
229 void register_function(state& L, const std::string& name, function* fun)
231 std::string u = name;
232 size_t split = u.find_last_of(".");
233 std::string u1;
234 std::string u2 = u;
235 if(split < u.length()) {
236 u1 = u.substr(0, split);
237 u2 = u.substr(split + 1);
239 recursive_lookup_table(L, u1);
240 if(!fun)
241 L.pushnil();
242 else {
243 void* ptr = reinterpret_cast<void*>(fun);
244 L.pushlightuserdata(ptr);
245 L.pushlightuserdata(&L);
246 L.pushcclosure(lua_trampoline_function, 2);
248 L.setfield(-2, u2.c_str());
249 L.pop(1);
252 void register_class(state& L, const std::string& name, class_base* fun)
254 fun->register_state(L);
257 typedef register_queue<state, function> regqueue_t;
258 typedef register_queue<state::callback_proxy, state::callback_list> regqueue2_t;
259 typedef register_queue<function_group, function> regqueue3_t;
260 typedef register_queue<class_group, class_base> regqueue4_t;
263 state::state() throw(std::bad_alloc)
264 : cbproxy(*this)
266 master = NULL;
267 lua_handle = NULL;
268 oom_handler = builtin_oom;
269 regqueue2_t::do_ready(cbproxy, true);
272 state::state(state& _master, lua_State* L)
273 : cbproxy(*this)
275 master = &_master;
276 lua_handle = L;
279 state::~state() throw()
281 if(master)
282 return;
283 for(auto i : function_groups)
284 i.first->drop_callback(i.second);
285 for(auto i : class_groups)
286 i.first->drop_callback(i.second);
287 regqueue2_t::do_ready(cbproxy, false);
288 if(lua_handle)
289 lua_close(lua_handle);
292 void state::builtin_oom()
294 std::cerr << "PANIC: FATAL: Out of memory" << std::endl;
295 exit(1);
298 void* state::builtin_alloc(void* user, void* old, size_t olds, size_t news)
300 if(news) {
301 void* m = realloc(old, news);
302 if(!m)
303 reinterpret_cast<state*>(user)->oom_handler();
304 return m;
305 } else
306 free(old);
307 return NULL;
311 function::function(function_group& _group, const std::string& func) throw(std::bad_alloc)
312 : group(_group)
314 regqueue3_t::do_register(group, fname = func, *this);
317 function::~function() throw()
319 regqueue3_t::do_unregister(group, fname);
322 class_base::class_base(class_group& _group, const std::string& _name)
323 : group(_group), name(_name)
325 registered = false;
328 class_base::~class_base() throw()
330 if(registered)
331 regqueue4_t::do_unregister(group, name);
334 void state::reset() throw(std::bad_alloc, std::runtime_error)
336 if(master)
337 return master->reset();
338 if(lua_handle) {
339 lua_State* tmp = lua_newstate(state::builtin_alloc, this);
340 if(!tmp)
341 throw std::runtime_error("Can't re-initialize Lua interpretter");
342 lua_close(lua_handle);
343 for(auto& i : callbacks)
344 i.second->clear();
345 lua_handle = tmp;
346 } else {
347 //Initialize new.
348 lua_handle = lua_newstate(state::builtin_alloc, this);
349 if(!lua_handle)
350 throw std::runtime_error("Can't initialize Lua interpretter");
352 for(auto i : function_groups)
353 i.first->request_callback([this](std::string name, function* func) -> void {
354 register_function(*this, name, func);
356 for(auto i : class_groups)
357 i.first->request_callback([this](std::string name, class_base* clazz) -> void {
358 register_class(*this, name, clazz);
362 void state::deinit() throw()
364 if(master)
365 return master->deinit();
366 if(lua_handle)
367 lua_close(lua_handle);
368 lua_handle = NULL;
371 void state::add_function_group(function_group& group)
373 function_groups.insert(std::make_pair(&group, group.add_callback([this](const std::string& name,
374 function* func) -> void {
375 this->function_callback(name, func);
376 }, [this](function_group* x) {
377 for(auto i = this->function_groups.begin(); i != this->function_groups.end();)
378 if(i->first == x)
379 i = this->function_groups.erase(i);
380 else
381 i++;
382 })));
385 void state::add_class_group(class_group& group)
387 class_groups.insert(std::make_pair(&group, group.add_callback([this](const std::string& name,
388 class_base* clazz) -> void {
389 this->class_callback(name, clazz);
390 }, [this](class_group* x) {
391 for(auto i = this->class_groups.begin(); i != this->class_groups.end();)
392 if(i->first == x)
393 i = this->class_groups.erase(i);
394 else
395 i++;
396 })));
399 void state::function_callback(const std::string& name, function* func)
401 if(master)
402 return master->function_callback(name, func);
403 if(lua_handle)
404 register_function(*this, name, func);
407 void state::class_callback(const std::string& name, class_base* clazz)
409 if(master)
410 return master->class_callback(name, clazz);
411 if(lua_handle)
412 register_class(*this, name, clazz);
415 bool state::do_once(void* key)
417 if(master)
418 return master->do_once(key);
419 pushlightuserdata(key);
420 rawget(LUA_REGISTRYINDEX);
421 if(type(-1) == LUA_TNIL) {
422 pop(1);
423 pushlightuserdata(key);
424 pushlightuserdata(key);
425 rawset(LUA_REGISTRYINDEX);
426 return true;
427 } else {
428 pop(1);
429 return false;
433 state::callback_list::callback_list(state& _L, const std::string& _name, const std::string& fncbname)
434 : L(_L), name(_name), fn_cbname(fncbname)
436 regqueue2_t::do_register(L.cbproxy, name, *this);
439 state::callback_list::~callback_list()
441 regqueue2_t::do_unregister(L.cbproxy, name);
442 if(!L.handle())
443 return;
444 for(auto& i : callbacks) {
445 L.pushlightuserdata(&i);
446 L.pushnil();
447 L.rawset(LUA_REGISTRYINDEX);
451 void state::callback_list::_register(state& _L)
453 callbacks.push_back(0);
454 _L.pushlightuserdata(&*callbacks.rbegin());
455 _L.pushvalue(-2);
456 _L.rawset(LUA_REGISTRYINDEX);
459 void state::callback_list::_unregister(state& _L)
461 for(auto i = callbacks.begin(); i != callbacks.end();) {
462 _L.pushlightuserdata(&*i);
463 _L.rawget(LUA_REGISTRYINDEX);
464 if(_L.rawequal(-1, -2)) {
465 char* key = &*i;
466 _L.pushlightuserdata(key);
467 _L.pushnil();
468 _L.rawset(LUA_REGISTRYINDEX);
469 i = callbacks.erase(i);
470 } else
471 i++;
472 _L.pop(1);
476 function_group::function_group()
478 next_handle = 0;
479 regqueue3_t::do_ready(*this, true);
482 function_group::~function_group()
484 for(auto i : functions)
485 for(auto j : callbacks)
486 j.second(i.first, NULL);
487 for(auto i : dcallbacks)
488 i.second(this);
489 regqueue3_t::do_ready(*this, false);
492 void function_group::request_callback(std::function<void(std::string, function*)> cb)
494 for(auto i : functions)
495 cb(i.first, i.second);
498 int function_group::add_callback(std::function<void(std::string, function*)> cb,
499 std::function<void(function_group*)> dcb)
501 int handle = next_handle++;
502 callbacks[handle] = cb;
503 dcallbacks[handle] = dcb;
504 for(auto i : functions)
505 cb(i.first, i.second);
506 return handle;
509 void function_group::drop_callback(int handle)
511 callbacks.erase(handle);
514 void function_group::do_register(const std::string& name, function& fun)
516 functions[name] = &fun;
517 for(auto i : callbacks)
518 i.second(name, &fun);
521 void function_group::do_unregister(const std::string& name)
523 functions.erase(name);
524 for(auto i : callbacks)
525 i.second(name, NULL);
528 class_group::class_group()
530 next_handle = 0;
531 regqueue4_t::do_ready(*this, true);
534 class_group::~class_group()
536 for(auto i : classes)
537 for(auto j : callbacks)
538 j.second(i.first, NULL);
539 for(auto i : dcallbacks)
540 i.second(this);
541 regqueue4_t::do_ready(*this, false);
544 void class_group::request_callback(std::function<void(std::string, class_base*)> cb)
546 for(auto i : classes)
547 cb(i.first, i.second);
550 int class_group::add_callback(std::function<void(std::string, class_base*)> cb,
551 std::function<void(class_group*)> dcb)
553 int handle = next_handle++;
554 callbacks[handle] = cb;
555 dcallbacks[handle] = dcb;
556 for(auto i : classes)
557 cb(i.first, i.second);
558 return handle;
561 void class_group::drop_callback(int handle)
563 callbacks.erase(handle);
566 void class_group::do_register(const std::string& name, class_base& fun)
568 classes[name] = &fun;
569 for(auto i : callbacks)
570 i.second(name, &fun);
573 void class_group::do_unregister(const std::string& name)
575 classes.erase(name);
576 for(auto i : callbacks)
577 i.second(name, NULL);
580 std::list<class_ops>& userdata_recogn_fns()
582 static std::list<class_ops> x;
583 return x;
586 std::string try_recognize_userdata(state& state, int index)
588 for(auto i : userdata_recogn_fns())
589 if(i.is(state, index))
590 return i.name();
591 //Hack: Lua builtin file objects and classobjs.
592 if(state.getmetatable(index)) {
593 state.pushstring("FILE*");
594 state.rawget(LUA_REGISTRYINDEX);
595 if(state.rawequal(-1, -2)) {
596 state.pop(2);
597 return "FILE*";
599 state.pop(1);
600 state.pushlightuserdata(&classtable_meta_key);
601 state.rawget(LUA_REGISTRYINDEX);
602 if(state.rawequal(-1, -2)) {
603 state.pop(2);
604 return "classobj";
606 state.pop(1);
608 return "unknown";
611 std::string try_print_userdata(state& L, int index)
613 for(auto i : userdata_recogn_fns())
614 if(i.is(L, index))
615 return i.print(L, index);
616 //Hack: classobjs.
617 if(L.getmetatable(index)) {
618 L.pushlightuserdata(&classtable_meta_key);
619 L.rawget(LUA_REGISTRYINDEX);
620 if(L.rawequal(-1, -2)) {
621 L.pop(2);
622 std::string cname = ((class_info*)L.touserdata(index))->obj->get_name();
623 return cname;
625 L.pop(1);
627 return "no data available";
630 int state::vararg_tag::pushargs(state& L)
632 int e = 0;
633 for(auto i : args) {
634 if(i == "")
635 L.pushnil();
636 else if(i == "true")
637 L.pushboolean(true);
638 else if(i == "false")
639 L.pushboolean(false);
640 else if(regex_match("[+-]?(|0|[1-9][0-9]*)(.[0-9]+)?([eE][+-]?(0|[1-9][0-9]*))?", i))
641 L.pushnumber(strtod(i.c_str(), NULL));
642 else if(i[0] == ':')
643 L.pushlstring(i.substr(1));
644 else
645 L.pushlstring(i);
646 e++;
648 return e;
651 class_base* class_base::lookup(state& L, const std::string& _name)
653 if(lookup_and_push(L, _name)) {
654 class_base* obj = ((class_info*)L.touserdata(-1))->obj;
655 L.pop(1);
656 return obj;
658 return NULL;
661 bool class_base::lookup_and_push(state& L, const std::string& _name)
663 L.pushlightuserdata(&classtable_key);
664 L.rawget(LUA_REGISTRYINDEX);
665 if(L.type(-1) == LUA_TNIL) {
666 //No classes.
667 L.pop(1);
668 return false;
670 //On top of stack there is class table.
671 L.pushlstring(_name);
672 L.rawget(-2);
673 if(L.type(-1) == LUA_TNIL) {
674 //Not found.
675 L.pop(2);
676 return false;
678 L.insert(-2);
679 L.pop(1);
680 return true;
683 std::set<std::string> class_base::all_classes(state& L)
685 L.pushlightuserdata(&classtable_key);
686 L.rawget(LUA_REGISTRYINDEX);
687 if(L.type(-1) == LUA_TNIL) {
688 //No classes.
689 L.pop(1);
690 return std::set<std::string>();
692 std::set<std::string> r;
693 L.pushnil();
694 while(L.next(-2)) {
695 L.pop(1); //Pop value.
696 if(L.type(-1) == LUA_TSTRING) r.insert(L.tostring(-1));
698 L.pop(1);
699 return r;
702 void class_base::register_static(state& L)
704 again:
705 L.pushlightuserdata(&classtable_key);
706 L.rawget(LUA_REGISTRYINDEX);
707 if(L.type(-1) == LUA_TNIL) {
708 L.pop(1);
709 L.pushlightuserdata(&classtable_key);
710 L.newtable();
711 L.rawset(LUA_REGISTRYINDEX);
712 goto again;
714 //On top of stack there is class table.
715 L.pushlstring(name);
716 L.rawget(-2);
717 if(L.type(-1) != LUA_TNIL) {
718 //Already registered.
719 L.pop(2);
720 return;
722 L.pop(1);
723 L.pushlstring(name);
724 //Now construct the object.
725 class_info* ci = (class_info*)L.newuserdata(sizeof(class_info));
726 ci->obj = this;
727 again2:
728 L.pushlightuserdata(&classtable_meta_key);
729 L.rawget(LUA_REGISTRYINDEX);
730 if(L.type(-1) == LUA_TNIL) {
731 L.pop(1);
732 L.pushlightuserdata(&classtable_meta_key);
733 L.newtable();
734 L.pushstring("__index");
735 L.pushlightuserdata(&L);
736 L.pushcclosure(class_info::index, 1);
737 L.rawset(-3);
738 L.pushstring("__newindex");
739 L.pushcfunction(class_info::newindex);
740 L.rawset(-3);
741 L.pushstring("__pairs");
742 L.pushlightuserdata(&L);
743 L.pushcclosure(class_info::pairs, 1);
744 L.rawset(-3);
745 L.rawset(LUA_REGISTRYINDEX);
746 goto again2;
748 L.setmetatable(-2);
749 L.rawset(-3);
750 L.pop(1);
753 void class_base::delayed_register()
755 regqueue4_t::do_register(group, name, *this);
758 functions::functions(function_group& grp, const std::string& basetable, std::initializer_list<entry> fnlist)
760 std::string base = (basetable == "") ? "" : (basetable + ".");
761 for(auto i : fnlist)
762 funcs.insert(new fn(grp, base + i.name, i.func));
765 functions::~functions()
767 for(auto i : funcs)
768 delete i;
771 functions::fn::fn(function_group& grp, const std::string& name, std::function<int(state& L, parameters& P)> _func)
772 : function(grp, name)
774 func = _func;
777 functions::fn::~fn() throw()
781 int functions::fn::invoke(state& L)
783 lua::parameters P(L, fname);
784 return func(L, P);